【问题标题】:What parser-generators with code separation and language extensibility would you recommend?您会推荐哪些具有代码分离和语言可扩展性的解析器生成器?
【发布时间】:2011-05-04 16:19:57
【问题描述】:

我正在寻找具有语法/代码分离的上下文无关语法解析器生成器,并且可以添加对新目标语言的支持。例如,如果我想要 Pascal 中的解析器,我可以编写自己的 pascal 代码生成器,而无需重新实现整个东西。

我知道大多数开源解析器生成器理论上都可以扩展,但我仍然更喜欢计划和记录可扩展性的东西。

在功能方面,我需要解析器至少支持 Python 风格的缩进,也许还需要做一些额外的工作。对生成的解析器类型没有要求,但我更喜欢快速的。

哪些是最知名/维护最多的选项?

流行的解析器生成器似乎主要使用我真的不喜欢的混合语法/代码方法。 Comparison list on Wikipedia 列出了一些,但我是这方面的新手,不知道该尝试哪个。

为什么我不喜欢混合语法/代码:因为这种方法看起来一团糟。语法就是语法,实现细节就是实现细节。它们是用不同语言编写的不同内容,将它们放在不同的地方很直观。

如果我想在另一个项目中使用不同的实现细节重用部分语法怎么办?如果我想用不同的语言编译解析器怎么办?所有这些都需要将语法分开。

【问题讨论】:

  • 你为什么不喜欢混合语法/代码?例如,您是否要构建一个具体语法树,然后将其单独转换为抽象语法树?
  • 我从来不理解反对混合解析器规则和语义操作的反对意见。创建解析树然后遍历它要慢得多,并且不允许语法灵活性(例如,最简单的情况:仅在满足给定谓词或 lambda 时才匹配一个数字)。我也认为创建一个单独的解析器没有用,它以多种语言输出。您总是只需要一个,也是最高效的一个。
  • @Gene Bushuyev:我会满足于调用虚函数并让我在解析器后代中完成这项工作。这几乎与混合解析/实现一样有效,但更清晰,嗯,合乎逻辑。规则以及它们的具体实施方式是不同的,不是吗?
  • 对于许多语言,我只需要一种我需要为其制作解析器的语言。如果您不是 C++ 或 Java,那么您的选择会突然变得非常有限。无论您的语言有多不幸,您基本上都会被任何一两个可用于您的语言的解析器所困扰。这就是为什么我想要一些可扩展的东西。我可以自己写一个后端。
  • 不,我不同意这些说法。使用虚函数的解析器通常很慢,因为许多语法规则很短并且经常检查。我什至不会去那里。其次,我从未见过以不同语义重用相同语法的解析器。即使在极少数情况下可行,它也非常低效,实际上会导致复杂且容易出错的代码。向语法规则添加语义操作使代码更清晰、更易于理解、更易于调试,并具有最佳性能。而且我已经用两种方式做了很多解析器。

标签: parser-generator


【解决方案1】:

大多数解析器生成器不会处理上下文无关文法。他们处理一些子集(LL(1),LL(k),LL(*),LALR(1),LR(k),...)。如果您选择其中之一,您几乎肯定必须修改您的语法以匹配解析器生成器的限制(没有左递归,有限的前瞻,......)。如果你想要一个真正的上下文无关解析器生成器,你需要一个早期解析器生成器(低效)、一个 GLR 解析器生成器(最实用的)或 PEG 解析器生成器(最后一个不是上下文无关的;它需要规则被排序以确定哪些优先)。

您似乎担心混合用于构建树的语法和解析器操作。

如果您构建的树不是语法的直接函数,则必须有某种方法将树构建机制与语法产生式联系起来。将它放在“靠近”语法产生式是一种方法,但会导致您的“混合”符号反对。

另一种方法是给每个规则一个名称(或一些唯一标识符),并将树构建机制设置为由名称索引的一侧。这样你的语法就不会被“其他东西”污染,这似乎是你的反对意见。我所知道的解析器生成器系统都没有这样做。一个尴尬的问题是你现在必须发明很多规则名称,而任何时候你有几百个名字本身就很不方便,而且很难让它们助记。

第三种方法是使语法成为函数,并自动生成树构建步骤。这根本不需要额外的东西来生成 AST。我所知道的唯一工具(可能还有其他工具,但我已经找了 20 多年了,但还没有看到)是我公司的产品,DMS Software Reengineering Toolkit。 [DMS 不仅仅是一个解析器生成器;它是一个完整的生态系统,用于为任意语言构建程序分析和转换工具,使用 GLR 解析引擎;是的,它处理 Python 样式的缩进]。

一个反对意见是这样的树是具体的、臃肿的和令人困惑的;如果做得对,那是不对的。 我对这个问题的回答: What is the difference between an Abstract Syntax Tree and a Concrete Syntax Tree? 讨论了我们如何从自动生成的压缩 CST 中获得 AST 的好处。

