我有一个带有这样子树的antlr语法:
^(type ID)
我要转换为:
^(type DUMMY ID)
其中type为'a'|'b'。
注意:我真正想做的是通过生成虚拟名称将匿名实例化转换为显式。
我已将其缩小到下面的语法,但我得到了这个:
(a bar) (b bar)
got td
got bu
Exception in thread "main" org.antlr.runtime.tree.RewriteEmptyStreamException: rule type
at org.antlr.runtime.tree.RewriteRuleElementStream._next(RewriteRuleElementStream.java:157)
at org.antlr.runtime.tree.RewriteRuleSubtreeStream.nextNode(RewriteRuleSubtreeStream.java:77)
at Pattern.bu(Pattern.java:382)
错误消息继续。到目前为止我的调试:
进行此类重写的正确方法是什么?
我的主要语法Rewrite.g:
grammar Rewrite;
options {
output=AST;
}
@members{
public static void main(String[] args) throws Exception {
RewriteLexer lexer = new RewriteLexer(new ANTLRStringStream("a foo\nb bar"));
RewriteParser parser = new RewriteParser(new CommonTokenStream(lexer));
CommonTree tree = (CommonTree)parser.test().getTree();
System.out.println(tree.toStringTree());
CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree);
Pattern p = new Pattern(nodes);
CommonTree newtree = (CommonTree) p.downup(tree);
}
}
type
: 'a'
| 'b'
;
test : id+;
id : type ID -> ^(type ID["bar"]);
DUMMY : 'dummy';
ID : ('a'..'z')+;
WS : (' '|'\n'|'r')+ {$channel=HIDDEN;};
和Pattern.g
tree grammar Pattern;
options {
tokenVocab = Rewrite;
ASTLabelType=CommonTree;
output=AST;
filter=true; // tree pattern matching mode
}
topdown
: td
;
bottomup
: bu
;
type
: 'a'
| 'b'
;
td
: ^(type ID) { System.out.println("got td"); }
;
bu
: ^(type ID) { System.out.println("got bu"); }
-> ^(type DUMMY ID)
;
做编译:
java -cp ../jar/antlr-3.4-complete-no-antlrv2.jar org.antlr.Tool Rewrite.g
java -cp ../jar/antlr-3.4-complete-no-antlrv2.jar org.antlr.Tool Pattern.g
javac -cp ../jar/antlr-3.4-complete-no-antlrv2.jar *.java
java -classpath .:../jar/antlr-3.4-complete-no-antlrv2.jar RewriteParser
编辑1:我也尝试过使用antlr4,我也遇到了同样的崩溃。
答案 0 :(得分:2)
要解密才能解决两个小问题,Rewrite
中的一个问题和Pattern
中的另一个问题。
Rewrite
语法生成^(type ID)
作为输出AST中的根元素,如输出(a bar) (b bar)
中所示。无法转换根元素,因为转换实际上是一种子交换形式:元素的父元素删除元素并用新的“转换”版本替换它。没有父母,您将收到错误Can't set single child to a list
。添加根是创建一个虚构的令牌ROOT
或任何你喜欢的名称,并在你的入门级规则的AST生成中引用它,如下所示:test : id+ -> ^(ROOT id+);
。
Pattern
语法,即产生错误的语法,被type
规则混淆:type : 'a' | 'b' ;
作为重写的一部分。我不知道这里的低级细节,但显然树编译器在编写转换时不会在type
中保持被访问根规则的状态,如^(type ID)
(或者它可以'或者不应该,或者可能是其他一些限制)。解决此问题的最简单方法是进行以下两项更改:
ID
中的type
中的规则Rewrite
从type: 'a' | 'b';
更改为type: ID;
,让文字“a”和“b”与词法分析器中的bu
匹配。 Pattern
中的规则^(ID ID)
与^(ID DUMMY ID)
匹配,然后转换为Rewrite
。现在对main
的{{1}}进行一些小的调试更改,输入"a foo\nb bar"
会产生以下输出:
(ROOT (a foo) (b bar))
got td
got bu
(a foo) -> (a DUMMY foo)
got td
got bu
(b bar) -> (b DUMMY bar)
(ROOT (a DUMMY foo) (b DUMMY bar))
以下是我更改过的文件:
grammar Rewrite;
options {
output=AST;
}
tokens {
ROOT;
}
@members{
public static void main(String[] args) throws Exception {
RewriteLexer lexer = new RewriteLexer(new ANTLRStringStream("a foo\nb bar"));
RewriteParser parser = new RewriteParser(new CommonTokenStream(lexer));
CommonTree tree = (CommonTree)parser.test().getTree();
System.out.println(tree.toStringTree());
CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree);
Pattern p = new Pattern(nodes);
CommonTree newtree = (CommonTree) p.downup(tree, true); //print the transitions to help debugging
System.out.println(newtree.toStringTree()); //print the final result
}
}
type : ID;
test : id+ -> ^(ROOT id+);
id : type ID -> ^(type ID);
DUMMY : 'dummy';
ID : ('a'..'z')+;
WS : (' '|'\n'|'r')+ {$channel=HIDDEN;};
tree grammar Pattern;
options {
tokenVocab = Rewrite;
ASTLabelType=CommonTree;
output=AST;
filter=true; // tree pattern matching mode
}
topdown
: td
;
bottomup
: bu
;
td
: ^(ID ID) { System.out.println("got td"); }
;
bu
: ^(ID ID) { System.out.println("got bu"); }
-> ^(ID DUMMY ID)
;
答案 1 :(得分:1)
我对树模式没有太多经验,有或没有重写。但是当在其中使用重写规则时,我相信您的选项还应包括rewrite=true;
。最终的ANTLR参考文献没有处理它们,所以我不完全确定(有关更多信息,请查看ANTLR wiki)。
然而,对于这种(相对)简单的重写,你并不需要单独的语法。您可以在其他解析器规则中使DUMMY
成为虚构的令牌并注入,如下所示:
grammar T;
options {
output=AST;
}
tokens {
DUMMY;
}
test : id+;
id : type ID -> ^(type DUMMY["dummy"] ID);
type
: 'a'
| 'b'
;
ID : ('a'..'z')+;
WS : (' '|'\n'|'r')+ {$channel=HIDDEN;};
将解析输入:
a bar
b foo
进入以下AST:
请注意,如果您的词法分析器还要将输入"dummy"
标记为DUMMY
令牌,请将tokens { ... }
块更改为:
tokens {
DUMMY='dummy';
}
你仍然可以在其他规则中注入 DUMMY
。