2016-12-23 13 views
1

我使用pyparsing解析算术表达式时遇到了问题。 我有以下语法:pyparsing中的setResultName问题

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` 现在是否有更好的方法

问候 阿婆

我对如何另一个小问题?分析结果。 我第一次尝试是使用一个循环,例如像

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。 不幸的是,如果我使用asDict,那么令牌的顺序就会丢失。

是否有可能获得一个有序字典和迭代的东西,如

for tag, token in tokens.iteritems(): 

其中tag speficies令牌的类型及其键(例如,op, value, identifier, expr...)和令牌是相应的令牌?

回答

1

如果您想要pyparsing将数字字符串转换为整数,您可以添加一个分析操作来完成解析时间。或者,使用预定义的整数和浮点(与pyparsing导入的命名空间类)在pyparsing_common定义的值:

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

对于您的命名问题,您可以添加解析动作来获得在中缀表示法的每个级别运行 - 代码下面,我添加了一个解析动作,它只是将'expr'名称添加到当前解析的组中。您也想加入“*”所有的OPS的这样反复操作人员得到相同的“保持所有,而不仅仅是过去的”行为结果名称:

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,因为它做的猜测来创建其输出的公平位。您可能最好手动导航解析的结果。另外,看一些pyparsing维基例子页面上的例子,尤其是SimpleBool.py,它使用类在中缀表示法中使用的每级解析动作。

编辑::

在这一点上,我真的要持续使用结果的名称,以指导分析结果的评价这条道路上劝阻你。请看看这两个方法递归在解析标记(注意,你要寻找的方法是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值] ...”,且这些值本身可以是整型,浮点型,或嵌套ParseResults - 但从未STRS ,至少不是我用这种方法硬编码的4个二元运算符。而不是自己尝试到特殊情况,以死亡来处理一元OPS和右关联OPS,请看看如何在eval_arith.py(http://pyparsing.wikispaces.com/file/view/eval_arith.py/68273277/eval_arith.py)完成后,通过估计类每个操作数类型相关联,而中缀表示法的每个级别。

+0

非常感谢你。这符合预期! – Apoptose

+0

我对如何解析结果的简短的跟进质询: – Apoptose

+0

谢谢你,这确实是一个更好的办法! – Apoptose