------------------------------------------------------------------------------
MC logo
Expression Calculator
[^] CSc 220 Home
------------------------------------------------------------------------------
[Introduction] [Boolean and Control] [Functions] [Arrays and Pointers] [Dynamic Arrays] [Array Errors] [Command Array] [Standard Containers] [Structs and Classes] [Automatic Pointer] [Multi-File Programs] [Copying Objects] [Templates] [Inheritance] [Plain C I/O] [Type Odds And Ends] [Plain C Strings] [File I/O]
[Bounded Safe Array] [Bounded Safe Array Driver] [PrintMe Class] [PrintMe2 Class] [PrintMeLots Class] [PrintMeLots2 Class] [PrintMe Test Driver] [Linked List Node Classes] [Linked List Classes] [Linked List Implementation] [Linked List Driver] [Expression Calculator]
ecalc.cc
/*
 * 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();
                }
        }
}