38

LL 解析器与 LR 解析器相比有哪些优势,以保证它们在当今的解析器生成器工具中相对受欢迎?

根据Wikipedia,LR 解析似乎比 LL 具有优势:

LR解析比LL解析可以处理更大范围的语言,并且在错误报告方面也更好,即当输入不符合语法时,它会尽快检测到语法错误。这与 LL(k)(或更糟糕的是,LL(*) 解析器)形成对比,后者可能会由于回溯而将错误检测推迟到语法的不同分支,这通常会使错误更难在具有长公共前缀的析取中定位.

注意:这不是家庭作业。当我发现 Antlr 是一个 LL 解析器生成器时,我感到很惊讶(尽管它的名字中有“LR”!)。

4

7 回答 7

35

如果您想要解析树/森林并且不介意黑盒,GLR 非常棒。它允许您输入任何您想要的CFG,代价是通过详尽的测试在解析时检查歧义,而不是静态地解决 LR/LALR 冲突。有人说这是一个很好的权衡。Ira Baxter 的 DMS 工具或 Elkhound 具有免费的 C++ 语法,对这类问题很有用。ANTLR对于一大类语言应用程序也很有用,但使用自上而下的方法,生成称为 LL(*) 的递归下降解析器,它允许语义谓词。我将在没有证据的情况下声明谓词允许您解析 CFG 之外的上下文相关语言。程序员喜欢在语法中插入动作,喜欢良好的错误处理,喜欢单步调试。LL在这三个方面都很擅长。LL 是我们手工完成的,因此更容易理解。不要相信LR 更擅长处理错误的维基百科废话。也就是说,如果您使用 ANTLR 进行很多回溯,那么使用 LL(*) 的错误确实更糟(PEG 有这个问题)。

重新回溯。GLR 也进行推测(即回溯),就像 PEG、ANTLR 和任何其他非确定性策略一样。在任何不确定的 LR 状态下,GLR “分叉”子解析器以尝试任何可行的路径。无论如何,LL 有很好的错误处理上下文。LR 知道它匹配一个表达式,LL 知道它是一个赋值或条件表达式IF;LR 知道它可能在任何一个中,但不确定——而这种不确定性正是它获得力量的地方。

GLR 是O(n^3)最坏的情况。Packrat/PEG 是O(n)最坏的情况。ANTLR 是O(n^2)由于循环前瞻 DFA 但O(n)在实践中。真的没关系。GLR 足够快。

ANTLRL ang R认知的另一种工具,而不是反 LR,但我也喜欢那个;)

坦白说,和很多 80 后的年轻程序员一样,我不懂 LALR,也不喜欢黑匣子(现在我深挖 GLR 引擎的美,但还是更喜欢 LL)。我构建了一个基于商业 LL(k) 的编译器,并决定构建一个工具来生成我手工构建的内容。ANTLR 并不适合所有人,像 C++ 这样的边缘情况可能会用 GLR 更好地处理,但很多人发现 ANTLR 适合他们的舒适区。自 2008 年 1 月以来,在 ANTLRWorks 中,ANTLR 的二进制 jar 和源 zip 总共有 134,000 次下载(根据 Google Analytics)。请参阅我们关于 LL(*) 的论文,其中包含大量经验数据。

于 2010-11-04T21:00:59.997 回答
12

如果您必须编写代码,递归下降 (LL) 是您可以实际执行的操作;人们实际上无法手动构建 L(AL)R 解析器。

鉴于现代解析器生成器将为您处理所有解析器构造,并且空间不是什么大问题,我更喜欢 LR 解析器,因为您不必与语法作斗争以使它们对您的特定解析器生成器有效(没有“删除所有左递归”的愚蠢)。

事实上,我更喜欢GLR 解析器,它几乎可以用上下文无关语法解析任何东西。不用担心左递归。没有转移/减少冲突的担忧。没有前瞻限制。

如果您想查看一个GLR 解析引擎可以处理的语言范围(包括著名的难以解析的 LL/LALR 语言 C++),您可以查看此处

于 2010-11-03T22:47:30.167 回答
3

根据我的个人经验(我在各种情况下都使用了这两种方法),最实际的区别是,使用 LL(k),您可以以更简单的方式定义语法(因为它是自上而下的),而无需关心许多可能的 reduce- LR 解析器经常发生的 reduce 或 shift-reduce 冲突。您唯一需要关心的是左递归,它必须转换为右递归。

另一件事是自上而下的方法通常意味着更高的复杂性(无论是空间还是时间),因为它必须在解析时存储整个树,并且在解决歧义之前它会增长很多。

于 2010-11-03T22:46:58.360 回答
1

我所熟悉的唯一优点是您可以轻松地手动编写 LL 解析器。LR 解析器更难手动编码(您通常使用解析器生成器)。

于 2010-11-04T21:05:21.737 回答
1

LL Parsing 的最坏情况复杂度为 O(n^4),而 LR 解析的最坏情况复杂度更好,O(n^3)。

(但没有人会写出 O(n^4) 的语法。)

https://en.wikipedia.org/wiki/Top-down_parsing

于 2018-02-14T23:21:21.660 回答
0

想到的一个原因是,在 LL 范式中创建一种需要任意回溯(咳嗽C++)的语言要容易得多。

于 2010-11-03T22:44:21.300 回答
0

根据 Laurence Tratt 的说法,LL 解析器有一个小而重要的利基市场,如果您需要:

尽可能高的性能和/或尽可能好的错误消息。

并手动编写递归下降解析器来实现这一点。

对于一个现实的编程语言语法,它通常需要很多人几个月的努力才能击败一个自动生成的解析器。

然而:

LL 解析器在很大程度上没有吸引力,因为缺少左递归使得表达许多标准编程语言结构变得很尴尬。

因此他的结论是,处理左递归的 LR 解析是可行的方法。

要对此进行更全面的审查,我推荐 Laurence Tratt 的优秀文章Which Parsing Approach?

于 2022-01-18T09:22:12.977 回答