关于解析:yacc/byacc/bison和lex/flex的适当使用

Appropriate uses for yacc/byacc/bison and lex/flex

我阅读的大多数与这些实用程序有关的帖子通常建议使用其他方法来获得相同的效果。例如,提到这些工具的问题通常至少有一个答案,其中包含以下一些内容:

  • 使用 boost 库(在此处插入适当的 boost 库)
  • 不要创建 DSL 使用(在此处插入最喜欢的脚本语言)
  • ant更好

假设开发者...

  • ... 熟悉 C 语言
  • ...确实知道至少一种脚本
    语言(例如 Python、Perl 等)
  • ...几乎必须写一些解析代码
    每个项目都在进行

所以我的问题是:

  • 什么是合适的情况
    非常适合这些实用程序?
  • 有没有(合理的)情况
    没有更好的地方
    yacc 以外的问题的替代方案
    和 lex(或衍生物)?
  • 实际解析问题的频率
    可以预期会遇到任何短
    yacc 和 lex 的到来
    更好地解决了最近
    解决方案?
  • 对于尚未开发的开发人员
    熟悉这些工具是否值得
    让他们投入时间
    学习他们的语法/习语?怎么做
    这些与其他解决方案相比?

lex/yacc 及其衍生工具在今天如此普遍的原因在于,它们存在的时间比其他工具要长得多,它们在文献中的覆盖面要广得多,而且它们传统上是随 Unix 操作系统提供的。这与它们与其他词法分析器和解析器生成器工具的比较几乎没有关系。

无论您选择哪种工具,总有一个重要的学习曲线。因此,一旦您使用了几次给定的工具并在使用它时变得相对舒适,您就不太可能愿意付出额外的努力来学习另一种工具。这很自然。

此外,在 1960 年代末和 1970 年代初创建 lex/yacc 时,硬件限制对解析提出了严峻挑战。 Yacc 使用的表驱动的 LR 解析方法在当时是最合适的,因为它可以通过使用相对较小的通用程序逻辑并通过将状态保存在磁带或磁盘上的文件中,以较小的内存占用来实现。诸如 LL 之类的代码驱动解析方法具有更大的最小内存占用,因为解析器程序的代码本身代表语法,因此它需要完全适合 RAM 才能执行,并将状态保持在 RAM 中的堆栈上。

当内存变得更加丰富时,更多的研究进入了不同的解析方法,例如 LL 和 PEG,以及如何使用这些方法构建工具。这意味着在 lex/yacc 系列之后创建的许多替代工具使用不同类型的语法。然而,切换语法类型也会产生显着的学习曲线。一旦您熟悉了一种语法,例如 LR 或 LALR 语法,您就不太可能想要切换到使用不同类型语法的工具,例如 LL 语法。

总体而言,lex/yacc 系列工具通常比最近出现的工具更简陋,后者通常具有复杂的用户界面,以图形方式可视化语法和语法冲突,甚至通过自动重构解决冲突。

因此,如果您之前没有使用任何解析器工具的经验,如果您无论如何都必须学习新工具,那么您可能应该考虑其他因素,例如语法和冲突的图形可视化、自动重构、良好的可用性文档,可以输出生成的词法分析器/解析器的语言等。不要仅仅因为"这是其他人似乎都在使用的"而选择任何工具。

以下是我能想到的使用 lex/yacc 或 flex/bison 的一些原因:

  • 开发人员已经熟悉 lex/yacc 或 flex/bison
  • 开发人员最熟悉和最熟悉 LR/LALR 语法
  • 开发人员有很多涉及 lex/yacc 的书籍,但没有涉及其他方面的书籍
  • 开发人员即将收到一份潜在的工作机会,并被告知 lex/yacc 技能会增加他被录用的机会
  • 开发人员无法从项目成员/利益相关者那里获得使用其他工具的支持
  • 环境已安装 lex/yacc,由于某种原因,安装其他工具不可行


是否值得学习这些工具将在很大程度上取决于(几乎完全取决于您编写了多少解析代码,或者您对按照一般顺序编写更多代码的兴趣程度。我已经使用它们很多次了)位,并发现它们非常有用。

你使用的工具并没有你想象的那么大。对于我必须处理的大约 95% 的输入,它们之间几乎没有什么区别,因此最好的选择就是我最熟悉和最舒服的那个。

当然,lex 和 yacc 产生(并要求您用 C (或 C )编写您的操作)。如果您对它们不满意,那么使用和生成您喜欢的语言(例如 Python 或 Java)的工具无疑是更好的选择。一方面,我不建议尝试将这样的工具与您不熟悉或不舒服的语言一起使用。特别是,如果你在一个产生编译器错误的操作中编写代码,你从编译器那里得到的帮助可能会比平时少得多,因此你确实需要足够熟悉该语言才能识别问题只有一个关于编译器注意到哪里出错的最小提示。


我们在我的办公室实施了一整套编程语言。我们为此使用它。我认为这是一种为事物编写解释器的快速简便的方法。可以想象,您几乎可以使用它们编写任何类型的文本解析器,但很多时候,要么 A) 自己快速编写它更容易,要么 B) 你需要比它们提供的更大的灵活性。


我认为避免创建新语言只是为了支持特定领域的语言是一个很好的建议。采用现有语言并使用域功能对其进行扩展将是更好地利用您的时间。

如果您出于其他原因尝试创建一种新语言,也许是为了研究语言设计,那么这些工具有点过时了。 antlr 等较新的生成器,甚至 ML 等较新的实现语言,使语言设计变得更加容易。

如果有充分的理由使用这些工具,那可能是因为它们的传统。您可能已经有一个需要增强的语言框架,它已经在其中一个工具中实现。您也可能会从关于这些旧工具的大量教程信息中受益,对于这些旧工具,没有为更新和更流畅的语言实现方式编写的语料库。


在之前的项目中,我需要一种能够对任意数据生成查询的方法,这种方法对于非技术人员来说很容易使用。数据是 CRM 类型的东西(如名字、姓氏、电子邮件地址等),但它旨在针对许多不同的数据库工作,所有数据库都具有不同的架构。

所以我开发了一个小的 DSL 来指定查询(例如 [FirstName]=\\'Joe\\' AND [LastName]=\\'Bloggs\\' 会选择每个名为 "Joe Bloggs" 的人)。它有一些更复杂的选项,例如"optedout(medium)"语法会选择所有选择不接收特定媒体(电子邮件、短信等)上的消息的人。有 "ingroup(xyz)" 可以选择特定组中的每个人,等等。

基本上,它允许我们指定像 "ingroup(\\'GroupA\\') 而不是 ingroup(\\'GroupB\\')" 这样的查询,它会被翻译成这样的 SQL 查询:

1
2
3
4
5
6
7
SELECT
    *
FROM
    Users
WHERE
    Users.UserID IN (SELECT UserID FROM GroupMemberships WHERE GroupID=2) AND
    Users.UserID NOT IN (SELECT UserID GroupMemberships WHERE GroupID=3)

(如您所见,查询不是尽可能高效,但我猜这就是机器生成所得到的)。

我没有使用 flex/bison,但我确实使用了解析器生成器(它的名字现在已经让我忘记了......)