You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 08:13:20