3

So I've implemented a parser using PLY — but all the PLY documentation deals with parse and tokenization errors by printing out error messages. I'm wondering what the best way to implement non-fatal error-reporting is, at an API level, to the caller of the parser. Obviously the "non-fatal" restriction means exceptions are out — and it feels like I'd be misusing the warnings module for parse errors. Suggestions?

4

2 回答 2

2

PLY 有一个 t_error() 函数,你可以在你的解析器中重写它来做任何你想做的事情。文档中提供的示例打印出一条错误消息并跳过有问题的字符 - 但您可以轻松更新遇到的解析失败列表,设置在 X 次失败后停止的阈值等 - http://www .dabeaz.com/ply/ply.html

4.9 错误处理

最后,t_error() 函数用于处理检测到非法字符时出现的词法错误。在这种情况下, t.value 属性包含未标记化的输入字符串的其余部分。在示例中,误差函数定义如下:

# Error handling rule
def t_error(t):
    print "Illegal character '%s'" % t.value[0]
    t.lexer.skip(1)

您可以通过使您的解析器成为一个类并在其中存储错误状态来利用它 - 这是一个非常粗略的示例,因为您必须创建多个 MyLexer 实例,然后 build() 它们,然后在需要多个词法分析器时使用它们进行解析同时运行。

您可以将错误存储与__hash__词法分析器实例本身结合起来,只需要构建一次。我不清楚在一个类中运行多个词法分析器实例的细节,但实际上这只是为了提供一个粗略的示例,说明如何捕获和报告非致命错误。

为此,我修改了 Ply 文档中的简单计算器类示例。

#!/usr/bin/python

import ply.lex as lex

class MyLexer:

    errors = []

    # List of token names.   This is always required
    tokens = (
       'NUMBER',
       'PLUS',
       'MINUS',
       'TIMES',
       'DIVIDE',
       'LPAREN',
       'RPAREN',
    )

    # Regular expression rules for simple tokens
    t_PLUS    = r'\+'
    t_MINUS   = r'-'
    t_TIMES   = r'\*'
    t_DIVIDE  = r'/'
    t_LPAREN  = r'\('
    t_RPAREN  = r'\)'

    # A regular expression rule with some action code
    # Note addition of self parameter since we're in a class
    def t_NUMBER(self,t):
        r'\d+'
        t.value = int(t.value)
        return t

    # Define a rule so we can track line numbers
    def t_newline(self,t):
        r'\n+'
        t.lexer.lineno += len(t.value)

    # A string containing ignored characters (spaces and tabs)
    t_ignore  = ' \t'

    # Error handling rule
    def t_error(self,t):
        self.errors.append("Illegal character '%s'" % t.value[0])
        t.lexer.skip(1)

    # Build the lexer
    def build(self,**kwargs):
        self.errors = []
        self.lexer = lex.lex(module=self, **kwargs)

    # Test it output
    def test(self,data):
        self.errors = []
        self.lexer.input(data)
        while True:
             tok = self.lexer.token()
             if not tok: break
             print tok

    def report(self):
        return self.errors

用法:

# Build the lexer and try it out
m = MyLexer()
m.build()           # Build the lexer
m.test("3 + 4 + 5")     # Test it
print m.report()
m.test("3 + A + B")
print m.report()

输出:

LexToken(NUMBER,3,1,0)
LexToken(PLUS,'+',1,2)
LexToken(NUMBER,4,1,4)
LexToken(PLUS,'+',1,6)
LexToken(NUMBER,5,1,8)
[]
LexToken(NUMBER,3,1,0)
LexToken(PLUS,'+',1,2)
LexToken(PLUS,'+',1,6)
["Illegal character 'A'", "Illegal character 'B'"]
于 2013-08-04T22:44:03.570 回答
0

查看第 9.2 节

9.2 运行时调试

要启用解析器的运行时调试,请使用debug解析选项。此选项可以是整数(仅打开或关闭调试)或记录器对象的实例。例如:

log = logging.getLogger()
parser.parse(input,debug=log)

如果传递了一个日志对象,您可以使用它的过滤级别来控制生成多少输出。该INFO级别用于生成有关规则缩减的信息。该DEBUG级别将显示有关解析堆栈、令牌移位和其他详细信息的信息。该ERROR级别显示与解析错误相关的信息。

logging模块是 CPython 标准库的一部分。

于 2014-04-08T06:10:05.333 回答