Expression Calculator
/* * Expression evaluator program. This program reads expressions of integers, * the operators +, -, *, /, % and parens, and evaluates them obeying the usual * operator precedence and paren grouping. */ #include <cctype> #include <iostream> #include <string> #include <list> #include <exception> using namespace std; /* * Exceptions for expression errors. */ class Bad_Expr: public exception { public: Bad_Expr(string msg) throw(): m_msg(msg) { } const char* what() const throw() { return m_msg.c_str(); } ~Bad_Expr() throw() { } private: string m_msg; }; /* * This is a "forward declaration." After this, the name is defined, but * you can't do much with it other than make pointers to it. */ class Expr_Stack; /* * Base class for items on the expression stack. */ class Expr_Stack_Elt { public: // What kind of thing this is. virtual bool is_operator() { return false; } virtual bool is_operand() { return false; } virtual bool is_left() { return false; } virtual bool is_right() { return false; } // Action to take when each type of item is read from input. // This operation must either place this on the stack, or // delete this. virtual void action(Expr_Stack &) = 0; }; /* * This is the expression evaluation stack, which is derived from the * library list class, refined to be a stack of Expr_Stack_Elt pointers. */ class Expr_Stack: public list<Expr_Stack_Elt *> { public: // Stack operations simply implemented in terms of the stl ops. void push(Expr_Stack_Elt *e) { push_front(e); } Expr_Stack_Elt *top() { return front(); } Expr_Stack_Elt *pop() { Expr_Stack_Elt *ret = front(); pop_front(); return ret; } // Empty the stack, delete contents. void wipe() { while(!empty()) delete pop(); } // Apply the Pop operand, operator, operand off the stack and apply // them, until reaching a left paren or higher precidence operator. // Throws if something goes wrong. void reduce(int prec_limit = -1) { while(merge_top(prec_limit)); } private: // Conditionally apply the top operand to its surrounding operators // at the top of the stack. Return success if this worked. Will not // merge past the end of the stack, a left paren, or if the // operator is of lower precedence than the indicated level. // Returns success. Throws if the stack is not well-formed. bool merge_top(int prec_limit = -1); }; /* * Operands -- integers on the stack. */ class Operand: public Expr_Stack_Elt { public: // Construct and extract. Operand(int i) { m_value = i; } int val() { return m_value; } // Action for reading an integer. virtual void action(Expr_Stack &s) { s.push(this); } // Print it. virtual void print(ostream &s) const { s << m_value; } // Identify its type. virtual bool is_operand() { return true; } private: // Value of the operand. int m_value; }; /* * This is +, -, * or /. */ class Operator: public Expr_Stack_Elt { public: // Construct and abstract. Operator(int p) { m_precedence = p; } int prec() { return m_precedence; } // Identify. virtual bool is_operator() { return true; } // Combine two operands according to whatever I am. This // just does whatever arithmetic is appropriate. virtual Operand * operate(Operand *a, Operand *b) = 0; // What to do with a new one. virtual void action(Expr_Stack &s) { // Perform pending operations of equal or greater precedence, // then push ourself onto the stack. s.reduce(m_precedence); s.push(this); } // Print the operator. virtual void print(ostream&s) const { s << "op[" << m_precedence << "]"; } private: // Precedence of the operator -- how strongly it sticks to the // surrounding operands. int m_precedence; }; /* * Each of the operators, with the appropriate operate method. */ class Oper_Plus: public Operator { public: Oper_Plus(): Operator(1) { } virtual Operand * operate(Operand *a, Operand *b) { return new Operand(a->val() + b->val()); } }; class Oper_Minus: public Operator { public: Oper_Minus(): Operator(1) { } virtual Operand * operate(Operand *a, Operand *b) { return new Operand(a->val() - b->val()); } }; class Oper_Times: public Operator { public: Oper_Times(): Operator(2) { } virtual Operand * operate(Operand *a, Operand *b) { return new Operand(a->val() * b->val()); } }; class Oper_Div: public Operator { public: Oper_Div(): Operator(2) { } virtual Operand * operate(Operand *a, Operand *b) { if(b->val() == 0) throw Bad_Expr("Division by zero."); return new Operand(a->val() / b->val()); } }; class Oper_Mod: public Operator { public: Oper_Mod(): Operator(2) { } virtual Operand * operate(Operand *a, Operand *b) { if(b->val() == 0) throw Bad_Expr("Division by zero."); return new Operand(a->val() % b->val()); } }; /* * Left paren. */ class Left: public Expr_Stack_Elt { public: virtual bool is_left() { return true; } virtual void print(ostream&s) const { s << '('; } virtual void action(Expr_Stack &s) { s.push(this); } }; /* Right paren. */ class Right: public Expr_Stack_Elt { public: virtual bool is_right() { return true; } virtual void print(ostream&s) const { s << ')'; } virtual void action(Expr_Stack &s) { // Perform all operations to the matching left paren. s.reduce(); // Remove the left paren. Expr_Stack_Elt *v = s.pop(); if(s.empty() || !s.top()->is_left()) throw Bad_Expr("Unbalanced right paren."); delete s.pop(); s.push(v); delete this; } }; /* * Class to read the input as a stream of Expr_Stack_Elt. */ class Reader { public: // Construct one. If an ostream is given, input is prompted // for on it. Reader(istream &i, ostream *o = NULL): in(i), out(o) { } // Read the next element. Expr_Stack_Elt * read(); // Clear the line. void clear() { if(atstart) return; char ch; while(ch = cin.get()) if(ch == '\n') break; atstart = true; }; // Check for eof. bool eof() { return in.eof(); } private: istream &in; // Reading stream. ostream *out; // Prompting stream (if any). bool atstart; // At the start of a line. // Read and return an integer from the stream. int readint() { int n; cin >> n; return n; } }; /* * Read in an item. This performs input, and creates and returns an * appropriate object. It returns null to indicate the end of the * the line. If a second pointer (to an ostream) is sent, it is used * to issue prompts at the start of each line. */ Expr_Stack_Elt * Reader::read() { // If we're at the start of the line, prompt. if(atstart) { if(out) *out << "ecalc> "; atstart = false; } // Clear out leading space, but don't read the non-space which // terminates it, and don't read past the end of the line. while(in.peek() == ' ' || in.peek() == '\t') in.get(); if(in.eof()) return NULL; // See if we have an operand. char tocome = in.peek(); if(isdigit(tocome)) return new Operand(readint()); // Okay. Read the character and act on it. These are all operators, // except \n, which sets the start of line flag and returns NULL. tocome = in.get(); while(tocome) { switch(tocome) { case '+': return new Oper_Plus; case '-': // This is tricky. Could be a negative number, but // we only count it that way if the very next char is // a digit. if(isdigit(in.peek())) return new Operand(-readint()); else return new Oper_Minus; case '*': return new Oper_Times; case '/': return new Oper_Div; case '%': return new Oper_Mod; case ')': return new Right; case '(': return new Left; case '\n': atstart = true; return NULL; default: // Bad input. throw Bad_Expr("Bad input character '" + string(1,tocome) + "'."); } } return NULL; } /* * Private helper function for Expr_Stack. This takes the operand, operator, * operand on the stack top, applies the operator, and replaces the three * with the result. The method throws and excpetion if the top part of the * stack does not have one of the following forms: * o operand as the only member of the stack. * o ( operand * o operand operator operand * The merge is canceled and false is returned, if: * o The stack has the first or second form. * o The prec_limit is given (not -1) and the operator has a lower * precedence. * The parameter, prec_limit means "precedence limit," and is used to end a * series of operations when a lower precedence operator is reached. For * instance, if a series of adjacent multiplies are being reduced, the * prec_limit will be the precedence for multiply. If the next operator is * add, the merge will be cancelled. */ bool Expr_Stack::merge_top(int prec_limit) { // Get the first operand. if(empty() || !top()->is_operand()) throw Bad_Expr("Missing operand."); Operand *rt_opnd = (Operand *)pop(); // Stack well-formed, but combine is canceled. if(empty() || top()->is_left() || (prec_limit != -1 && top()->is_operator() && ((Operator *)top())->prec() < prec_limit)) { // Found a lower precedence. Return what we poped // and indicate that we could not merge. push(rt_opnd); return false; } if(!top()->is_operator()) throw Bad_Expr("Successive operands."); // Perform the combination. Operator * optor = (Operator *)pop(); if(!top()->is_operand()) throw Bad_Expr("Operator lacks left operand."); Operand *lf_opnd = (Operand *)pop(); push(optor->operate(lf_opnd, rt_opnd)); delete lf_opnd; delete optor; delete rt_opnd; return true; } main() { Expr_Stack s; // Evaluation stack. Reader in(cin, &cout); // Prompting reader. while(true) { Expr_Stack_Elt *e = NULL; try { // Read, finish of finished. e = in.read(); if(in.eof()) break; if(e != NULL) // Perform the action on what we just read. e->action(s); else { // Line done. Complete the expression. // Apply any remaining operations. s.reduce(); // Print the result. Stack should contain // exactly one item, an operand. if(s.empty() || !s.top()->is_operand()) throw Bad_Expr("Incomplete expression."); Operand *res = (Operand *)s.pop(); if(!s.empty()) { delete res; throw Bad_Expr("Incomplete expression."); } cout << res->val() << endl; delete res; } } catch(const Bad_Expr& e) { // Print the message, clean up, and let the loop // start again. cout << e.what() << endl; s.wipe(); in.clear(); } } }