Antlr4报mismatched input '<' expecting '<' 无词法歧义问题排查
使用Antlr4编写语法规则时,预期语法可匹配如下输入:
i,j : bool; setvar : set<bool>; i > 5; j < 10;
实际运行解析时持续报错:line 3:13 mismatched input '<' expecting '<'。初步排查时判断这类错误通常源于词法歧义,但检查语法后发现<仅在单个token规则中定义,不存在重复定义的情况。
使用的完整语法规则如下:
//// Parser Rules grammar MLTL1; start: block*; block: var_list ';' | expr ';' ; var_list: IDENTIFIER (',' IDENTIFIER)* ':' type ; type: BASE_TYPE | KW_SET REL_LT BASE_TYPE REL_GT ; expr: expr REL_OP expr | '(' expr ')' | IDENTIFIER | INT ; //// Lexical Spec // Types BASE_TYPE: 'bool' | 'int' | 'float' ; // Keywords KW_SET: 'set' ; // Op groups for precedence REL_OP: REL_EQ | REL_NEQ | REL_GT | REL_LT | REL_GTE | REL_LTE ; // Relational ops REL_EQ: '==' ; REL_NEQ: '!=' ; REL_GT: '>' ; REL_LT: '<' ; REL_GTE: '>=' ; REL_LTE: '<=' ; IDENTIFIER : LETTER (LETTER | DIGIT)* ; INT : SIGN? NONZERODIGIT DIGIT* | '0' ; fragment SIGN : [+-] ; fragment DIGIT : [0-9] ; fragment NONZERODIGIT : [1-9] ; fragment LETTER : [a-zA-Z_] ; COMMENT : '#' ~[\r\n]* -> skip; WS : [ \t\r\n]+ -> channel(HIDDEN);
为排查问题编写了如下Python测试脚本,直接输出词法分析生成的token序列:
from antlr4 import InputStream, CommonTokenStream import MLTL1Lexer import MLTL1Parser input=""" i,j : bool; setvar: set<bool>; i > 5; j < 10; """ lexer = MLTL1Lexer.MLTL1Lexer(InputStream(input)) stream = CommonTokenStream(lexer) stream.fill() tokens = stream.getTokens(0,100) for t in tokens: print(str(t.type) + " " + t.text) parser = MLTL1Parser.MLTL1Parser(stream) parse_tree = parser.start() print(parse_tree.toStringTree(recog=parser))
测试发现>和<被分配了相同的token类型值,和二者为独立定义token的预期不符。
这是ANTLR4词法规则的机制导致的:
ANTLR4中,只有标记为fragment的词法规则才能被其他词法规则引用。如果非fragment的词法规则引用了其他非fragment词法规则,ANTLR不会保留被引用规则的独立token类型,而是会把被引用规则的匹配逻辑直接内联到当前规则中,被引用的规则本身不会生成独立的token类型。
当前语法里把REL_OP定义为非fragment的词法规则,同时引用了REL_EQ/REL_LT/REL_GT等其他非fragment词法规则,最终所有比较运算符(</>/==等)都会被统一识别为REL_OP类型的token,单独定义的REL_LT/REL_GT等规则根本不会作为独立token存在。
这也是报错信息看起来矛盾的原因:解析set<bool>这段时,parser规则期望拿到类型为REL_LT、文本为<的token,但词法实际输出的是类型为REL_OP、文本为<的token,二者类型ID不匹配,哪怕文本完全一致,ANTLR也会判定匹配失败,抛出mismatched input '<' expecting '<'的错误。
按照ANTLR的设计规范,词法规则只负责定义最细粒度的独立token,不同token的组合逻辑要放到语法规则层实现,修改步骤如下:
- 删除词法规则部分的
REL_OP定义,保留REL_EQ/REL_NEQ/REL_GT/REL_LT/REL_GTE/REL_LTE这些独立的比较运算符词法规则不变 - 在语法规则部分新增
rel_op规则,作为所有比较运算符的集合:
rel_op: REL_EQ | REL_NEQ | REL_GT | REL_LT | REL_GTE | REL_LTE;
- 把原有
expr规则中引用的REL_OP替换为新定义的语法规则rel_op:
expr: expr rel_op expr | '(' expr ')' | IDENTIFIER | INT ;
修改后重新生成词法、语法分析器,所有比较运算符会被识别为独立的token类型,set<bool>的泛型写法和表达式中的比较运算都可以正常匹配。
内容的提问来源于stack exchange,提问作者cgjohannsen

