【问题标题】:Pyparsing/Python Binary Boolean Expression to XML Nesting Issue (2.7.10)Pyparsing/Python 二进制布尔表达式到 XML 嵌套问题 (2.7.10)
【发布时间】: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


    【解决方案1】:

    这是您的代码,只做了一些更改:

    • 将“AND”、“OR”和“NOT”更改为关键字表达式,结果名称为“operator”,以便将它们包裹在&lt;operator&gt; 标签中
    • operatorPrecedence(最近重命名为infixNotation)创建的expr 的内部表达式修改结果名称
    • 从 parseString 返回的单项列表中提取第 0 个元素
    • 在调用 asXML 时添加最外层的标记名称

    .

    operator = pp.oneOf(">= <= != > < =")("Operator")
    number = pp.Regex(r"[+-]?\d+(:?\.\d*)?(:?[eE][+-]?\d+)?")("SecondExpression")
    identifier = pp.Word(pp.alphas, pp.alphanums + "_" + ".")("FirstExpression")
    comparison_term = identifier | number
    condition = pp.Group(comparison_term + operator + comparison_term)("MainBody")
    
    # define AND, OR, and NOT as keywords, with "operator" results names
    AND_ = pp.Keyword("AND")("operator")
    OR_ = pp.Keyword("OR")("operator")
    NOT_ = pp.Keyword("NOT")("operator")
    
    expr = pp.operatorPrecedence(condition,[
                                (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 = "group"
    
    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)[0] # extract item 0 from single-item list
    text = out.asXML("expression") # add tag for outermost element
    print text
    

    打印:

    <expression>
      <group>
        <group>
          <MainBody>
            <FirstExpression>Param1</FirstExpression>
            <Operator>=</Operator>
            <SecondExpression>1</SecondExpression>
          </MainBody>
          <operator>AND</operator>
          <MainBody>
            <FirstExpression>Param2</FirstExpression>
            <Operator>=</Operator>
            <SecondExpression>1</SecondExpression>
          </MainBody>
        </group>
        <operator>OR</operator>
        <group>
          <MainBody>
            <FirstExpression>Param3</FirstExpression>
            <Operator>=</Operator>
            <SecondExpression>1</SecondExpression>
          </MainBody>
          <operator>AND</operator>
          <MainBody>
            <FirstExpression>Param4</FirstExpression>
            <Operator>=</Operator>
            <SecondExpression>1</SecondExpression>
          </MainBody>
        </group>
      </group>
      <operator>AND</operator>
      <group>
        <group>
          <group>
            <MainBody>
              <FirstExpression>Param5</FirstExpression>
              <Operator>=</Operator>
              <SecondExpression>0</SecondExpression>
            </MainBody>
            <operator>AND</operator>
            <MainBody>
              <FirstExpression>Param6</FirstExpression>
              <Operator>=</Operator>
              <SecondExpression>1</SecondExpression>
            </MainBody>
          </group>
          <operator>OR</operator>
          <group>
            <MainBody>
              <FirstExpression>Param7</FirstExpression>
              <Operator>=</Operator>
              <SecondExpression>0</SecondExpression>
            </MainBody>
            <operator>AND</operator>
            <MainBody>
              <FirstExpression>Param8</FirstExpression>
              <Operator>=</Operator>
              <SecondExpression>1</SecondExpression>
            </MainBody>
          </group>
        </group>
        <operator>AND</operator>
        <group>
          <group>
            <MainBody>
              <FirstExpression>Param9</FirstExpression>
              <Operator>=</Operator>
              <SecondExpression>0</SecondExpression>
            </MainBody>
            <operator>AND</operator>
            <MainBody>
              <FirstExpression>Param10</FirstExpression>
              <Operator>=</Operator>
              <SecondExpression>1</SecondExpression>
            </MainBody>
          </group>
          <operator>OR</operator>
          <group>
            <MainBody>
              <FirstExpression>Param11</FirstExpression>
              <Operator>=</Operator>
              <SecondExpression>0</SecondExpression>
            </MainBody>
            <operator>AND</operator>
            <MainBody>
              <FirstExpression>Param12</FirstExpression>
              <Operator>=</Operator>
              <SecondExpression>1</SecondExpression>
            </MainBody>
          </group>
        </group>
      </group>
    </expression>
    

    因此,就目前而言,您肯定走在正确的轨道上,但我认为我们必须将结果名称破解到 expr 的内部未记录成员变量中的事实是一个危险信号,而且很有可能,您很快就会达到使用operatorPrecedence 所能做的极限。

    您可能必须实现自己的递归解析器才能完全控制所有元素和子元素的命名方式。您甚至可能需要实现自己的asXML() 版本来控制是否获得中间级别,例如上面显示的&lt;group&gt; 标记。

    【讨论】:

    • 非常感谢您的帮助。这肯定让我走得更远,但我仍然不在那里。我已经尝试实现递归语法。除了您的解决方案之外,我还在我最初的问题中添加了一些额外的信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-30
    • 2016-04-06
    相关资源
    最近更新 更多