18

我注意到明显缺乏用函数式语言创建解析器的 LL 解析器。我一直在寻找但没有成功的理想发现是为 ANTLR 风格的 LL(*) 语法生成 Haskell 解析器(对语法进行模次小重新格式化),并且令每一个具有功能的解析器生成器感到惊讶我发现的语言目标是某种 LR 解析器。

我想将我正在研究的这种语言的解析器转换为语言本身的功能特性,从 ANTLR 到自托管,如果我可以将某些几乎可以肯定在另一种功能语言中正确的东西移植到我的语言中,这将有很大帮助(最好是我熟悉的 Haskell 和 Scala),而不必完全从头开始重写它,尽管最终我可能会这样做,因为核心语言很小。

在这一点上,甚至不仅仅是解决方案,我很好奇为什么没有这样的 LL(*) 甚至 LL(k) 解析器生成器,但是有许多 LR 生成器,因为 LL 似乎天生就更容易。

4

6 回答 6

20

造成这种情况的主要原因是大多数用函数式语言编写的 LL(k) 解析器只是使用解析器组合器实现的,因为生成解析器组合器库的最简单路径是递归下降

Haskell 的parsecattoparsecpolyparse以及 Scala 的常用解析器组合器都产生了有效的 LL(*) 解析器。

parsec 和 attoparsec 都要求您使用显式的 try 组合器来进行回溯,但这只是为了提高效率,scala解析器组合器也可以处理packrat 解析

考虑Brent Yorgey 最近的未绑定公告中的以下片段:

parseAtom = parens parseTerm
    <|> var <$> ident
    <|> lam <$> brackets ident <*> parseTerm

很容易看到原始语法。

LR 解析器需要更复杂的预处理来生成表以有效执行,因为使用递归上升之类的直接手动编码非常糟糕。

通过将解析器组合器实现为 EDSL 而不是外部工具,您可以更好地使用编程语言的高级特性。您可以使部分语法更高阶,将词法分析器直接构建到解析器中,等等。典型的 LR 解析器生成器不能做这些事情,或者只能在有限的上下文中以特别的方式提供它们,因为需要最终能够发出表格。

于 2011-04-01T14:43:37.870 回答
5

你启发了我将一个旧的爱好项目发布到https://github.com/nrnrnr/ebnf。它支持标准 ML 的 LL(1) 解析器生成。适应 Haskell 并不难,只要你能找到懂 Icon 的人。

爱德华关于人们倾向于使用组合子进行解析的评论是非常正确的,但是一个工具可以找到 FIRST 和 FOLLOW 集合并且会抱怨歧义。

EBNF 的主要优点是它的表示法:序列、Maybe类型和保留字都原生支持,没有额外的麻烦。

于 2014-06-25T18:01:02.920 回答
3

SML 拥有 ml-antlr 已有几年了:

http://www.classes.cs.uchicago.edu/archive/2007/winter/22610-1/docs/lpt-manual.pdf

Scheme也有sllgen。

至于为什么 LR 解析器生成器比 LL 多得多 - 手动编写 LR 解析器很困难,所以你真的需要一个生成器。使用 LL 解析器,手动编码的实现仍然与语法匹配,因此对生成器的需求要少得多。

于 2011-04-01T07:49:05.220 回答
2

使用 Scala,您可以使用所有现有的 Java 工具而无需太多开销。JavaCC 是一个 LL(k) 解析器生成器。您可以使用它自动创建具体的语法树并在 Scala 中执行其他所有操作。我实际上是为一个小项目这样做的,仅仅是因为 JavaCC 语法已经存在。

于 2011-04-01T06:06:48.107 回答
1

在描述正在开发的 LL(k) 解决方案之前,我将解释为什么我没有使用我知道的其他可用选项。

  1. 解析器组合库,即递归下降解析器,不会:

    • 保证上下文无关文法(CFG),因为它们不计算首集和后集。
    • 执行高效的表驱动 k 标记的前瞻。相反,他们进行低效的回溯。
    • 不要做更有效的基于堆栈的解析算法。

    缺乏上下文自由表现为语法中的歧义,例如源代码行开头的运算符(在未发送分号的行之后)是否是其右侧表达式的前缀一元,或前面源代码行末尾的表达式上的中缀二元运算符。

  2. JavaCC 有以下缺点:

    • 将词法分析器和解析器生成混为一谈。
    • 过于冗长的 BNF 语法语法。
    • 输出 Java,我想要 Scala(最终是 Copute)。
    • afaics 不会在语法和 AST 中对名称进行紧密耦合。
    • afaics 单片源代码,例如不能(轻松)提取模块以生成第一组和后续组(以便我可以插入自己的解析器生成)。

我正在尝试创建一个将输出到 Scala 的 LL(k) 解析器生成器,然后希望引导以输出我正在开发的语言(Copute,它将编译为 Scala)。

我目前的尝试是使用 SLK 语法语法的一个子集,因此可以使用 SLK 工具来验证语法是否与上下文无关。

于 2011-12-02T04:32:08.923 回答
-2

您可以使用ANTLR在 Java(以及其他)中生成 LL* 解析器,从而生成.classresp.jar文件。

于 2011-04-04T19:39:52.433 回答