5

一个简单的例子:

(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,因此理论上可以确保仅丢弃空白标记并使用此信息将它们拼凑在一起,但它看起来容易出错。

有一个更好的方法吗?

4

2 回答 2

3

有一个更好的方法吗?

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

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

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

演示/Tg

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 并 cd-ing 到包含该目录的demo目录并执行以下命令来运行此演示:

java -cp demo/antlr-3.3.jar org.antlr.Tool demo/Tg
javac -cp 演示/antlr-3.3.jar 演示/*.java
java -cp .:demo/antlr-3.3.jar demo.Main

这将产生以下输出:

树:(+ 12 42)
匹配:12 + 42;
于 2012-10-17T20:27:28.563 回答
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() 将产生“生成”该节点的字符串。

于 2016-12-13T20:10:16.003 回答