CMPS 144 Fall 2019
Programming Assignment #3: Arithmetic Expression Evaluation
Due: 11:59pm, Wednesday, October 30

Specification

Your goal in this assignment is to complete the development of a collection of Java components (i.e., interfaces and classes) that provide a means for evaluating arithmetic expressions. The interfaces are provided in full, as is a Java application that can be used for testing purposes.

The following outline lists the names of all the components in a way that indicates their inheritance structure. Some of the names provide a link to a completed interface or class; one provides a link to an incomplete class; the rest simply tell you the name of a class that you must build from scratch. The names of Java interfaces are in italics.

For the purposes of this assignment, arithmetic expressions involve integer literals, binary infix arithmetic operators, and parentheses. Here are some examples:1

26 4 + 9 * ( 13 + 8 ) - 34
3 + 47 ( 5 * 3 ) - ( 17 / 2 ^ 4 ) * ( ( 2 + 3 ) / ( 7 - 9 ) )
( 2 - 6 ) * 11 ( ( 4 + 2 ) )

You certainly recognize the operator symbols for the four basic arithmetic operations: +, -, *, and /. The ^ operator is the power, or exponentiation, operator. For example, 3 ^ 5 means three raised to the fifth power.

Notice that these are not the fully-parenthesized arithmetic expressions (FPAEs) that were discussed earlier in the course, but rather a larger class of expressions that includes FPAEs as special cases.

Evaluating FPAEs is rather simple, due to their very restricted form. Evaluating our more general class of expressions is more difficult, because the order in which the operators in an expression are to be applied depends not only upon parenthesization and the (left-to-right) order in which the operators appear, but also upon the operators' precedence factors (which these days students remember by the acronym "PEMDAS").

We follow usual convention by assuming that the power/exponentiation operator (^) has highest precedence, followed by multiplication (*) and division (/), followed by addition (+) and subtraction (-). For purposes of making our evaluation algorithm a little easier to describe, we also consider the left parenthesis (() to be an operator and we assign to it the lowest possible precedence.

Another convention that we adopt is that operators of equal precedence associate to the left. Which means, for example, that the expression 5 - 2 + 3 is taken to be equivalent to ( 5 - 2 ) + 3 rather than to 5 - ( 2 + 3 ). This conforms to the usual convention, except in the case of exponentiation, which is usually considered to associate to the right.

As with FPAEs, our more general class of arithmetic expressions can be evaluated using a pair of stacks, one to hold operators and the other to hold operands. But the algorithm is, not surprisingly, more complicated than what is needed for FPAEs. A pseudocode version is found below. In order to simplify it a little, we assume that the given expression has been enclosed in an "extra" pair of mated parentheses. (Thus, for example, before evaluating ( 3 + 5 ) * 7, we would alter it to become ( ( 3 + 5 ) * 7 ).)


Algorithm

operatorStk = empty stack of operators
operandStk = empty stack of operands
while (there are more tokens to process) {
   token = next token
   if (token is an integer literal) {
      push token onto operandStk
   }
   else if (token is a left parenthesis) {
      push token onto operatorStk
   }
   else if (token is an operator) {
      while (token's precedence factor is <= to that of operator at top of operatorStk) {
         pop that operator;
         pop two values from operandStk
         apply the operator to those two values
         push the result onto operandStk
      }
      push token onto operatorStk
   }
   else if (token is a right parenthesis) {
      while (operator at top of operatorStk is not a left parenthesis) {
         pop that operator;
         pop two values from operandStk
         apply the operator to those two values
         push the result onto operandStk
      }
      pop the left parenthesis off operatorStk
   }
}
return (what should be) the lone item on operandStk

The Java translation of the pseudocode boolean expressions that are for the purpose of determining exactly what kind of token is stored in token would use Java's instanceof operator. For example, the condition

token is a left parenthesis

translates to

token instanceof LeftParen


Program Submission

You should submit five Java classes (source code, not bytecode) to the prog3_dir folder: ArithExprEvaluator, SubtractOp, MultiplyOp, DivideOp, and PowerOp.

Your code will be tested in an environment in which all the other Java components are exactly as were given to you.

Optionally (for a little extra credit), you can submit an improved version of the ArithExprScanner class that does not rely upon the tokens in an arithmetic expression being separated from each other by spaces. (Activity #2 in Lab #5 had you do something very similar.)


Footnotes

[1] Due to the lack of sophistication of the given ArithExprScanner class, the atomic components (i.e., tokens) in our expressions are separated by spaces.