arithmetic_hardcoded.rs raw

use std::io::Cursor;

use neotoma::{
    cache::ParsingCache,
    literal::Literal,
    optional::Optional,
    parser::{Parser, Source, parse},
    recursive::Recursive,
    result::{Error, ParseResult},
    utf8class::Utf8Class,
};

// Arithmetic expression AST
#[derive(Debug, Clone, PartialEq)]
enum ArithmeticExpr {
    Number(String),
    Variable(String),
    Real(String, String), // integer part, fractional part
    BinaryOp {
        left: Box<ArithmeticExpr>,
        op: String,
        right: Box<ArithmeticExpr>,
    },
    Parenthesized(Box<ArithmeticExpr>),
}

// Number parser
#[derive(Clone)]
struct NumberParser;

impl<Ctx> Parser<Ctx> for NumberParser {
    type Output = ArithmeticExpr;

    fn read<S>(
        &self,
        source: &mut Source<S>,
        cache: &mut impl ParsingCache,
        _context: &mut Ctx,
    ) -> ParseResult<Self::Output>
    where
        S: neotoma::parser::Parsable,
    {
        let integer = Utf8Class::with_min("0123456789", 1);
        if let Ok(digits) = integer.parse(source, cache, _context) {
            return Ok(ArithmeticExpr::Number(digits));
        }
        Err(Error::NoMatch)
    }
}

// Variable parser
#[derive(Clone)]
struct VariableParser;

impl<Ctx> Parser<Ctx> for VariableParser {
    type Output = ArithmeticExpr;

    fn read<S>(
        &self,
        source: &mut Source<S>,
        cache: &mut impl ParsingCache,
        _context: &mut Ctx,
    ) -> ParseResult<Self::Output>
    where
        S: neotoma::parser::Parsable,
    {
        let alpha = Utf8Class::from_predicate_min(|c| c.is_ascii_alphabetic(), 1);
        if let Ok(var) = alpha.parse(source, cache, _context) {
            return Ok(ArithmeticExpr::Variable(var));
        }
        Err(Error::NoMatch)
    }
}

// Real number parser (integer.integer)
#[derive(Clone)]
struct RealParser;

impl<Ctx> Parser<Ctx> for RealParser {
    type Output = ArithmeticExpr;

    fn read<S>(
        &self,
        source: &mut Source<S>,
        cache: &mut impl ParsingCache,
        _context: &mut Ctx,
    ) -> ParseResult<Self::Output>
    where
        S: neotoma::parser::Parsable,
    {
        let integer = Utf8Class::with_min("0123456789", 1);
        let point = Literal::from_bytes_const(b".");

        if let Ok(int_part) = integer.parse(source, cache, _context) {
            if point.parse(source, cache, _context).is_ok() {
                if let Ok(frac_part) = integer.parse(source, cache, _context) {
                    return Ok(ArithmeticExpr::Real(int_part, frac_part));
                }
            }
        }
        Err(Error::NoMatch)
    }
}

// Factor parser - handles numbers, variables, reals, and parenthesized expressions
struct FactorParser {
    expr_parser: Recursive<ArithmeticExpression>,
}

impl FactorParser {
    fn new() -> Self {
        Self {
            expr_parser: Recursive::new(),
        }
    }
}

impl<Ctx> Parser<Ctx> for FactorParser {
    type Output = ArithmeticExpr;

    fn read<S>(
        &self,
        source: &mut Source<S>,
        cache: &mut impl ParsingCache,
        _context: &mut Ctx,
    ) -> ParseResult<Self::Output>
    where
        S: neotoma::parser::Parsable,
    {
        let whitespace = Optional::new(Utf8Class::whitespace());

        let oparen = Literal::from_bytes_const(b"(");
        if oparen.parse(source, cache, _context).is_ok() {
            let _ = whitespace.parse(source, cache, _context);
            if let Ok(inner_expr) = self.expr_parser.parse(source, cache, _context) {
                let _ = whitespace.parse(source, cache, _context);
                let cparen = Literal::from_bytes_const(b")");
                if cparen.parse(source, cache, _context).is_ok() {
                    return Ok(ArithmeticExpr::Parenthesized(Box::new(inner_expr)));
                }
            }
        }

        // Try real number first (more specific than integer)
        let real_parser = RealParser;
        if let Ok(real_expr) = real_parser.parse(source, cache, _context) {
            return Ok(real_expr);
        }

        // Try variable
        let var_parser = VariableParser;
        if let Ok(var_expr) = var_parser.parse(source, cache, _context) {
            return Ok(var_expr);
        }

        // Try integer
        let num_parser = NumberParser;
        if let Ok(num_expr) = num_parser.parse(source, cache, _context) {
            return Ok(num_expr);
        }

        Err(Error::NoMatch)
    }
}

