分数计算器Lexer分词异常:无空格分隔时无法识别FRACTION令牌
解决ANTLR词法分析器分数无空格分隔的分词异常问题
问题现象
当分数表达式无空格分隔时,词法分析器会错误地将部分内容合并为不符合规则的令牌,导致语法解析失败。
示例输入
2|1+4|1*5|2-18|5
错误信息
line 1:11 no viable alternative at input '2|1+4|1*5|2-18|5'
正确格式与预期输出
带空格输入:
2|1 + 4|1 * 5|2 - 18|5
预期输出:
42|5
已尝试的无效方案
- 方案一:调整WS规则
FRACTION: '-'? [0-9]+ '|' [0-9]+; WS: [ \t\r\n]+ -> skip;
- 方案二:将运算符定义为单独令牌(完整语法见用户提供代码)
问题根源
核心是减号(MINUS)与分数负号的词法歧义:当-出现在数字/分数之后时,词法分析器错误地将其识别为后续分数的负号(合并进FRACTION令牌),而非作为减号运算符,导致令牌序列不符合语法规则;无空格时的拆分异常只是该歧义的外在表现。
解决方案
通过将负号处理从词法层移到语法层,消除歧义,同时确保运算符被正确识别:
修改后的完整语法
grammar FractionCalc; @members { public static int count = 0; } prog : expr NEWLINE { Console.WriteLine($expr.value); count++; } | NEWLINE | expr EOF { Console.WriteLine($expr.value); count++; } ; expr returns [Fraction value] : e1=expr PLUS e2=term { $value = $e1.value + $e2.value; } | e1=expr MINUS e2=term { $value = $e1.value - $e2.value; } | t=term { $value = $t.value; } ; term returns [Fraction value] : t1=term MUL f=factor { $value = $t1.value * $f.value; } | t1=term DIV f=factor { $value = $t1.value / $f.value; } | f=factor { $value = $f.value; } ; factor returns [Fraction value] : MINUS f=factor { $value = $f.value.Negate(); } // 处理一元负号 | f=FRACTION { $value = Fraction.Parse($f.text); } | LPAREN e=expr RPAREN { $value = $e.value; } ; // 移除FRACTION开头的负号,仅匹配正分数 FRACTION: [0-9]+ '|' [0-9]+; PLUS: '+'; MINUS: '-'; MUL: '*'; DIV: '/'; LPAREN: '('; RPAREN: ')'; NEWLINE: '\r'? '\n'; WS: [ \t]+ -> skip;
修改说明
- 调整FRACTION规则:移除开头的
-,仅匹配正分数,从根源避免负号与减号的词法冲突。 - 添加一元负号语法分支:在
factor规则中增加MINUS f=factor,通过语法逻辑处理负分数(调用Fraction的Negate方法实现取反)。 - 保留独立运算符令牌:确保
+、-、*、/始终被识别为单独令牌,无论输入是否带空格,词法分析器都会正确拆分。
验证效果
输入无空格表达式2|1+4|1*5|2-18|5时,词法分析器会正确拆分为:FRACTION(2|1), PLUS(+), FRACTION(4|1), MUL(*), FRACTION(5|2), MINUS(-), FRACTION(18|5)
语法解析可正常计算,输出预期结果42|5。
内容的提问来源于stack exchange,提问作者Saruman
相关产品推荐
相关产品推荐

