如何消除Lark语法表达式歧义?不同版本解析异常问题
Lark语法跨版本解析歧义问题解决
我编写了一段自认为无歧义的Lark语法,但在不同Lark版本下解析结果异常,代码如下:
import lark syntax = r""" stmt: mov_stmt | special_stmt mov_stmt: reg ASSIGN (reg | const) special_stmt: ("RS" SPECIAL_ASSIGN const) reg: REG const: DEC_NUM REG.2: /R[0-7]|RS/ DEC_NUM: /0|[1-9]\d*/i ASSIGN: "=" SPECIAL_ASSIGN: "&=" WS: /[ \t]+/ %ignore WS """ parser = lark.Lark(syntax, start="stmt", parser="lalr") print(parser.parse("R3 = 7")) # 1. 正常 print(parser.parse("R3 = R7")) # 2. 正常 print(parser.parse("RS &= 1")) # 3. 在lark==1.1.9下失败;期望匹配special_stmt print(parser.parse("RS = R7")) # 4. 在lark-parser==0.12.0下失败;期望匹配mov_stmt
版本异常表现
- 在
lark-parser==0.12.0中,第4次调用解析RS = R7失败,报错信息:
lark.exceptions.UnexpectedToken: Unexpected token Token('ASSIGN', '=') at line 1, column 4. Expected one of: * SPECIAL_ASSIGN Previous tokens: [Token('RS', 'RS')]
- 在
lark==1.1.9中,第3次调用解析RS &= 1失败,报错信息:
lark.exceptions.UnexpectedToken: Unexpected token Token('SPECIAL_ASSIGN', '&=') at line 1, column 4. Expected one of: * ASSIGN Previous tokens: [Token('REG', 'RS')]
我认为该语法本应无歧义:=应始终对应mov_stmt,&=应始终对应special_stmt(仅适用于reg为RS的情况)。尝试为不同终结符设置优先级,但无效果,请问该如何消除这种歧义?
解决方案
问题根源是LALR解析器的前瞻决策冲突,以及不同版本中令牌匹配、优先级处理的差异。最直接且跨版本兼容的解决方式是拆分寄存器规则,明确区分普通寄存器和RS,避免解析器在看到RS时产生歧义。
修改后的语法代码:
import lark syntax = r""" stmt: mov_stmt | special_stmt mov_stmt: (general_reg | RS) ASSIGN (general_reg | RS | const) special_stmt: RS SPECIAL_ASSIGN const general_reg: REG const: DEC_NUM REG.2: /R[0-7]/ DEC_NUM: /0|[1-9]\d*/i ASSIGN: "=" SPECIAL_ASSIGN: "&=" WS: /[ \t]+/ %ignore WS """ parser = lark.Lark(syntax, start="stmt", parser="lalr") print(parser.parse("R3 = 7")) # 正常 print(parser.parse("R3 = R7")) # 正常 print(parser.parse("RS &= 1")) # 正常 print(parser.parse("RS = R7")) # 正常
修改说明
- 将原有的
reg规则拆分为general_reg(匹配R0-R7)和单独的RS令牌,让解析器能明确区分两种寄存器类型。 - 调整
mov_stmt规则,允许RS作为赋值的源或目标;special_stmt规则则明确绑定RS与SPECIAL_ASSIGN,彻底消除了前瞻阶段的规则冲突。
这种方式无需依赖优先级设置,在不同Lark版本中都能稳定工作。
内容的提问来源于stack exchange,提问作者Jonathon Reinhart
相关产品推荐
相关产品推荐

