递归下降语法分析

Grammar for recursive descent parsing

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

不一定。

要构建递归下降解析器(无回溯),您需要消除或解决所有预测冲突。所以一个决定性的测试是看语法是否是 LL(1);根据定义,LL(1) 文法没有预测冲突。 Left-factoring 和 left-recursion 消除对于此任务是必要的,但它们可能还不够,因为预测冲突可能隐藏在两个相互竞争的 non-terminals:

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

上面的问题(或者至少是一个问题)是当解析器期望 item 并看到 ID 时,它无法知道 [=15] 中的哪一个=] 和 expr2 来尝试。 (这是一个预测冲突:两个 non-terminals 都可以被预测到。)在这种特殊情况下,很容易看出如何消除该冲突,但它并不是真正的 left-factoring,因为它首先结合两个 non-terminals。 (在完整的语法中,这可能会被摘录,将两者结合起来 non-terminals 可能要困难得多。)

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

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

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

翻译成这样的东西:

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

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