// Term parser - handles multiplication and division
struct TermParser {
    factor_parser: FactorParser,
}

impl TermParser {
    fn new() -> Self {
        Self {
            factor_parser: FactorParser::new(),
        }
    }
}

impl<Ctx> Parser<Ctx> for TermParser {
    type Output = ArithmeticExpr;

    fn read<S>(
        &self,
        source: &mut Source<S>,
        cache: &mut impl ParsingCache,
        _context: &mut Ctx,
    ) -> ParseResult<Self::Output>
    where
        S: neotoma::parser::Parsable,
    {
        let whitespace = Optional::new(Utf8Class::whitespace());
        let multiply = Literal::from_bytes_const(b"*");
        let divide = Literal::from_bytes_const(b"/");

        // Parse first factor
        if let Ok(mut left) = self.factor_parser.parse(source, cache, _context) {
            // Try to parse operator and second factor
            let _ = whitespace.parse(source, cache, _context);

            // Check for multiply
            if multiply.parse(source, cache, _context).is_ok() {
                let _ = whitespace.parse(source, cache, _context);
                if let Ok(right) = self.factor_parser.parse(source, cache, _context) {
                    left = ArithmeticExpr::BinaryOp {
                        left: Box::new(left),
                        op: "*".to_string(),
                        right: Box::new(right),
                    };
                }
            }
            // Check for divide (if multiply didn't match)
            else if divide.parse(source, cache, _context).is_ok() {
                let _ = whitespace.parse(source, cache, _context);
                if let Ok(right) = self.factor_parser.parse(source, cache, _context) {
                    left = ArithmeticExpr::BinaryOp {
                        left: Box::new(left),
                        op: "/".to_string(),
                        right: Box::new(right),
                    };
                }
            }

            return Ok(left);
        }

        Err(Error::NoMatch)
    }
}

// Main expression parser - handles addition and subtraction
struct ArithmeticExpression {
    term_parser: TermParser,
}

impl ArithmeticExpression {
    fn new() -> Self {
        ArithmeticExpression {
            term_parser: TermParser::new(),
        }
    }
}

impl Default for ArithmeticExpression {
    fn default() -> Self {
        Self::new()
    }
}

impl<Ctx> Parser<Ctx> for ArithmeticExpression {
    type Output = ArithmeticExpr;

    fn read<S>(
        &self,
        source: &mut Source<S>,
        cache: &mut impl ParsingCache,
        _context: &mut Ctx,
    ) -> ParseResult<Self::Output>
    where
        S: neotoma::parser::Parsable,
    {
        let whitespace = Optional::new(Utf8Class::whitespace());
        let plus = Literal::from_bytes_const(b"+");
        let minus = Literal::from_bytes_const(b"-");

        // Parse first term
        if let Ok(mut left) = self.term_parser.parse(source, cache, _context) {
            // Try to parse operator and second term
            let _ = whitespace.parse(source, cache, _context);

            // Check for plus
            if plus.parse(source, cache, _context).is_ok() {
                let _ = whitespace.parse(source, cache, _context);
                if let Ok(right) = self.term_parser.parse(source, cache, _context) {
                    left = ArithmeticExpr::BinaryOp {
                        left: Box::new(left),
                        op: "+".to_string(),
                        right: Box::new(right),
                    };
                }
            }
            // Check for minus (if plus didn't match)
            else if minus.parse(source, cache, _context).is_ok() {
                let _ = whitespace.parse(source, cache, _context);
                if let Ok(right) = self.term_parser.parse(source, cache, _context) {
                    left = ArithmeticExpr::BinaryOp {
                        left: Box::new(left),
                        op: "-".to_string(),
                        right: Box::new(right),
                    };
                }
            }

            return Ok(left);
        }

        Err(Error::NoMatch)
    }
}

