如何从Haskell中的语法规范构建抽象语法树?
我正在从事一个项目,该项目涉及在一个非常小的Java子集中优化某些结构,用BNF形式化 如果我在Java中这样做,我将使用JTB和JavaCC的组合来构建AST。然后,访问者被用来操纵这棵树。但是,考虑到Haskell中有大量用于解析的库(parsec、happy、alex等),我在选择合适的库时有点困惑如何从Haskell中的语法规范构建抽象语法树?,haskell,abstract-syntax-tree,parsec,happy,zipper,Haskell,Abstract Syntax Tree,Parsec,Happy,Zipper,我正在从事一个项目,该项目涉及在一个非常小的Java子集中优化某些结构,用BNF形式化 如果我在Java中这样做,我将使用JTB和JavaCC的组合来构建AST。然后,访问者被用来操纵这棵树。但是,考虑到Haskell中有大量用于解析的库(parsec、happy、alex等),我在选择合适的库时有点困惑 那么,简单地说,当在BNF中指定语言时,哪个库提供了构建AST的最简单方法?在惯用的Haskell中修改这棵树的最佳方法是什么?在Haskell中,有两种主要的解析方法,解析组合符或解析器生成
那么,简单地说,当在BNF中指定语言时,哪个库提供了构建AST的最简单方法?在惯用的Haskell中修改这棵树的最佳方法是什么?在Haskell中,有两种主要的解析方法,解析组合符或解析器生成器。既然你已经有了BNF,我建议后者 一个好的是。GHC的解析器IIRC就是用这个来编写的,所以你会有很好的同伴 接下来,您将有一大堆要解析的数据声明:
data JavaClass = {
className :: Name,
interfaces :: [Name],
contents :: [ClassContents],
...
}
data ClassContents = M Method
| F Field
| IC InnerClass
还有表达方式和你需要的任何东西。最后,您将把这些组合成如下内容
data TopLevel = JC JavaClass
| WhateverOtherForms
| YouWillParse
一旦有了它,整个AST就可以表示为一个TopLevel
,或者它们的列表,具体取决于您解析的类/文件的数量
从这里开始取决于你想做什么。有许多库,例如syb
(废弃样板文件),可以让您编写非常简洁的树遍历和修改<代码>镜头也是一个选项。至少签出Data.Traversable
和Data.Foldable
要修改树,您可以执行以下操作
ignoreInnerClasses :: JavaClass -> JavaClass
ignoreInnerContents c = c{contents = filter isClass $ contents c}
-- ^^^ that is called a record update
where isClass (IC _) = True
isClass _ = False
然后,您可能会使用类似于syb
的东西来编写
everywhere (mkT ignoreInnerClass) toplevel
它将遍历所有内容并将ignoreInnerClass
应用于所有javaclass
。这可以在lens
和许多其他库中实现,但是syb
非常容易阅读 Alex+很高兴
有许多方法可以修改/研究解析术语(AST)。要搜索的关键字是“数据类型通用”编程。但要注意:这是一个复杂的话题
这里提供了拉链的通用实现:
另外,我从未使用过bnfc meta(由@phg建议),但我强烈建议您查看(关于hackage:)。基本方法是用带注释的BNF样式编写语法,它将自动为语法生成AST、解析器和漂亮的打印机
BNFC的适用程度取决于语法的复杂性。如果它不是上下文无关的,您可能很难取得任何进展(我确实在构建上下文相关扩展方面取得了一些成功,但该代码现在可能已经有点过时了)。另一个缺点是AST将非常直接地反映语法规范。但是,既然您已经有了BNF规范,那么为BNFC添加必要的注释应该相当简单,因此这可能是获得可用AST的最快方法。即使您决定采用另一种方法,您也可以将生成的数据类型作为手写版本的起点。您还可以查看Haskell编译器系列,该系列很好地介绍了如何使用alex并乐于解析Java的子集:。因为您的语法可以用BNF表示,它属于可以使用移位减少解析器(LALR语法)有效解析的语法类。这种高效的解析器可以由解析器生成器yacc/bison(C,C++)或其Haskell等价物Happy生成 这就是为什么我会在你的情况下使用“快乐”。它采用BNF形式的语法规则,并直接从中生成解析器。生成的解析器将接受语法规则描述的语言,并生成AST(抽象语法树)。《快乐用户指南》相当不错,让您快速入门: 要转换生成的AST,泛型编程是一个好主意。以下是关于如何在Haskell中从零开始以实用的方式实现这一点的经典解释:
我正是利用这一点为一种小型领域特定语言构建了一个编译器,这是一个简单而简洁的解决方案。还有一种更酷、更具实验性的可能性,即使用模板haskell从源代码中提供的BNF构造解析器,称为。不过,我不知道它在实际应用中有多有用。(如果您是Haskell的新手…)在Haskell或其他现代函数语言(如SML和Caml)中没有必要使用JTB这样的树生成器。代数类型与JTB或类似工具的树规范一样简洁——它们是用函数语言定义数据类型的标准方法。在Haskell中,您还可以自动派生数据类型上的许多有用函数,如显示,序列化和结构平等。有两种高级工具用于AST操作和优化:
uuagc
属性语法的预处理器和hoopl
用于基于可组合事实格的优化的通用库。如果您有BNF,这绝对是最简单的方法!数据类型泛型编程看起来确实非常有趣。我试试看。我喜欢清楚的答案。尽管我只处理一个子集,但从头开始编写数据类型的工作太多了。然而,我计划使用syb进行转换。