CMPS 144L Fall 2019
Lab #5: FPAE's and Stacks (or Lab #4 during Intersession 2020)

Background

As you should recall from lecture, an FPAE (fully-parenthesized arithmetic expression) is either an integer literal (e.g., "605") or of the form

( E op F )

where E and F are themselves FPAE's and op is a binary infix arithmetic operator (i.e., one of +, -, *, or /).

Thus, an FPAE is composed of (up to) four kinds of elements, or tokens: left parentheses, right parentheses, integer literals, and operators. FPAE's that are simply integer literals are said to be atomic, while those that include one or more operators are said to be composite. Intuitively, an FPAE is an arithmetic expression in which every composite subexpression is explicitly parenthesized. It follows that, in an FPAE, the number of pairs of mated parentheses is equal to the number of operators.

Note that we are assuming here, in order to make it easy to identify the individual tokens of an FPAE using a Scanner object (i.e., an instance of the class java.util.Scanner), that adjacent tokens are separated by one or more spaces.

Examples of FPAE's:

57 ( ( 5 + 2 ) * ( 72 - ( 8 / 3 ) ) )
( 3 - 15 ) ( ( ( 7 + 9 ) * 10 ) / ( ( 4 * 16 ) / ( 25 - 13 ) ) )


Activity #1

You are given an incomplete version of the FPAE_Utils class. Its two public methods need work, and it is your task in this lab to put them into working order. One of them, isWellFormed(), determines whether or not the String passed to it qualifies as a well-formed (i.e., syntactically correct) FPAE. The other one, valueOf(), evaluates the FPAE passed to it, which is assumed to be well-formed.

Several other methods that you should find useful are provided within the class, including a main() method for the purpose of testing your work. The Integer.parseInt() method, which translates a numeral (i.e., a String that is a sequence of decimal digit characters) into the corresponding number, will be vital, too.

You also are given the complete Java class StackViaArray, of which FPAE_Utils is a client.


isWellFormed()

To determine whether or not a string is a well-formed FPAE, this method is to make use of a stack. The stack is to hold integer codes that represent left parentheses, operators, and operands.1 You will notice the declarations of the int constants LEFT_PAREN_CODE, OPERATOR_CODE, and OPERAND_CODE in the method.

As we scan the string from left to right, we push onto the stack the codes corresponding to any tokens that are not right parentheses. When a right parenthesis is encountered, we pop the stack four times and verify that what came out were the codes for, respectively, an operand, an operator, an operand, and a left parenthesis. If there is any deviation from this (or if the stack had fewer than four items on it in the first place), the string is not a well-formed FPAE. If, on the other hand, what came out of the stack meets our expectations, then we push onto it the code for an operand and continue scanning.

Assuming that everything has gone well to the point that the last token has been processed, all that remains to verify is that the stack has exactly one element on it and that element is the code for an operand. Otherwise, the string is not a well-formed FPAE.


valueOf()

Consistent with what was demonstrated in lecture, this method uses two stacks, one to hold operators that have been encountered (but have not yet been applied) and the other to hold the values of subexpressions that already have been evaluated (i.e., operands) and that are waiting to have an operator applied to them.

As usual, we scan the string from left to right. If an integer literal is encountered, its value is pushed onto the operand stack. If an operator is encountered, it is pushed onto the operator stack. If a left parenthesis is encountered, it is ignored! If a right parenthesis is encountered, the operator stack is popped and the operand stack is popped twice. The operator that was popped is applied to the two operands that were popped, and the result is pushed onto the operand stack. (Note that the operands popped are the right and left, respectively, operands of the operator, not the other way around.)

After the last token is processed, the operator stack will necessarily be empty and the operand stack will have exactly one value in it, and that will be the value of the FPAE.



Activity #2

A weakness of the isWellFormed() and valueOf() methods that you worked on in Activity #1 is that they assume that adjacent tokens in an FPAE are separated by one or more spaces. The purpose of this activity is to "fix" that shortcoming.

To do so, you are to complete the development of a FPAE_Scanner class. Like the java.util.Scanner class, it has methods hasNext() and next(), as well as a constructor that receives (via parameter) a String "to be scanned". Of course, FPAE_Scanner's constructor "expects" the given String to be an FPAE, meaning one that contains left and right parentheses, operator symbols, and integer literals, and where spaces between tokens are optional.

To test your work, do the following. Make a copy of your FPAE_Utils.java file (that you completed during Activity #1) and call the new file FPAE_Utils2.java.

Within the new file:


Footnotes

[1] An operand is, in effect, a subexpression all of whose tokens already have been processed. Another way to look at it is that an operand is a subexpression that, had we been evaluating the FPAE instead of checking its well-formedness, already would have been evaluated, and thus reduced to an integer literal.