解决PLY中表达式语法的移进/归约冲突(含内嵌函数)
我用PLY开发解释器语法,实现内嵌函数调用(比如vector的pop()/join()、通用toString()、转换函数parseInt())时遇到移进/归约冲突。新增对象属性访问规则并添加('left', 'PUNTO')优先级后,仍出现以下冲突:
WARNING: shift/reduce conflict for PUNTO in state 43 resolved as shift state 43 (74) expresion -> acceso_atrib . (75) acceso_atrib -> acceso_atrib . PUNTO expresion ! shift/reduce conflict for PUNTO resolved as shift MAS reduce using rule 74 (expresion -> acceso_atrib .) MENOS reduce using rule 74 (expresion -> acceso_atrib .) MULTI reduce using rule 74 (expresion -> acceso_atrib .) DIV reduce using rule 74 (expresion -> acceso_atrib .) MOD reduce using rule 74 (expresion -> acceso_atrib .) IGUAL reduce using rule 74 (expresion -> acceso_atrib .) DIFERENTE reduce using rule 74 (expresion -> acceso_atrib .) MENOR_QUE reduce using rule 74 (expresion -> acceso_atrib .) MAYOR_QUE reduce using rule 74 (expresion -> acceso_atrib .) MENOR_IGUAL reduce using rule 74 (expresion -> acceso_atrib .) MAYOR_IGUAL reduce using rule 74 (expresion -> acceso_atrib .) AND reduce using rule 74 (expresion -> acceso_atrib .) OR reduce using rule 74 (expresion -> acceso_atrib .) PUNTO_COMA reduce using rule 74 (expresion -> acceso_atrib .) PAREN_CIERRE reduce using rule 74 (expresion -> acceso_atrib .) COMA reduce using rule 74 (expresion -> acceso_atrib .) CORCHETE_CIERRE reduce using rule 74 (expresion -> acceso_atrib .) DOS_PUNTOS reduce using rule 74 (expresion -> acceso_atrib .) PUNTO shift and go to state 87 ! PUNTO [ reduce using rule 74 (expresion -> acceso_atrib .) ]
以下是解决该冲突的具体建议:
调整语法规则结构,明确层级:
冲突根源是expresion -> acceso_atrib和acceso_atrib -> acceso_atrib PUNTO expresion的歧义——解析器遇到PUNTO时,无法判断是继续移进以构建更长的属性访问链,还是先把当前acceso_atrib归约为expresion。
建议重构规则,将属性访问和方法调用整合为同一个规则分支,确保连续的属性访问(如a.b.c)或方法调用后接属性访问(如a.pop().b)能被优先完整解析,而非中途归约为普通表达式。示例规则:expresion -> acceso_atrib | aritmetica_expresion | comparacion_expresion | ...其他表达式类型... acceso_atrib -> IDENTIFICADOR | acceso_atrib PUNTO IDENTIFICADOR | acceso_atrib PAREN_ABIERTO argumentos PAREN_CIERRE这里把方法调用直接纳入
acceso_atrib规则,因为方法本质是对象的函数属性,这样连续的属性访问操作会被优先处理。提升
PUNTO的优先级:
属性访问的优先级原本就高于算术、比较、逻辑等所有运算符,所以要把('left', 'PUNTO')放在优先级列表的最顶端,确保解析器遇到PUNTO时优先处理属性访问,而非归约前面的表达式。示例优先级配置:precedence = ( ('left', 'PUNTO'), # 最高优先级,确保属性访问优先解析 ('left', 'OR'), ('left', 'AND'), ('nonassoc', 'IGUAL', 'DIFERENTE', 'MAYOR_QUE', 'MENOR_QUE', 'MAYOR_IGUAL', 'MENOR_IGUAL'), ('left', 'MAS', 'MENOS'), ('left', 'MULTI', 'DIV', 'MOD'), # 其他优先级规则... )清理冗余规则,消除歧义:
检查是否存在其他expresion子规则能生成带PUNTO的结构,这类规则会和acceso_atrib产生冲突。确保只有acceso_atrib规则负责处理所有带.的属性访问和方法调用场景。用PLY调试工具定位具体场景:
在创建解析器时传入debug=True参数,运行后查看状态43的完整上下文,确认冲突触发的具体输入场景(比如是否是表达式嵌套中的属性访问、连续多个方法调用等),根据实际场景针对性调整规则。
内容的提问来源于stack exchange,提问作者luisepa1420

