问题描述
我正在使用 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解析树修改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!