最近使用Antlr4时,遇到了编写的语法存在错误,但生成语法树并不显示错误,只是将报错位置及之后的全部token丢弃的情况。在此对案例进行分享,并分享一下我的解决方案。
一 问题复现
下边我用一个简化后的案例复现这个现象
文法文件如下(ASSIGN.g4):
grammar ASSIGN;
pfile
: assignStmt + //文件由一行或多行赋值语句组成
;
assignStmt
: ID '=' ID operator ID ';' // 赋值语句是 a = b + c; 格式
;
operator
: '+'|'-'|'*'|'/'
;
ID: [a-zA-Z]+ ;
WS: [ \t\r\n]+ -> skip;
下面我们基于此文法生成词法分析器和语法分析器
> antlr4 ASSIGN.g4
> javac *.java
下面我们写一个测试文件(test.txt),文件中包含错误的语法
a = b + c;
+
d = e + f;
我们可以看到,第二行的语法是错误的,只有一个加号,不满足assignStmt语法
下面我们使用这个测试文件生成语法树看下
> grun ASSIGN pfile -gui test.txt
结果如下,没有显示任何报错,语法树只有第一行的内容!!!
二 排查思路
语法树没有报错,难道之后的token都被丢了吗?于是我先排查了一下token
> grun ASSIGN pfile -tokens test.txt
[@0,0:0='a',<ID>,1:0]
[@1,2:2='=',<'='>,1:2]
[@2,4:4='b',<ID>,1:4]
[@3,6:6='+',<'+'>,1:6]
[@4,8:8='c',<ID>,1:8]
[@5,9:9=';',<';'>,1:9]
[@6,12:12='+',<'+'>,2:0]
[@7,15:15='d',<ID>,3:0]
[@8,17:17='=',<'='>,3:2]
[@9,19:19='e',<ID>,3:4]
[@10,21:21='+',<'+'>,3:6]
[@11,23:23='f',<ID>,3:8]
[@12,24:24=';',<';'>,3:9]
[@13,25:24='<EOF>',<EOF>,3:10]
由结果我们可以看到,所有的token都被正确的识别了,那只能是语法分析的问题了。之后我再通过trace分析了一下语法分析的过程
> grun ASSIGN pfile -trace test.txt
enter pfile, LT(1)=a
enter assignStmt, LT(1)=a
consume [@0,0:0='a',<7>,1:0] rule assignStmt
consume [@1,2:2='=',<1>,1:2] rule assignStmt
consume [@2,4:4='b',<7>,1:4] rule assignStmt
enter operator, LT(1)=+
consume [@3,6:6='+',<3>,1:6] rule operator
exit operator, LT(1)=c
consume [@4,8:8='c',<7>,1:8] rule assignStmt
consume [@5,9:9=';',<2>,1:9] rule assignStmt
exit assignStmt, LT(1)=+
exit pfile, LT(1)=+
通过trace的最后两行,我们可以看到,在正确识别第一行的assignStmt后遇到了token(+),此时这个token无法让语法分析器匹配任意一个规则,而且此时从最顶层规则pfile退出,语法也是完整的,所以直接从顶层退出,且没有报错。
三 解决方案
分析出原因后,解决方案也就有了,我们强制pfile规则匹配到文件结束就可以了,如果不能顺利匹配到文件结束,则说明存在语法错误
pfile规则进行如下修改
pfile
: assignStmt + EOF//文件由一行或多行赋值语句和文件结束符组成
;
重新生成词法分析器和语法分析器,之后重新生成语法树进行验证
> grun ASSIGN pfile -gui test.txt
line 2:0 extraneous input '+' expecting {<EOF>, ID}
此时第二行的语法错误被检测到。
本文完~