CMPS 144 Spring 2022
Prog. Assg. #3: Building Expression-Trees from Expression-Strings
Due: 11:59pm, Friday, March 11

Background

We are accustomed to reading and writing arithmetic expressions that are in infix form, such as 13 * (4 + 7) - 2. In such expressions, each binary operator appears between its two operands. In this example, the first (or left) operand of the multiplication operator * is the atomic expression 13 and its second (or right) operand is the compound expression (4 + 7). (An atomic expression is a naked numeric literal, whereas a compound expression is one in which at least one operator is present.)

Two other well-known forms of expressions are prefix and postfix. As its name suggests, in a prefix (respectively, postfix) expression each operator precedes (respectively, follows) its two operands. To illustrate, below is a table in which each row shows the same expression in its three different forms.

InfixPrefixPostfix
1515 15
18 + 5 + 18 5 18 5 +
13 * (4 + 7) − 2 − * 13 + 4 7 2 13 4 7 + * 2 −
(3 + 7) * 2 − 24 / (1 + 5) − * + 3 7 2 / 24 + 1 5 3 7 + 2 * 24 1 5 + / −

You will notice that, in the examples above, the prefix and postfix forms have no parentheses. This is not a coincidence; rather, it is a property of those forms that parentheses are never needed. This gives them an advantage over the infix form, in which it is often necessary to use parentheses —and to rely upon the conventions of operator precedence (PEMDAS) where parentheses are omitted— to make expressions unambiguous.

Using parentheses to more clearly show the association between operators and their operands in the most complicated expression above, we would have

Postfix: (((3 7 +) 2 *) (24 (1 5 +) /) -)
Prefix: (- (* (+ 3 7) 2) (/ 24 (+ 1 5)))

Formally, compound expressions in the three forms have the following syntax (where <op> is an operator, such as +):

Expression-Tree for (3+7)*2 − 24/(1+5)
          -
         / \
        /   \
       /     \
      /       \
     *        ÷
    / \      / \
   /   \    /   \
  +     2  24    +
 / \            / \
3   7          1   5
Infix: ( <infix-expr> <op> <infix-expr> )
Prefix: <op> <prefix-expr> <prefix-expr>
Postfix: <postfix-expr> <postfix-expr> <op>

Compound expressions are hierarchical and recursive in that they contain as components other expressions, which are typically referred to as subexpressions. To show the hierarchical structure of an expression more clearly, it can be rendered as an expression-tree. The leaves of such a tree correspond to the atomic subexpressions that appear within the expression and the subtrees rooted at interior nodes correspond to the compound subexpressions within the expression. As an example, consider the expression-tree to the right, which is yet another representation of the expression whose infix form is (3+7)*2 − 24/(1+5).


Java Artifacts

The student is provided with the following Java artifacts, one of which is to be augmented and two of which are incomplete.


Sample Run of ExprTreeTester Application

As usual, user input is shown in boldface. Notice that the program expects consecutive tokens to be separated from each other by at least one space.

Welcome to the Expression Tree Tester Program

(0) Quit
(1) Infix Expression
(2) Prefix Expression
(3) Postfix Expression
> 1
Enter infix expression: ( ( ( 3 + 7 ) * 2 ) - ( 1 + 5 ) )
Value is 14
Prefix form: - * + 3 7 2 + 1 5
Infix form: ( ( ( 3 + 7 ) * 2 ) - ( 1 + 5 ) )
Postfix form: 3 7 + 2 * 1 5 + -

(0) Quit
(1) Infix Expression
(2) Prefix Expression
(3) Postfix Expression
> 2
Enter prefix expression: + / 12 4 * 9 1
Value is 12
Prefix form: + / 12 4 * 9 1
Infix form: ( ( 12 / 4 ) + ( 9 * 1 ) )
Postfix form: 12 4 / 9 1 * +

(0) Quit
(1) Infix Expression
(2) Prefix Expression
(3) Postfix Expression
> 3
Enter postfix expression: 1 2 3 4 * 5 - + /
Value is 0
Prefix form: / 1 + 2 - * 3 4 5
Infix form: ( 1 / ( 2 + ( ( 3 * 4 ) - 5 ) ) )
Postfix form: 1 2 3 4 * 5 - + /

(0) Quit
(1) Infix Expression
(2) Prefix Expression
(3) Postfix Expression
> 0

Goodbye from the Expression Tree Tester Program


How to Build an Expression Tree from an Expression String

