ANTLR解析树修改

时间:2014-05-06 06:42:16

标签: parsing tree antlr grammar

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

3 个答案:

答案 0 :(得分:4)

虽然目前没有真正的支持或树重写工具,但很有可能。它甚至没那么痛苦。

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的转换器。这真是太痛苦了。

答案 1 :(得分:4)

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

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

然后解析字符串,得到一个包含所需修改的解析树。

这在某些方面肯定是hackish,因为你解析了两次字符串。另一方面,该解决方案不依赖于antlr实现细节。

答案 2 :(得分:0)

我需要类似的东西来进行简单的转换。我最终使用了一个 ParseTreeWalker 和一个自定义的 ...BaseListener,我在其中覆盖了 enter... 方法。在此方法中,ParserRuleContext.children 可用且可以操作。

class MyListener extends ...BaseListener {
    @Override
    public void enter...(...Context ctx) {
        super.enter...(ctx);
        ctx.children.add(...);
    }
}

new ParseTreeWalker().walk(new MyListener(), parseTree);