ANTLR4变量声明语法错误:换行符与错误位置不符问题排查
解析器错误排查:错误行/列偏移与多余换行问题
问题背景
我正在开发一款解析器,要求变量声明包含标识符与可选值初始化;若以var开头,则必须进行值初始化,需以赋值符号(<-)加表达式开头,表达式类型在编译时分配给变量。
ANTLR4语法规则
TRUE : 'true'; FALSE: 'false'; //types NUMBER: 'number'; BOOL: 'bool'; STRING: 'string'; RETURN: 'return'; VAR: 'var'; DYNAMIC: 'dynamic'; FUNC: 'func'; FOR: 'for'; UNTIL: 'until'; BY: 'by'; BREAK: 'break'; CONTINUE: 'continue'; IF: 'if'; ELSE: 'else'; ELIF: 'elif'; BEGIN: 'begin'; END: 'end'; /* LOGIC OPERATORS */ NOT: 'not'; AND: 'and'; OR: 'or'; /* ARITHMETIC OPERATORS */ // NEV: '-'; //overlap with SUB ADD: '+'; SUB: '-'; MUL: '*'; DIV: '/'; MOD: '%'; /* STRING OPERATORS */ CONCAT : '...'; /* RELATIONAL OPERATORS */ EQ: '='; NOT_EQ: '!='; LT: '<'; GT: '>'; LTE: '<='; GTE: '>='; STRINGCMP: '=='; // Compare two same strings, STRING operand's type ASSIGN : '<-'; /* SEPERATORS */ R_OPEN: '('; //ROUND R_CLOSE: ')'; SQ_OPEN: '['; //SQUARE SQ_CLOSE: ']'; C_OPEN: '{'; //CURLY C_CLOSE: '}'; COMMA: ','; SEMI: ';'; /* LITERAL */ // Fragment fragment DIGIT : [0-9]; fragment DECIMALPART : DIGIT* ('.' DIGIT | DIGIT '.') DIGIT*; fragment INTEGER : DIGIT DIGIT*; fragment EXPONENTPART : [eE] ([-+])? INTEGER; NUMBER_LITERAL : INTEGER EXPONENTPART? | DECIMALPART EXPONENTPART? ; /* IDENTIFIER */ ID: [A-Za-z_][A-Za-z0-9_]*; // INT: [0-9]+; literal: NUMBER_LITERAL | TRUE | FALSE ; program: NEWLINE* declare+ EOF; declare: (var_decl | func_decl) NEWLINE+; // All declarations and statements in this programming language // must end with a newline character. // Types prim_type: NUMBER | BOOL | STRING; //variable declaration var_decl: (varDecl | arrayDecl ) ; func_decl: 'func declaration'; arrayDecl: 'array declaration'; varDecl: (DYNAMIC | prim_type) ID (ASSIGN (literal | ID))? | VAR ID ASSIGN (literal | ID) ; // newline character NEWLINE: '\n' | '\r\n'; /* COMMENT */ LINECMT : '##' ~[\n\r\f]* -> skip; WS : [ \t\f\b]+ -> skip ; // skip spaces, tabs, newlines ERROR_CHAR: . {raise ErrorToken(self.text)};
测试用例
def test_204(self): input = ''' var UyYv ## tv2xjibo,@ number u7i[6.385,406.606,76.243E39] <- 84.870E+57 ''' expect = '''Error on line 2 col 23: ''' self.assertTrue(TestParser.test(input, expect, 204))
问题现象
预期错误输出:
Error on line 2 col 23:
实际解析器输出:
Error on line 2 col 22:
实际结果存在两个问题:
- 错误列位置偏移1位(预期23,实际22)
- 多了一个换行符
解决思路
1. 列位置偏移问题
- 注释与列计数的冲突:
LINECMT规则直接跳过注释内容,但ANTLR默认的Token列号计算是基于原始输入的,若错误报告逻辑没有同步跳过注释的字符长度,会导致列号偏差。 - 计数基准差异:确认错误报告的列号是1-based还是0-based。预期的col23如果是1-based计数,实际col22可能是代码中用了0-based计数,需统一基准。
- 空白字符的列数计算:检查
WS规则的处理是否影响列号统计,被跳过的空白字符是否被正确计入列数,ANTLR的CommonToken列号是基于原始输入的,若自定义了Token位置逻辑可能导致偏差。
2. 多余换行符问题
- ErrorToken实现问题:检查
ERROR_CHAR中抛出的ErrorToken类,是否在生成错误信息时额外添加了换行符,或者错误消息拼接逻辑重复插入换行。 - 输入隐藏换行:查看测试输入的原始内容,是否包含
\r等隐藏换行符,导致输出时多渲染出一个换行。 - 错误模板冗余:确认错误消息的生成模板,是否在末尾重复添加了换行,比如预期是
Error on line X col Y: \n,实际代码输出了Error on line X col Y: \n\n。
额外验证点
- 完善
arrayDecl规则:当前arrayDecl是占位符,输入中的number u7i[6.385,...]无法被匹配,会触发ERROR_CHAR,需打印所有Token的行号、列号和文本,明确错误Token的实际位置。 - 开启ANTLR Token追踪:通过打印生成的所有Token信息,对比错误Token的位置与预期的差异,定位问题根源。
内容的提问来源于stack exchange,提问作者FantasticGoose
相关产品推荐
相关产品推荐

