网上有很多例子如何使用Boost Spirit实现计算器。还有关于如何向其添加取幂运算符的问题,例如this one。
但是,当涉及指数运算符和一元否定运算符的组合时,这些例子都会失败。
考虑以下表达式:
-4 ^ 2
- (12/3)^ 2
您认为答案应该是什么?任何理智的数学家都会回答-16。因为取幂运算优先于一元否定运算。
但是,从网上分享的示例中实现语法规则的计算器将回答16。
但是,更值得注意的是,尝试在Microsoft Excel中输入这些公式。你得到的是16! (感叹号,不是因子)。甚至还有note in Wikipedia关于这个微软"例外"规则。
但对我们来说,开发人员构建工程应用程序当然是不可接受的。它必须是-16,否则飞机将开始分崩离析。所以,问题是如何调整示例语法规则来遵循它。
以下是我们使用Boost Spirit" Classic"的计算器版本:
definition(calculator const & self ) {
expression
= factor
>> *( ('+' >> factor)[self.add_op]
| ('-' >> factor)[self.sub_op]
)
;
factor
= powerterm
>> *( ('*' >> powerterm)[self.mult_op]
| ('/' >> powerterm)[self.div_op]
)
;
powerterm
= term
>> *( ('^' >> powerterm)[self.exp_op]
| ("**" >> powerterm)[self.exp_op]
)
;
term
= boost::spirit::classic::real_p[self.real_op]
| '(' >> expression >> ')'
| ('-' >> term)[self.neg_op]
| ('+' >> term)
;
}
它与Excel匹配时生成的输出是不可接受的:
info = parse("-4^2", calc, scspirit::space_p);
// generates PUSH(-4);PUSH(2);EXPONENTIATE; which is 16
info = parse("-(12/3)^2", calc, scspirit::space_p);
// generates PUSH(12);PUSH(3);DIVIDE;NEGATE;PUSH(2);EXPONENTIATE; which is 16
答案 0 :(得分:1)
必须对原始代码做一些事情。
首先,在表达" term"时,否定(和正面)操作不应适用于" term",而应适用于" factor"。
其次,不要使用boost :: spirit :: classic :: real_p,而是使用boost :: spirit :: classic :: u real_p,无符号变体。
term
= boost::spirit::classic::ureal_p[self.real_op]
| '(' >> expression >> ')'
| ('-' >> factor)[self.neg_op]
| ('+' >> factor)
;
现在,输出正确:
info = parse("-4^2", calc, scspirit::space_p);
// generates PUSH(4);PUSH(2);EXPONENTIATE;NEGATE; which is -16
info = parse("-(12/3)^2", calc, scspirit::space_p);
// generates PUSH(12);PUSH(3);DIVIDE;PUSH(2);EXPONENTIATE;NEGATE; which is -16
恢复了理智。
这是一个正式的语法:
expression ::= expression [{ ('+'|'-') factor}];
factor ::= powerterm [{ ('*'|'/') factor}];
powerterm ::= powerterm [{('^'|'**') powerterm}];
term ::= number | '(' expression ')' | '-' factor | '+' factor;
其中'数字'是一个正数
当然,Excel仍然是一个问题,由于它在金融领域被广泛使用,我们的经济一团糟。