1

网上有很多例子如何用 Boost Spirit 实现一个计算器。还回答了有关如何向其添加幂运算符的问题,例如this one

但是,当涉及到幂运算符和一元否定运算符的组合时,这些示例就失败了。

考虑以下表达式:

-4^2

-(12/3)^2

你认为答案应该是什么?任何理智的数学家都会回答-16。因为求幂运算优先于一元求反运算。

但是,根据网络上共享的示例执行语法规则的计算器将回答 16。

但是,更值得注意的是,尝试在 Microsoft Excel 中输入这些公式。你会得到16!(感叹号,不是阶乘)。维基百科中甚至有一条关于微软对该规则的“例外”的注释。

但是对于我们这些构建工程应用程序的开发人员来说,这当然是不可接受的。它必须是 -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
4

1 回答 1

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

于 2014-10-02T17:20:45.230 回答