arithmetic_grammar.rs
raw
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");
}