#[test]
fn hardcoded_arithmetic_parser() {
    let expression = ArithmeticExpression::new();

    let cursor = Cursor::new(br#"1 + 2 * 3"#);
    let mut source = Source::new(cursor);

    let result = parse(expression, &mut source);

    // Just verify it parses without error for now
    assert!(result.is_ok());

    // We can also check the structure
    if let Ok(expr) = result {
        match expr {
            ArithmeticExpr::BinaryOp { left, op, right } => {
                assert_eq!(op, "+");
                // Left should be "1"
                if let ArithmeticExpr::Number(n) = *left {
                    assert_eq!(n, "1");
                }
                // Right should be "2 * 3"
                if let ArithmeticExpr::BinaryOp {
                    left: l2,
                    op: op2,
                    right: r2,
                } = *right
                {
                    assert_eq!(op2, "*");
                    if let (ArithmeticExpr::Number(n2), ArithmeticExpr::Number(n3)) = (*l2, *r2) {
                        assert_eq!(n2, "2");
                        assert_eq!(n3, "3");
                    }
                }
            }
            _ => panic!("Expected binary operation"),
        }
    }
}

#[test]
fn arithmetic_with_parentheses() {
    let expression = ArithmeticExpression::new();
    let cursor = Cursor::new(br#"(1 + 2) * 3"#);
    let mut source = Source::new(cursor);

    let result = parse(expression, &mut source);
    assert!(result.is_ok());
}

#[test]
fn arithmetic_with_variables() {
    let expression = ArithmeticExpression::new();
    let cursor = Cursor::new(br#"x + y * z"#);
    let mut source = Source::new(cursor);

    let result = parse(expression, &mut source);
    assert!(result.is_ok());
}

#[test]
fn complex_expression() {
    let expression = ArithmeticExpression::new();
    let cursor = Cursor::new(br#"1 + 2 + 3 * 3"#);
    let mut source = Source::new(cursor);

    let result = parse(expression, &mut source);
    assert!(
        result.is_ok(),
        "Complex expression should parse successfully"
    );
}

#[test]
fn two_levels_of_nesting() {
    let expression = ArithmeticExpression::new();
    let cursor = Cursor::new(br#"((1 + 2))"#);
    let mut source = Source::new(cursor);

    let result = parse(expression, &mut source);
    println!("Two levels result: {result:?}");
    assert!(
        result.is_ok(),
        "Two levels of nesting should parse successfully"
    );
}

#[test]
fn three_levels_of_nesting() {
    let expression = ArithmeticExpression::new();
    let cursor = Cursor::new(br#"(((1 + 2)))"#);
    let mut source = Source::new(cursor);

    let result = parse(expression, &mut source);
    assert!(
        result.is_ok(),
        "Three levels of nesting should parse successfully"
    );
}

#[test]
fn five_levels_of_nesting() {
    let expression = ArithmeticExpression::new();
    let cursor = Cursor::new(br#"((((1 + 2))))"#);
    let mut source = Source::new(cursor);

    let result = parse(expression, &mut source);
    assert!(
        result.is_ok(),
        "Five levels of nesting should parse successfully"
    );

    // Verify the structure has the right nesting
    if let Ok(expr) = result {
        let mut current = &expr;
        let mut depth = 0;

        // Count how deep the parentheses nesting goes
        while let ArithmeticExpr::Parenthesized(inner) = current {
            depth += 1;
            current = inner;
        }

        assert_eq!(depth, 4, "Should have 4 levels of parentheses nesting");
    }
}

#[test]
fn ten_levels_of_nesting() {
    let expression = ArithmeticExpression::new();
    let cursor = Cursor::new(br#"(((((((((1 + 2)))))))))"#);
    let mut source = Source::new(cursor);

    let result = parse(expression, &mut source);
    assert!(
        result.is_ok(),
        "Ten levels of nesting should parse successfully"
    );

    // Verify the structure has the right nesting
    if let Ok(expr) = result {
        let mut current = &expr;
        let mut depth = 0;

        // Count how deep the parentheses nesting goes
        while let ArithmeticExpr::Parenthesized(inner) = current {
            depth += 1;
            current = inner;
        }

        assert_eq!(depth, 9, "Should have 9 levels of parentheses nesting");
    }
}