PEG:我的if语句语法有什么错?

问题描述

我正在使用rust-peg实现类似于OCaml的语言,并且我的解析器有一个错误。 我定义了if语句语法,但是它不起作用。

我猜测测试用例的输入被解析为Apply(Apply(Apply(Apply(f,then),2) else),4)。我的意思是"then"被解析为Ident,而不是关键字。

我不知道要修复此apply-expression语法。你有什么主意吗?

#[derive(Clone,PartialEq,Eq,Debug)]
pub enum Expression {
    Number(i64),If {
        cond: Box<Expression>,conseq: Box<Expression>,alt: Box<Expression>,},Ident(String),Apply(Box<Expression>,Box<Expression>),}

use peg::parser;
use toplevel::expression;
use Expression::*;

parser! {
pub grammar toplevel() for str {

    rule _() = [' ' | '\n']*

    pub rule expression() -> Expression
        = expr()

    rule expr() -> Expression
        = if_expr()
        / apply_expr()

    rule if_expr() -> Expression
        = "if" _ cond:expr() _ "then" _ conseq:expr() _ "else" _ alt:expr() {
            Expression::If {
                cond: Box::new(cond),conseq: Box::new(conseq),alt: Box::new(alt)
            }
        }

    rule apply_expr() -> Expression
        = e1:atom() _ e2:atom() { Apply(Box::new(e1),Box::new(e2)) }
        / atom()

    rule atom() -> Expression
        = number()
        / id:ident() { Ident(id) }

    rule number() -> Expression
        = n:$(['0'..='9']+) { Expression::Number(n.parse().unwrap()) }

    rule ident() -> String
        = id:$(['a'..='z' | 'A'..='Z']['a'..='z' | 'A'..='Z' | '0'..='9']*) { id.to_string() }
}}

fn main() {
    assert_eq!(expression("1"),Ok(Number(1)));
    assert_eq!(
        expression("myFunc 10"),Ok(Apply(
            Box::new(Ident("myFunc".to_string())),Box::new(Number(10))
        ))
    );

    // Failed
    assert_eq!(
        expression("if f then 2 else 3"),Ok(If {
            cond: Box::new(Ident("f".to_string())),conseq: Box::new(Number(2)),alt: Box::new(Number(3))
        })
    );
}
thread 'main' panicked at 'assertion Failed: `(left == right)`
  left: `Err(ParseError { location: LineCol { line: 1,column: 11,offset: 10 },expected: ExpectedSet { expected: {"\"then\"","\' \' | \'\\n\'"} } })`,right: `Ok(If { cond: Ident("f"),conseq: Number(2),alt: Number(3) })`',src/main.rs:64:5

解决方法

PEG使用有序选择。这意味着当您为某个规则R = A / B编写R时,如果位置A成功解析,它将从不尝试B,甚至如果选择A会在以后导致问题。这是无上下文语法的核心区别,并且经常被忽略。

特别是,当您编写apply = atom atom / atom时,如果有可能连续解析两个原子,则绝不会尝试仅解析一个原子,即使这意味着剩下的原子以后没有任何意义。

结合使用thenelse是语法中的完美标识符这一事实,就可以看到问题。