我发现有C#antlr4语法。我为该语法为C#构建了antlr4解析器。有用。我可以走遍解析树,看看有些节点有一些孩子。
现在我想从这个解析树中生成C#源代码。
我可以以某种方式生成(语法之外)antlr解析器的逆,而不是解析,当给定解析树将生成将导致此解析树的源代码?
编辑:
我目前在CoffeeScript中的尝试是使用原始源以及antlr放入节点的开始和停止位置,使用源代码片段来处理源代码树,然后再次遍历以打印源代码。唯一的问题是多个节点从源代码中的完全相同的空间开始。为了解决这个问题,我有一些令人讨厌的逻辑,只将源代码放在最深的节点中:
antlr = require 'antlr4'
{CSharp4Parser} = require './CSharp4Parser'
{CSharp4Lexer} = require './CSharp4Lexer'
input = "namespace A { class B {}; class C {} }"
cstream = new antlr.InputStream(input)
lexer = new CSharp4Lexer(cstream)
tstream = new antlr.CommonTokenStream(lexer)
parser = new CSharp4Parser(tstream)
parser.buildParseTrees = true ;
tree = parser.compilation_unit();
decorateWithSource = new antlr.tree.ParseTreeListener();
start =
prev: null
stop =
prev: null
o = (msg) -> process.stdout.write(msg)
decorateWithSource.enterEveryRule = (a) ->
if start.prev
start.prev.before = input.substr(start.prev.start.start, a.start.start - start.prev.start.start)
if stop.prev
stop.prev.after = input.substr(stop.prev.stop.start, a.start.start - stop.prev.stop.start)
start.prev = a
stop.prev = null
decorateWithSource.exitEveryRule = (a) ->
if start.prev
start.prev.before = input.substr(start.prev.start.start, a.stop.start - start.prev.start.start)
if stop.prev
stop.prev.after = input.substr(stop.prev.stop.start, a.stop.start - stop.prev.stop.start)
start.prev = null
stop.prev = a
walker = new antlr.tree.ParseTreeWalker();
walker.walk(decorateWithSource, tree);
stop.prev.after = input.substr(stop.prev.stop.start)
printOut = new antlr.tree.ParseTreeListener();
printOut.enterEveryRule = (a) ->
o (a.before || ''), ' -> '+parser.ruleNames[a.ruleIndex]
printOut.exitEveryRule = (a) ->
o (a.after || ''), ' < '+parser.ruleNames[a.ruleIndex]
walker.walk(printOut, tree);
我想要做的是将C#源文件(来自重新编译的一些东西)读入树中,然后通过OMeta编写的变换器(将我的环境缩小到具有OMeta实现的语言) C#,js或coffeescript,可能是其他人)然后回写固定的源代码。
也许手动行走解析树来生成代码对我来说已经足够了。
答案 0 :(得分:3)
不容易; ANTLR实际上并不是为此而设计的。
您可以调查StringTemplates,它可以让您遍历树并吐出大致正确的代码。
如果您想要更详细地重新生成源,这还不够。在How to build a prettyprinter上查看我的答案。