数据结构课的作业。利用两个堆栈,一个存放输入的操作数和操作符,一个用来实现后缀自动机,包括了正负号的识别。基于”Data Strctures and Probem Solving Using C++“。
//
// Evaluator.h
// Calculator
//
// Created by Z on 12-11-26.
// Copyright (c) 2012年 Z. All rights reserved.
//
#ifndef __Calculator__Evaluator__
#define __Calculator__Evaluator__
#include <iostream>
#include <stdlib.h>
#include <math.h>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
using namespace std;
enum TokenType {EOL, VALUE, OPAREN, CPAREN, EXP, MULT, DIV, PLUS, MINUS, NEGATIVE, POSITIVE};
//PREC_TABLE matches order of TokenType enum
struct Precedence
{
int inputSymbol;
int topOfStack;
}PREC_TABLE[] =
{
{ 0, -1 }, { 0, 0 }, // EOL, VALUE
{ 100, 0 }, { 0, 99 }, // OPAREN, CPAREN
{ 6, 5 }, // EXP
{ 3, 4 }, { 3, 4 }, // MULT, DIV
{ 1, 2 }, { 1, 2 }, // PLUS, MINUS
{ 90, 90}, { 90, 90} //NEGATIVE, POSITIVE
};
/*Token Class: stores both a TokenType, and if the token is a VALUE, its numeric value
//
//Construction: with a TokenType and a NumericType
//
//Public Operations:
//TokenType getType() --> Return the type of the Tokenizer
//const NumericType & getValue() const --> Return the numeric value if the type is VALUE
*/
template <class NumericType>
class Token {
private:
TokenType theType;
NumericType theValue;
public:
Token(TokenType tt = EOL, const NumericType &nt = 0): theType(tt), theValue(nt){}
TokenType getType() const{
return theType;
}
const NumericType & getValue() const{
return theValue;
}
};
/*
*/
template <class NumericType>
class Tokenizer {
istream & is;
public:
Tokenizer(istream & input) : is(input){}
Token<NumericType> getToken(TokenType previousType);
};
template <class NumericType>
class Evaluator {
private:
vector<TokenType> opStack; //Operator stack for conversion. converts infix to postfix
vector<NumericType> postFixStack; //Postfix machine stack, stores operands
istringstream str; //The character stream
NumericType getTop(); //Get top of postfix stack
void binaryOp(TokenType topOp); //Process a binary operator
void unaryOp(TokenType topOp); //Process an unary operator
void processToken(const Token<NumericType> &lastToken);
public:
Evaluator(const string & s) : str(s){
opStack.push_back(EOL);
}
NumericType getValue(); //Do the evaluation
};
//Find the next token, skipping blanks, and return it.
//Print error message if input is unrecognized.
template <class NumericType>
Token<NumericType> Tokenizer<NumericType> :: getToken(TokenType previousType){
char ch1;
NumericType theValue;
//Skip blanks
while (is.get(ch1) && ch1 == ' ')
;
if (is.good() && ch1 != '\n' && ch1 != '\0') {
switch (ch1) {
case '^':
return EXP;//返回一个TokenType,通过只有一个参数的Token的构造函数进行隐式类型转换
case '/':
return DIV;
case '*':
return MULT;
case '(':
return OPAREN;
case ')':
return CPAREN;
case '+':
if (previousType == VALUE || previousType == CPAREN) {//只有当之前输入的是数字或者是右括号时认为+是加号,否则认为+是正号
return PLUS;
}else{
return POSITIVE;
}
case '-':
if (previousType == VALUE || previousType == CPAREN) {//只有当之前输入的是数字或者是右括号时认为-是减号,否则认为-是负号
return MINUS;
}else{
return NEGATIVE;
}
default:
is.putback(ch1);
if (!(is >> theValue)) {//if the input is not a number, show error info
cerr << "Parse error" <<endl;
return EOL;
}
return Token<NumericType>(VALUE,theValue);
//the Token contains type VALUE and its numeric value
}
}
return EOL;
}
template <class NumericType>
NumericType Evaluator<NumericType> :: getValue(){
Tokenizer<NumericType> tok(str);
Token<NumericType> lastToken;
TokenType previousType = EOL;
do {
lastToken = tok.getToken(previousType);
previousType = lastToken.getType();
processToken(lastToken);
} while (lastToken.getType() != EOL);
if (postFixStack.empty()) {
cerr << "Missing operand!" << endl;
return 0;
}
NumericType theResult = postFixStack.back();
postFixStack.pop_back();
if (!postFixStack.empty()) {
cerr << "Warning: missing operators!" << endl;
}
return theResult;
}
template <class NumericType>
NumericType Evaluator<NumericType>::getTop( )
{
if( postFixStack.empty( ) )
{
cerr << "Missing operand" << endl;
return 0;
}
NumericType tmp = postFixStack.back( );
postFixStack.pop_back( );
return tmp;
}
/*
* Handles the binary operator case
*/
template <class NumericType>
void Evaluator<NumericType> :: binaryOp(TokenType topOp){
if (topOp == OPAREN) {
cerr << "Unbalanced parentheses" << endl;
opStack.pop_back();
return;
}
NumericType rhs = getTop();
NumericType lhs = getTop();
if (topOp == EXP) {
postFixStack.push_back(pow(lhs, rhs));
}
if (topOp == PLUS) {
postFixStack.push_back(rhs + lhs);
}
if (topOp == MINUS) {
postFixStack.push_back(lhs - rhs);
}
if (topOp == MULT) {
postFixStack.push_back(lhs * rhs);
}
if (topOp == DIV) {
if (rhs != 0) {
postFixStack.push_back(lhs / rhs);
}else{
cerr << "Division by zero" << endl;
postFixStack.push_back(lhs);
}
}
opStack.pop_back();
}
/*
* Handles the unary operator case
*/
template <class NumericType>
void Evaluator<NumericType> :: unaryOp(TokenType topOp){
if (topOp == OPAREN) {
cerr << "Unbalanced parentheses" << endl;
opStack.pop_back();
return;
}
NumericType rhs = getTop();
if (topOp == POSITIVE) {
postFixStack.push_back(rhs);
}
if (topOp == NEGATIVE) {
postFixStack.push_back(-rhs);
}
opStack.pop_back();
}
// After token is read, use operator precedence parsing
// algorithm to process it; missing opening parentheses
// are detected here.
template <class NumericType>
void Evaluator<NumericType>::processToken( const Token<NumericType> & lastToken )
{
TokenType topOp;
TokenType lastType = lastToken.getType( );
switch( lastType )
{
case VALUE:
postFixStack.push_back( lastToken.getValue( ) );
return;
case CPAREN:
while( ( topOp = opStack.back( ) ) != OPAREN && topOp != EOL)
if (topOp != NEGATIVE && topOp != POSITIVE) {
binaryOp( topOp );
}else{
unaryOp(topOp);
}
if( topOp == OPAREN )
opStack.pop_back( ); // Get rid of opening parentheseis
else
cerr << "Missing open parenthesis" << endl;
break;
default: // General operator case
while( PREC_TABLE[ lastType ].inputSymbol <=
PREC_TABLE[ topOp = opStack.back( ) ].topOfStack ){
if (topOp == NEGATIVE || topOp == POSITIVE) {
unaryOp(topOp);
}else{
binaryOp( topOp );
}
}
if( lastType != EOL )
opStack.push_back( lastType );
break;
}
}
#endif /* defined(__Calculator__Evaluator__) */