带指数运算符的表达式语法和使用Boost Spirit的一元否定运算符

时间:2014-10-02 17:20:45

标签: boost boost-spirit exponent unary-operator negate

网上有很多例子如何使用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

1 个答案:

答案 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仍然是一个问题,由于它在金融领域被广泛使用,我们的经济一团糟。