使用 pyparsing 解析数学表达式

2023-12-24

我正在尝试使用 pyparsing 来解析数学表达式。我知道我可以从 pyparsing 网站复制示例计算器,但我想理解它,以便稍后添加。我来这里是因为我试图理解这个例子,但我做不到,所以我尽力了,我得到了这个:

symbol = (
    pp.Literal("^") |
    pp.Literal("*") |
    pp.Literal("/") |
    pp.Literal("+") |
    pp.Literal("-")
)
operation = pp.Forward()
atom = pp.Group(
    pp.Literal("(").suppress() + operation + pp.Literal(")").suppress()
) | number
operation << (pp.Group(number + symbol + number + pp.ZeroOrMore(symbol + atom)) | atom)
expression = pp.OneOrMore(operation)


print(expression.parseString("9-1+27+(3-5)+9"))

打印:

[[9, '-', 1, '+', 27, '+', [[3, '-', 5]], '+', 9]]

它有效,有点。我想要优先级并且全部排序Groups,但经过多次尝试后,我找不到办法。或多或少像这样:

[[[[9, '-', 1], '+', 27], '+', [3, '-', 5]], '+', 9]

我想保持它的 AST 外观,我想从中生成代码。

我确实看到了operatorPrecedence班级?如同Forward,但我认为我也不明白它是如何工作的。

EDIT:

尝试更深入operatorPrecedence我得到了这个:

expression = pp.operatorPrecedence(number, [
    (pp.Literal("^"), 1, pp.opAssoc.RIGHT),
    (pp.Literal("*"), 2, pp.opAssoc.LEFT),
    (pp.Literal("/"), 2, pp.opAssoc.LEFT),
    (pp.Literal("+"), 2, pp.opAssoc.LEFT),
    (pp.Literal("-"), 2, pp.opAssoc.LEFT)
])

它不处理括号(我不知道是否需要对结果进行后处理),我需要处理它们。


这个解析问题的实际名称是“中缀表示法”(在最新版本的 pyparsing 中,我将其重命名为operatorPrecedence to infixNotation)。要查看中缀表示法解析的典型实现,请查看 pyparsing wiki 上的 fourFn.py 示例。在那里,您将看到这个简化的 BNF 的实现,用于实现 4 函数算术,并具有运算优先级:

operand :: integer or real number
factor :: operand | '(' expr ')'
term :: factor ( ('*' | '/') factor )*
expr :: term ( ('+' | '-') term )*

因此,表达式是由加法或减法运算分隔开的一项或多项。

一项是由乘法或除法运算分隔开的一个或多个因子。

因子要么是最低级别的操作数(在本例中,只是整数或实数),要么是包含在 () 中的 expr。

请注意,这是一个递归解析器,因为因子在 expr 的定义中间接使用,但 expr 也用于定义因子。

在 pyparsing 中,这看起来大致如下(假设已经定义了整数和实数):

LPAR,RPAR = map(Suppress, '()')
expr = Forward()
operand = real | integer
factor = operand | Group(LPAR + expr + RPAR)
term = factor + ZeroOrMore( oneOf('* /') + factor )
expr <<= term + ZeroOrMore( oneOf('+ -') + term )

现在使用expr,您可以解析其中任何一个:

3
3+2
3+2*4
(3+2)*4

The infixNotationpyparsing 辅助方法负责所有递归定义和分组,并允许您将其定义为:

expr = infixNotation(operand,
        [
        (oneOf('* /'), 2, opAssoc.LEFT),
        (oneOf('+ -'), 2, opAssoc.LEFT),
        ])

但这掩盖了所有基础理论,因此如果您想了解这是如何实现的,请查看 fourFn.py 中的原始解决方案。

[编辑 - 2022 年 12 月 18 日] 对于那些寻找预定义解决方案的人,我已经打包了infixNotation进入它自己的 pip 可安装包,名为正负 https://pypi.org/project/plusminus/. plusminus定义了一个BaseArithmeticParser用于创建支持这些运算符的可立即运行的解析器和求值器的类:

  **   ÷   >=  ∈  in   ?:
  *    +   ==  ∉  not  |absolute-value|
  //   -   !=  ∩  and
  /    <   ≠   ∪  ∧
  mod  >   ≤   &  or
  ×    <=  ≥   |  ∨

以及这些函数:

  abs    ceil   max
  round  floor  str
  trunc  min    bool

The BaseArithmeticParser类允许您为自己的特定于域的表达式定义其他运算符和函数,示例展示了如何使用自定义函数和运算符定义解析器,以进行掷骰子、零售价格折扣等。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 pyparsing 解析数学表达式 的相关文章

随机推荐