Each of the three stubbed methods in the provided ExprTreeBuilder class is intended to accept an expression-string and to produce as its result the corresponding expression-tree (which, of course, will be an instance of (one of the child classes of) ExprTree). As its name suggests, fromInfix() handles expression-strings in infix form, while fromPostfix() and fromPrefix() handle expression-strings in, respectively, postfix and prefix forms.

fromInfix()

To keep things simple, the fromInfix() method assumes that the expression-string given to it is fully-parenthesized (i.e., is an FPAE). You are already familiar with an algorithm that evaluates an FPAE. An algorithm for building an expression tree from an FPAE is very similar and appears below. Use it as a guide in completing the fromInfix() method.

Infix Expression-String to Expression-Tree
operandStk = empty stack
operatorStk = empty stack
do while there are more tokens to process
   token = next token
   if token is a left parenthesis
      do nothing
   else if token is a numeral
      k = integer value described by token (use Integer.parseInt())
      e = atomic expression tree holding value k
      operandStk.push(e)
   else if token is an operator
      operatorStk.push(token)
   else // token is a right parenthesis
      operand2 = operandStk.pop()
      operand1 = operandStk.pop()
      operator = operatorStk.pop()
      e = compound expression tree whose components 
          are operator, operand1, and operand2
      operandStk.push(e) 
   fi
od
return lone expression tree on operandStk

fromPostfix()

An algoritm for transforming a postfix expression-string into an expression-tree follows. Use it as a guide in completing the fromPostfix() method. Below that is a diagram that traces execution of the algorithm when fed as input the postfix expression-string  3 7 + 2 * 24 1 5 + / −. The diagram shows the contents of the stack after each token of the expression is processed. (Note that each element on the stack is an expression-tree.)

Postfix Expression-String to Expression-Tree
stk = empty stack
do while there are more tokens to process
   token = next token
   if token is a numeral
      k = integer value described by token
      e = atomic expression tree holding value k
      stk.push(e)
   else // token is an operator
      operand2 = stk.pop()
      operand1 = stk.pop()
      e = compound expression tree whose components 
          are the token, operand1, and operand2
      stk.push(e)
   fi
od
return lone expression tree remaining on stk

Snapshots of stack during processing of
postfix expression-string  3 7 + 2 * 24 1 5 + / −
   |   |
   | 3 |
   +---+
   |   |
   | 7 |
   |---|
   | 3 |
   +---+
  |       |
  |   +   |
  |  / \  |
  | 3   7 |
  +-------+
  |       |
  |   2   |
  |-------|
  |   +   |
  |  / \  |
  | 3   7 |
  +-------+
|           |
|      *    |
|     / \   |
|    /   \  |
|   +     2 |
|  / \      |
| 3   7     |
+-----------+
|           |
|    24     |
|-----------|
|      *    |
|     / \   |
|    /   \  |
|   +     2 |
|  / \      |
| 3   7     |
+-----------+
|           |
|     1     |
|-----------|
|    24     |
|-----------|
|      *    |
|     / \   |
|    /   \  |
|   +     2 |
|  / \      |
| 3   7     |
+-----------+
|           |
|     5     |
|-----------|
|     1     |
|-----------|
|    24     |
|-----------|
|      *    |
|     / \   |
|    /   \  |
|   +     2 |
|  / \      |
| 3   7     |
+-----------+
|           |
|    +      |
|   / \     |
|  1   5    |
|-----------|
|    24     |
|-----------|
|      *    |
|     / \   |
|    /   \  |
|   +     2 |
|  / \      |
| 3   7     |
+-----------+
|      ÷     |
|     / \    |
|    /   +   |
|   /   / \  |
| 24   1   5 |
|------------|
|      *     |
|     / \    |
|    /   \   |
|   +     2  |
|  / \       |
| 3   7      |
+------------+
|                       |
|           -           |
|          / \          |
|         /   \         |
|        /     \        |
|       /       \       |
|      *         ÷      |
|     / \       / \     |
|    /   \     /   \    |
|   +     2   24    +   |
|  / \             / \  |
| 3   7           1   5 |
+-----------------------+ 

fromPrefix()

As for building an expression-tree from a prefix expression-string, an algorithm for that is obtained by making two changes to the algorithm above for postfix expression-strings:
  1. Process the tokens in reverse order (i.e., from right-to-left). To accomplish this, it suffices to push the tokens onto a stack and then to process them in the order in which they come out of the stack (which will be, of course, the reverse of their order in the original expression-string).
  2. Swap the instructions that assign values to operand1 and operand2.

Program Submission

As usual, submit each Java class that you were expected to complete to the appropriate folder.