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

fit*_*thu 11 language-agnostic algorithm parsing

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

svi*_*nja 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 *
        ...
Run Code Online (Sandbox Code Playgroud)

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

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


Tyl*_*den 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的长期传统.