Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用pyparsing改进错误消息_Python_Pyparsing - Fatal编程技术网

Python 使用pyparsing改进错误消息

Python 使用pyparsing改进错误消息,python,pyparsing,Python,Pyparsing,编辑:我制作了第一个版本,Eike帮助我在这方面取得了相当大的进步。我现在陷入了一个更具体的问题,我将在下面描述它。你可以看一下表格中的原始问题 我使用pyparsing来解析一种用于从数据库请求特定数据的小型语言。它具有许多关键字、运算符和数据类型以及布尔逻辑 我试图改进当用户出现语法错误时发送给他的错误消息,因为当前的错误消息不是很有用。我设计了一个小示例,与我使用上述语言所做的类似,但要小得多: #!/usr/bin/env python

编辑:我制作了第一个版本,Eike帮助我在这方面取得了相当大的进步。我现在陷入了一个更具体的问题,我将在下面描述它。你可以看一下表格中的原始问题


我使用pyparsing来解析一种用于从数据库请求特定数据的小型语言。它具有许多关键字、运算符和数据类型以及布尔逻辑

我试图改进当用户出现语法错误时发送给他的错误消息,因为当前的错误消息不是很有用。我设计了一个小示例,与我使用上述语言所做的类似,但要小得多:

#!/usr/bin/env python                            

from pyparsing import *

def validate_number(s, loc, tokens):
    if int(tokens[0]) != 0:
        raise ParseFatalException(s, loc, "number musth be 0")

def fail(s, loc, tokens):
    raise ParseFatalException(s, loc, "Unknown token %s" % tokens[0])

def fail_value(s, loc, expr, err):
    raise ParseFatalException(s, loc, "Wrong value")

number =  Word(nums).setParseAction(validate_number).setFailAction(fail_value)
operator = Literal("=")

error = Word(alphas).setParseAction(fail)
rules = MatchFirst([
    Literal('x') + operator + number,
])

rules = operatorPrecedence(rules | error , [
    (Literal("and"), 2, opAssoc.RIGHT),
])

def try_parse(expression):
    try:
        rules.parseString(expression, parseAll=True)
    except Exception as e:
        msg = str(e)
        print("%s: %s" % (msg, expression))
        print(" " * (len("%s: " % msg) + (e.loc)) + "^^^")
所以基本上,我们能用这种语言做的唯一的事情,就是写一系列的
x=0
,加上
和括号

现在,当使用
和括号时,错误报告不是很好。考虑下面的例子:

>>> try_parse("x = a and x = 0") # This one is actually good!
Wrong value (at char 4), (line:1, col:5): x = a and x = 0
                                              ^^^
>>> try_parse("x = 0 and x = a")
Expected end of text (at char 6), (line:1, col:1): x = 0 and x = a
                                                         ^^^
>>> try_parse("x = 0 and (x = 0 and (x = 0 and (x = a)))")
Expected end of text (at char 6), (line:1, col:1): x = 0 and (x = 0 and (x = 0 and (x = a)))
                                                         ^^^
>>> try_parse("x = 0 and (x = 0 and (x = 0 and (x = 0)))")
Expected end of text (at char 6), (line:1, col:1): x = 0 and (x = 0 and (x = 0 and (xxxxxxxx = 0)))
                                                         ^^^
