ANTLR解析树修改

Mar*_*ran 8 tree grammar parsing antlr

我正在使用ANTLR4为我的语法创建一个解析树,我想要做的是修改树中的某些节点.这将包括删除某些节点并插入新节点.这背后的目的是优化我正在编写的语言.我还没有找到解决这个问题的方法.最好的方法是什么?

Nth*_*alk 5

尽管目前尚无真正的支持或工具来重写树,但仍有可能这样做。甚至没有那么痛苦。

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);
}
Run Code Online (Sandbox Code Playgroud)

必须如果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));
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经使用这种方法编写了一个编译器,该编译器将同步内部语言编译为异步javascript。真是痛苦。


Joe*_*e23 5

另一种方法是编写一个ParseTreeVisitor将树转换回字符串的方法。(在某些情况下这可能是微不足道的,因为您只是TerminalNode.getText()aggregateResult(..). 中调用和连接。)

然后将修改添加到此访问者,以便生成的字符串表示包含您尝试实现的修改。

然后解析字符串,你会得到一个带有所需修改的解析树。

这在某些方面肯定是骇人听闻的,因为您对字符串进行了两次解析。另一方面,该解决方案不依赖于 antlr 实现细节。