本文介绍了ANTLR解析树修改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm using ANTLR4 to create a parse tree for my grammar, what I want to do is modify certain nodes in the tree. This will include removing certain nodes and inserting new ones. The purpose behind this is optimization for the language I am writing. I have yet to find a solution to this problem. What would be the best way to go about this?

推荐答案

虽然目前没有真正支持或工具来重写树,但很有可能做到.甚至没有那么痛苦.

While there is currently no real support or tools for tree rewriting, it is very possible to do. It's not even that painful.

ParseTreeListener 或您的 MyBaseListener 可以与 ParseTreeWalker 一起使用来遍历您的解析树.

The ParseTreeListener or your MyBaseListener can be used with a ParseTreeWalker to walk your parse tree.

从这里,您可以使用 ParserRuleContext.removeLastChild() 删除节点,但是在执行此操作时,您必须注意 ParseTreeWalker.walk:

From here, you can remove nodes with ParserRuleContext.removeLastChild(), however when doing this, you have to watch out for 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.

You must replace removed nodes with something if the walker has visited parents of those nodes, I usually pick empty ParseRuleContext objects (this is because of the cached value of n in the method above). This prevents the ParseTreeWalker from throwing a NPE.

添加节点时,确保将 ParseRuleContext 上的可变父节点设置为新的父节点.此外,由于上述方法中缓存了 n,一个好的策略是检测需要更改的位置.希望您的更改在 walk 中进行,因此 ParseTreeWalker 将在同一遍中遍历它们(否则您可能需要多次传递...)

When adding nodes, make sure to set the mutable parent on the ParseRuleContext to the new parent. Also, because of the cached n in the method above, a good strategy is to detect where the changes need to be before you hit where you want your changes to go in the walk, so the ParseTreeWalker will walk over them in the same pass (other wise you might need multiple passes...)

您的伪代码应如下所示:

Your pseudo code should look like this:

public void enterRewriteTarget(@NotNull MyParser.RewriteTargetContext ctx){
    if(shouldRewrite(ctx)){
        ArrayList<ParseTree> nodesReplaced = replaceNodes(ctx);
        addChildTo(ctx, createNewParentFor(nodesReplaced));
    }
}

我已经使用这种方法编写了一个将同步内部语言编译成异步 javascript 的转译器.非常痛苦.

I've used this method to write a transpiler that compiled a synchronous internal language into asynchronous javascript. It was pretty painful.

这篇关于ANTLR解析树修改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-01 17:52