CMPS 144L Fall 2022
Lab #5: Using Stacks to Process FPAE's

Activity #1: Parenthesis Matcher

Welcome to the parenthesis matcher.

> (( The cat ) ate (( a rat ) very ) quickly) ()
Correctly nested

> (()(())
         ^ Error here

> The ( gorn ) spoke ) in a husky) voice.
                     ^ Error here

>

Goodbye.
The ParenthesisMatcher class has an isProperlyNested() method whose purpose is to check to see if the parentheses in a given String are properly matched/nested. It also includes a main() method, strictly for the purpose of testing. To the right you can see a sample run of the program. The only characters that are not ignored by isProperlyNested() are left and right parentheses (i.e., '(' and ')').

As it stands, the manner in which that method works is to scan the given String from left to right, using a stack to store the as-yet-unmatched left parentheses. Each time a '(' is encountered, it is pushed onto the stack. Each time a ')' is encountered, its matching '(' is popped off the stack. If an attempt to pop the stack fails, because it is empty, it means that the encountered ')' has no mate and thus the expression is not properly nested. If the end of the string is reached with one or more left parentheses remaining on the stack, it means that those left parentheses have no mates and thus the expression is not properly nested.

The stack used by isProperlyNested() is an instance of the Java class StackViaArray, which implements the Java interface Stack.

It may have occurred to you that using a stack to solve this problem is unnecessarily complicated, because a simpler solution would use a single integer value to keep track of how many as-yet-unmatched left parentheses have been encountered.

But your task in Activity #1 is to augment the isProperlyNested() method so that it takes account not only of parentheses but also of square brackets. For this generalized version of the problem, use of a stack is justified.

For example, ( [ ( ) ] ) [ ] is properly nested, but ( ( [ ) ] ) is not, because there is an as-yet-unmatched '[' between the leftmost ')' and its mate. (A square bracket cannot be matched with a parenthesis.)

Submit your updated version of the Java class (i.e., the file ParenthesisMatcher.java) to the appropriate dropbox


Activity #2: Validating and Evaluating FPAEs

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 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.

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), here we will deem that consecutive tokens in an FPAE must be separated from each other by one or more spaces.

Examples of FPAE's:

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

Your Task

You are given an incomplete version of the FPAE_Utils class. Its two public methods (other than main()) are incomplete, and it is your task 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.

Enter FPAE: ( ( 5 + 3 ) * 7 )
( ( 5 + 3 ) * 7 )
56

Enter FPAE: ( ( 5  3 + ) * 7 )
( ( 5  3 + ) * 7 )
Expression found not to be well-formed.

Enter FPAE:
Goodbye.
Several other methods that you should find useful are provided within the class, including a main() method for the purpose of testing your work. (See sample run to the right.) The Integer.parseInt() method, which translates a numeral (i.e., a String that is a sequence of decimal digit characters) into the corresponding value of type int, will be vital, too.

As in Activity #1, you should make use of the Java class StackViaArray (which implements the Java interface Stack).

Now we consider the two methods of interest.

isWellFormed():

To determine whether or not a string is a well-formed FPAE, this method uses a stack to hold integer codes that represent left parentheses, operators, and operands. You will notice the declarations of the int constants LEFT_PAREN_CODE, OPERATOR_CODE, and OPERAND_CODE in the method, to serve that purpose. (To decide whether an expression is syntactically valid, we need not distinguish between one operator and another. Nor do we need to distinguish between the numerical values of any operands whose corresponding subexpressions have already been processed. This is why it suffices for each item on the stack to have one of only three possible values, one value indicating a left parenthesis, one value indicating an operator, and a third value to indicate an operand.)

Here is how the method should work:
As it scans the string from left to right, it pushes onto the stack the integer codes corresponding to any tokens that are not right parentheses. When it encounters a right parenthesis, it pops the stack four times and verifies 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 this expectation, it pushes onto the stack the code for an operand and continues 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 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 of which holds operators that have been encountered (but have not been applied yet, because their corresponding right parentheses are yet to be encountered). The other stack holds the values of subexpressions (i.e., operands) that have been evaluated already and that are waiting to have an operator applied to them.

Here is how the method should work:

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

Upon completing the work, submit your FPAE_Utils.java file to the appropriate dropbox.