实际上,如果解析器在
之后不能解析(这里的解析很重要),那么它就不会再产生好的错误消息:(

我的意思是parse,因为如果它可以解析5,但在解析操作中“验证”失败,它仍然会生成一条很好的错误消息。但是,如果它不能解析有效的数字(如
a
)或有效的关键字(如
xxxxxx
),它就会停止生成正确的错误消息


有什么想法吗?

Pyparsing总是会有一些不好的错误消息,因为它会回溯。错误消息是在解析器尝试的最后一个规则中生成的。解析器不知道错误真正在哪里,它只知道没有匹配规则

要获得好的错误消息,您需要一个尽早放弃的解析器。这些解析器不如Pyparsing灵活,但大多数传统编程语言都可以使用这种解析器进行解析。(C++和Scala IMHO不能。)

要改进Pyparsing中的错误消息,请使用
-
运算符,它与
+
运算符类似,但不会回溯。您可以这样使用它:

assignment = Literal("let") - varname - "=" - expression
def validate_odd_number(s, loc, toks):
    value = toks[0]
    value = int(value)
    if value % 2 == 0:
        raise MyFatalParseException(
            "not an odd number. Line {l}, column {c}.".format(l=lineno(loc, s),
                                                              c=col(loc, s)))
这是Pyparsing的作者写的一篇文章

编辑

您还可以为执行验证的解析操作中的无效数字生成正确的错误消息。如果数字无效,则会引发Pyparsing未捕获的异常。此异常可能包含正确的错误消息

解析操作可以有三个参数[1]:

  • s=正在解析的原始字符串(请参见下面的注释)
  • loc=匹配子字符串的位置
  • toks=匹配令牌的列表,打包为
    ParseResults
    对象
还有三种有用的帮助方法可用于创建良好的错误消息[2]:

  • lineno(loc,string)
    -函数用于给出字符串中位置的行号;第一行是第1行,换行符开始新行
  • col(loc,string)
    -函数用于给出字符串中位置的列号;第一列是第1列,换行符将列号重置为1
  • line(loc,string)
    -用于检索表示
    lineno(loc,string)
    的文本行。在打印异常诊断消息时非常有用
然后,验证分析操作将如下所示:

assignment = Literal("let") - varname - "=" - expression
def validate_odd_number(s, loc, toks):
    value = toks[0]
    value = int(value)
    if value % 2 == 0:
        raise MyFatalParseException(
            "not an odd number. Line {l}, column {c}.".format(l=lineno(loc, s),
                                                              c=col(loc, s)))
[1]

[2]

编辑

这里[3]是问题当前(2013-4-10)脚本的一个改进版本。它正确地显示了示例错误,但在错误的位置指示了其他错误。我相信我的Pyparsing(“1.5.7”)版本中存在错误,但可能我不理解Pyparsing是如何工作的。问题是:

  • ParseFatalException似乎并不总是致命的。当我使用自己的异常时,脚本会按预期工作
  • -
    操作员似乎不工作

[3]

它只起到了很小的作用:不是偶数(在字符0处),(行:1,列:1):x==1,y==1(而错误在“y”上)是的,这是一个棘手的领域,我也在努力获取好的错误消息。我遇到的一个问题是
operatorPrecedence
它重写
规则并返回一个复杂的解析器,它可以真正解析表达式。错误消息的质量主要取决于
operatorPrecedence
的实现,而不是取决于您的代码。基本上你必须设计你的语言,以获得良好的错误报告。要获得良好的错误消息,有一种非常愚蠢的语言是有帮助的:
var a as Int;let a=2;
我的语言本质上非常简单:7个比较运算符(=,!=,=,and in),一些布尔运算符(not,and,or,xor),它们只是关键字OP值“组合在一起。这真的非常简单。值可能有点棘手,但我插入了一个完整的验证框架,它工作得很好,除了值不好时,在我的OP中会出现错误:/对变量名也有一个验证解析操作。或者有一个全面的变量名,如
Word(alphas)
,并在其上放置一个总是引发异常的解析操作。或者,您可以在一个级别上执行验证。拥有一个解析器
Word(alphas)-“==”-Word(nums)
并对其执行更复杂的解析操作,以查找合法的变量名,并确保数字的正确性。目前,这将是最后的解决方案:)原因很简单:解析器回溯:
“a=0”
是一个完整的程序。解析器恰好在最后一次测试这个假设,而且它也失败了,因为在
“a=0”
之后有更多的文本。这就是解析器希望“文本结束”的原因。它基本上是
操作员接收
的一个实现细节。尽可能插入
-
以防止回溯,这将导致错误