Compiler construction 删除左递归

Compiler construction 删除左递归,compiler-construction,grammar,Compiler Construction,Grammar,下面的语法留下了递归 E= E+T|T T= T*F|F F= a|b|c 如何删除它?有什么通用的过程吗?正如其他人所指出的,有一个用右递归代替左递归的通用过程。其他答案很好地展示了如何使用这个通用过程来删除给定语法中的左递归 下面是一个解决方案,它以特定于该语法的方式重新构造给定语法 E= T+E|T T= F*T|F F= a|b|c 是的,有一个通用程序,请参见示例 应该提到的是,这会从左到右改变+和*的关联性。也就是说,在a+b+c之前被解析为(a+b)+c,现在被解析为a+(b+

下面的语法留下了递归

E= E+T|T
T= T*F|F
F= a|b|c

如何删除它?有什么通用的过程吗?

正如其他人所指出的,有一个用右递归代替左递归的通用过程。其他答案很好地展示了如何使用这个通用过程来删除给定语法中的左递归

下面是一个解决方案,它以特定于该语法的方式重新构造给定语法

E= T+E|T
T= F*T|F
F= a|b|c

是的,有一个通用程序,请参见示例

应该提到的是,这会从左到右改变
+
*
的关联性。也就是说,在
a+b+c
之前被解析为
(a+b)+c
,现在被解析为
a+(b+c)

这不是一个加法和乘法的问题,而是一个减法和除法的问题


Wikipedia的文章更详细地介绍了左递归删除过程及其复杂性。

例如,一般过程可以在中找到。我将演示
E=E+T | T

E
是左递归非终结符。
+T
是非空序列(alpha)。
T
是不以E(beta)开头的序列

我们为“rest”创建一个新的非终结符,即非空序列。这将处理递归

E'=epsilon |+TE'

我们修改了原始规则,使用新的非终结符来处理递归

E=TE'

生产

E = E+T
  | T
T = T*F
  | F
F = a|b|c

E= T ('+' T)*
T= F ('*' F)*
F= a|b|c
以保持与第一个E断开处使用
A(E,T)
处理相同的处理。新版本使用:

ret = T1;
while(set.more()) ret = A(ret, set.pop_front().T);

你的意思是,你提出的左递归文法是自由的?我想知道,因为我在一门正式语言的课程中遇到了它,它是左递归的,那么如何解决这个问题呢?(关于逆转关联性)我看到了很多这种删除左递归算法的副本(直接的或间接的),他们总是写关于破坏关联性的警告,但我还没有在任何这样的文章中看到任何解决这个问题的方法。有什么解决办法吗?还是仅仅是问题?另一个问题是:为什么这样做?我问这个问题是因为我没有看到任何可靠的算法证明。有“证明”表明,从转换后的语法中获得的语言仍然是相同的,但对我来说,这种证明中存在一个错误:假设语法生成相同的输出,则是相同的。因为它不是!这是一种完全不同的语言,因为它有不同的解析树,所以机器对它的“理解”是不同的。将“抽象语法树”解释为“抽象(语法树)”和(抽象语法)树”之间的区别是一样的——两种不同的东西。左递归和右递归语法也是一样的。一个是左关联的,另一个是右关联的。我不知道有什么方法可以让左递归语法具有右关联性。你知道吗?在数学中(我认为是计算机科学的超集),在使用术语之前,我们要小心定义“相同的”、“相似的”、“相等的”、“等价的”、“同构的”等。语言是一组字符串,它们是字母表中有限的有序符号序列。集合相等是定义良好的。如果
A
B
的一个子集,
B
A
的一个子集,我们说两组
A
B
相等。我们可以证明,显然,这些算法就是这样的。因此,生成不同解析树的两个语法可以严格地表示为用集合来描述相同的语言。如果你想说:
A->aA|@
A->aA|@
不同,那么一定要这样做。你有自由定义你认为合适的术语。但是,当涉及到语法时,请给我一个严格、精确的“相同性”定义。我怀疑你的观点可能是这样的:“如果语言的每个元素都有一个解析树的双射,那么两个语法在结构上是同构的。如果它们的自然定义的有向图是同构的,那么两个解析树是同构的。”这是一个可以做任何事情的野兽,但是开始用一些东西做证明
ret = T1;
while(set.more()) ret = A(ret, set.pop_front().T);