我正在使用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。真是痛苦。

09-27 11:50