11

編集:私は最初のバージョンを作成しましたが、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ですand

現在、andと 括弧が使用されている場合、エラー報告があまり良くない場合があります。次の例を検討してください。

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

実際、パーサーが a の後の何かを解析できない場合(ここで解析することが重要です) and、適切なエラー メッセージが生成されないようです :(

つまり、 parseを意味します。5 を解析できても、「検証」が parse アクションで失敗した場合でも、適切なエラー メッセージが生成されるからです。aただし、有効な数値 ( など) または有効なキーワード ( など)を解析できない場合xxxxxx、正しいエラー メッセージの生成が停止します。

何か案が?

4

1 に答える 1

12

Pyparse はバックトラックするため、常に多少悪いエラー メッセージが表示されます。エラー メッセージは、パーサーが試行する最後のルールで生成されます。パーサーは、エラーが実際にどこにあるかを知ることはできません。一致するルールがないことだけを知っています。

適切なエラー メッセージを得るには、早期にあきらめるパーサーが必要です。これらのパーサーは Pyparsing ほど柔軟ではありませんが、ほとんどの従来のプログラミング言語はそのようなパーサーで解析できます。(C++ と Scala IMHO はできません。)

Pyparsing のエラー メッセージを改善するには、-演算子を使用します。演算子のように機能+しますが、バックトラックしません。次のように使用します。

assignment = Literal("let") - varname - "=" - expression

これは、Pyparsing の作成者によるエラー報告の改善に関する小さな記事です。

編集

また、検証を行う解析アクションで無効な数値に対して適切なエラー メッセージを生成することもできます。数値が無効な場合、Pyparsing によってキャッチされない例外が発生します。この例外には適切なエラー メッセージが含まれている場合があります。

解析アクションは 3 つの引数を持つことができます [1]:

  • s = 解析される元の文字列 (以下の注を参照)
  • loc = 一致する部分文字列の場所
  • toks =ParseResultsオブジェクトとしてパッケージ化された、一致したトークンのリスト

適切なエラー メッセージを作成するための 3 つの便利なヘルパー メソッドもあります [2]。

  • lineno(loc, string)- 文字列内の位置の行番号を与える関数; 最初の行は 1 行目で、改行は新しい行を開始します。
  • col(loc, string)- 文字列内の位置の列番号を与える関数; 最初の列は列 1 で、改行は列番号を 1 にリセットします。
  • line(loc, string)- を表すテキスト行を取得する関数lineno(loc, string)。例外の診断メッセージを出力するときに役立ちます。

検証の解析アクションは次のようになります。

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] http://pythonhosted.org/pyparsing/pyparsing.pyparsing.ParserElement-class.html#setParseAction

[2] HowToUsePyparsing

編集

[3] は、質問の現在の (2013-4-10) スクリプトの改良版です。例のエラーは正しく取得されますが、他のエラーは間違った位置に示されます。私のバージョンの Pyparsing ('1.5.7') にはバグがあると思いますが、Pyparsing がどのように機能するのか理解していないだけかもしれません。問題は次のとおりです。

  • ParseFatalException は常に致命的ではないようです。独自の例外を使用すると、スクリプトは期待どおりに機能します。
  • オペレーターが動か-ないようです。

[3] http://pastebin.com/7E4kSnkm

于 2013-04-09T08:59:22.857 に答える