【问题标题】:Problems with setResultName in pyparsingpyparsing 中的 setResultName 问题
【发布时间】:2016-12-23 16:03:32
【问题描述】:

我在使用 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输出列表:

<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“hack”作为解决方法(我从这里得到了这个想法:`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/float。因此,当我尝试调用 getResultName 时出现异常。 不幸的是,当我使用asDict 时,令牌的顺序丢失了。

是否有可能获得 ordered dict 并使用类似

的方式迭代其键
for tag, token in tokens.iteritems():

tag 指定令牌的类型(例如,op, value, identifier, expr...),而令牌是对应的令牌?

【问题讨论】:

    标签: pyparsing


    【解决方案1】:

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

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

    对于您的命名问题,您可以添加解析操作以在每个级别的 infixNotation 上运行 - 在下面的代码中,我添加了一个解析操作,它只是将“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,因为它必须进行相当多的猜测才能创建其输出。您最好手动导航解析的结果。此外,请查看 pyparsing wiki 示例页面上的一些示例,尤其是 SimpleBool.py,它使用类用于 infixNotation 中使用的每级解析操作。

    编辑::

    在这一点上,我真的想劝阻您不要继续使用结果名称来指导解析结果的评估。请查看这两种在解析后的标记上递归的方法(注意您要查找的方法是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]...”,值本身可以是整数、浮点数或嵌套的 ParseResults - 但绝不是 strs ,至少对于我在此方法中硬编码的 4 个二元运算符来说不是。与其尝试特例来处理一元操作和右关联操作,不如在 eval_arith.py (http://pyparsing.wikispaces.com/file/view/eval_arith.py/68273277/eval_arith.py) 中通过将评估器类关联到每个操作数类型和每个级别的操作数来查看这是如何完成的中缀符号。

    【讨论】:

    • 我有一个关于如何解析结果的简短后续问题:
    • 谢谢,这确实是一个更好的方法!
    • Pyparsing 不再托管在 wikispaces.com 上。转至github.com/pyparsing/pyparsing
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多