kevinmehall/rust-peg

Mutual recursion with `#[cache_left_rec]` fails

kevinmehall opened this issue · 0 comments

Discussed in #358

Originally posted by stqcky July 30, 2023
Hi, I'm having some issues using the #[cache_left_rec] attribute.

Here's my code:

#[derive(Clone, PartialEq, Debug)]
pub enum TestExpression {
    FunctionCall {
        callee: Box<TestExpression>,
        arguments: Vec<TestExpression>
    },
    Identifier(String)
}

peg::parser! {
    grammar test() for str {
        pub rule identifier() -> TestExpression = i:$(['a'..='z']+) {
            TestExpression::Identifier(i.to_string())
        }

        #[cache_left_rec]
        pub rule expression() -> TestExpression = exp:(function_call() / identifier()) {
            exp
        }
         
        rule lparen() = "("
        rule rparen() = ")"

        #[cache_left_rec]
        pub rule function_call() -> TestExpression = callee:expression() lparen() arg:(expression() ** ",") rparen() {
            TestExpression::FunctionCall { callee: Box::new(callee), arguments: arg }
        }
    }
}


pub fn parse() {
    let input = "abc()";
    let a = test::expression(&input);
    println!("{:?}", a);
}

When I try to match the string abc() with the rule function_call it works fine, returning Ok(FunctionCall { callee: Identifier("abc"), arguments: [] }).
But when I try to match the same string with the expression rule it doesn't work.
Here's the trace:

[PEG_TRACE] Attempting to match rule `expression` at 1:1
[PEG_TRACE] Attempting to match rule `function_call` at 1:1
[PEG_TRACE] Cached fail of rule expression at 1:1
[PEG_TRACE] Failed to match rule `function_call` at 1:1
[PEG_TRACE] Attempting to match rule `identifier` at 1:1
[PEG_TRACE] Matched rule `identifier` at 1:1 to 1:4
[PEG_TRACE] Matched rule `expression` at 1:1 to 1:4
[PEG_TRACE] Attempting to match rule `expression` at 1:1
[PEG_TRACE] Cached fail of rule function_call at 1:1
[PEG_TRACE] Attempting to match rule `identifier` at 1:1
[PEG_TRACE] Matched rule `identifier` at 1:1 to 1:4
[PEG_TRACE] Matched rule `expression` at 1:1 to 1:4
[PEG_TRACE] Attempting to match rule `expression` at 1:1
[PEG_TRACE] Attempting to match rule `function_call` at 1:1
[PEG_TRACE] Cached fail of rule expression at 1:1
[PEG_TRACE] Failed to match rule `function_call` at 1:1
[PEG_TRACE] Attempting to match rule `identifier` at 1:1
[PEG_TRACE] Matched rule `identifier` at 1:1 to 1:4
[PEG_TRACE] Matched rule `expression` at 1:1 to 1:4
[PEG_TRACE] Attempting to match rule `expression` at 1:1
[PEG_TRACE] Cached fail of rule function_call at 1:1
[PEG_TRACE] Attempting to match rule `identifier` at 1:1
[PEG_TRACE] Matched rule `identifier` at 1:1 to 1:4
[PEG_TRACE] Matched rule `expression` at 1:1 to 1:4
Err(ParseError { location: LineCol { line: 1, column: 4, offset: 3 }, expected: ExpectedSet { expected: {"['a' ..= 'z']", "EOF"} } })

It looks as if it just discards the possibility of the expression being a function_call at the very beginning and just matches abc as an identifier, leaving () which then causes the error.
I would really appreciate some help, as I have banged my head against this for like 2 days.