这个实现只是一个思路,需要容错和优化。表达式语法分析如下:
*****************
* program:
* END
* expr_list END
* expr_list:
* expression PRINT // PRINT is ;
* expression PRINT expr_list
* expression:
* expression + term
* expression - term
* term
* term:
* term / primary
* term * primary
* primary
* primary:
* NUMBER
* NAME // this is variable
* NAME = expression
* - primary
* ( expression )
*****************
文件列表:
- calc.cc
- calc.h
- main.cc
- makefile
用法:
xiwang@ubuntu:~/Dev/calc$ make g++ -Wall -I. -D_DEBUG -c calc.cc -o calc.cc.o g++ -Wall -I. -D_DEBUG -c main.cc -o main.cc.o g++ -D_DEBUG -L. calc.cc.o main.cc.o -o calc -lpthread xiwang@ubuntu:~/Dev/calc$ ./calc r = 2.5; 2.5 //<输出> area = pi * r * r; 19.635 //<输出> ^C
所有代码》》》》
文件:calc.cc
// calc.cc #include "calc.h" #include <ctype.h> #include <iostream> #include <map> #include <string> using namespace std; // error function int no_of_errors; double error(const string& s) { no_of_errors++; cerr << "error: " << s << '\n'; return 1; } // END error function Token_value curr_tok = PRINT; double expr(bool get) { double left = term(get); for (;;) { switch (curr_tok) { case PLUS: left += term(true); break; case MINUS: left -= term(true); break; default: return left; } } } double term(bool get) { double left = prim(get); for (;;) { switch (curr_tok) { case MUL: left *= prim(true); break; case DIV: if (double d = prim(true)) { left /= d; break; } return error("divide by 0"); default: return left; } } } double number_value; string string_value; map<string, double> table; // var table double prim(bool get) { if (get) get_token(); switch (curr_tok) { case NUMBER: { double v = number_value; get_token(); return v; } case NAME: { double& v = table[string_value]; if (get_token() == ASSIGN) v = expr(true); return v; } case MINUS: return -prim(true); case LP: { double e = expr(true); if (curr_tok != RP) return error(") expected"); get_token(); return e; } default: return error("primary expected"); } } // --BEGIN-- OPTIMIZE get_token function // Token_value get_token() // { // char ch = 0; // cin >> ch; // // switch (ch) { // case 0: // return curr_tok = END; // assign and return // case ';': // case '*': // case '/': // case '+': // case '-': // case '(': // case ')': // case '=': // return curr_tok = Token_value(ch); // case '0': case '1': case '2': case '3': case '4': // case '5': case '6': case '7': case '8': case '9': // case '.': // cin.putback(ch); // cin >> number_value; // return curr_tok = NUMBER; // default: // if (isalpha(ch)) { // cin.putback(ch); // cin >> string_value; // return curr_tok = NAME; // } // error("bad token"); // return curr_tok = PRINT; // } // } Token_value get_token() { char ch = 0; // --BEGIN-- ignore blanks except '\n' // do { // if (!cin.get(ch)) return curr_tok = END; // } while (ch != '\n' && isspace(ch)); cin >> ch; // ---END--- ignore blanks except '\n' switch (ch) { case 0: return curr_tok = END; // assign and return case ';': case '*': case '/': case '+': case '-': case '(': case ')': case '=': return curr_tok = Token_value(ch); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': cin.putback(ch); cin >> number_value; return curr_tok = NUMBER; default: if (isalpha(ch)) { // --BEGIN-- Optimize to avoid meeting blank issue // cin.putback(ch); // cin >> string_value; string_value = ch; while (cin.get(ch) && isalnum(ch)) string_value.push_back(ch); cin.putback(ch); // ---END_-- Optimize to avoid meeting blank issue return curr_tok = NAME; } error("bad token"); return curr_tok = PRINT; } } // ---END--- OPTIMIZE get_token function
文件:calc.h
/** * calc.h ***************** * program: * END * expr_list END * expr_list: * expression PRINT // PRINT is ; * expression PRINT expr_list * expression: * expression + term * expression - term * term * term: * term / primary * term * primary * primary * primary: * NUMBER * NAME // this is variable * NAME = expression * - primary * ( expression ) */ #ifndef CALC_H_ #define CALC_H_ #include <map> #include <string> using namespace std; enum Token_value { NAME, NUMBER, END, PLUS='+', MINUS='-', MUL='*', DIV='/', PRINT=';', ASSIGN='=', LP='(', RP=')' }; // for DRIVER PROGRAM extern Token_value curr_tok; extern map<string, double> table; // var table extern int no_of_errors; extern double expr(bool get); extern Token_value get_token(); extern double error(const string& s); // END for DRIVER PROGRAM extern double term(bool get); extern double prim(bool get); #endif
文件:main.cc (驱动程序)
/** * main.cc * * EAMPLE * ./calc 'rate=1.1934;150/rate;19.75/rate;217/rate' */ #include <iostream> #include <sstream> #include "calc.h" using namespace std; // --BEGIN-- COMMENT SIMPLE DRIVER // // DRIVER PROGRAM // int main(int argc, char **argv) { // table["pi"] = 3.1415926535897932385; // PRE-DEFINED NAMES // table["e"] = 2.7182818284590452354; // // while (cin) { // get_token(); // if (curr_tok == END) break; // if (curr_tok == PRINT) continue; // cout << expr(false) << endl; // } // // return no_of_errors; // } // ---END--- COMMENT SIMPLE DRIVER istream* input; int main(int argc, char * argv[]) { switch (argc) { case 1: input = &cin; break; case 2: input = new istringstream(argv[1]); break; default: error("too many arguments"); return 1; } table["pi"] = 3.1415926535897932385; // PRE-DEFINED NAMES table["e"] = 2.7182818284590452354; while (*input) { get_token(); if (curr_tok == END) break; if (curr_tok == PRINT) continue; cout << expr(false) << '\n'; } if (input != &cin) delete input; return no_of_errors; }
文件:makefile
# Makefile, 2012-06-23 T1110 # TODO # ---------------------------------- NAME = calc VERSION = 1.0.0 RELEASE = 01 # ---------------------------------- BIN = $(NAME) OBJS = $(patsubst %.cpp,%.cpp.o,$(wildcard $(SRC_DIR)/*.cpp)) OBJS += $(patsubst %.cc,%.cc.o,$(wildcard $(SRC_DIR)/*.cc)) OBJS += $(patsubst %.C,%.C.o,$(wildcard $(SRC_DIR)/*.C)) # TODO # BEGIN ---------------------------------- # Folders SRC_DIR = . # Flags CXXFLAGS = -Wall CXXFLAGS += -I$(SRC_DIR) CPPFLAGS = -D_DEBUG LDFLAGS = -L$(SRC_DIR) LIBS = -lpthread # END ---------------------------------- .PHONY: all clean all: $(BIN) $(BIN): $(OBJS) $(CXX) $(CPPFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) # source files %.cpp.o: %.cpp $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $^ -o $@ %.cc.o: %.cc $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $^ -o $@ %.C.o: %.C $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $^ -o $@ clean: $(RM) $(BIN) $(OBJS)