通过语法分析进行表达式求值
记得大二刚学C++时,老师给的表达式求值作业,当时括号都没有要求,后来学数据结构,表达式求值用的是算符优先法,后来学了编译原理,真是深深地佩服计算机科学的前辈们,我觉得编译原理中的自动机和自下而上的语法分析方法非常厉害,刚开始学C,完全就是靠自己的那点机灵去琢磨程序,后来学了数据结构,可以说对思想是一个洗礼,不管解决什么问题,首先想到的都是数据结构,然后再是算法,而不像以前那样毫无章法。现在学了编译原理,可以说对自己又是一个很大的提高。下面是仿照《The C++ Programming Language》中的Desk Caculator利用语法分析写的一个简单的表达式求值的类:
Program:
Expression End
Expression:
Expression + term | Expression - term | term
term:
term / operand | term *operand|operand
operand:
NUMBER|-operand|+operand|(Expression)
代码:
#ifndef EXPREVAL_H
#define EXPREVAL_H
#include <string>
#include <iostream>
#include <sstream>
#include <cctype>
using namespace std;
//TokenValue enumeration declaration
enum TokenValue
{
START, END, NUMBER, NAME,
ASSIGN = '=', LP = '(', RP = ')',
PLUS = '+', MINUS = '-', MUL = '*', DIV = '/'
};
class ExprEvalException
{
public:
ExprEvalException() { strMsg.assign("unknown error"); }
ExprEvalException(const string& msg) { strMsg.assign(msg); }
string getMessage() const { return strMsg; }
private:
string strMsg;
};
//class ExprEval declaration
class ExprEval
{
public:
ExprEval(const char*); //constructor initialized with char*
ExprEval(const string&); //constructor initialized with string
virtual ~ExprEval();
double getValue() const; //
private:
double evalulation(); //plus and minus
double term(); //multiply and divide
double getOperand(); //get operand
TokenValue getToken(); //get next token
double getNumber(); //get number
private:
string strExpr;
istringstream* pInput;
double numberValue;
double result;
TokenValue currToken;
};
#endif
//ExprEval.cpp
#include "ExprEval.h"
//class ExprEval implementation
ExprEval::ExprEval(const char* expr)
{
if(NULL == expr)
{
throw ExprEvalException("Null pointer in ExprEval::ExprEval");
}
strExpr.assign(expr);
numberValue = 0;
currToken = START;
pInput = new istringstream(strExpr);
result = evalulation();
}
ExprEval::ExprEval(const string& expr)
{
strExpr.assign(expr);
numberValue = 0;
currToken = START;
pInput = new istringstream(strExpr);
result = evalulation();
}
ExprEval::~ExprEval()
{
delete pInput;
}
double ExprEval::getValue() const
{
return result;
}
double ExprEval::evalulation()
{
double left = term();
while(1) //loop: term() + term() + term() - term() ...;
{
switch(currToken)
{
case PLUS:
left += term();
break;
case MINUS:
left -= term();
break;
default:
return left;
}
}
}
double ExprEval::term()
{
double left = getOperand();
double right;
while(1) //loop: getOperand() * getOperand() / getOperand()
{
switch(currToken)
{
case MUL:
left *= getOperand();
break;
case DIV:
right = getOperand();
if(right == 0)
{
throw ExprEvalException("Divided by 0");
}
left /= right;
break;
default:
return left;
}
}
}
//get next operand, return it and get the next TokenValue;
double ExprEval::getOperand()
{
double v;
currToken = getToken();
switch(currToken)
{
case NUMBER:
currToken = getToken();
return numberValue;
case PLUS:
return getOperand();
case MINUS:
return -getOperand();
case LP:
//recursion invoking to evalulate the expression between LP and RP
v = evalulation();
//ensure RP
if(currToken != RP)
{
throw ExprEvalException("Lack of RP");
}
getToken();
return v;
default:
throw ExprEvalException("Operand needed");
}
}
TokenValue ExprEval::getToken()
{
char c = 0;
(*pInput) >> c;
switch(c)
{
case 0:
return currToken = END;
case '*':
case '/':
case '-':
case '+':
case '(':
case ')':
case '=':
return currToken = TokenValue(c);
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
(*pInput).putback(c);
(*pInput) >> numberValue;
return currToken = NUMBER;
default:
throw ExprEvalException("illegal character");
}
}
#ifndef EXPREVAL_H
#define EXPREVAL_H
#include <string>
#include <iostream>
#include <sstream>
#include <cctype>
using namespace std;
//TokenValue enumeration declaration
enum TokenValue
{
START, END, NUMBER, NAME,
ASSIGN = '=', LP = '(', RP = ')',
PLUS = '+', MINUS = '-', MUL = '*', DIV = '/'
};
class ExprEvalException
{
public:
ExprEvalException() { strMsg.assign("unknown error"); }
ExprEvalException(const string& msg) { strMsg.assign(msg); }
string getMessage() const { return strMsg; }
private:
string strMsg;
};
//class ExprEval declaration
class ExprEval
{
public:
ExprEval(const char*); //constructor initialized with char*
ExprEval(const string&); //constructor initialized with string
virtual ~ExprEval();
double getValue() const; //
private:
double evalulation(); //plus and minus
double term(); //multiply and divide
double getOperand(); //get operand
TokenValue getToken(); //get next token
double getNumber(); //get number
private:
string strExpr;
istringstream* pInput;
double numberValue;
double result;
TokenValue currToken;
};
#endif
//ExprEval.cpp
#include "ExprEval.h"
//class ExprEval implementation
ExprEval::ExprEval(const char* expr)
{
if(NULL == expr)
{
throw ExprEvalException("Null pointer in ExprEval::ExprEval");
}
strExpr.assign(expr);
numberValue = 0;
currToken = START;
pInput = new istringstream(strExpr);
result = evalulation();
}
ExprEval::ExprEval(const string& expr)
{
strExpr.assign(expr);
numberValue = 0;
currToken = START;
pInput = new istringstream(strExpr);
result = evalulation();
}
ExprEval::~ExprEval()
{
delete pInput;
}
double ExprEval::getValue() const
{
return result;
}
double ExprEval::evalulation()
{
double left = term();
while(1) //loop: term() + term() + term() - term() ...;
{
switch(currToken)
{
case PLUS:
left += term();
break;
case MINUS:
left -= term();
break;
default:
return left;
}
}
}
double ExprEval::term()
{
double left = getOperand();
double right;
while(1) //loop: getOperand() * getOperand() / getOperand()
{
switch(currToken)
{
case MUL:
left *= getOperand();
break;
case DIV:
right = getOperand();
if(right == 0)
{
throw ExprEvalException("Divided by 0");
}
left /= right;
break;
default:
return left;
}
}
}
//get next operand, return it and get the next TokenValue;
double ExprEval::getOperand()
{
double v;
currToken = getToken();
switch(currToken)
{
case NUMBER:
currToken = getToken();
return numberValue;
case PLUS:
return getOperand();
case MINUS:
return -getOperand();
case LP:
//recursion invoking to evalulate the expression between LP and RP
v = evalulation();
//ensure RP
if(currToken != RP)
{
throw ExprEvalException("Lack of RP");
}
getToken();
return v;
default:
throw ExprEvalException("Operand needed");
}
}
TokenValue ExprEval::getToken()
{
char c = 0;
(*pInput) >> c;
switch(c)
{
case 0:
return currToken = END;
case '*':
case '/':
case '-':
case '+':
case '(':
case ')':
case '=':
return currToken = TokenValue(c);
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
(*pInput).putback(c);
(*pInput) >> numberValue;
return currToken = NUMBER;
default:
throw ExprEvalException("illegal character");
}
}