LL和递归下降解析器之间的区别?

时间:2009-06-25 15:32:22

标签: parsing grammar context-free-grammar recursive-descent ll

我最近正在努力教自己解析器(语言/无上下文语法)是如何工作的,除了一件事,大部分解析器似乎都有意义。我特别关注 LL(k)语法,其中两个主要算法似乎是LL parser(使用堆栈/解析表)和{{3} (简单地使用递归)。

据我所知,递归下降算法适用于所有LL(k)语法,可能更多,而LL解析器适用于所有LL(k)语法。然而,递归下降解析器显然要比LL解析器简单得多(正如LL一个比LR一个简单)。

所以我的问题是,使用任何一种算法时可能遇到的优点/问题是什么?为什么有人会选择LL进行递归下降,因为它适用于同一组语法并且实现起来比较棘手?

1 个答案:

答案 0 :(得分:90)

LL通常是比递归下降更有效的解析技术。事实上,在最坏的情况下,一个天真的递归下降解析器实际上将是 O(k ^ n)(其中 n 是输入大小)。一些技术,例如memoization(产生Packrat解析器)可以改进这一点,也可以扩展解析器接受的语法类,但总有一个空间权衡。 LL解析器(据我所知)总是线性时间。

另一方面,你的直觉是正确的,即递归下降解析器可以处理比LL更大的语法类。递归下降可以处理LL(*)(即无限预测)的任何语法以及一小组模糊语法。这是因为递归下降实际上是PEG的直接编码实现,或Parser Expression Grammar(s)。具体来说,析取运算符(a | b)不是可交换的,这意味着a | b不等于b | a。递归下降解析器将按顺序尝试每个替代方案。因此,如果a与输入匹配,即使b 与输入匹配,它也会成功。这样可以简单地通过正确排序析取来处理经典的“最长匹配”歧义,例如悬空else问题。

总而言之,可能使用递归下降实现LL(k)解析器,以便它以线性时间运行。这通过基本上内联预测集来完成,以便每个解析例程在恒定时间内确定给定输入的适当生产。不幸的是,这种技术消除了处理整个类的语法。一旦我们进入预测性解析,悬空else等问题就不再能够轻松解决。

至于为什么选择LL而不是递归下降,主要是效率和可维护性问题。递归下降解析器明显更容易实现,但它们通常更难维护,因为它们所代表的语法不存在于任何声明形式中。大多数非平凡的解析器用例都使用解析器生成器,如ANTLR或Bison。使用这些工具,算法是直接编码的递归下降还是表驱动的LL(k)无关紧要。

值得关注的是,值得研究的是recursive-ascent,这是一种在递归下降之后直接编码的解析算法,但能够处理任何LALR语法。我还要深入研究parser combinators,这是一种将递归下降解析器组合在一起的功能性方法。

相关问题