2016-12-23 2 views
1

У меня проблема с разбором арифметических выражений с использованием pyparsing. У меня есть следующая грамматика:Проблемы с setResultName в pyparsing

numeric_value = (integer_format | float_format | bool_format)("value*") 
identifier = Regex('[a-zA-Z_][a-zA-Z_0-9]*')("identifier*") 

operand = numeric_value | identifier 

expop = Literal('^')("op") 
signop = oneOf('+ -')("op") 
multop = oneOf('* /')("op") 
plusop = oneOf('+ -')("op") 
factop = Literal('!')("op") 

arithmetic_expr = infixNotation(operand, 
    [("!", 1, opAssoc.LEFT), 
    ("^", 2, opAssoc.RIGHT), 
    (signop, 1, opAssoc.RIGHT), 
    (multop, 2, opAssoc.LEFT), 
    (plusop, 2, opAssoc.LEFT),] 
    )("expr") 

Я хотел бы использовать это для разбора арифметических выражений, например,

expr = "9 + 2 * 3" 
parse_result = arithmetic_expr.parseString(expr) 

У меня есть две проблемы здесь.

Во-первых, когда я сбросить результат, я получаю следующее:

[['9', '+', ['2', '*', '3']]] 
- expr: ['9', '+', ['2', '*', '3']] 
    - op: '+' 
    - value: ['9'] 

Соответствующий выход XML IST:

<result> 
    <expr> 
    <value>9</value> 
    <op>+</op> 
    <value> 
     <value>2</value> 
     <op>*</op> 
     <value>3</value> 
    </value> 
    </expr> 
</result> 

То, что я хотел бы иметь в том, что ['2', '*', '3'] показывает, как expr, т.е.

<result> 
    <expr> 
    <value>9</value> 
    <op>+</op> 
    <expr> 
     <value>2</value> 
     <op>*</op> 
     <value>3</value> 
    </expr> 
    </expr> 
</result> 

Однако, я не уверен, хо использовать setResultName() для этого.

