虽然目前没有真正的支持或树重写工具,但很有可能做到这一点。甚至没有那么痛苦。
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。很痛苦。