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 的定义And_PhraseOr_Phrase以便它们只匹配具有两个或多个元素的列表,但不知道如何做到这一点。

我也尝试过使用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

我相信你的第二种方法operatorPrecedence是更好的方法。但是,您有几个问题。一是您的关键字“and”和“or”也是您定义的单词。您可能应该像这样设置它:

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

这将防止“和”和“或”被匹配为单词。

另一个问题是您没有正确设置 operatorPrecedence。它的第一个参数应该是“原子”表达式——可以出现在运算符之间的基本元素。operatorPrecedence 自动设置必要的嵌套。通过将 Phrase 作为原子传递,您正在创建一个额外的嵌套级别,这将导致它异常运行。相反,operatorPrecedence 的第一个参数应该是 Word(或者pyp.OneOrMore(Word)如果您想允许多字操作数)。

另外,operatorPrecedence 会自动处理单个原子的情况,所以你不需要把^ Word业务放在里面。这意味着你可以省去直接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>

这不完全是您想要的形式,因为操作数位于嵌套列表中而不是包装它们,但是您应该能够使用 a 重新配置结构parseAction(operatorrPrecedence 允许您为每个操作数传递一个)。

(另外,您最初想要的数据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 回答