use neotoma::grammar::{GrammarParser, GrammarResult}; use neotoma::prelude::*; use std::io::Cursor; #[test] fn arithmetic_grammar() { // Define a comprehensive arithmetic grammar with proper operator precedence and recursion let grammar_text = r#" @start complete_expression complete_expression = (expression eof) expression = additive_expr additive_expr = (| addition subtraction multiplicative_expr) addition = (multiplicative_expr (? whitespace) "+" (? whitespace) additive_expr) subtraction = (multiplicative_expr (? whitespace) "-" (? whitespace) additive_expr) multiplicative_expr = (| multiplication division primary_expr) multiplication = (primary_expr (? whitespace) "*" (? whitespace) multiplicative_expr) division = (primary_expr (? whitespace) "/" (? whitespace) multiplicative_expr) primary_expr = (| number variable parenthesized_expr) number = digits variable = alpha parenthesized_expr = ("(" (? whitespace) expression (? whitespace) ")") "#; // Parse the grammar definition let grammar_parser = GrammarParser::new(); let mut grammar_input = Cursor::new(grammar_text.as_bytes()); let mut grammar_source = Source::new(&mut grammar_input); let grammar = parse(grammar_parser, &mut grammar_source) .expect("Arithmetic grammar should parse successfully"); // Verify grammar properties assert_eq!( grammar.start_rule(), Some("complete_expression"), "Should have correct start rule" ); assert!( grammar.rule_exists("complete_expression"), "Should have complete_expression rule" ); assert!( grammar.rule_exists("expression"), "Should have expression rule" ); assert!( grammar.rule_exists("additive_expr"), "Should have additive_expr rule" ); assert!( grammar.rule_exists("multiplicative_expr"), "Should have multiplicative_expr rule" ); assert!( grammar.rule_exists("primary_expr"), "Should have primary_expr rule" ); assert!(grammar.rule_exists("addition"), "Should have addition rule"); assert!( grammar.rule_exists("subtraction"), "Should have subtraction rule" ); assert!( grammar.rule_exists("multiplication"), "Should have multiplication rule" ); assert!(grammar.rule_exists("division"), "Should have division rule"); // Test cases: (input, should_succeed, description) let test_cases = vec![ // Basic cases ("42", true, "Simple number"), ("x", true, "Single variable"), // Binary operations ("1+2", true, "Simple addition"), ("3-1", true, "Simple subtraction"), ("2*3", true, "Simple multiplication"), ("8/2", true, "Simple division"), // Operator precedence (multiplication/division before addition/subtraction) ( "1+2*3", true, "Addition with multiplication (precedence test)", ), ("2*3+1", true, "Multiplication with addition"), ("10-2*3", true, "Subtraction with multiplication"), ("8/2+1", true, "Division with addition"), // Parentheses overriding precedence ("(1+2)*3", true, "Parentheses override precedence"), ( "2*(3+4)", true, "Multiplication of parenthesized expression", ), ("(10-2)/2", true, "Division of parenthesized expression"), // Variables in expressions ("x+y", true, "Variable addition"), ("a*b+c", true, "Variables with precedence"), ("(x+y)*z", true, "Variables with parentheses"), // Whitespace handling ("1 + 2", true, "Addition with spaces"), ("3 * 4", true, "Multiplication with spaces"), ("( 1 + 2 ) * 3", true, "Parentheses with spaces"), // Complex expressions testing recursion ("1+2+3+4", true, "Chain addition (left recursion)"), ("2*3*4", true, "Chain multiplication"), ("1+2*3+4", true, "Mixed operations with precedence"), ("(1+2)*(3+4)", true, "Two parenthesized expressions"), ("((1+2)*3)+4", true, "Nested operations"), // Deep recursion through parentheses ("((((1))))", true, "Deep parentheses nesting"), ("(((a+b)))", true, "Deep nesting with variables"), // Very complex expressions ("a*b+c*d", true, "Multiple terms"), ("(a+b)*(c-d)", true, "Complex parenthesized operations"), ("x+y*z-a/b", true, "All four operations"), ("(x+y)*(z-w)+(a+b)/(c-d)", true, "Very complex expression"), // Extreme recursion test ("1+2+3+4+5+6+7+8+9+10", true, "Long addition chain"), // Error cases ("", false, "Empty input"), ("abc123", false, "Invalid mixed alphanumeric"), ("1++2", false, "Double operator"), ("+1", false, "Leading operator"), ("1+", false, "Trailing operator"), ("(1", false, "Unclosed parenthesis"), ("1)", false, "Unmatched closing parenthesis"), ("((1)", false, "Mismatched parentheses"), ("1 2", false, "Missing operator between numbers"), ("x y", false, "Missing operator between variables"), ]; println!( "Testing arithmetic grammar with {} test cases...", test_cases.len() ); for (input, should_succeed, description) in test_cases { let mut expr_input = Cursor::new(input.as_bytes()); let mut expr_source = Source::new(&mut expr_input); let result = parse(grammar.clone(), &mut expr_source); if should_succeed { let parse_result = result.unwrap_or_else(|_| panic!("{description} should parse: '{input}'")); // Log successful parse for verification let result_summary = match &parse_result { GrammarResult::Literal(bytes) => { format!("Literal({})", String::from_utf8_lossy(bytes)) } GrammarResult::Unicode(s) => format!("Unicode({s})"), GrammarResult::Bytes(bytes) => format!("Bytes({})", String::from_utf8_lossy(bytes)), GrammarResult::Sequence(seq) => format!("Sequence({} items)", seq.len()), GrammarResult::Alternative(alt) => format!("Alternative({alt:?})"), GrammarResult::Repetition(rep) => format!("Repetition({} items)", rep.len()), GrammarResult::Optional(opt) => format!("Optional({})", opt.is_some()), GrammarResult::Empty => "Empty".to_string(), }; println!(" ✓ {description}: '{input}' -> {result_summary}"); // Additional validation for specific cases match input { "42" => { if let GrammarResult::Unicode(num) = parse_result { assert_eq!(num, "42", "Number should parse correctly"); } } "x" => { if let GrammarResult::Unicode(var) = parse_result { assert_eq!(var, "x", "Variable should parse correctly"); } } _ => {} // Other cases just need to succeed } } else { assert!(result.is_err(), "{description} should fail: '{input}'"); println!(" ✓ {description}: '{input}' correctly failed"); } } println!("Arithmetic grammar integration test completed successfully!"); // Final comprehensive test: parse a very complex expression that exercises // multiple levels of recursion and all grammar rules let complex_expression = "((a+b)*c+(d-e)*f)/(g+h*i)"; let mut complex_input = Cursor::new(complex_expression.as_bytes()); let mut complex_source = Source::new(&mut complex_input); let complex_result = parse(grammar, &mut complex_source); assert!( complex_result.is_ok(), "Very complex expression should parse successfully" ); println!(" ✓ Final complex test: '{complex_expression}' parsed successfully"); }