【问题标题】:Grammar for recursive descent parsing递归下降解析语法
【发布时间】:2020-11-05 18:00:45
【问题描述】:

有没有简单的方法来判断一个简单的语法是否适合递归下降?消除左递归和左分解语法是否足以实现这一目标?

【问题讨论】:

    标签: parsing grammar context-free-grammar


    【解决方案1】:

    不一定。

    要构建递归下降解析器(无需回溯),您需要消除或解决所有预测冲突。因此,一项确定性测试是查看语法是否为 LL(1);根据定义,LL(1) 语法没有预测冲突。此任务需要左分解和左递归消除,但它们可能还不够,因为预测冲突可能隐藏在两个相互竞争的非终结符后面:

     list  ::= item list'
     list' ::= ε 
             | ';' item list'
     item  ::= expr1
             | expr2
     expr1 ::= ID '+' ID
     expr2 ::= ID '(' list ')
    

    上述问题(或至少一个问题)是,当解析器期望item 并看到ID 时,它不知道要尝试expr1expr2 中的哪一个. (这是一个预测冲突:两个非终结符都可以预测。)在这种特殊情况下,很容易看出如何消除这种冲突,但它并不是真正的左分解,因为它从组合两个非终结符开始。 (在完整的语法中,这可能会被摘录,组合两个非终结符可能要困难得多。)

    在一般情况下,没有一种算法可以将任意文法转换为 LL(1) 文法,甚至无法判断该文法识别的语言是否也具有 LL(1) 文法。 (但是,很容易判断语法本身是否为 LL(1)。)因此总会涉及到一些艺术和/或实验。

    我认为值得补充的是,您实际上并不需要在实际递归下降解析器中消除左递归,因为您通常可以将其转换为 while 循环而不是递归。例如,抛开上面两个expr 类型的问题不谈,带有重复运算符的扩展 BNF 中的原始语法可能类似于

    list ::= item (';' item)*
    

    翻译成类似这样的东西:

    def parse_list():
        parse_item()
        while peek(';'):
            match(';')
            parse_item()
    

    (省略错误检查和 AST 构建。)

    【讨论】:

    • 在这种特殊情况下,很容易看出如何消除这种冲突,但它并不是真正的左分解,因为它从组合两个非终结符开始。很高兴知道那个重构被称为 ("item : ID '+' ID | ID '(' list ')'" => "item : ID ('+' ID | '(' list ')')") .我还没有找到一个名字,所以我称之为“分组”,因为它结合了 alt 和一个 EBNF 组。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-01
    • 2015-08-07
    • 2012-05-21
    • 1970-01-01
    • 2016-07-06
    相关资源
    最近更新 更多