You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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_programp_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

火山引擎 最新活动