3
expr = Ref('expr')
block = '{' + Repeat(expr) + '}'
expr = block | Token(re='[0-9]')
START = expr

lrparsingこれは Python 用のモジュールを使用した文法です。モジュールは、文法の競合を報告しません。

{{0}}エラー で文字列を解析できませんlrparsing.ParseError: line 1 column 5: Got '}' when expecting __end_of_input__ while trying to match block in state 11

段階的なスタック状態は次のとおりです。

shift  '{'; ItemSet:5='{'
shift  '{'; ItemSet:5='{' ItemSet:5='{'
shift  /[0-9]/; ItemSet:4=/[0-9]/ ItemSet:5='{' ItemSet:5='{'
reduce '}'; ItemSet:4=/[0-9]/ -- ItemSet:7=expr ItemSet:5='{' ItemSet:5='{'
reduce '}'; ItemSet:7=expr -- ItemSet:9=expr ItemSet:5='{' ItemSet:5='{'
shift  '}'; ItemSet:11='}' ItemSet:9=expr ItemSet:5='{' ItemSet:5='{'

これは、それがシフトしていることを意味し、次に に減少するの{{0を見て、最初にシフトせずに再び減少するため、バジーザスが混乱します。}0expr}

これはバグですか、それとも私は非常に単純で愚かなことをしていますか?

それが私の文法である場合、どうすればそれをリファクタリングして、私の永遠の熱狂的で情熱的な欲求を満たすことができるでしょうか? それがバグである場合、誰かが動作する lrparsing に最も似た構文を持つ python モジュールに私を導くことができますか?

編集:次のようにリファクタリング:

blocks = Ref('blocks')
block = Ref('block')
expr = Ref('expr')
blocks = blocks + block | THIS*0 # THIS*0 is the idiomatic way of getting the empty string
block = '{' + expr + '}'
expr = blocks | Token(re='[0-9]')
START = expr

適切な解析を可能にします。今の私への質問は...なぜですか?lrparsing解析の問題について以前に私に不平を言ったような気がします...Repeat単に期待どおりに機能しないのですか?

4

3 に答える 3

4

Lrparse の作成者はこちら。Serge が言ったように、これはバグであり、1.0.8 で修正されています。これは、Serge が Source Forge の but tracker で報告したために発生したものです。そうでなければ、私は知りませんでした。ありがとうセルジュ。

おそらくバグであるというコメントは、Repeat()lrparsesが何をするのか理解していないことを示唆しています。Lrparsing はかなり複雑な獣です。Python プログラマーにとって自然な方法で文法を入力できます。次に、一連のプロダクションである LR(1) パーサー ジェネレーターが理解できるものにコンパイルします。次に、それらの生成物から LR(1) 解析テーブルを生成します。最後に、入力言語と解析テーブルを LR(1) パーサーにフィードして、解析ツリーを生成します。価値のあることとして、バグは解析テーブルを生成する部分にありました。

このような一連の変換をデバッグすることは、各ステップが何を生成するかを確認できなければ、ほぼ不可能です。したがって、lrparsing には、repr_xxxx()各ステップの出力を表示する機能があります。最初の変換は、文法の解析です。結果は次のように表示されrepr_grammar()ます。

<G> = START + __end_of_input__
START = expr
block = '{' + expr * () + '}'
expr = block | /[0-9]/

これは、質問で提示された元の文法と非常によく似ています。次のステップは、プロダクションでこれらのルールをコンパイルすることです。これは、LR(1) パーサー ジェネレーターが理解できるものです。これらはによって印刷されrepr_productions()ます:

<G> = START __end_of_input__
START = expr
block = '{' '}'
block = '{' block.Sequence.Repeat '}'
block.Sequence.Repeat = expr
block.Sequence.Repeat = block.Sequence.Repeat expr
expr = block
expr = /[0-9]/

block.Sequence.Repeat、 を処理するために導入された新しい非端末 lrparseRepeat()です。これらの表現は、元の文法を忠実に表現しているように見えます。

lrparsing は、 のように導入された非終端記号を非表示にするために邪魔になりませんblock.Sequence.Repeat。たとえば、それらは出力解析ツリーには表示されません。つまり、lrparsing ユーザーがそれらを気にする必要はありません - 2 つのケースを除いて。これらの 2 つのケースは、エラーの回復と、解析エンジンのログ出力を理解しようとすることです。前者は複雑な手法であり、ほとんど試みられません。しかし、lrparsing が何をしているかを理解しようとするために、後者を調べた人もいます。LR(1) パーサーが認識しようとしているプロダクションを確認できない限り、ログはあまり意味がありません。しかし、それらを見たことがあれば、 にバグがないことを知っていたでしょうRepeat()

生成された LR(1) 解析テーブルをダンプすることもできます。LR(1) パーサーがどのように機能するかを本当に理解したい場合は、それを理解しようとする必要があります。解析が非常に興味深いトピックであると思われる場合を除き、お勧めしません。

于 2014-08-24T17:26:39.103 に答える
1

lrparse にはバグがあります。再帰的な繰り返しが正しく考慮されません。

実際の問題は、拡張編集で行ったように、単純な再帰によって解決できますが、混乱は少し少なくなります。

block = Ref('block')
block = '{' + block + '}' | Token(re='[0-9]')
START = block

元の文法では などの入力が許可されていたことにも注意してください{{0{1}}}。(その理由は、繰り返し可能な部分が単純な数に、またはexpr再度拡張できるからです。) 2 番目の文法を考えると、おそらくそれは望まなかったでしょう。

pyparsingでいくつかの作業を完了しましたが、構文はかなり異なります。類似の例:

from pyparsing import Forward, Literal, nums, oneOf, Word

l = lambda c: Literal(c).suppress()
block = Forward()
block << (Word(nums, exact=1) ^ l('{') + block + l('}'))
print(block.parseString("{{0}}"))

出力:

['0']

それが役立つことを願っています。

于 2014-01-05T02:56:08.190 に答える