ANTLR4中解析器规则的EOF为何影响词法分析器?及相关问题
问题背景
ANTLR4中解析器规则包含EOF为何会影响词法分析器?以下通过示例说明:
基础语法
grammar Simple; file : lines ; lines : (ID | INT | STRING)+ '\r'? '\n'; ID : [a-zA-Z_]*; INT : [0-9]*; STRING : '"' ~["\r\n]* '"'; COMMA : ','; WS : [ \t\f]+ -> skip ;
示例1 | 有效输入
输入内容:
aaa 999
分词结果:
[@0,0:2='aaa',<3>,1:0] [@1,3:3='\n',<2>,1:3] [@2,4:6='999',<4>,2:0]
示例2 | 无效输入
在输入中添加逗号但未修改解析器规则,未触发报错,且词法分析器在999处停止分词:
输入内容:
aaa 999 777,rrr fff,456,iom
分词结果:
[@0,0:2='aaa',<3>,1:0] [@1,3:3='\n',<2>,1:3] [@2,4:6='999',<4>,2:0]
示例3 | 解析器调整
修改起始规则,添加EOF:
file : lines EOF ;
输入内容同示例2,分词结果:
[@0,0:2='aaa',<3>,1:0] [@1,3:3='\n',<2>,1:3] [@2,4:6='999',<4>,2:0] [@3,7:7='\n',<2>,2:3] [@4,8:10='777',<4>,3:0] [@5,11:11=',',<6>,3:3] [@6,12:14='rrr',<3>,3:4] [@7,15:15='\n',<2>,3:7] [@8,16:18='fff',<3>,4:0] [@9,19:19=',',<6>,4:3] [@10,20:22='456',<4>,4:4] [@11,23:23=',',<6>,4:7] [@12,24:26='iom',<3>,4:8] [@13,27:27='\n',<2>,4:11] [@14,28:27='<EOF>',<-1>,5:0]
解析器报错信息:
[2:0 | [@2,4:6='999',<4>,2:0] | why? mismatched input '999' expecting <EOF>]
技术问询及解答
针对示例2,提出以下问题并逐一解答:
为何解析器未触发任何报错?
ANTLR4解析器采用贪婪匹配逻辑,只要能完整匹配起始规则就会终止解析,不会校验后续未匹配的输入。示例2中,lines规则已经匹配到aaa\n999,满足(ID|INT|STRING)+ '\r'? '\n'的要求,解析器判定file规则已完成匹配,因此不会抛出错误。词法分析器为何在
999处停止分词,而非读取至文件末尾?
ANTLR4的词法分析器是按需分词:它不会一次性处理所有输入,而是根据解析器的请求逐个生成词法单元。当解析器完成lines规则匹配后,不再向词法分析器请求下一个单元,词法分析器自然停止工作,不会继续处理后续的777,rrr等内容。此前认为ANTLR4中词法分析器与解析器相互独立无通信,为何示例2中词法分析器停止位置与示例3中解析器报错位置一致?
二者并非完全独立,解析器是词法分析器的驱动方:解析器每需要一个词法单元,就会触发词法分析器生成一个。示例2中,解析器匹配完lines规则后停止请求,词法分析器便停在999处;示例3中,file规则要求lines后必须跟EOF,解析器匹配完lines后会继续请求下一个单元,发现无法匹配EOF,报错位置就是lines规则的结束点——也就是示例2中词法分析器停止的位置,本质是同一个规则匹配终点的两种不同表现。
内容的提问来源于stack exchange,提问作者raffian

