我正在使用ANTLR4为我的语法创建一个分析树,我想要做的是修改树中的某些节点。这将包括删除某些节点并插入新的节点。这背后的目的是对我正在编写的语言进行优化。我还没有找到解决这个问题的方法。最好的方法是什么?
最佳答案
虽然目前尚无真正的支持或工具来重写树,但很有可能做到这一点。甚至没有那么痛苦。ParseTreeListener
或您的MyBaseListener
可以与ParseTreeWalker
一起使用,以遍历您的解析树。
在这里,您可以使用ParserRuleContext.removeLastChild()
删除节点,但是在执行此操作时,您必须当心ParseTreeWalker.walk
:
public void walk(ParseTreeListener listener, ParseTree t) {
if ( t instanceof ErrorNode) {
listener.visitErrorNode((ErrorNode)t);
return;
}
else if ( t instanceof TerminalNode) {
listener.visitTerminal((TerminalNode)t);
return;
}
RuleNode r = (RuleNode)t;
enterRule(listener, r);
int n = r.getChildCount();
for (int i = 0; i<n; i++) {
walk(listener, r.getChild(i));
}
exitRule(listener, r);
}
如果walker访问了这些节点的父节点,则必须替换掉已删除的节点,我通常会选择空的
ParseRuleContext
对象(这是由于上述方法中n
的缓存值)。这样可以防止ParseTreeWalker
抛出NPE。添加节点时,请确保将
ParseRuleContext
上的可变父级设置为新的父级。另外,由于上述方法中缓存了n
,一个好的策略是在击中您希望将更改保存到walk
的位置之前,检测出需要将更改保存在的位置,因此ParseTreeWalker
将在同一位置遍历它们通过(否则,您可能需要多次通过...)您的伪代码应如下所示:
public void enterRewriteTarget(@NotNull MyParser.RewriteTargetContext ctx){
if(shouldRewrite(ctx)){
ArrayList<ParseTree> nodesReplaced = replaceNodes(ctx);
addChildTo(ctx, createNewParentFor(nodesReplaced));
}
}
我已经使用这种方法编写了一个编译器,该编译器将同步内部语言编译为异步javascript。真是痛苦。