从 PLY 中的解析器规则发出错误信号

Signalling an error from a parser rule in PLY

我正在使用 PLY 来解析自定义定义文件的命令。命令每行定义一个,每个命令都应以保留关键字开头,后跟一些字符串。我已经成功地为语法编写了一个词法分析器和解析器,但是我在从生产中提出 SyntaxError 时遇到了问题。

根据 PLY's documentation,这可以简单地通过从解析器规则的主体中抛出 SyntaxError 来实现:

If necessary, a production rule can manually force the parser to enter error recovery. This is done by raising the SyntaxError exception like this:

def p_production(p):
    'production : some production ...'
    raise SyntaxError

我的代码在遇到无效语法时会在产生式中引发 SyntaxError,但是当我 运行 程序时不会引发此错误。这是一个最小的工作示例:

from ply import lex, yacc

class Parser(object):
    # reserved keyword tokens
    reserved = {
        "r": "R"
    }

    # top level tokens
    tokens = [
        'CHUNK',
        'NEWLINE'
    ]

    # add reserved tokens
    tokens += reserved.values()

    # ignore spaces and tabs
    t_ignore = ' \t'

    def __init__(self):
        # lexer and parser handlers
        self.lexer = lex.lex(module=self)
        self.parser = yacc.yacc(module=self)

    def parse(self, text):
        # pass text to yacc
        self.parser.parse(text, lexer=self.lexer)

    # detect new lines
    def t_newline(self, t):
        r'\n+'
        # generate newline token
        t.type = "NEWLINE"
        return t

    def t_CHUNK(self, t):
        r'[a-zA-Z0-9_=.:]+'
        # check if chunk is a keyword
        t.type = self.reserved.get(t.value.lower(), 'CHUNK')
        return t

    def t_error(self, t):
        raise SyntaxError("token error")

    def p_instruction_list(self, p):
        '''instruction_list : instruction
                            | instruction_list instruction'''
        pass

    # match instruction on their own lines
    def p_instruction(self, p):
        '''instruction : command NEWLINE
                       | NEWLINE'''
        pass

    def p_command(self, p):
        '''command : R CHUNK CHUNK CHUNK CHUNK'''
        # parse command
        if p[2] not in ["a", "b"]:
            raise SyntaxError("invalid thing")

    def p_error(self, p):
        raise SyntaxError("parsing error")

if __name__ == "__main__":
    parser = Parser()
    parser.parse("""
    r a text text text
    r c text text text
    r b text text text
    """)

上面的例子 运行s 没有输出任何东西,这意味着它已经成功解析了文本,即使由于行 r c text text text 应该在 p_command 中引发语法错误(第二个标记 c 无效;只有 ab 有效。

我做错了什么?

您有责任打印错误消息,但您没有:

One important aspect of manually setting an error is that the p_error() function will NOT be called in this case. If you need to issue an error message, make sure you do it in the production that raises SyntaxError.

我认为 p_error() 不应该加注 SyntaxError。它应该只打印一条适当的消息(或以其他方式记录发生错误的事实)并让错误恢复继续进行。但是无论如何,在这种情况下它不会被调用,如上面的引用所示。

我也不是 100% 相信让词法分析器加注 SyntaxError。对于词法错误,我的首选策略是将它们传递给解析器,从而将错误处理集中在一个地方。

如果您不关心错误恢复,请不要在任何规则中使用 error 标记。该令牌仅用于错误恢复。如果只是想一遇到错误就抛出异常,在p_error里做,在不会自动调用的地方显式调用p_error(比如token错误和检测到的错误)在语义动作中)。您可以抛出 ValueError 或从中派生的东西;我会远离 SyntaxError,这对 Ply 和 Python 通常具有特殊意义。