也谈表达式分析和计算
昨天看到 王博煒 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 >
轉載于:https://www.cnblogs.com/dancefire/archive/2009/02/04/1985872.html
總結
以上是生活随笔為你收集整理的也谈表达式分析和计算的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何将mysql5的sql文件导入到my
- 下一篇: Vcastr 3.0 - flash v