2015-11-04 2 views
1

Мне нужно разобрать вложенные двоичные булевы выражения в дерево XML. Например, возьмем выражениеPyparsing/Python Binary Boolean Expression to XML Nesting Issue (2.7.10)

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 может быть параметром или mainbody (здесь вложенности), оператор всегда =, <,>, И, ИЛИ, и secondexpression является либо целым или mainbody

Всегда будут группы из трех человек, иначе наименьший дискретный объект будет состоять из первого выражения оператора и второго выражения.

Код, который я придумал (это мой первый раз, когда я использовал 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 должен быть в состоянии сделать это с группами как-то, но я не могу понять это.

Может ли кто-нибудь предложить предложение о том, как этого достичь?

Благодаря

EDIT 11/5/15:

здание от того, что Павел написал я прибыл в этот код с (хорошо предназначенной быть) рекурсивной грамматики:

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 

Будет ли бесконечная рекурсия. Изменение | на + в линиях 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. Я пробовал настраивать грамматику и часто получаю бесконечную рекурсию, поэтому я чувствую, что что-то не так в моем определении грамматики.Мне нужно, чтобы это работало для любого числа двоичных группировок (PARAMETER = INTEGER) с помощью AND/OR.

Любые предложения?

Благодаря

ответ

0

Вот ваш код с помощью всего лишь несколько изменений:

  • изменения «И», «ИЛИ» и «НЕ» для ключевого слова выражения, с результатами имени «оператора», так что они будут получать завернутые в <operator> теги
  • взломать имя результатов для внутреннего выражения expr созданного operatorPrecedence (которая недавно была переименована в infixNotation)
  • экстракции 0'th е lement из списка одного элемента вернулся из parseString
  • добавить крайнее имя тега в вызове 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() для контроля того, получаете ли вы промежуточные уровни, например, теги <group>, показанные выше.

+0

Я очень ценю помощь. Это определенно дало мне дальше, но меня все еще не было. Я попытался реализовать рекурсивную грамматику. Я добавил дополнительную информацию к моему первоначальному вопросу с тем, что я пробовал в дополнение к вашему решению. –