【问题标题】:Removing left recursion删除左递归
【发布时间】:2011-02-08 18:31:54
【问题描述】:

下面的语法有左递归

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

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

【问题讨论】:

    标签: compiler-construction grammar


    【解决方案1】:

    是的,有一个通用程序,参见例如wikipedia.

    E = TE'
    E'= (e) | +TE'
    T = FT'
    T'= (e) | *FT'
    F = a | b | c
    
    // (e) is "epsilon" which is defined as the empty string
    

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

    这不是加法和乘法的问题,但它会是例如减法和除法的问题。

    维基百科文章有更多关于左递归删除过程及其复杂性的详细信息。

    【讨论】:

    • 那么如何解决这个问题呢? (反转关联性)我看到了这个删除左递归算法的许多副本(直接或间接),他们总是写关于破坏关联性的警告,但我还没有在任何此类文章中看到任何解决该问题的方法.有什么解决办法吗?还是只有问题?另一个问题是:为什么会这样?
    • 我问是因为我还没有看到该算法的任何可靠证明。有“证明”表明从转换后的语法中获得的语​​言仍然是相同的,但对我来说,这样的证明中有一个错误:假设如果语法产生相同的输出,那么它是相同的。因为它不是!这是一种完全不同的语言,因为它有不同的解析树,所以机器对它的“理解”也不同。将“抽象语法树”解释为“抽象(语法树)”和“(抽象语法)树”之间的区别相同 - 两个不同的东西。
    • 左递归和右递归语法也是如此。一种是左联想,另一种是右联想。而且我不知道有什么方法可以使左递归语法具有右关联性。你知道吗?
    • 喜欢这个,我会感兴趣的:)
    • 这个答案可以通过解释e来改进。没有这个,对于那些还不熟悉解析器理论的人来说,它是非常不透明的。 (当然,您提供了一个维基百科页面的链接,但该页面以“这篇文章可能对大多数读者来说过于技术性而无法理解”开头,这对我来说当然是这样)。
    【解决方案2】:

    生产

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

    E= T ('+' T)*
    T= F ('*' F)*
    F= a|b|c
    

    为了保持与A(E,T) 处理第一个 E 分离的相同处理。新版本使用:

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

    【讨论】:

      【解决方案3】:

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

      这是一种以特定于该语法的方式重构给定语法的解决方案。

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

      【讨论】:

      • 所以你实际上是在说你提出的左递归免费语法?我想知道,因为我在形式语言的课程中遇到了它,并且它被声明为左递归?
      【解决方案4】:

      例如,可以在Wikipedia 找到一般程序。我会为E = E+T|T演示:

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

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

      E' = epsilon|+TE'

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

      E = TE'

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-11
        相关资源
        最近更新 更多