如何使用ANTLR重写具有复合根的子树?
我有一个antlr语法,其子树如下:如何使用ANTLR重写具有复合根的子树?,antlr,antlr3,Antlr,Antlr3,我有一个antlr语法,其子树如下: ^(type ID) 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;}; 我想转换为: ^(ty
^(type ID)
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;};
我想转换为:
^(type DUMMY ID)
其中类型为“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)
错误消息将继续。到目前为止,我的调试:
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;};
和模式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,但我也遇到了同样的崩溃。我对树模式没有太多经验,无论是否重写。但是当在其中使用重写规则时,我相信您的选项还应该包括
rewrite=true代码>。最终的ANTLR引用不能处理它们,所以我不能完全确定(请查看以获取更多信息)
然而,对于这种(相对)简单的重写,实际上并不需要单独的语法。您可以将DUMMY
设为一个虚构的标记,并将其注入其他解析器规则中,如下所示:
^(type ID)
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
进入以下步骤:
请注意,如果您的lexer还打算将输入“dummy”
标记为dummy
标记,请将标记{…}
块更改为:
tokens {
DUMMY='dummy';
}
您仍然可以在其他规则中插入一个伪
。要使重写工作正常,有两个小问题需要解决,一个是重写中的问题,另一个是模式中的问题
重写
语法在输出AST中生成^(type ID)
作为根元素,如输出(a条)(b条)
中所示。无法转换根元素,因为转换实际上是子交换的一种形式:元素的父元素删除该元素,并将其替换为新的“转换”版本。如果没有父项,则会出现错误无法将单个子项设置为列表
。添加根是创建一个虚构的令牌root
或任何您喜欢的名称,并在您的入门级规则的AST生成中引用它,就像这样:test:id+->^(root id+代码>
产生错误的模式
语法被类型
规则弄糊涂了:类型:“a”|“b”代码>作为重写的一部分。我不知道这里的底层细节,但显然树解析器在编写转换时不维护访问的根规则的状态,如^(type ID)
中的type
(或者它不能或不应该,或者可能是其他一些限制)。解决此问题的最简单方法是进行以下两项更改:
- 让文本“a”和“b”匹配lexer中的规则
ID
,方法是将Rewrite
中的规则type
从键入:“a”|“b”代码>只需类型:ID代码>李>
- 让
模式中的规则bu
与^(ID)
匹配,并转换为^(ID伪ID)
现在,对Rewrite
的main
进行一些小的调试更改后,输入“一个foo\nb条”
将产生以下输出:
(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))
以下是我更改的文件:
重写
模式g
非常好的问题,顺便说一句。一个正确的问题,这样其他人可以看到你看到的确切错误!独生子女信息非常有用。非常感谢。在使用ID而不是a | b时,使用lexer/parser的目的不就失败了吗?我的实际语法比较复杂。a和b是树可以从中开始的许多ID之一。我只想要有a和b的。我遗漏了什么吗?@mmccoo你可以用最适合你的标记来描述“a”和“b”(我把“a”和“b”塞进ID
,因为ID
已经描述了它们),只要你最终得到了一个特定的标记来匹配模式。例如,假设lexer规则TYPE
是TYPE:'a'|'b'代码>和令牌解析器规则id
是id:TYPE-id代码>。然后Pattern
的bu
变成bu:^(类型ID)->^(类型伪ID)代码>。如果有帮助的话,我可以更新我的答案。@mmccoo如果您不知道,另一件需要注意的事情是,在解析器规则中使用字符串会在幕后悄悄地创建一个新的lexer规则。因此,令牌规则键入:“a”|“b”代码>被视为类型:SOMERULE_A | SOMERULE_B
,其中SOMERULE\u A
和SOMERULE\u B
是新的、不相关的词法规则,分别匹配“A”和“B”(我不知道它们的实际名称,因为它们在正常意义上不可见)。在lexer中显式测试“a”和“b”,如在中键入:“a'|“b”代码>,使转换树和跟踪令牌相关问题变得更容易。我最初的回答是,“是的!就是这样。”。。。。但是当我这样做的时候,其他一些寻找a | c和a | b | c的法律规则变得无法实现。所有这些让我怀疑人们是否真的使用重写。看起来非常有用,但我在google上看不到太多关于它的流量。@mmccoo“无法访问的lexer规则”警告是一个非常常见的ANTLR问题,不管是好是坏,都与重写无关。我认为重写被低估了,但我怀疑大多数ANTLR语法不够复杂,不足以证明使用它们是正确的,或者人们满足于在令牌解析器中以奇怪的方式操纵AST输出(与Bart K的示例不同,Bart K的示例是一种避免编写令牌插入重写器的有用方法)。