【问题标题】:ANTLR Parse tree modificationANTLR 解析树修改
【发布时间】:2014-05-06 06:42:15
【问题描述】:

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

【问题讨论】:

  • 不,AFAIK,没有办法做到这一点。你在说什么样的优化?另见:stackoverflow.com/questions/21278743/build-ast-in-antlr4
  • 我正在寻找修改树,更具体地说是终端节点及其值,例如,如果我有一个表达式 2 + 4,我想用值为 6 的节点替换它。这不是什么大的优化,但如果有就好了。
  • 不,没有办法替换解析树中的节点/子树。也许稍后:github.com/antlr/antlr4/issues/369
  • 好的,感谢您的宝贵时间。

标签: parsing tree antlr grammar


【解决方案1】:

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

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。很痛苦。

【讨论】:

    【解决方案2】:

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

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

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

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

    【讨论】:

      【解决方案3】:

      我需要类似的东西来进行简单的转换。我最终使用了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);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-01-12
        • 1970-01-01
        • 1970-01-01
        • 2012-04-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多