boost::spirit用法及EBNF表达式基础


昨天看到 王博炜 Blog中《五进制》这篇文章。其中关于5进制到10进制的转换自然没有什么意思,这篇文章给的代码主要是讨论如何进行表达式分析和计算的。作者自制了一个Stack,并且用其形成了两个堆栈分别用于存储数值和运算符。比较典型的表达式处理的方法。从实现上看,代码有些臃肿,而且必要的优化很少,另外就是没有充分利用标准提供的便利。比如那个Stack完全没有必要自制,STL提供的std::stack<T>可以很好的完成任务。

而今天我要做的是,使用boost::spirit来实现同样的表达式分析和计算。众所周知,boost是C++中质量很高的库,被称为准标准库,因为其存在的一个很重要的目的就是为下一代C++库提供预案。目前已经有大量的boost库成为了C++标准库的一部分。我现在要用的是Boost的Spirit库。这个库可以直接在C++代码中撰写EBNF。学过编译原理的朋友应该对此都很熟悉,这是一种比堆栈更灵活的解析表达式甚至程序的方式。

如果我们要处理四则运算的表达式,那么我们只需要在C++中写入下列EBNF的定义:

    group       = '(' >> expression >> ')';
    factor      = integer | group;
    term        = factor >> *(('*' >> factor) | ('/' >> factor));
    expression  = term >> *(('+' >> term) | ('-' >> term));

我们就构成了这个表达式的格式定义,它可以很轻松的处理下列表达式的运算:

    12345
    -12345
    +12345
    1 + 2
    1 * 2
    1/2 + 3/4
    1 + 2 + 3 + 4
    1 * 2 * 3 * 4
    (1 + 2) * (3 + 4)
    (-1 + 2) * (3 + -4)
    1 + ((6 * 200) - 20) / 6
    (1 + (2 + (3 + (4 + 5))))

很简单吧?

使用过yacc或者*lex的朋友对这类定义肯定很熟悉。但是所不同的是,他们都是让用户写一个模板,然后用yacc或者*lex处理模板生成相应语言的程序。程序臃肿且很难阅读。而且由于不是自己写的程序,调整起来总要经过一步手续,比较繁琐。

而使用C++的朋友则不用有这种烦恼,Boost的Spirit充分利用了C++强大的语法功能。我们可以直接在程序中写入上述的表达式定义,然后我们的程序就支持这些表达式的处理了。不需要任何额外的程序处理。所需要的仅仅是include一些头文件而已。是的,仅仅是include一些头文件。不要担心需要安装什么额外的东西,或者需要链接什么库,因为Spirit的实现完全是头文件组成的,我们不需要链接任何库。把boost的头文件路径放到编译期中,直接编译就ok了。很轻巧。

下面就是我用Boost Spirit实现的四则运算表达式的代码,由于我的重点是表达式的解析和计算,因此我没有特别处理五进制到十进制的转换问题。但是添加起来显然不麻烦。我只给出了一个五进制整数部分的输出。如果表达式出错,可以直接用箭头指出哪里有错。很方便调试:) 而且代码量是原文章的五分之一。编译后也仅仅是35KB,也不是很臃肿的。

大家多了解标准库,多了解Boost,C++的编码也是很有趣味的。

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>

#include <iostream>
#include < string>
#include <cmath>
#include <limits>

using  namespace boost::spirit;
using  namespace boost::spirit::qi;
using  namespace boost::spirit::ascii;
using  namespace boost::spirit::arg_names;

template <typename Iterator>
struct calculator : grammar<Iterator,  double(), space_type>
{
    calculator() : calculator::base_type(expression)
    {
        expression = term[_val = _1]
        >> *( ('+' >> term[_val += _1]) | ('-' >> term[_val -= _1]) );

        term = factor[_val = _1]
        >> *( ('*' >> factor[_val *= _1]) | ('/' >> factor[_val /= _1]) );

        factor = double_[_val = _1]    | '(' >> expression[_val = _1] >> ')'
            |   ('-' >> factor[_val = -_1]) | ('+' >> factor[_val = _1]);
    }

    rule<Iterator,  double(), space_type> expression, term, factor, number;
};

//      http://www.jb.man.ac.uk/~slowe/cpp/itoa.html
std:: string itoa( int value,  int  base) {
     const  int MAX_DIGITS = 35;
     const  char* DIGITS = "0123456789abcdefghijklmnopqrstuvwxyz";
    std:: string buf;
    buf.reserve( MAX_DIGITS );  //  Pre-allocate enough space.
     if ( base < 2 ||  base > 36)  return buf;
     int quotient = value;
     do {
        buf.push_back(DIGITS[ std::abs(quotient %  base) ]);
        quotient /=  base;
    }  while ( quotient );
     if ( value < 0) buf.push_back('-');
    std::reverse( buf.begin(), buf.end() );
     return buf;
}

int main( int argc,  char* argv[])
{
    std::cout << "请输入一个表达式,如:3+2.5*(6-25/4)-8.32" << std::endl << std::endl;
    std::cout << "或输入q退出。" << std::endl << std::endl;
    std::cout << "> ";
    calculator<std:: string::const_iterator> calc;

    std:: string str;
     double result;
     while (std::getline(std::cin, str))
    {
         if (str.empty() || str[0] == 'q' || str[0] == 'Q')
             break;

        std:: string::const_iterator iter = str.begin();
        std:: string::const_iterator end = str.end();
         bool r = phrase_parse(iter, end, calc, result, space);

         if (r && iter == end)
        {
            std::cout << "输入语法正确,表达式的值为:";

             if (result == std::numeric_limits< double>::infinity())
                std::cout << "∞";
             else  if (result == std::numeric_limits< double>::quiet_NaN())
                std::cout << "结果非数值";
             else
            {
                std::cout << result << std::endl;
                std::cout << "整数部分转换为5进制为:" << itoa(static_cast< int>(result), 5);
            }
            std::cout << std::endl;

        }
         else
        {
            std::cout << "[输入的表达式错误]" << std::endl;
            std::cout << str << std::endl;
            std::cout << std:: string(iter - str.begin(), '-') << "^" << std::endl;
        }
        std::cout << std::endl << "> ";
    }
     return 0;
}




运行结果如下:

请输入一个表达式,如:3+2.5*(6-25/4)-8.32

或输入q退出。

> 3+2.5*(6-25/4)-8.32
输入语法正确,表达式的值为:-5.945
整数部分转换为5进制为:-10

> -6
输入语法正确,表达式的值为:-6
整数部分转换为5进制为:-11

> 6
输入语法正确,表达式的值为:6
整数部分转换为5进制为:11

> 1/0
输入语法正确,表达式的值为:∞

> 23 + 4 ((5)-3* 6) + (-1)
[输入的表达式错误]
23 + 4 ((5)-3* 6) + (-1)
-------^

> 23 + 4 ( ( 5-3*6) +1)
[输入的表达式错误]
23 + 4 ( ( 5-3*6) +1)
-------^

> 23 + 4 + ( -5 *3)
输入语法正确,表达式的值为:12
整数部分转换为5进制为:22

>





  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值