Mic*_*x2a 5 python pyparsing operator-precedence
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()
Run Code Online (Sandbox Code Playgroud)
但是,运行此程序将无限递归,这不是我想要的.相反,我希望我的语法能够处理嵌套短语,以便上述程序可以解析为与下面相同的东西:
>>> 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>
Run Code Online (Sandbox Code Playgroud)
我试图修改定义And_Phrase,Or_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()
Run Code Online (Sandbox Code Playgroud)
...因为它根本没有产生一个列表:
>>> test("Hello world and bob")
<Expression>
<Word>Hello</Word>
<Word>world</Word>
<Word>and</Word>
<Word>bob</Word>
</Expression>
Run Code Online (Sandbox Code Playgroud)
如何修改规则定义以便它们处理嵌套列表?
我相信你的第二种方法operatorPrecedence是更好的方法。但是,您有几个问题。一是您的关键字“and”和“or”也是您定义的单词。你可能应该这样设置:
and_tok = pyp.Keyword("and")
or_tok = pyp.Keyword("or")
Word = ~(and_tok | or_tok) + pyp.Word(pyp.alphas)("Word")
Run Code Online (Sandbox Code Playgroud)
这将防止“and”和“or”被匹配为单词。
另一个问题是您没有正确设置operatorPrecedence。它的第一个参数应该是“原子”表达式——可以出现在运算符之间的基本元素。operatorPrecedence 自动设置必要的嵌套。通过将 Phrase 作为原子传递,您将创建一个额外的嵌套级别,这将导致它疯狂运行。相反,operatorPrecedence 的第一个参数应该是 Word(或者pyp.OneOrMore(Word)如果您想允许多字操作数)。
另外,operatorPrecedence会自动处理单个原子的情况,所以你不需要把业务^ Word放在那里。这意味着您可以省去Phrase并Expression直接使用操作符优先级。
把所有这些放在一起,你会得到这个:
Expression = (
pyp.operatorPrecedence(pyp.OneOrMore(Word), [
(and_tok, 2, pyp.opAssoc.LEFT),
(or_tok, 2, pyp.opAssoc.LEFT)
])
)
Run Code Online (Sandbox Code Playgroud)
结果是这样的:
>>> 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>
Run Code Online (Sandbox Code Playgroud)
这并不完全是您想要的形式,因为操作数位于嵌套列表内而不是包装它们,但您应该能够使用 a 重新配置结构parseAction(operatorrPrecedence 允许您为每个操作数传递一个)。
(另外,您原来想要的数据test("TestA and TestB and TestC or TestD")与您的描述不一致。如果您希望“and”和“or”具有相同的优先级,那么这将(TestA and TestB and TestC) or TestD像上面的示例一样括起来。如果您想(TestC or TestD)一起括起来,您需要给予“或”更高的优先级。)