5

Pyparsing には次のおもちゃの文法があります。

import pyparsing as pp

or_tok = "or"
and_tok = "and"
lparen = pp.Suppress("(")
rparen = pp.Suppress(")")

Word = pp.Word(pp.alphas)("Word")

Phrase = pp.Forward()

And_Phrase = pp.Group(pp.delimitedList(Phrase, and_tok))("And_Phrase")
Or_Phrase = pp.Group(pp.delimitedList(Phrase, or_tok))("Or_Phrase")

Phrase << (pp.Optional(lparen) + (And_Phrase ^ Or_Phrase) + pp.Optional(rparen)) ^ Word

Expression = pp.OneOrMore(Word ^ Phrase)("Expression")


def test(text):
    output = Expression.parseString(text)
    print output.asXML()

ただし、このプログラムを実行すると、無限に再帰が発生します。これは、私が望んでいたことではありません。むしろ、上記のプログラムが以下と同等のものに解決されるように、文法でネストされたフレーズを処理できるようにしたかったのです。

>>> test("TestA and TestB and TestC or TestD")
<Expression>
    <And_Phrase>
        <Word>TestA</Word>
        <Word>TestB</Word>
        <Or_Phrase>
            <Word>TestC</Word>
            <Word>TestD</Word>
        </Or_Phrase>
    </And_Phrase>
</Expression>

And_Phraseandの定義を変更して、Or_Phrase2 つ以上の要素を持つリストのみに一致するようにしようとしましたが、その方法がわかりませんでした。

も使用してみpyparsing.operatorPrecedenceましたが、正しくやったとは思いません:

import pyparsing as pp

or_tok = "or"
and_tok = "and"
lparen = pp.Suppress("(")
rparen = pp.Suppress(")")

Word = pp.Word(pp.alphas)("Word")

Phrase = pp.Forward()

Phrase << Word ^ \
        pp.operatorPrecedence(Phrase, [
            (and_tok, 2, pp.opAssoc.LEFT),
            (or_tok, 2, pp.opAssoc.LEFT)
        ])

Expression = pp.OneOrMore(Word ^ Phrase)("Expression")

def test(text):
    output = Expression.parseString(text)
    print output.asXML()

...リストがまったく生成されなかったため:

>>> test("Hello world and bob")
<Expression>
  <Word>Hello</Word>
  <Word>world</Word>
  <Word>and</Word>
  <Word>bob</Word>
</Expression>

入れ子になったリストを処理できるようにルール定義を変更するにはどうすればよいですか?

4

1 に答える 1

3

あなたの2番目のアプローチoperatorPrecedenceは、より良い方法だと思います。ただし、いくつかの問題があります。1 つは、あなたのキーワード「and」と「or」は、あなたの定義による単語でもあるということです。おそらく次のように設定する必要があります。

and_tok = pyp.Keyword("and")
or_tok = pyp.Keyword("or")
Word = ~(and_tok | or_tok) + pyp.Word(pyp.alphas)("Word")

これにより、「and」と「or」が単語として一致しなくなります。

もう 1 つの問題は、operatorPrecedence を正しく設定していないことです。その最初の引数は、「アトム」式であると想定されています --- 演算子間で発生する可能性のある基本要素です。operatorPrecedence は、必要なネストを自動的に設定します。Atom として Phrase を渡すことで、追加レベルのネストを作成し、暴走させます。代わりに、operatorPrecedence の最初の引数は Word にする必要があります (またはpyp.OneOrMore(Word)複数単語のオペランドを許可する場合)。

^ Wordさらに、operatorPrecedence は単一のアトムのケースを自動的に処理するため、そこにビジネスを用意する必要はありません。これは、省略しPhraseて、Expression直接 operatorPrecedence にすることができることを意味します。

すべてをまとめると、次のようになります。

Expression = (
    pyp.operatorPrecedence(pyp.OneOrMore(Word), [
        (and_tok, 2, pyp.opAssoc.LEFT),
        (or_tok, 2, pyp.opAssoc.LEFT)
    ])
)

結果は次のようになります。

>>> test("Hello and Bob")

<ITEM>
  <ITEM>
    <Word>Hello</Word>
    <AND>and</AND>
    <Word>Bob</Word>
  </ITEM>
</ITEM>

>>> test("TestA and TestB and TestC or TestD")

<ITEM>
  <ITEM>
    <ITEM>
      <Word>TestA</Word>
      <AND>and</AND>
      <Word>TestB</Word>
      <AND>and</AND>
      <Word>TestC</Word>
    </ITEM>
    <OR>or</OR>
    <Word>TestD</Word>
  </ITEM>
</ITEM>

オペランドはネストされたリスト内にあり、それらをラップするのではなく内部にあるため、これはまさにあなたが望んでいた形式ではありませんが、parseAction(operatorrPrecedence を使用すると、オペランドごとに 1 つを渡すことができます) を使用して構造を再構成できるはずです。

(また、元の目的のデータtest("TestA and TestB and TestC or TestD")は説明と矛盾しています。「and」と「or」を同じ優先順位にしたい場合は、上記の例のように、これをのように括弧で囲みます。一緒に括弧(TestA and TestB and TestC) or TestDを付けたい場合は、(TestC or TestD)、「または」の優先順位を高くする必要があります。)

于 2012-08-23T03:41:02.763 に答える