我正在使用这个按比例缩小的语法解析类似C ++的声明(删除了许多细节以使其成为一个完全有效的示例)。它无法神秘地工作(至少对我而言)。它与上下文相关谓词的使用有关吗?如果是,实现“计算子节点逻辑数量”的正确方法是什么?
grammar CPPProcessor;
cppCompilationUnit : decl_specifier_seq? init_declarator* ';' EOF;
init_declarator: declarator initializer?;
declarator: identifier;
initializer: '=0';
decl_specifier_seq
locals [int cnt=0]
@init { $cnt=0; }
: decl_specifier+ ;
decl_specifier : @init { System.out.println($decl_specifier_seq::cnt); }
'const'
| {$decl_specifier_seq::cnt < 1}? type_specifier {$decl_specifier_seq::cnt += 1;} ;
type_specifier: identifier ;
identifier:IDENTIFIER;
CRLF: '\r'? '\n' -> channel(2);
WS: [ \t\f]+ -> channel(1);
IDENTIFIER:[_a-zA-Z] [0-9_a-zA-Z]* ;
我需要实施标准C ++规则,type_specifier
下允许的decl_specifier_seq
不超过1 type_specifier
。
decl_specifier_seq
之前的语义谓词似乎是解决方案。并且计数自然在decl_specifier_seq
中声明为局部变量,因为嵌套int t=0;
是可能的。
但似乎与我使用的上下文相关的语义谓词将产生不正确的解析,即引用$ attributes的语义谓词。首先是一个输入文件,结果正确(以说明普通的解析树是什么样的):
int t;
和解析树:
但是,没有'= 0'的输入有助于解析
0
1
line 1:4 no viable alternative at input 't'
1
$decl_specifier_cnt::cnt
解析失败,出现“无可行替代”错误(控制台中打印的数字是t
值的调试打印,作为测试条件的验证)。即,语义谓词不能阻止type_specifier
被解析为t
而init_declarator
不再被视为$decl_specifier_seq::cnt
。这里有什么问题?是因为使用了$decl_specifier_seq::cnt
的上下文相关谓词吗?
是否意味着上下文相关谓词不能用于实现“计算子节点数”的逻辑?
修改
我尝试了新版本,其谓词使用成员变量而不是....
@parser::members {
public int cnt=0;
}
decl_specifier
@init {System.out.println("cnt:"+cnt); }
:
'const'
| {cnt<1 }? type_specifier {cnt++;} ;
,而且令人惊讶的是语法现在可以证明Context Dependent谓词确实导致了先前的语法失败:
/*$ctx*/
产生了一个普通的解析树:
如果我们必须使用成员变量来替换局部变量以避免上下文敏感的谓词,那么这就产生了如何支持嵌套规则的问题?
一个奇怪的结果是,如果我在谓词后添加decl_specifier
@init {System.out.println("cnt:"+cnt); }
:
'const'
| {cnt<1 /*$ctx*/ }? type_specifier {cnt++;} ;
,它会再次失败:
line 1:4 no viable alternative at input 't'
no viable alternative
使用/*$ctx*/
解析失败。尽管实际逻辑仅使用成员变量,为什么$decl_specifier_seq::cnt
会导致解析失败,就像使用/*$ctx*/
时一样?
并且,如果没有{{1}},则会出现与@init块之前调用的谓词相关的另一个问题(described here)
答案 0 :(得分:1)
ANTLR 4在两种情况下评估语义谓词。
代码生成器不会评估目标语言的语义,因此当$ctx
出现/*$ctx*/
时,它无法知道{{1}}在语义上是不相关的。这两种情况都会导致谓词被视为依赖于上下文。