递归下降语法分析
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 构建。)
有没有一种简单的方法可以判断一个简单的语法是否适合递归下降?消除左递归和左分解语法是否足以实现此目的?
不一定。
要构建递归下降解析器(无回溯),您需要消除或解决所有预测冲突。所以一个决定性的测试是看语法是否是 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 构建。)