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");
}