关于递归下降解析器的复杂性

时间:2013-06-01 05:33:17

标签: algorithm parsing language-agnostic

众所周知,递归下降解析器在某些情况下可能需要指数时间;谁能指点我的样品,这会发生什么?特别感兴趣的是PEG的情况(即优先选择)。

2 个答案:

答案 0 :(得分:11)

这是因为你可以在不同的递归分支中多次解析相同的东西(在同一位置检查相同的规则)。这有点像使用递归计算第n个斐波纳契数。

Grammar:

A -> xA | xB | x
B -> yA | xA | y | A
S -> A

Input:
xxyxyy

Parsing:
xA(xxyxyy)
    xA(xyxyy)
        xA(yxyy) fail
        xB(yxyy) fail
        x(yxyy) fail
    xB(xyxyy)
        yA(yxyy)
            xA(xyy)
                xA(yy) fail
                xB(yy) fail
                x(yy) fail
            xB(xyy)
                yA(yy)
                    xA(y) fail
                    xB(y) fail
                    x(y) fail
                xA(yy) fail *
            x(xyy) fail
        xA(yxyy) fail *
        y(yxyy) fail
        A(yxyy)
            xA(yxyy) fail *
            xB(yxyy) fail *
            x(yxyy) fail *
    x(xyxyy) fail
xB(xxyxyy)
    yA(xyxyy) fail
    xA(xyxyy) *
        xA(yxyy) fail *
        xB(yxyy) fail *
        ...

* - 我们在同一位置解析规则,我们已在另一个分支中解析它。如果我们保存了结果 - 哪些规则在哪些位置失败 - 我们知道xA(xyxyy)第二次失败,我们不会再次通过它的整个子树。我不想写出整件事,但你可以看到它会多次重复相同的子树。

什么时候会发生 - 你有很多重叠的转换。优先选择不会改变事物 - 如果最低优先级规则最终成为唯一正确的规则(或者没有一个是正确的),则必须检查所有规则。

答案 1 :(得分:10)

如果输入和语法的组合使得需要大量的回溯,那么任何自上而下的解析器,包括递归下降,理论上可以成为指数。如果语法使得确定性选择被放置在长序列的末尾,则会发生这种情况。例如,如果你有像&这样的符号意思是“所有先前的弊端实际上是加号”,然后有像“((((((((a - b) - c) - d) - e&)”这样的数据“然后解析器必须向后并将所有加号变为最小值。如果您开始沿着这些行创建嵌套表达式,则可以创建一个有效的非终止输入集。

你必须意识到你正在这里踩到一个政治问题,因为现实是大多数正常的语法和数据都不是这样的,然而,有很多人系统地说是递归下降,因为这并不容易自动制作RD。所有早期解析器都是LALR,因为它们比RD更容易自动生成。所以发生的事情是每个人都写了LALR和badmouthed RD,因为在过去,制作RD的唯一方法是手工编写代码。例如,如果您阅读龙书,您会发现Aho& Ullman只在RD上写了一段,基本上只是一个意识形态的删除说“RD很糟糕,不要这样做。”

当然,如果你开始手工编码RD(就像我一样),你会发现它们比LALR好得多,原因有很多。在过去,你总是可以告诉编译器有一个手工编码的RD,因为它有定位精度的有意义的错误消息,而具有LALR的编译器会显示错误发生的距离实际上是50行。自从过去以来,情况已经发生了很大的变化,但是你应该意识到,当你开始阅读RD上的FUD时,它就源于长期以来在“某些圈子”中口头诋毁RD的传统。