我正在开发一个项目,该项目涉及在一小部分Java中优化某些构造,在BNF中形式化。
如果我在Java中这样做,我会使用构建AST的JTB和JavaCC的组合。然后访客用于操纵树。但是,考虑到在Haskell中解析庞大的库(parsec,happy,alex等),我在选择合适的库时有点困惑。
所以,简单地说,当在BNF中指定一种语言时,哪个库提供了构建AST的最简单方法?在惯用的Haskell中修改这个树的最佳方法是什么?
答案 0 :(得分:7)
在Haskell中有两种主要的解析方法,解析组合器或解析器生成器。既然你已经有了BNF,我建议使用后者。
好的是alex。 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
(废弃样板),可以让您编写非常简洁的树遍历和修改。 lens
也是一种选择。至少请查看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
应用于所有JavaClasses
。这可以在lens
和许多其他库中执行,但syb
非常容易阅读。
答案 1 :(得分:4)
Alex + Happy。
有许多方法可以修改/调查解析的术语(AST)。要搜索的关键字是“datatype-generic”编程。但要注意:这是一个复杂的话题......
http://people.cs.uu.nl/andres/Rec/MutualRec.pdf
http://www.cs.uu.nl/wiki/GenericProgramming/Multirec
它有一个拉链的通用实现:
http://hackage.haskell.org/packages/archive/zipper/0.3/doc/html/Generics-MultiRec-Zipper.html
答案 2 :(得分:4)
我从未使用bnfc-meta
(由@phg建议),但强烈建议您查看BNFC(关于hackage:http://hackage.haskell.org/package/BNFC)。基本方法是用带注释的BNF样式编写语法,它会自动为语法生成AST,解析器和漂亮的打印机。
BNFC的合适程度取决于语法的复杂程度。如果它不是没有上下文的,那么你可能很难取得任何进展(我确实成功地破解了上下文敏感的扩展,但是这些代码现在可能有点烂了)。另一个缺点是你的AST将直接反映语法规范。但由于您已经有了BNF规范,因此为BNFC添加必要的注释应该相当简单,因此它可能是获得可用AST的最快方法。即使您决定采用不同的路线,也可以将生成的数据类型作为手写版本的起点。
答案 3 :(得分:2)
您也可以查看Haskell编译器系列,它很好地介绍了使用alex并且很乐意解析Java的一个子集:http://bjbell.wordpress.com/haskell-compiler-series/。
答案 4 :(得分:1)
由于您的语法可以用BNF表示,因此它在使用shift-reduce解析器(LALR语法)有效解析的语法类中。这种有效的解析器可以由解析器生成器yacc / bison(C,C ++)或其Haskell等效的“Happy”生成。
这就是为什么我会在你的情况下使用“快乐”。它采用BNF形式的语法规则,并直接从中生成解析器。生成的解析器将接受语法规则描述的语言并生成AST(抽象语法树)。快乐的用户指南非常好,让您快速入门: http://www.haskell.org/happy/doc/html/
要转换生成的AST,通用编程是一个好主意。以下是从头开始以实用的方式在Haskell中执行此操作的经典解释: http://research.microsoft.com/en-us/um/people/simonpj/papers/hmap/
我已经用它来构建一个针对小型域特定语言的编译器,这是一个简单而简洁的解决方案。