【问题标题】:Signalling an error from a parser rule in PLY从 PLY 中的解析器规则发出错误信号
【发布时间】:2018-11-21 02:18:05
【问题描述】:

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

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

如有必要,生产规则可以手动强制解析器进入错误恢复。这是通过像这样引发 SyntaxError 异常来完成的:

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
    """)

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

我做错了什么?

【问题讨论】:

    标签: python parsing grammar lexer ply


    【解决方案1】:

    您有责任打印错误消息,但您不需要:

    手动设置错误的一个重要方面是p_error() 函数在这种情况下不会被调用。如果您需要发出错误消息,请确保在引发 SyntaxError 的生产中执行此操作。

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

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

    如果您不关心错误恢复,请不要在任何规则中使用 error 标记。该令牌仅用于错误恢复。如果您只想在遇到错误时立即抛出异常,请在p_error 中执行此操作,并在不会自动调用的地方显式调用p_error(例如令牌错误和语义操作中检测到​​的错误) .你可以抛出ValueError 或派生出来的东西;我会远离 SyntaxError,它对 Ply 和 Python 具有特殊意义。

    【讨论】:

    • 谢谢。 SyntaxError 似乎很特别,你不能简单地抛出 SyntaxError 并期望它最终停止执行。在解析器的__init__ 调用中启用debug=1 帮助我看到了这个问题:当我的自定义SyntaxError 被抛出时,一个error 令牌被添加到堆栈中,它没有找到接受的规则它,所以它被默默地丢弃。我尝试添加一个接受语法错误的规则,但这会导致无限循环,所以我只是求助于抛出不是来自SyntaxError 的自定义异常。
    • 您能分享如何在解析器中处理error 标记的示例代码吗?当我尝试让instruction_list 接受error 令牌时,我遇到了无限循环问题。
    • instruction_list 不是检测错误的自然场所,因为它没有重新同步令牌。我会通过添加产生式instruction: error NEWLINE 将错误规则放入指令中。这将在新的 NEWLINE 字符处重新同步。我不知道你是如何得到无限循环的,除非它是在你不应该抛出 SyntaxError 的时候。总的来说,如果您有一些代码无法按预期运行并且您需要帮助,您应该尝试编写一个特定的问题minimal reproducible example 和特定症状。 “我遇到了麻烦”和“它没有用”都不是足够的问题报告。
    • 我通过上面的示例添加了您指定的产品并添加了一个新规则:def p_error_handler,它使用了产品instruction : error NEWLINE。在方法体中,它引发p[1]。即使规则高于p_instruction,它也不会抛出错误。但是,如果我制作instruction : error(即没有NEWLINE),它似乎确实有效。拥有NEWLINE 重要吗?作为记录,我不在乎遇到错误后恢复。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-04
    • 2017-07-17
    • 1970-01-01
    • 1970-01-01
    • 2012-03-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多