【问题标题】:PEG: What is wrong wrong with my grammar for if statement?PEG:我的 if 语句语法有什么问题?
【发布时间】:2023-03-31 08:15:01
【问题描述】:

我正在使用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 库不熟悉,但是如果它有向规则添加谓词的方法,您可以创建一组关键字,然后将谓词添加到需要的 ident 规则中标识符不在关键字集中。您还需要一个关键字规则,以确保将 ifFunction thenArg elseArg 之类的内容解析为函数调用,而不是 if Function then Arg else Arg

标签: parsing rust grammar peg


【解决方案1】:

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

特别是,当您编写 apply = atom atom / atom 时,如果可以连续解析两个原子,则它永远不会尝试仅解析一个原子,即使这意味着其余原子不解析'以后就没有意义了。

将此与 thenelse 在您的语法中是非常好的标识符这一事实相结合,您就会得到您所看到的问题。

【讨论】:

    猜你喜欢
    • 2013-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多