使用Antlr访问器编辑AST

3
我是AntLR的新手,我正在努力做以下事情:
我想要做的是,在解析源文件(当然,我有一个有效的语法)并将AST存储在内存中后,通过访问者API去更改一些内容,然后再将其打印出来。
例如:
int foo() {
    y = x ? 1 : 2;
}

并将其转化为:

int foo() {
    if (x) {
       y = 1;
    else {
       y = 2;
    }
}

到目前为止,我已经有了解析这样的语法所需的适当语法,并且还编写了一些访问者方法,在正确位置时会调用这些方法。令我困惑的是,在访问期间我无法更改文本。

理想情况下,我希望有这样的功能:

public Void visitTernExpr(SimpleCParser.TernExprContext ctx) { 
  ctx.setText("something");
  return null; 
}

在我的主程序中,我希望将这个AST通过不同的访问者进行编辑,每个访问者都专门从事某些工作。 就像这样:

ANTLRInputStream input = new ANTLRInputStream(new FileInputStream(filename));
SimpleCLexer lexer = new SimpleCLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
SimpleCParser parser = new SimpleCParser(tokens);
ProgramContext ctx = parser.program();

MyChecker1 mc1 = new MyChecker1();
mc1.visit(ctx);
MyChecker2 mc2 = new MyChecker2();
mc1.visit(ctx);

ctx.printToFile("myfile");

有没有办法在AntLR中做这些事情,或者我所走的方向非常错误?
2个回答

3
你可以通过破坏AST节点和链接来完成ANTLR。你需要创建所有替换子树节点并将它们拼接到原位。然后,你需要实现“输出源代码文本”的遍历树;我建议你为此目的调查“字符串模板”。
但最终,你需要做很多工作才能达到这个效果。这是因为ANTLR工具的目标主要集中在“解析”上,其余的则由你来处理。
如果你想要用一组语法替换另一组,你真正需要的是一个程序转换系统。这些工具旨在内置所有上述功能,因此你不必重新发明轮子。它们通常也有源到源的转换,使得像你所展示的任务变得更加容易实现。
要使用我们的DMS程序转换引擎完成你的示例,你需要编写一个转换规则,然后应用它:
rule replace_ternary_assignment_by_ifthenelse
   (l: left_hand_side, c: expression, e1: expression, e2: expression):
       statement -> statement
    "\l = \c ? \e1 : \e2;"
=>  " if (\c)  \l = \e1; else \l = \e2 ";

DMS解析您的代码,构建AST,查找重写匹配项,为您构造/拼接所有这些替换节点。最后,DMS具有内置的漂亮打印机以重新生成文本。所有这些的目的是让您继续完成修改代码的任务,而不是在执行任务之前创建一个全新的工程任务。阅读我的文章“解析之后的生活”,可以通过我的个人简介或通过谷歌搜索更多相关主题来轻松找到。

[如果您访问DMS wikipedia页面,您会发现逆转换被用作示例,这很有趣]。


0
我会使用监听器,而且在遍历AST时可以修改它。您可以创建if/else上下文的新实例,然后用它替换三元运算符上下文。这是可能的,因为您有对规则父级的引用以及一个广泛的API来处理每个规则子级。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接