CS 107 - Machine Problem 5

Infix to Postfix Expression Conversion and Evaluation

Due: 4/28/2003 at 2:00 pm

For this assignment, your program is to read in various infix equations. For each infix equation, your program is to convert into the same equation in postfix form and then evaluate the postfix equation. An infix equation is the way equations are typically written, for example:

     5 + 12 * (10 - 4) - (8 / 4 / 2)
The equation is called "infix" since the operator is in between the operands it is to use. In an postfix equation, the operator comes after operands it is to use. You may have heard of postfix notation call "reverse Polish" notation. This name comes from the nationality of the philosopher who came up with prefix notation (postfix has been described as the reverse of prefix notation). The advantage of both prefix and postfix notation is that no parentheses are needed to properly express the equation as infix may need. Since there are no parentheses, the rules to evaluate the expression are simplier. The above infix equation in postfix would be:
     5 12 10 4 - * + 8 4 / 2 / -

The Stack Data Structure

To convert the equation from infix to postfix and to evaluate the postfix expression, we need to use a very simple data structure called a stack. Stacks can be implemented in a number of ways. You program will use one the simplier implementation using a dynamic array. We will first discuss how a stack works when implemented using a static array. Then we will discuss the changes needed to make the implementation as a static array. Your program will need to have the stack written as a class in order to receive full credit.

A stack has three primary operations:

These operation have elements accessed in a Last-In-First-Out order (often called LIFO). A stack works great when you need to access values in the reverse order in which they are encountered.

To implement the stack as an array, we need three pieces of data.

  1. The array
  2. The maximum size of the array
  3. The number of elements currently stored on the stack, often called the "stack pointer", which is initialized to zero.
To perform the push operation, first store a value onto the stack at the position indicated by the stack pointer. Then we increment the stack pointer. For example, if the stack/array was empty (the stack pointer equal to 0) and we pushed the values of 3, 15, 9 and 21 (in that order) the stack/array would be as follows with the stack pointer equaling 4.
     position:   0   1   2   3   4   5   6   7   8   9   ...
     value:      3  15   9  21

To perform the pop operation, first we decrement the stack point and then remove and return the value at the position indicated by the stack pointer. If we were to pop the above stack 3 times, the first value returned would be 21 (with the stack pointer equaling 3), the second value returned would be 9 (with the stack pointer equaling 2), and the third value returned would be 15 (with the stack pointer equaling 1).

To perform the top operation, you return the value at the position of the stack pointer minus one. Note that the value of the stack pointer does NOT change. If we were to perform a top operation after the above three pops were done, the top operation would return the value of 3 (3 is at position zero, which is at the stack pointer minus one). If we were to do another top operations immediately following this top operation, the value returned would still be 3.

A stack always has potential for an error. This is when the stack is empty and either a pop or top operation is performed. Thus we should added another operation to the stack, isEmpty. This operation will return true if the stack is empty and false otherwise. Your program should always check to see if the stack is empty before doing a pop or top operation.

A stack that is implemented with a static array has another potential error. This is when the stack is full and a push operation is performed. TO deal with this error, your program must use a dynamic array instead of a static array to implement the stack. When using a dynamic array, the push operation should check if the array if full before attempting to store the value in the array. If the array is not full, proceed as normal. If the array is full, allocate a new dynamic array of twice the size of the current array, copy all of the values from the current array to the new array, deallocate the current array and make the new array the one used for all of the stack operations. When your program creates the stack, the array should have 5 positions in it. Every time array is full when a push operation is performed, print a message stating "A new array is being allocated of size X for the stack." The size of the stack should change from 5 to 10 to 20 to 40 etc. whenever the array gets filled.

Performing Infix to Postfix Conversion

Our infix expression will contain numeric values and operators. The numeric values will be any positive or negative integer or floating point number. The operators will be: These operators have three levels of precedence. We will add two more operators to help with the processing of the program. The first operator will be a semicolon, ;, which will indicate the end of an expression. The second operator will be the pound symbol, #, (this is sometimes called the sharp symbol or number symbol) which will indicate the program should end. Your program should end immediately upon encountering the pound symbol even if the program is in the middle of processing an equation. Your program should skip over any "white space" characters in the input (space, tab or newline). The best way to check for white space characters is to use the library function of isspace(). Any other input encountered by your program should result in an error message as an "unknown operator". This error message should output the unknown operator as part of the error message. The unknown operator should be discarded and your program should continue processing the expression. The following is some sample data that can be found in the file mp5a.data.
     5 + 12 * (10 - 4) ;
     5 + 12 * (10 - 4) - (8 / 4 / 2) ;
     2.25 * 3.0 + 1.25;
     64/8/2;
     -12 - -7 ;
     -12--7;
     12 + 4 * 5 + 7 * 21 - 4 / 2 - 8 - 2 + 5 * 3
        + ( 2 * 2 * 2 * 2 * 2 ) * ( 3 + 3 + 3 + 3 ) ;

     5 3 7 + / - 4 ;
     5 + 3 / 7 - 4 ;

     5
     +
     7
     *
     2
     +
     8
     ;

     2+2+2+2+2+2+2+2+2+2+2+2+2+2+2+2+2+2+2+2+2+2+2+2+2+2+2+2 ;
     2+(2+(2+(2+(2+(2+(2+(2+(2+(2+(2+(2+(2+(2+(2+(2+(2+(2+(2+
       (2+(2+(2+(2+(2+(2+(2+(2+2)))))))))))))))))))))))))) ;
     2*(2+(2+(2*(2+(2+(2*(2+(2+(2*(2+(2+(2*(2+(2+(2*(2*(2*(2*
       (2+(2+(2*(2*(2*(2*(2*(2+2)))))))))))))))))))))))))) ;
     #

