这个实现只是一个思路,需要容错和优化。表达式语法分析如下:
*****************
* 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
*****************
* 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)