📅  最后修改于: 2023-12-03 15:07:16.317000             🧑  作者: Mango
左递归(left recursion)是一种文法的形式,其中一个非终结符号可以直接或间接地推导出自身,这种文法会导致递归下降分析器的无限循环和死循环问题。因此,在语法分析器的设计中,需要对语法中存在的左递归进行处理,使其变为右递归或无递归。
本文主要介绍如何删除语法中的直接和间接左递归。
直接左递归是非常显然的,只要存在形如 $A \rightarrow A\alpha\ |\ \beta$ 的规则,其中 $\alpha$ 和 $\beta$ 均为符号串,那么 $A$ 就存在直接左递归。
对于直接左递归的处理,常用的方法是通过引入新的非终结符号来消除直接左递归。
如果文法存在直接左递归:
A -> Aα | β1 | β2 | ... | βm
则可以通过引入新的非终结符号 B,将其改写为:
A -> β1 B | β2 B | ... | βm B
B -> α B | ε
其中 $ε$ 是空串。这个过程可以递归地进行,直到文法中所有直接左递归被消除。
下面是一个例子,其中 $E$ 产生式存在直接左递归:
E -> E + T | T
T -> F * T | F
F -> ( E ) | id
可以先将左途的部分分离出来,变成:
E -> TE'
E' -> + TE' | ε
T -> FT'
T' -> * FT' | ε
F -> ( E ) | id
这时候已经没有直接左递归了,但是仍然有间接左递归。
间接左递归是指存在形如 $A \rightarrow B\alpha$ 和 $B \rightarrow A\beta$ 的规则,其中 $\alpha$ 和 $\beta$ 均为符号串。
对于间接左递归的处理,一种常见的方法是运用递归下降解析器的启发,将其改写为右递归形式。
如果文法存在间接左递归:
A -> Bα1 | Bα2 | ... | Bαn | γ1 | γ2 | ... | γm
B -> Aβ1 | Aβ2 | ... | Aβn | δ1 | δ2 | ... | δk
则可以通过引入新的非终结符号 C,将其改写为:
A -> γ1 C | γ2 C | ... | γm C
C -> α1 C | α2 C | ... | αn C | δ1 | δ2 | ... | δk | ε
B -> β1 A | β2 A | ... | βn A | ε
其中 $ε$ 是空串。同样,这个过程可以递归地进行,直到文法中所有间接左递归被消除。
下面是一个经典的例子,其中 $E$ 和 $T$ 产生式存在间接左递归:
E -> T + E | T
T -> F * T | F
F -> ( E ) | id
可以将其改写为:
E -> TA
A -> + T A | ε
T -> FB
B -> * F B | ε
F -> ( E ) | id
这时候已经没有任何形式的左递归。
在语法分析器设计中,消除文法中的左递归是必要的步骤。通过引入新的非终结符号,递归地消除直接和间接左递归,可以得到一个无递归的文法。
在实际的编译器设计中,左递归消除是一个比较繁琐的过程,需要考虑较多的细节问题。因此,建议大家使用成熟的编译器设计工具,如 ANTLR,可以自动消除文法中的左递归。