To convert an expression from infix to postfix, we must first determine if the next (valid) item in the equation is a number or an operator.

When reading in items for your program you will need to read everything in as characters and convert to numeric values yourself. You should refer the parseinput.cpp example from lecture to help determine how to read in the numeric values. One twist with this program is that the minus sign can be both an operator and the first character in a numeric value. When you encounter a minus sign, you will need to look at the next character in the input to determine whether the minus sign is part of a number or an operator. If the next character in the input is a digit or the floating point, then the minus sign is part of a numeric value. Otherwise the minus sign is an operator.

Let us step throught the algorithm to convert the input of

     5 + 12 * (10 - 4) ;
into a postfix expression.
   Step 1.
     Current item:     5 (numeric value)
     Stack:            empty
     Postfix Equation: 5
     Send numeric value directly to Postfix Equation.

   Step 2.
     Current item:     + (operator)
     Stack:            +
     Postfix Equation: 5
     Since stack is empty, nothing to pop.  Then push +.

   Step 3.
     Current item:     12 (numeric value) 
     Stack:            +
     Postfix Equation: 5 12
     Send numeric value directly to Postfix Equation.

   Step 4.
     Current item:     * (operator)
     Stack:            + *
     Postfix Equation: 5 12
     Since + has lower precedence than *, nothing to pop.  Then push *.

   Step 5.
     Current item:     ( (operator)
     Stack:            + * (
     Postfix Equation: 5 12
     Since * has lower precedence than (, nothing to pop.  Then push (.

   Step 6.
     Current item:     10 (numeric value)
     Stack:            + * (
     Postfix Equation: 5 12 10
     Send numeric value directly to Postfix Equation.

   Step 7.
     Current item:     - (operator)
     Stack:            + * ( -
     Postfix Equation: 5 12 10
     Since top of stack is (, nothing to pop.  Then push -.

   Step 8.
     Current item:     4 (numeric value) 
     Stack:            + * ( -
     Postfix Equation: 5 12 10 4
     Send numeric value directly to Postfix Equation.

   Step 9.
     Current item:     ) (operator) 
     Stack:            + * 
     Postfix Equation: 5 12 10 4 -
     Pop operators from the stack until ( is popped.

   Step 10.
     Current item:     ; (operator) 
     Stack:            empty 
     Postfix Equation: 5 12 10 4 - * + ;
     Pop operator from the stack until it is empty, then add ; to the
     postfix equation.
If your program encounters an error while translating an infix expression into a postfix expression, print out the appropriate error message and as much of the postfix expression as has been so far created. Then clear the stack and the postfix expression. Finally restart translating an infix expresion into a postfix expression at the next item in the input. Your program will only attempt to evaluted the postfix expression if the postfix expression was created without error.

Evaluation of a Postfix Equation

The first thing your program should do is to output the postfix equation. To evaluate a postfix expression, we will again need to use the stack. However, the stack will contain numeric values instead of operators. When an error is encountered, print an appropriate error message and clear the stack. After the postfix expression has been evaluated, start translating the next infix expression into postfix. Your program should continue this until the pound operator is encountered.

The evaluate the postfix equation of:

     5 12 10 4 - * + ;
We do the following:
   Step 1.
     Current item:  5 (numeric value)
     Stack:         5
     The numeric value is pushed onto the stack.

   Step 2.
     Current item:  12 (numeric value)
     Stack:         5 12
     The numeric value is pushed onto the stack.

   Step 3.
     Current item:  10 (numeric value)
     Stack:         5 12 10
     The numeric value is pushed onto the stack.

   Step 4.
     Current item:  4 (numeric value)
     Stack:         5 12 10 4
     The numeric value is pushed onto the stack.

   Step 5.
     Current item:  - (operator)
     Stack:         5 12 6
     The numeric values of 4 and 10 are popped from the stack and
     the result of 10 - 4 is pushed onto the stack.
     Note: the first operand in the subtraction is the second value
     popped while the second operand is the first value popped.  This
     is very important since x - y is not the same as y - x.

   Step 6.
     Current item:  * (operator)
     Stack:         5 72
     The numeric values of 6 and 12 are popped from the stack and
     the result of 12 * 6 is pushed onto the stack.

   Step 7.
     Current item:  + (operator)
     Stack:         77
     The numeric values of 72 and 5 are popped from the stack and
     the result of 5 + 72 is pushed onto the stack.

   Step 8.
     Current item:  ; (operator)
     Stack:         77


A Class for Your Stack Type

In this program all valid data will either be an operator value (a character) or a numeric value (a double). I have created a class that will store either an operator or a numeric value, but not both. The class will keep track of which type of data is stored in it. This may be useful to you, since you can then create a class to be a stack of this class to be used for both the operator stack for the translation and the numeric stack for the evaulation. You do not have to use this class and you may alter the class if needed to fit better into your program. A full program showing the class and its usage is in stackelem.cpp and can also be copied into icarus account using cp ~troy/stackelem.cpp ..

class StackElem
{
// This class will store either a character or double data type.
// The names reflect the intended usage of this class with a program
// for infix to postfix equation translation and postfix equation
// evaluation.

// The class has two data members:
//  the first is an instance of an enumerated type to tell whether
//     the instance of the class is an operator/character or a
//     number/double.  THis value could be undefined in case the
//     instance of the class is created by the default constructor.
//
//  the second is an instance of a union that will hold either the
//     character value or the double value

 public:
   // create the enumerated type to determine the value stored in 
   //  the instance
   enum SEType {UNDEFINED, OPERATOR, NUMBER};

 private:
   // create the union to hold either the character or the double value
   union SEUnion
      {
       char oper;
       double numb;
      };

   // data member definition
   SEType et;		// the instance of the enumerated type
   SEUnion eu;		// the instance of the union

 public:
   // default constructor - type is unknown
   StackElem ()
     {
      et = UNDEFINED;
      eu.oper = '\0';
      eu.numb = 0.0;
     }

   // constructor to set up the instance to hold an operator
   StackElem (char op)
     {
      et = OPERATOR;
      eu.oper = op;
     }

   // constructor to set up the instance to hold a numeric value
   StackElem (double num)
     {
      et = NUMBER;
      eu.numb = num;
     }

   // a method to store an operator in the instance
   void setOperator(char op)
     {
      et = OPERATOR;
      eu.oper = op;
     }

   // a method to store a numeric value in the instance
   void setNumber(double num)
     {
      et = NUMBER;
      eu.numb = num;
     } 

   // a method to return the type of value stored in the instance
   SEType getType()
     {
      return et;
     }

   // a method to access an operator value
   // This method will return true if the instance does indeed
   //   store an operator and false otherwise
   // The method uses a pass-by-reference parameter to send the 
   //   operator value back to the calling code.  If the instance
   //   does not contain an operator the null character is sent 
   //   back to the calling code.
   bool getOperator (char &op)
     {
      if (et == OPERATOR)
         {
          op = eu.oper;
          return true;
         }
      else
         {
          op = '\0';
          return false;
         }
     }

   // a method to access an operator value
   // This method will return the operator value if the
   //   instance does indeed store an operator value.
   //   Otherwise the null character is returned.
   char getOperator ()
     {
      if (et == OPERATOR)
          return eu.oper;
      else
          return '\0';
     }

   // a method to access a numeric value
   // This method will return true if the instance does indeed
   //   store a numeric and false otherwise
   // The method uses a pass-by-reference parameter to send the 
   //   numeric value back to the calling code.  If the instance
   //   does not contain an numeric the value of 0.0 is sent 
   //   back to the calling code.
   bool getNumber (double &num)
     {
      if (et == NUMBER)
         {
          num = eu.numb;
          return true;
         }
      else
         {
          num = 0.0;
          return false;
         }
     }

   // a method to access a numeric value
   // This method will return the numeric value if the
   //   instance does indeed store a numeric value.
   //   Otherwise the value of 0.0 is returned.
   double getNumber ()
     {
      if (et == NUMBER)
          return eu.numb;
      else
          return 0.0;
     }

   // This method is meant for debugging purposes.
   // It outputs the type and value of the instance to cout.
   void output ()
     {
      if (et == UNDEFINED)
         cout << "UNDEFINED.";
      else if (et == OPERATOR)
         cout << "OPERATOR: " << eu.oper;
      else if (et == NUMBER)
         cout << "NUMBER: " << eu.numb;
     }
};

General Requirements

Your program is to be written using functions and methods. You are to decide what the functions and methods should be.

Your program must be written using good programming style. This includes (but is not limited to) such items as:

Your program will be submitted electronically on the icarus system using the turnin command. The project name for this will be mp5. Your program will graded based on how it executes on the icarus system using the g++ compiler. Getting a program to work on another system or using another compiler does NOT mean it will work on the icarus system using the g++ compiler. Programs that do not work properly on the icarus system using the g++ compiler will lose points. Also programs that are not submitted via the turnin command will also lose points. Emailing a program is not an acceptable form of electronic submission.