diff --git a/nimbleparse/src/main.rs b/nimbleparse/src/main.rs index 2c672f854..2d57f1231 100644 --- a/nimbleparse/src/main.rs +++ b/nimbleparse/src/main.rs @@ -229,3 +229,71 @@ fn main() { process::exit(1); } } + +#[cfg(test)] +mod test { + use cfgrammar::{ + yacc::{YaccGrammar, YaccKind, YaccOriginalActionKind}, + RIdx, Span, + }; + use lrlex::{DefaultLexeme, LRNonStreamingLexerDef, LexerDef}; + use lrpar::{parser::AStackType, RTParserBuilder, lex_api::NonStreamingLexer}; + use num_traits::ToPrimitive; + use std::sync::atomic::{AtomicU8, Ordering}; + + static COUNT: AtomicU8 = AtomicU8::new(0); + + type TestAction<'a> = &'a dyn Fn( + RIdx, + &dyn NonStreamingLexer, + Span, + std::vec::Drain>, + (), + ); + + fn test_action( + _ridx: RIdx, + _lexer: &dyn NonStreamingLexer, + _span: Span, + _astack: std::vec::Drain>, + _param: (), + ) { + let x = COUNT.load(Ordering::SeqCst); + if x < std::u8::MAX { + COUNT.store(x + 1, Ordering::SeqCst); + } else { + panic!("actions executed too many times") + } + } + + #[test] + fn nefarious_recursive_rule() { + let mut lexerdef = LRNonStreamingLexerDef::::from_str( + r" + %% + a 'a' + [\t\n ] ;", + ) + .unwrap(); + let grm = YaccGrammar::new( + YaccKind::Original(YaccOriginalActionKind::NoAction), + r"%% + Start: Bar; + Foo: 'a' | ; + Bar: Foo | Foo Bar;", + ) + .unwrap(); + let (_, state_tbl) = lrtable::from_yacc(&grm, lrtable::Minimiser::Pager).unwrap(); + let rule_ids = grm + .tokens_map() + .iter() + .map(|(&n, &i)| (n, usize::from(i).to_u32().unwrap())) + .collect(); + lexerdef.set_rule_ids(&rule_ids); + let lexer = lexerdef.lexer("a"); + let pb = RTParserBuilder::new(&grm, &state_tbl); + let actions: &[TestAction] = &[&test_action, &test_action, &test_action]; + pb.parse_actions(&lexer, actions, ()); + pb.parse_generictree(&lexer); + } +}