【问题标题】:Why parser-generators instead of just configurable-parsers?为什么使用解析器生成器而不是可配置解析器?
【发布时间】:2012-01-08 18:59:07
【问题描述】:

标题总结了它。大概任何可以使用源代码生成解析器生成器(本质上将要解析的语法硬编码到程序中)可以完成的任何事情都可以使用可配置解析器完成(它将维护待解析的语法) -解析软编码为数据结构)。

我想硬编码的代码生成解析器将获得性能奖励,减少一级间接,但是必须编译和运行它(或动态语言中的exec())和整体代码生成的笨拙似乎是一个很大的缺点。代码生成解析器还有其他我不知道的好处吗?

我看到代码生成使用的大多数地方都是为了解决语言元编程能力的限制(即 Web 框架、AOP、与数据库的接口),但整个 lex-parse 的事情看起来非常简单和静态,不需要您从代码生成中获得的任何额外的元编程活力。是什么赋予了?性能优势有那么大吗?

【问题讨论】:

  • @BartKiers:解析器生成器生成的解析器不比生成器本身更复杂或更通用吗?换句话说,如果解析器生成器可以生成足够复杂的代码来解析某些东西,为什么它不能只生成代码、编译并执行内存中的代码来立即解析呢?那不等于直接解析它吗? (可能不是,因为解析器生成器确实存在,我只是不知道为什么)
  • 通过一些努力,可以获得lex和yacc(flex和bison)生成的表,并在非标准的骨架中使用它们。最大的问题是连接用户提供的操作。

标签: parsing lexical-analysis


【解决方案1】:

如果你想要的只是一个可以通过传递语法规则来配置的解析器,那是可以实现的。 Earley 解析器将解析任何仅给定一组规则的上下文无关语言。价格是重要的执行时间:O(N^3),其中 N 是输入的长度。如果 N 很大(因为它对于许多可解析的实体来说都是如此),您可以以非常慢的解析结束。

这就是解析器生成器 (PG) 的原因。如果你解析很多文档,慢解析是个坏消息。编译器是人们解析大量文档的程序,没有程序员(或他的经理)希望程序员等待编译器。还有很多其他的东西需要解析:SQL 查询、JSON 文档……所有这些都具有“没有人愿意等待”的属性。

PG 所做的是做出许多必须在运行时发生的决定(例如,对于 Earley 解析器),并在解析器生成时预先计算这些结果。因此,一个 LALR(1) PG(例如 Bison)将生成在 O(N) 时间内运行的解析器,这在实际情况下显然要快得多。 (ANTLR 对 LL(k) 解析器做了类似的事情)。如果您想要通常线性的完全上下文无关解析,您可以使用称为GLR parsing 的LR 解析变体;这为您带来了“可配置”(Earley)解析器的便利性,具有更好的典型性能。

这种提前预计算的思想一般称为partial evaluation,即给定一个函数F(x,y),并且知道x总是某个常数x_0,计算一个新的函数F'(y) =F(x0,y),其中仅取决于 x 值的决策和计算是预先计算的。 F' 通常比 F 运行得快很多。在我们的例子中,F 类似于通用解析(例如,Earley 解析器),x 是一个语法参数,其中 x0 是一个特定的语法,而 F' 是一些解析器基础结构 P 和其他由 PG 计算的代码/表,使得 F'=PG(x)+P。

在您的问题的 cmets 中,似乎有人对为什么不只是在运行时运行有效的解析器生成器感兴趣。简单的答案是,它支付了您希望在运行时消除的大部分间接费用。

【讨论】:

  • 谢谢,这非常有用!是否可以说代码生成解析器是一种您可以做的优化,因为您正在解析的语法通常在运行时不会改变,因此可以硬编码到程序中?这种事情是否仍然适用于像 LISP 这样的语言,你可以在运行时动态地组合解析器的 AST? (附注:我没有接受过词法分析器/解析或形式语言理论方面的教育,我只是想了解事物背后的一般原则,而代码生成并不是你经常看到的步骤)
  • @LiHaoyi:大多数优化都有一个关于它们背后的恒定性的假设。部分评估的重点是,虽然 PE 运行一次可能很昂贵,但 F'(y) 通常比 F(x,y) 便宜很多,只要 x [语法] 保持不变,您就可以安全地使用F'(y)。 lisp 甚至是这样:它有一个称为 S 表达式的“常量”语法。
  • 作为结语,我最近学习了如何使用 Scala 提供的 Parser Combinator 库。我的理解是,在其他语言中存在类似的库(例如 PyParsing、(F)Parsec),它们可以让你做类似的事情,而无需代码生成。这些如何适合您的答案?他们是在运行时将预处理步骤作为初始化的事情,还是在根本没有预处理的情况下遭受性能损失?
  • 我对这类库了解不多。是的,您可以使用语言的动态描述进行解析;查看 Earley 解析器。关键是,如果您这样做,您的解析器将不如“预编译”的解析器那么有效。对于许多只偶尔读取小文档的应用程序来说,这可能无关紧要。但是如果应用程序经常读取巨大的文档(任何源代码处理工具,例如编译器),或者它被很多人使用,那么动态解析技术效率低下,你不应该这样做,即使它当时很方便你编码。
猜你喜欢
  • 1970-01-01
  • 2013-03-18
  • 2018-09-22
  • 2011-04-12
  • 2012-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多