antlr书中有以下示例代码,用于使用语义谓词来解决语法歧义:
// predicates/PredCppStat.g4
@parser::members {
Set<String> types = new HashSet<String>() {{add("T");}};
boolean istype() { return types.contains(getCurrentToken().getText());}
}
stat: decl ';' {System.out.println("decl "+$decl.text);}
| expr ';' {System.out.println("expr "+$expr.text);}
;
decl: ID ID
| {istype()}? ID '(' ID ')'
;
expr: INT
| ID
| {!istype()}? ID '(' expr ')'
;
ID : [a-zA-Z]+ ;
INT : [0-9]+ ;
WS : [ \t\n\r]+ -> skip ;
这里,谓词是规则中调用的第一个函数,用于确定是否应该触发规则。它使用getCurrentToken()来做出决定。
但是,如果我们稍微改变语法,使用分层名称而不是简单ID,如下所示:
decl: ID ID
| {istype()}? hier_id '(' ID ')'
;
expr: INT
| ID
| {!istype()}? hier_id '(' expr ')'
;
hier_id : ID ('.' ID)* ;
然后istype()谓词不能再使用getCurrentToken来做出决定。它需要hier_id中的整个标记链来确定链是否是类型符号。
这意味着,我们需要执行以下操作之一:
(1)将谓词放在hier_id之后,并从istype()访问这些值。这可能吗?我试过了,我在生成的代码上遇到了编译器错误。
(2)将语法分解为子规则,然后在消耗hier_id标记之后放置istype()。但这会破坏语法的可读性,我不想这样做。
解决此问题的最佳方法是什么?我使用的是antlr-4.6。
答案 0 :(得分:0)
一种解决方案是使ID本身包含&#39;。&#39;,从而使hier_id成为词法分析器。在这种情况下,语义谓词对getCurrentToken()的调用将可以访问完整的名称链。
请注意,如果hier_id成为词法分析器,hier_id将包含ID。这需要付出代价。如果语法只有ID的其他引用(我猜它会有),那么你必须在所有这些情况下添加谓词以避免错误匹配。这会降低解析器的速度。
所以我猜这个问题,就其一般意义而言(即如果当前的知识信息不足以做出决定,如何通过修脚来限制规则)仍然需要Antlr4专家来回答。