------------------------------------------------------------------------------
MC logo
Expression Calculator
[^] Code Examples
------------------------------------------------------------------------------
<<Multiple Inheritance ecalc.cc Template Functions>>
/*
 * Expression evaluator program.
 */

#include <ctype.h>
#include <iostream>
#include <sstream>
#include <string>
#include "genstack.h"

using namespace std;

/* This is a forward declaration noting that Expr_Stack is a class, but
   its definition will come later. */
class Expr_Stack;

/* Base class for items on the expression stack. */
class Expr_Stack_Elt: public 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;
};

/* A stack extended with a few conveniences. */
class Expr_Stack: public Stack
{
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);

public:
        // Save a bunch of casting. 
        Expr_Stack_Elt *pop() { return (Expr_Stack_Elt*)Stack::pop(); }
        Expr_Stack_Elt *top() { return (Expr_Stack_Elt*)Stack::top(); }

        // 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)); }
};

/* Operands -- integers on the stack. */
class Operand: public Expr_Stack_Elt
{
private:
        // Value of the operand.
        int value;
public:
        // Construct and extract.
        Operand(int i) { value = i; }
        int val() { return value; }

        // Action for reading an integer.
        virtual void action(Expr_Stack &s);

        // Print it.
        virtual void print(ostream&s) const;

        // Identify its type.
        virtual bool is_operand();
};
bool Operand::is_operand() { return true; }
void Operand::action(Expr_Stack &s) { s.push(this); }
void Operand::print(ostream&s) const { s << value; }
/* These don't work inside the function because they are virtual.
   That stinks. */

/*  This is +, -, * or /. */
class Operator: public Expr_Stack_Elt
{
private:
        // Precedence of the operator -- how strongly it sticks to the
        // surrounding operands.
        int precedence;
public:
        // Construct and abstract.
        Operator(int p) { precedence = p; }
        int prec() { return precedence; }

        // Identify.
        virtual bool is_operator();

        // 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);

        // Print the operator.
        virtual void print(ostream&s) const;
};
bool Operator::is_operator() { return true; }
void Operator::action(Expr_Stack &s)
{
        // Perform pending operations of equal or greater precedence,
        // then push ourself onto the stack.
        s.reduce(precedence);
        s.push(this);
}
void Operator::print(ostream&s) const { 
        s << "op[" << 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);
};
Operand * Oper_Plus::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);
};
Operand * Oper_Minus::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);
};
Operand * Oper_Times::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);
};
Operand * Oper_Div::operate(Operand *a, Operand *b) {
        if(b->val() == 0) throw "Division by zero.";
        return new Operand(a->val() / b->val());
}

// Left paren.
class Left: public Expr_Stack_Elt
{
public:
        virtual bool is_left();
        virtual void print(ostream&s) const;
        virtual void action(Expr_Stack &s);
};
bool Left::is_left() { return true; }
void Left::action(Expr_Stack &s) { s.push(this); }
void Left::print(ostream&s) const { s << '('; }

// Right paren.
class Right: public Expr_Stack_Elt
{
public:
        virtual bool is_right();
        virtual void print(ostream&s) const;
        virtual void action(Expr_Stack &s);
};
bool Right::is_right() { return true; }
void Right::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 "Unbalanced right paren.";
        delete s.pop();
        s.push(v);
        delete this;
}
void Right::print(ostream&s) const { s << ')'; }

// Class to read the input as a stream of items.
class Reader {
private:
        istream &in;            // Reading stream.
        ostream *out;           // Prompting stream (if any).
        bool atstart;           // At the start of a line.
public:
        // Construct one.  If an ostream is given, input is prompted
        // for on it.
        Reader(istream &i, ostream *o = 0): in(i), out(o) { }

        // Read the next element.
        Expr_Stack_Elt * read();

        // Clear the line.
        void clear();

        // Check for eof.
        bool eof() { return in.eof(); }
};

// Read in an item.  This performs input, and creates an 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.
        while(in.peek() == ' ' || in.peek() == '\t')
                in.get();
        if(in.eof()) return 0;

        // See if we have an operand.
        char tocome = in.peek();
        if(isdigit(tocome)) {
                // Ah!  An operator.
                int n;
                cin >> n;
                return new Operand(n);
        }

        // Okay.  Readh the character and act on it.
        tocome = in.get();
        while(tocome) {
                switch(tocome)
                {
                case '+': return new Oper_Plus;
                case '-': return new Oper_Minus;
                case '*': return new Oper_Times;
                case '/': return new Oper_Div;
                case ')': return new Right;
                case '(': return new Left;
                case '\n': 
                        atstart = true;
                        return 0;
                default:
                        // Bad input.  
                        throw "Bad input character.";
                }
        }
        return 0;
}

/*
 * Clear to the end of the line
 */
void Reader::clear()
{
        if(atstart) return;
        char ch;
        while(ch = cin.get()) if(ch == '\n') break;     
        atstart = true;
};

// Private helper function for merge.  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 combine 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.
bool Expr_Stack::merge_top(int prec_limit)
{
        // Get the first operand.
        if(empty() || !top()->is_operand()) throw "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 "Successive operands.";

        // Perform the combination.
        Operator * optor = (Operator *)pop();
        if(!top()->is_operand()) throw "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 = 0;
                try {
                        // Read, finish of finished.
                        e = in.read();
                        if(in.eof()) break;

                        if(e != 0)
                                // 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 "Incomplete expression.";
                                Operand *res = (Operand *)s.pop();
                                if(!s.empty()) {
                                        delete res;
                                        throw "Incomplete expression.";
                                }
                                cout << res->val() << endl;
                                delete res;
                        }
                }
                catch(const char *msg) {
                        // Print the message, clean up, and let the loop
                        // start again.
                        cout << msg << endl;
                        s.wipe();
                        in.clear();
                }
        }
}

This program uses the generalized stack class we defined earlier. Its hierarchy:

<<Multiple Inheritance Template Functions>>