Ply.yacc报错‘ERROR: no rules of the form p_rulename are defined’排查——C-Minus语言解析器开发问题
解决PLY报错:"ERROR: no rules of the form p_rulename are defined"
嘿,我一眼就看出问题出在哪了——你的语法规则定义方式不符合PLY的查找逻辑,咱们一步步理清楚:
为什么会报这个错?
PLY的yacc.yacc()函数默认会在当前模块的全局作用域里找所有以p_开头的函数,把它们当作语法规则。但你把p_program和p_declaration_list塞进了Parser类里,PLY根本找不到这些藏在类里的方法。另外,你的类方法还犯了个Python基础错误:类里的实例方法必须把self作为第一个参数,你写的def p_program(p):是不符合规范的。
两种修复方案任你选
方案一:把规则移到全局作用域(最简单)
直接把语法规则函数从Parser类里拿出来,放到parser.py的全局空间里:
import ply.yacc as yacc import lexer tokens = lexer.tokens # 全局作用域的规则函数,PLY能直接找到 def p_program(p): 'program: declaration_list' p[0] = p[1] def p_declaration_list(p): '''declaration_list: declaration_list declaration | declaration''' p[0] = (0, (p[1], 0)) # 如果不需要类封装,可以直接删掉Parser类;需要的话保留也没关系 class Parser(): pass
然后修改主程序的导入和解析器初始化:
# 主程序里不用导入Parser类,直接导入parser模块 import ply.yacc as yacc import ply.lex as lex from tabulate import tabulate import sys from lexer import * import parser # 这里改了,直接导入模块 # ... 词法分析的代码不变 ... print('passou aqui 1') parser = yacc.yacc() # 现在PLY能找到全局的p_*规则了 with open(sys.argv[1], 'r') as f: # 注意:解析器用parse()方法,不是input()! parse_result = parser.parse(f.read()) # 如果你想打印语法树,可以直接print(parse_result)
方案二:把规则封装在类里(更符合OOP)
如果你坚持要用类来封装规则,那得让PLY知道去类里找规则,同时修正方法的参数:
修改parser.py:
import ply.yacc as yacc import lexer tokens = lexer.tokens class Parser(): def __init__(self): # 创建解析器时,指定module为当前类实例 self.parser = yacc.yacc(module=self) # 必须加self作为第一个参数,这是Python类方法的要求 def p_program(self, p): 'program: declaration_list' p[0] = p[1] def p_declaration_list(self, p): '''declaration_list: declaration_list declaration | declaration''' p[0] = (0, (p[1], 0))
然后主程序里这样用:
from parser import Parser # ... 词法分析部分不变 ... print('passou aqui 1') # 实例化Parser类,用它内部的parser对象来解析 cm_parser = Parser() with open(sys.argv[1], 'r') as f: parse_result = cm_parser.parser.parse(f.read())
额外提个小问题
你的测试C-Minus代码里有个小bug:函数gcd只定义了参数u,但函数里用了v,这会在后续分析时出错,但和当前的PLY报错无关,先记下来就行。
内容的提问来源于stack exchange,提问作者Tiago de Luna




