目录
消除左递归
在进行语法分析时,一个文法要能产生无限个不同的句子,必须具有递归产生式。
但是在语法分析中,左递归会导致语法分析的回溯,会导致较大的时间和空间开销,所以,我们需要消除左递归,将其转化为右递归。从而消除两义性,使得我们能够适应确定的自上而下的语法分析器分析语法。
在学习数据结构与算法时,我们便知道,递归分为两种,既直接递归与间接递归。同样,左递归也分为直接左递归与间接左递归。
直接左递归文法示例如下:
A -> Aa
间接左递归示例如下:
S -> Aa
A -> Sb|c
接下来,我们演示如何正确消除这些左递归。
消除直接左递归
消除直接左递归的方法是,将左递归的产生式改写成等价的右递归产生式。
具体操作方法为(该方法为博主总结,为初学时所用,博主才疏学浅,若有错漏之处,欢迎批评指正):
1、引入新的非终结符A'(假设存在左递归的非终结符为A,写为A'易于标识与理解)
2、将不含原非终结符的其他产生式引出为α(若该非终结符不含其他产生式,则α为ε)
3、将含左递归的原非终结符的产生式的其他符号引出为η1 | η2 |...| ηn
4、将α与A'结合为新的、非终结符A的产生式 A - >αA'
5、增加A'的产生式为η1 A’ | η2 A’ |...| ηn A’
6、为A'增加一条产生式为ε
例如,文法的产生式为:
A -> Aγ|w
则消除左递归后的文法产生式为:
A -> wA' A' -> yA'|ε
容易发现,上述两个文法产生式等价。
再比如,文法的产生式为:
I -> IL | ID | L
则消除左递归后的文法产生式为:
I -> LI' I' -> LI' | DI' | ε
容易发现,上述两个文法产生式也是等价的。
又一例,原文法的产生式为:
S -> Sabc|abc|bc|c
则消除左递归后的文法产生式为:
S -> abcS'|bcS'|cS' S' -> abcS'|ε
容易发现,上述两个文法产生式也是等价的。
一般而言:
一般地,设文法G含有直接左递归的产生式为
A → Aa1 | Aaz |···| Aan | β
其中,β为不含左递归的候选式。引入文法G',相应于A的产生式为
A -> βA'
A'-> a1 A' | a2 A' |···| an A' |ε
显然第二条文法不含左递归且与第一条文法等价。
消除间接左递归
设间接左递归文法为G,消除文法G的间接左递归的算法如下:
(1)将文法G的所有非终结符按任一给定的顺序排列,设为A1,A2,···,An;
(2)消除可能的左递归;
for (i = 1; i <= n ; i++) { for (j = 1; J <= i-1; j++){ 把一个形如Ai -> Aja的产生式改写为A1 -> δ1a|δa|···|δka; (其中,A -> δ1|δ2|···|δx是A,的所有产生式) } 消除A1产生式的直接左递归; }
(3)化简。删除多余产生式,即在从文法开始符开始的任何推导中都不出现的非终结符的产生式。
以上方法来自于王晓斌老师主编的《程序设计语言与编译——语言的设计与实现》一书。
博主理解如下(博主才疏学浅,若有错漏之处,欢迎批评指正):两次For循环是为了尽可能将存在间接递归的产生式结合,生成一个正确、完备、唯一的直接左递归产生式,再利用消除直接左递归的方法消除左递归。
举例如下:
设文法G为:
S -> Qc|c
Q -> Rb|b
R -> Sa|a
将文法按照R、Q、S排序,则可以将关于Q的产生式改写为:
Q -> Sab|ab|b
之后,可以进一步将关于S的产生式改写为:
S -> Sabc|abc|bc|c
于是,上述的间接左递归被改写为了直接左递归,容易发现,上述两组文法产生式也是等价的。
消除左递归得到:
S -> abcS'|bcS'|cS'
S' -> abcS'|ε
分析发现,在这个产生式集合中,关于Q的产生式和关于R的产生式为多余产生式,可删除。
于是我们得到了不含左递归且与原文法等价的文法:
S -> abcS'|bcS'|cS'
S' -> abcS'|ε
算法并未对非终结符的排序加以规定,排序不同,结果可能不同,但彼此还是等价的。
通过上述变换已将文法变换成无公共左因子、无左递归的等价文法。
结语
消除左递归笔记至此结束,若有其他编译原理的相关问题,可参考博主的其他博客。