PLY使用正则定义NAME token后变量赋值出现语法错误问题排查
问题原因
核心是词法分析阶段的token匹配优先级冲突:
is字符串同时满足IStoken的正则规则r'is'和NAMEtoken的正则规则r"[a-zA-Z]+\w*",两者匹配的字符串长度相同,当前规则下is被识别为NAME类型而非预期的IS类型,导致语法规则NAME IS STRING匹配失败,触发语法错误。- PLY处理token匹配时,函数定义的token规则和字符串定义的
t_*规则排序逻辑特殊,相同匹配长度下,保留字的简单规则很容易被覆盖性更强的NAME规则吞掉,这也是这类问题的常见诱因。 - 另外你代码中
STRING的正则r'\".*?\"'是HTML转义后的错误写法,实际需要调整为匹配双引号的正常正则。
解决方案
采用PLY官方推荐的保留字处理方案,将所有保留字放到NAME token的处理逻辑中判断,避免匹配冲突,修改后代码如下:
import ply.lex as lex import ply.yacc as yacc # 定义保留字字典 reserved = { 'show': 'SHOW', 'is': 'IS' } # tokens需要包含所有保留字对应的类型 tokens = ('LPAREN','RPAREN','STRING','NAME', *reserved.values()) t_LPAREN ='\(' t_RPAREN = '\)' t_STRING = r'"[^"]*"' # 修复STRING正则,匹配双引号包裹的内容 t_ignore = " \t" def t_NAME(t): r"[a-zA-Z]+\w*" # 判断当前匹配到的字符串是不是保留字 t.type = reserved.get(t.value.lower(), 'NAME') return t def t_error(t): print("Syntax error at %s"%t.value) t.lexer.skip(1) Variables = {} def p_show(p): '''statement : SHOW LPAREN STRING RPAREN | SHOW LPAREN NAME RPAREN''' value = p.slice[3] if value.type == 'NAME': print(Variables[value.value]) else: print(value.value) def p_is(p): 'statement : NAME IS STRING' Variables[p[1]] = p[3] def p_variable(p): 'statement : NAME' return Variables[p.slice[1].value] def p_error(p): print("Syntax error at %s"%p.value) lex.lex() yacc.yacc() s ='MY_STRING is "this is a string"' yacc.parse(s) # 测试读取变量 yacc.parse('show(MY_STRING)')
补充说明
如果你不想调整NAME的处理逻辑,也可以把IS、SHOW的规则改成函数定义,并且放在t_NAME函数的前面,相同匹配长度下,先定义的规则优先级更高,也能解决冲突。但保留字字典的方案扩展性更强,后续新增保留字只需要更新字典即可,不需要调整其他逻辑。
内容的提问来源于stack exchange,提问作者Jael Andres
相关产品推荐
相关产品推荐

