我试图使用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
,但我不认为我理解它是如何工作的。
编辑:
深入探究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)
])
哪个不处理括号(我不知道我是否必须对结果进行后期处理),我需要处理它们。
答案 0 :(得分:9)
此解析问题的实际名称是“中缀表示法”(在最新版本的pyparsing中,我将operatorPrecedence
重命名为infixNotation
)。要查看中缀表示法解析的典型实现,请查看pyparsing wiki上的fourFn.py示例。在那里,您将看到这个简化的BNF的实现,以实现4函数算法,优先于操作:
operand :: integer or real number
factor :: operand | '(' expr ')'
term :: factor ( ('*' | '/') factor )*
expr :: term ( ('+' | '-') term )*
因此,表达式是由加法或减法运算分隔的一个或多个术语。
术语是由乘法或除法运算分隔的一个或多个因子。
一个因子是最低级别的操作数(在这种情况下,只是整数或实数),或者是一个包含在()中的expr。
请注意,这是一个递归解析器,因为factor在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
infixNotation
pyparsing helper方法负责所有递归定义和分组,并允许您将其定义为:
expr = infixNotation(operand,
[
(oneOf('* /'), 2, opAssoc.LEFT),
(oneOf('+ -'), 2, opAssoc.LEFT),
])
但是这会掩盖所有基础理论,所以如果你想了解它是如何实现的,那么看看fourFn.py中的原始解决方案。