【发布时间】:2015-11-04 21:34:50
【问题描述】:
我需要将嵌套的二进制布尔表达式解析为 XML 树。例如取表达式
expression2 = "((Param1 = 1 AND Param2 = 1 ) \
OR (Param3 = 1 AND Param4 = 1)) \
AND \
(((Param5 = 0 AND Param6 = 1 ) \
OR(Param7 = 0 AND Param8 = 1)) \
AND \
((Param9 = 0 AND Param10 = 1 ) \
OR(Param11 = 0 AND Param12 = 1)))"
本质上是(Expression) (Operator) (Expression) 术语的组合。
我需要输出是这些表达式与 XML 中适当标签的组合。又名
<MainBody>
<FirstExpression>
Parameter
</FirstExpression>
<Operator>=</Operator>
<SecondExpression>
1
</SecondExpression>
</MainBody>
其中 firstexpression 可以是参数或主体(这里是嵌套),运算符始终为 =、、AND、OR,secondexpression 可以是整数或主体
总会有三个一组 - 也就是最小的离散对象将由第一个表达式运算符和第二个表达式组成。
我想出的代码(这是我第一次使用 python)让我有点明白了。
import pyparsing as pp
import xml.etree.ElementTree as ET
operator = pp.Regex(">=|<=|!=|>|<|=").setName("operator").setResultsName("Operator")
number = pp.Regex(r"[+-]?\d+(:?\.\d*)?(:?[eE][+-]?\d+)?").setResultsName("SecondExpression")
identifier = pp.Word(pp.alphas, pp.alphanums + "_" + ".").setName("FirstExpression").setResultsName("FirstExpression")
comparison_term = identifier | number
condition = pp.Group(comparison_term + operator + comparison_term).setResultsName("MainBody")
expr = pp.operatorPrecedence(condition,[
("NOT", 1, pp.opAssoc.RIGHT, ),
("AND", 2, pp.opAssoc.LEFT, ),
("OR", 2, pp.opAssoc.LEFT, ),
])
expression2 = "((Param1 = 1 AND Param2 = 1 ) \
OR (Param3 = 1 AND Param4 = 1)) \
AND \
(((Param5 = 0 AND Param6 = 1 ) \
OR(Param7 = 0 AND Param8 = 1)) \
AND \
((Param9 = 0 AND Param10 = 1 ) \
OR(Param11 = 0 AND Param12 = 1)))"
out = expr.parseString(expression2)
text = out.asXML()
f = open('rules.xml','w+')
f.write(text)
f.close()
root = ET.parse("rules.xml").getroot()
print ET.tostring(root)
这会输出这种形式的 XML:
<ITEM>
<ITEM>
<ITEM>
<MainBody>
<MainBody>
<FirstExpression>Param1</FirstExpression>
<Operator>=</Operator>
<SecondExpression>1</SecondExpression>
</MainBody>
<ITEM>AND</ITEM>
<MainBody>
<FirstExpression>Param2</FirstExpression>
<Operator>=</Operator>
<SecondExpression>1</SecondExpression>
</MainBody>
</MainBody>
<ITEM>OR</ITEM>
<MainBody>
<MainBody>
<FirstExpression>Param3</FirstExpression>
<Operator>=</Operator>
<SecondExpression>1</SecondExpression>
</MainBody>
<ITEM>AND</ITEM>
<MainBody>
<FirstExpression>Param4</FirstExpression>
<Operator>=</Operator>
<SecondExpression>1</SecondExpression>
</MainBody>
</MainBody>
</ITEM>
<ITEM>AND</ITEM>
<ITEM>
<ITEM>
<MainBody>
<MainBody>
<FirstExpression>Param5</FirstExpression>
<Operator>=</Operator>
<SecondExpression>0</SecondExpression>
</MainBody>
<ITEM>AND</ITEM>
<MainBody>
<FirstExpression>Param6</FirstExpression>
<Operator>=</Operator>
<SecondExpression>1</SecondExpression>
</MainBody>
</MainBody>
<ITEM>OR</ITEM>
<MainBody>
<MainBody>
<FirstExpression>Param7</FirstExpression>
<Operator>=</Operator>
<SecondExpression>0</SecondExpression>
</MainBody>
<ITEM>AND</ITEM>
<MainBody>
<FirstExpression>Param8</FirstExpression>
<Operator>=</Operator>
<SecondExpression>1</SecondExpression>
</MainBody>
</MainBody>
</ITEM>
<ITEM>AND</ITEM>
<ITEM>
<MainBody>
<MainBody>
<FirstExpression>Param9</FirstExpression>
<Operator>=</Operator>
<SecondExpression>0</SecondExpression>
</MainBody>
<ITEM>AND</ITEM>
<MainBody>
<FirstExpression>Param10</FirstExpression>
<Operator>=</Operator>
<SecondExpression>1</SecondExpression>
</MainBody>
</MainBody>
<ITEM>OR</ITEM>
<MainBody>
<MainBody>
<FirstExpression>Param11</FirstExpression>
<Operator>=</Operator>
<SecondExpression>0</SecondExpression>
</MainBody>
<ITEM>AND</ITEM>
<MainBody>
<FirstExpression>Param12</FirstExpression>
<Operator>=</Operator>
<SecondExpression>1</SecondExpression>
</MainBody>
</MainBody>
</ITEM>
</ITEM>
</ITEM>
</ITEM>
显然这不是我想要的,因为唯一带有标签的对象处于最深层次。对于比这更大的规则,我需要它尽可能深——本质上是一个包含 Mainbody、FirstExpression、Operator 和 Second Expression 的二叉树。
我还需要在标签内放置整数值,这是另一件我还没弄清楚该怎么做的事情。
我认为 pyparsing 应该能够以某种方式对组执行此操作,但我无法弄清楚。
任何人都可以就如何实现这一点提出建议吗?
谢谢
2015 年 11 月 5 日编辑:
根据 Paul 所写的内容,我使用(很好的)递归语法得出了这段代码:
import pyparsing as pp
operator = pp.oneOf(">= <= != > < =")("operator")
integer = pp.Regex(r"[+-]?\d+(:?\.\d*)?(:?[eE][+-]?\d+)?")("integer")
parameter = pp.Word(pp.alphas, pp.alphanums + "_" + "." + "-")("parameter")
comparison_term = parameter | integer
firstExpression = pp.Forward()
secondExpression = pp.Forward()
mainbody = pp.Group(firstExpression + operator + secondExpression)("Mainbody")
firstExpression << pp.Group(parameter | pp.Optional(mainbody))("FirstExpression")
secondExpression << pp.Group(integer | pp.Optional(mainbody))("SecondExpression")
AND_ = pp.Keyword("AND")("operator")
OR_ = pp.Keyword("OR")("operator")
NOT_ = pp.Keyword("NOT")("operator")
expr = pp.operatorPrecedence(mainbody,[
(NOT_, 1, pp.opAssoc.RIGHT, ),
(AND_, 2, pp.opAssoc.LEFT, ),
(OR_, 2, pp.opAssoc.LEFT, ),
])
# undocumented hack to assign a results name to (expr) - RED FLAG
expr.expr.resultsName = "Mainbody"
expression1 = "((Param1 = 1) \
OR (Param2 = 1))"
out = expr.parseString(expression1)[0] # extract item 0 from single-item list
text = out.asXML("Mainbody") # add tag for outermost element
print text
无限递归。改变 | to + 在 firstExpression 和 secondExpression 行中解决了这个问题,但我相信它会导致解析器永远不会寻找要分组的主体。
我已经包含了一个简化的规则,因此我可以显示我想要获得的确切输出。
此代码生成:
<Mainbody>
<Mainbody>
<FirstExpression>
<parameter>Param1</parameter>
</FirstExpression>
<operator>=</operator>
<SecondExpression>
<integer>1</integer>
</SecondExpression>
</Mainbody>
<operator>OR</operator>
<Mainbody>
<FirstExpression>
<parameter>Param2</parameter>
</FirstExpression>
<operator>=</operator>
<SecondExpression>
<integer>1</integer>
</SecondExpression>
</Mainbody>
</Mainbody>
我想要得到什么
<Mainbody>
<FirstExpression>
<Mainbody>
<FirstExpression>
<parameter>Param1</parameter>
</FirstExpression>
<operator>=</operator>
<SecondExpression>
<integer>1</integer>
</SecondExpression>
</Mainbody>
</FirstExpression>
<operator>OR</operator>
<SecondExpression>
<Mainbody>
<FirstExpression>
<parameter>Param2</parameter>
</FirstExpression>
<operator>=</operator>
<SecondExpression>
<integer>1</integer>
</SecondExpression>
</Mainbody>
</SecondExpression>
</Mainbody>
看起来我看到的问题是解析器没有正确地将主体标记/识别/分组为 FirstExpression 或 SecondExpression。我已经尝试过调整语法并且经常得到无限递归,所以我觉得我的语法定义有问题。我需要它来处理通过 AND/OR 分组的任意数量的二进制 (PARAMETER = INTEGER)。
有什么建议吗?
谢谢
【问题讨论】:
标签: python xml nested boolean pyparsing