递归下降语法分析

问题描述

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

解决方法

不一定。

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

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

上述问题(或至少一个问题)是,当解析器期望item并看到ID时,它无法知道expr1中的哪个和expr2尝试。 (这是一个预见的冲突:可以预测两个非终端。)在这种特殊情况下,很容易看到如何消除该冲突,但是由于它是通过合并两个非终端而开始的,因此并不是真正的左因式分解。 (而且在完整的语法中,这可能是摘录的,将两个非终结符合并可能会更加困难。)

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

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

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

翻译成类似以下内容的

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

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