我在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_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()
...因为它根本没有产生一个列表:
>>> test("Hello world and bob")
<Expression>
<Word>Hello</Word>
<Word>world</Word>
<Word>and</Word>
<Word>bob</Word>
</Expression>
如何修改规则定义以便它们处理嵌套列表?
答案 0 :(得分:3)
我相信你使用operatorPrecedence
的第二种方法是更好的方法。但是,你有几个问题。一个是您的关键字“和”和“或”也是您定义的单词。你可能应该这样设置:
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>
这不完全是您想要的形式,因为操作数位于嵌套列表中而不是包装它们,但您应该能够使用parseAction
重新配置结构(operatorrPrecedence允许您传入一个每个操作数)。
(另外,test("TestA and TestB and TestC or TestD")
的原始所需数据与您的描述不一致。如果您希望“和”和“或”具有相同的优先级,那么这将包含为(TestA and TestB and TestC) or TestD
,如它在上面的例子中。如果你想要(TestC or TestD)
括起来,你需要给“或”更高的优先权。)