For this assignment you need to understand and implement basic stack and binary tree operations. Lots of resources for these are posted in the lecture notes and the detailed schedule. Also, you will be expected to use exceptions to detect and handle invalid input. If you are not familiar with handling them in try/catch blocks, review the intro Java exceptions examples. As always, read and review before starting the assignment.
We are used to seeing arithmetic and boolean expressions in what is called infix notation where binary operators appear between their operands, and unary operators appear to the left of their operand, such as 4+5*(6-23)/-7. A fully parenthesized infix expression is one in which the order of operations is completely determined by explicit parentheses; (4+((5*(6-23))/(-7))) is a fully parenthesized version of the expression above. There are also two other notations, prefix and postfix notation, where the operators appear before (prefix) or after (postfix) their operands. The prefix version of our example is + 4 / * 5 - 6 23 - 7 and the postfix version is 4 5 6 23 - * 7 - / +.
A classic way to do expression evaluation is using stacks and trees. The stack is used when parsing either a prefix or postfix expression, to create an expression tree in which the internal nodes are the operators and the external nodes are the data values. Here is the tree corresponding to the arithmetic expression above (each operator and operand is enclosed in ' ' to differentiate them from the slashes that represent the tree edges):
'+'
/ \
'4' '/'
/ \
'*' '-'
/ \ \
'5' '-' '7'
/ \
'6' '23'
You can see that each type of notation corresponds to a particular binary tree traversal: infix - inorder, prefix - preorder, and postfix - postorder. For this assignment you are going to write a program that works with all three types of notation, but for set operations instead of arithmetic ones.
There are many on-line resources explaining the process of using a stack to create an expression tree. I recommend starting with wikipedia as usual. Chapters 3 and 4 in the Weiss textbook also have a thorough discussion of arithmetic expression evaluation using stacks and trees.
Write a set of JUnit4 tests that will thoroughly test any implementation of this Stack.java interface. Name your file StackTest.java and submit on Blackboard. We also advise using it to test whatever Stack implementation you end up using for the application below.
Write a program that will evaluate Set expressions which use the operators 'U' for union and 'I' for intersection. For simplicity, we will only process sets of integers, but it should be pretty straightforward to adapt your solution to sets of other types. We will also assume that both operators have the same precedence. A set will be represented in standard format, such as {1, 2, 3}. Your program must read a plain text file containing set expressions, one per line, and then process each line writing the results to an output file. Get the names of the input and output files from the user when the program begins.
Your main program must be able to do three main things for each expression in the input file:
For each valid line of input you must generate several lines of output in this exact order: the prefix version, the infix version, the postfix version (ie, the input itself), the resulting set (after complete expression evaluation), and a blank line. Some examples of input expressions and corresponding results are given below. You must format your results with exactly the same spacing and labels.
Input #1: {3} {5} U
Output #1:
prefix: U {3} {5}
infix: ({3} U {5})
postfix: {3} {5} U
result: {3, 5}
Input #2: {3} {5} I
Output #2:
prefix: I {3} {5}
infix: ({3} I {5})
postfix: {3} {5} I
result: {}
Input #3: {3, 4, 5} {4, 5, 6} I {1, 2} U
Output #3:
prefix: U I {3, 4, 5} {4, 5, 6} {1, 2}
infix: (({3, 4, 5} I {4, 5, 6}) U {1, 2})
postfix: {3, 4, 5} {4, 5, 6} I {1, 2} U
result: {1, 2, 4, 5}
Input #4: {1, 5, 9} {1, 2, 3, 4, 5} {3, 4, 5} {6, 7} U U I
Output #4:
prefix: I {1, 5, 9} U {1, 2, 3, 4, 5} U {3, 4, 5} {6, 7}
infix: ({1, 5, 9} I ({1, 2, 3, 4, 5} U ({3, 4, 5} U {6, 7})))
postfix: {1, 5, 9} {1, 2, 3, 4, 5} {3, 4, 5} {6, 7} U U I
result: {1, 5}
Your program should be able to detect and handle when an input string is not a properly formed postfix integer Set expression using exceptions. The types of errors you must catch are invalid operators, invalid operands, and invalid operator/operand combinations. All your program has to do when something invalid is detected is output an error message and continue, skipping past the invalid input line. You are not expected to say exactly what is wrong with an invalid expression - the notations in parentheses below are just for your understanding benefit. (Hint: if you use the stack correctly, it will be pretty easy to recognize a variety of invalid expressions.)
Here are some examples of invalid postfix integer set expressions, and the reasons why. Remember that your program does not have to say what is wrong with them, just catch that they are invalid.
{3} U {5} (ill-formed expression, not postfix)
U {3} {5} (ill-formed expression, not postfix)
{a} {b} U (invalid operand)
2 3 I (invalid operand)
{1} {5} * (invalid operator)
{2 3} {5} U (invalid operand)
{3} {4} {5} I (ill-formed expression)
{3} {4} I {5} {6} U (ill-formed expression)