在antlr中,有没有办法在AST模式下获取CommonTree的解析文本?

时间:2012-10-17 19:17:21

标签: java antlr

一个简单的例子:

(grammar):

stat: ID '=' expr NEWLINE -> ^('=' ID expr)

expr: atom '+' atom -> ^(+ atom atom)

atom: INT | ID

...

(input text): a = 3 + 5

'3 + 5'的相应CommonTree包含一个'+'标记和两个子标记(3,5)。

此时,恢复解析为此树('3 + 5')的原始输入文本的最佳方法是什么?

我已经获得CommonTree对象中单个标记的文本,位置和行号,因此从理论上讲,可以确保只丢弃空白标记并使用此信息将它们拼凑在一起,但它看起来很容易出错。

有更好的方法吗?

2 个答案:

答案 0 :(得分:3)

  

有更好的方法吗?

更好,我不知道。当然, 是另一种方式。你决定什么更好。

另一种选择是创建自定义AST节点类(和相应的节点适配器),并在解析期间将匹配的文本添加到此AST节点。这里的诀窍是不使用skip(),它会丢弃词法分析器中的令牌,而是将其放在HIDDEN通道上。这实际上是相同的,但是,这些(隐藏的)令牌匹配的文本仍然可以在解析器中使用。

快速演示:将所有这3个文件放在名为demo的目录中:

演示/ T.G

grammar T;

options {
  output=AST;
  ASTLabelType=XTree;
}

@parser::header {
  package demo;
  import demo.*;
}

@lexer::header {
  package demo;
  import demo.*;
}

parse
 : expr EOF -> expr
 ;

expr
@after{$expr.tree.matched = $expr.text;}
 : Int '+' Int ';' -> ^('+' Int Int)
 ;

Int
 : '0'..'9'+
 ;

Space
 : ' ' {$channel=HIDDEN;}
 ;

演示/ XTree.java

package demo;

import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;

public class XTree extends CommonTree {

  protected String matched;

  public XTree(Token t) {
    super(t);
    matched = null;
  }
}

演示/ Main.java

package demo;

import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;

public class Main {

  public static void main(String[] args) throws Exception {
    String source = "12    +  42 ;";
    TLexer lexer = new TLexer(new ANTLRStringStream(source));
    TParser parser = new TParser(new CommonTokenStream(lexer));
    parser.setTreeAdaptor(new CommonTreeAdaptor(){
      @Override
      public Object create(Token t) {
        return new XTree(t);
      }
    }); 
    XTree root = (XTree)parser.parse().getTree();
    System.out.println("tree    : " + root.toStringTree());
    System.out.println("matched : " + root.matched);    
  }
}

您可以通过打开shell并转到保存demo目录的目录来运行此演示并执行以下命令:

java -cp demo/antlr-3.3.jar org.antlr.Tool demo/T.g
javac -cp demo/antlr-3.3.jar demo/*.java
java -cp .:demo/antlr-3.3.jar demo.Main

将产生以下输出:

tree    : (+ 12 42)
matched : 12    +  42 ;

答案 1 :(得分:0)

另一种可能性是使用具有多种TokenRewriteStream方法的toString()

借用@Bart Kiers的例子Demo/Main.java

TokenRewriteStream tokens = new TokenRewriteStream(lexer)
TParser parser = new TParser(tokens);
...
tokens.toString(n.getTokenStartIndex(), n.getTokenStopIndex() + 1).trim()

所以给你的解析树的任何节点'n',如上所述在它上面调用toString()将产生“生成”这个节点的字符串。