关于 DMS 方案的好消息是基本语法不会因解析支持而臃肿。不太好的消息是你会发现很多其他的东西你想与语法规则相关联(漂亮的打印规则、属性计算、树合成……),然后你又回到了相同的选择。 DMS 拥有所有这些“其他东西”,并通过多种方式解决关联问题:

  • 通过在语法规则旁边放置其他相关的描述性形式(产生您抱怨的混合)。对于漂亮打印规则,我们可以容忍这一点,因为事实上,语法(解析)规则与漂亮打印(反解析)规则相邻是很好的。我们还允许将属性计算放在语法规则附近以提供关联。

  • 虽然 DMS 允许规则有名称,但这只是为了方便程序代码访问,而不是将其他机制与规则相关联。

  • DMS 提供了第三种方式来关联这些机制(尤其是属性文法计算),方法是将规则本身用作一种巨型名称。因此,您可以在一个地方编写语法和漂亮打印规则,而在其他地方您可以再次编写带有关联属性计算的语法规则。原则上,这就像给每个规则一个名称(好吧,一个签名)并将计算与名称相关联。但它也允许我们定义许多不同的属性计算(用于不同的目的)并将它们与它们的规则相关联,而不会弄乱基本语法。我们的工具会检查(规则,关联计算)在基本语法中是否有有效规则,因此当基本语法发生变化时,它会相对地跟踪需要修复的内容。

这是我的工具(我是架构师),您不应该将此作为建议,只是一种偏见。这种偏见得到了 DMS 解析(无需抱怨)C、C++、Java、C#、IBM Enterprise COBOL、Python、F77/F90/F95 以及 column6 continue/F90 continue 和嵌入式 C 预处理器指令在大多数情况下启动的能力的支持), Mumps、PHP4/5 和许多其他语言。

【讨论】:

  • 请注意,如果您可以控制 K 的大小,LL(K) 和 LR(K) 通常对于大多数语法来说已经足够好了。
  • @T.E.D.:他们不会处理模棱两可的语法或那些需要无限前瞻的语法。 LL(K) 不会处理左递归。你找不到很多 LR(k) 解析器生成器。 GLR 是一个实用的答案。
  • @Ira Baxter - 这更像是一个理论问题,而不是现实世界的问题,除非你试图解析人类语言或其他东西。大多数编程语言都可以通过 LL(K) 很好地处理。哎呀,大多数都可以通过 YACC 和它唯一的 LR(1) 来处理。
  • @T.E.D.:有点工作?您可以使任何解析器生成器进程几乎任何语言。您这样做的方式是让生成的解析器“接受太多”并添加后处理以消除解析器生成器无法接受的内容。虽然您可以完成这项工作,但通常您可能期望完成很多更多的工作。众所周知,GCC 放弃了使用 Bison (YACC) 解析 C++。 ANTLR 人(LL(inf?))没有工作的 C++ 解析器。 ...
  • @T.E.D: ...尝试使用其中任何一个解析 FORTRAN,并正确获取循环结构(使用共享循环终止语句),或解析 COBOL 并正确获取数据声明嵌套(COBOL 编号结构元素的嵌套深度而不是使用括号)。所以,是的,你可以做到。 GLR 带来的好处是可以以更少的努力做好它。具有种类繁多的前端的 DMS 就是一个很好的例子。 DMS 有很多非 GLR 机制可以提供帮助,因此 GLR 并不是完整的答案。 ...
【解决方案2】:

首先,任何体面的解析器生成器都将足够强大以支持 Python 的缩进。随着语言的发展,这并不是那么奇怪。您应该尝试解析列敏感语言,例如 Fortran77...

其次,我不认为您真的需要解析器本身是“可扩展的”,对吗?您只是希望能够使用它来分析和解析您想到的一两种语言,对吗?同样,任何体面的解析器生成器都可以做到这一点。

第三,你并没有真正说出你不喜欢的语法和代码之间的混合情况。你宁愿全部用元语言实现(有点难),还是全部用代码实现?

假设是后者,我知道有几个语言解析器生成器工具包。第一个是Boost's Spirit,它是用C++实现的。我用过,效果很好。但是,当我使用它时,您几乎需要“升压学”的研究生学位才能充分理解它的错误消息,以便在合理的时间内让任何东西正常工作。

我知道的另一个是OpenToken,它是一个用Ada 实现的解析器生成工具包。 Ada 没有 C++ 模板中的error-novel 问题,因此 OpenToken 更易于使用。但是,你必须在 Ada 中使用它...

典型的函数式语言允许您(大部分)在语言本身内实现您喜欢的任何子语言,这要归功于它们对 lambda 和元编程等事物的内在良好支持。然而,他们的解析器往往更慢。如果您只是解析一两个配置文件,那真的完全没有问题。如果您一次要解析数百个文件,这将是一个巨大的问题。

【讨论】:

  • 我认为他的意思是,如果有意义的话,他可能希望解析器生成器最终支持他正在实现的语言作为最终解析器的语言。
  • @500 - 内部服务器错误 - 并非如此
  • 澄清了第二点; --edit:将在一分钟内添加对第三点的说明。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-05
相关资源
最近更新 更多