Во-вторых, к сожалению, когда я хочу перебирать результаты, я получаю строки для простых частей. Таким образом, я использую XML «взломать» в качестве обходного пути (я получил эту идею здесь: `pyparsing`: iterating over `ParsedResults` Есть ли лучший способ в настоящее время

С наилучшими пожеланиями APO

У меня есть еще один маленький вопрос о том, как? анализировать результаты. Моя первая попытка была использовать цикл, как, например,

def recurse_arithmetic_expression(tokens): 
    for t in tokens: 
     if t.getResultName() == "value": 
      pass # do something... 
     elif t.getResultName() == "identifier": 
      pass # do something else.. 
     elif t.getResultName() == "op": 
      pass # do something completely different... 
     elif isinstance(t, ParseResults): 
      recurse_arithmetic_expression(t) 

Однако, к сожалению, t может быть строкой или INT/поплавок. Таким образом, я получаю исключение при попытке вызвать getResultName. К сожалению, w Я использую asDict, порядок жетонов утерян.

Можно ли получить упорядоченный Dict и перебирать свои ключи с чем-то вроде

for tag, token in tokens.iteritems(): 

где tag speficies типа маркеров (например, op, value, identifier, expr...) и маркер соответствующего маркера?

ответ

1

Если вы хотите, чтобы pyparsing преобразовывал числовые строки в целые числа, вы можете добавить синтаксическое действие, чтобы это было сделано во время разбора. OR, используйте предустановленное целое и поплавок значения, определенное в pyparsing_common (класс имен импортируются с Pyparsing):

numeric_value = (pyparsing_common.number | bool_format)("value*") 

Для вашего вопроса присвоения имен, вы можете добавить синтаксический анализ действия, чтобы запустить на каждом уровне инфиксной нотации - в коде ниже, я добавляю действие синтаксического анализа, которое просто добавляет имя «expr» к текущей анализируемой группе.Вы также хотите добавить «*» для всех вашего опса, так что повторяющиеся операторы получают то же самое «сохранить все, а не только последнее» поведением имен результатов:

bool_format = oneOf("true false") 
numeric_value = (pyparsing_common.number | bool_format)("value*") 
identifier = Regex('[a-zA-Z_][a-zA-Z_0-9]*')("identifier*") 

operand = numeric_value | identifier 

expop = Literal('^')("op*") 
signop = oneOf('+ -')("op*") 
multop = oneOf('* /')("op*") 
plusop = oneOf('+ -')("op*") 
factop = Literal('!')("op*") 


def add_name(s,l,t): 
    t['expr'] = t[0] 

arithmetic_expr = infixNotation(operand, 
    [("!", 1, opAssoc.LEFT, add_name), 
    ("^", 2, opAssoc.RIGHT, add_name), 
    (signop, 1, opAssoc.RIGHT, add_name), 
    (multop, 2, opAssoc.LEFT, add_name), 
    (plusop, 2, opAssoc.LEFT, add_name),] 
    )("expr") 

Посмотрите, как эти результаты выглядят в настоящее время:

arithmetic_expr.runTests(""" 
    9 + 2 * 3 * 7 
""") 

print(arithmetic_expr.parseString('9+2*3*7').asXML()) 

дает:

9 + 2 * 3 * 7 
[[9, '+', [2, '*', 3, '*', 7]]] 
- expr: [9, '+', [2, '*', 3, '*', 7]] 
    - expr: [2, '*', 3, '*', 7] 
    - op: ['*', '*'] 
    - value: [2, 3, 7] 
    - op: ['+'] 
    - value: [9] 


<expr> 
    <expr> 
    <value>9</value> 
    <op>+</op> 
    <expr> 
     <value>2</value> 
     <op>*</op> 
     <value>3</value> 
     <op>*</op> 
     <value>7</value> 
    </expr> 
    </expr> 
</expr> 

Примечание: Я вообще отговорить людей от использования asXML, поскольку он должен сделать справедливый бит угадывания, чтобы создать свой вывод. Вероятно, вам лучше провести вручную анализируемые результаты. Кроме того, посмотрите на некоторые примеры на странице примеров википарации wiki, особенно SimpleBool.py, которая использует классы для действий синтаксического анализа, используемых в infixNotation.

EDIT ::

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

result = arithmetic_expr.parseString('9 + 2 * 4 * 6') 

def iterate_over_parsed_expr(tokens): 
    for t in tokens: 
     if isinstance(t, ParseResults): 
      tag = t.getName() 
      print(t, 'is', tag) 
      iterate_over_parsed_expr(t) 
     else: 
      print(t, 'is', type(t)) 

iterate_over_parsed_expr(result) 

import operator 
op_map = { 
    '+' : operator.add, 
    '-' : operator.sub, 
    '*' : operator.mul, 
    '/' : operator.truediv 
    } 
def eval_parsed_expr(tokens): 
    t = tokens 
    if isinstance(t, ParseResults): 
     # evaluate initial value as left-operand 
     cur_value = eval_parsed_expr(t[0]) 
     # iterate through remaining tokens, as operator-operand pairs 
     for op, operand in zip(t[1::2], t[2::2]): 
      # look up the correct binary function for operator 
      op_func = op_map[op] 
      # evaluate function, and update cur_value with result 
      cur_value = op_func(cur_value, eval_parsed_expr(operand)) 

     # no more tokens, return the value 
     return cur_value 
    else: 
     # token is just a scalar int or float, just return it 
     return t 

print(eval_parsed_expr(result)) # gives 57, which I think is the right answer 

eval_parsed_expr опирается на структуру анализируемых лексем, а не по именам результатов , Для этого ограниченного случая токены - все двоичные операторы, поэтому для каждой вложенной структуры итоговые маркеры являются «значением [op value] ...», а сами значения могут быть int, float или вложенными ParseResults, но никогда не strs , по крайней мере, не для 4 двоичных операторов, которые я жестко закодировал в этом методе. Вместо того, чтобы прибегать к специальным случаям самостоятельно, чтобы справиться с унарными операциями и право-ассоциативными операциями, пожалуйста, посмотрите, как это делается в eval_arith.py (http://pyparsing.wikispaces.com/file/view/eval_arith.py/68273277/eval_arith.py), сопоставляя классы оценщиков с каждым типом операнда и каждый уровень infixNotation ,

+0

спасибо. Это работает так, как ожидалось! – Apoptose

+0

У меня есть короткий вопрос о том, как разобрать результаты: – Apoptose

+0

Спасибо, это действительно лучший подход! – Apoptose

Смежные вопросы