一个简单的例子:
(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
对象中单个标记的文本,位置和行号,因此从理论上讲,可以确保只丢弃空白标记并使用此信息将它们拼凑在一起,但它看起来很容易出错。
有更好的方法吗?
答案 0 :(得分:3)
有更好的方法吗?
更好,我不知道。当然, 是另一种方式。你决定什么更好。
另一种选择是创建自定义AST节点类(和相应的节点适配器),并在解析期间将匹配的文本添加到此AST节点。这里的诀窍是不使用skip()
,它会丢弃词法分析器中的令牌,而是将其放在HIDDEN
通道上。这实际上是相同的,但是,这些(隐藏的)令牌匹配的文本仍然可以在解析器中使用。
快速演示:将所有这3个文件放在名为demo
的目录中:
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;}
;
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;
}
}
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()将产生“生成”这个节点的字符串。