(此版本为过程式,对应于《The C++ Programming Language 3rd》第六章)
表达式语法分析如下(递归定义):
*****************
* program:
* END //END is end-of-input
* expr_list END
* expr_list:
* expression PRINT // PRINT is semicolon
* 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 )
* *****************
完整文件(不包括Makefile)
calc.h
/**
* calc.h
*****************
* program:
* END //END is end-of-input
* expr_list END
* expr_list:
* expression PRINT // PRINT is semicolon
* 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
#include
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
calc.cpp
// calc.cpp
#include "calc.h"
#include
#include
#include
#include
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
main.cpp
/**
* main.cpp
*
* EAMPLE
* ./calc 'rate=1.1934;150/rate;19.75/rate;217/rate'
*/
#include
#include
#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;
}