PLY单状态下如何处理词法规则子集冲突实现一对多规则匹配
单状态下的可行解决方案
首先明确问题根源:PLY词法分析默认遵循两个匹配规则:
- 能匹配最长字符串的规则优先
- 多个规则匹配到相同长度字符串时,代码中先定义的规则优先
原代码中t_TRUE和t_SINGLE_CHAR匹配t/T时长度均为1,且t_TRUE定义在前,因此无论上下文是什么,t/T永远会被识别为TRUE,导致var2 T匹配失败。
不需要切换词法状态,单状态下有两种成熟实现方案:
方案1:取消独立TRUE token,语法层做上下文校验
这是最简单的实现方式,直接删除独立的t_TRUE规则,所有单个字母统一返回SINGLE_CHAR,在语法动作中根据前置关键字做值校验即可。
词法部分修改
tokens = ( 'VAR1', 'VAR2', 'SINGLE_CHAR', ) def t_VAR1(t): r'var1' return t def t_VAR2(t): r'var2' return t def t_SINGLE_CHAR(t): r'[A-Za-z]' return t # 记得加空白符忽略规则,否则词法解析会报错 t_ignore = ' \t\r\n'
语法部分修改
def p_expression_variable2(p): '''variable2 : VAR2 SINGLE_CHAR''' print("got VAR2, 跟随字符:%s" % p[2]) def p_expression_variable1(p): '''variable1 : VAR1 SINGLE_CHAR''' # 校验var1后面的字符必须是t/T if p[2].lower() != 't': raise SyntaxError(f"var1 后仅允许跟随 t/T,当前输入字符:{p[2]}") print("got VAR1")
这个方案不会产生任何语法冲突,因为两个产生式的第一个终结符分别是VAR1和VAR2,完全没有重叠,PLY可以无歧义完成解析。
适用场景:TRUE仅出现在VAR1后方、语法规则简单的场景,代码量最少。
方案2:保留独立TRUE token,词法层做轻量上下文判断
如果你需要保留独立的TRUE token类型(比如后续TRUE会出现在其他语法位置),可以在词法分析器实例上挂载一个轻量标记记录上下文,全程不切换PLY的词法状态,在匹配单字符时动态决定token类型即可。
完整实现代码
import ply.lex as lex import ply.yacc as yacc tokens = ( 'VAR1', 'VAR2', 'TRUE', 'SINGLE_CHAR', ) t_ignore = ' \t\r\n' def t_VAR1(t): r'var1' # 标记后续需要匹配TRUE t.lexer.expect_true = True return t def t_VAR2(t): r'var2' # 标记后续匹配普通单字符 t.lexer.expect_true = False return t def t_SINGLE_CHAR(t): r'[A-Za-z]' # 根据上下文动态设置token类型 if getattr(t.lexer, 'expect_true', False) and t.value.lower() == 't': t.type = 'TRUE' else: t.type = 'SINGLE_CHAR' # 重置标记 t.lexer.expect_true = False return t # 词法错误处理 def t_error(t): print(f"非法字符:{t.value[0]}") t.lexer.skip(1) # 语法规则和原有写法完全一致 def p_expression_variable2(p): '''variable2 : VAR2 SINGLE_CHAR''' print("got VAR2") def p_expression_variable1(p): '''variable1 : VAR1 TRUE''' print("got VAR1") # 语法错误处理 def p_error(p): print(f"语法错误,输入非法:{p.value if p else 'EOF'}") # 初始化解析器 lexer = lex.lex() parser = yacc.yacc() # 测试用例 parser.parse('var1 T') # 输出 got VAR1 parser.parse('var2 T') # 输出 got VAR2 parser.parse('var2 a') # 输出 got VAR2 parser.parse('var1 a') # 触发语法错误
这个方案全程运行在默认的INITIAL词法状态下,没有使用PLY的条件词法接口,完全符合单状态要求,同时保留了独立的TRUE token,语法层不需要写额外的校验逻辑。
适用场景:TRUE需要在多个语法位置复用、希望语法规则保持语义清晰的场景。
内容的提问来源于stack exchange,提问作者rh939
相关产品推荐
相关产品推荐

