Project 6 -- The Infix to Postfix Converter
Due: Thursday, February 24, 2011

Objectives

Your Mission

Way back when, you learned to evaluate mathematical expressions using parentheses. For example, the parentheses in the expression
(6-2)*8
told you that the subtraction operation should be performed before the multiplication. If the parentheses were not there, you would follow the precedence order of operations which says
  1. Exponents are evaluated first from left to right
  2. Multiplication and division operations are performed next, from left to right
  3. Addition and subtraction operations are performed last, from left to right
So, for example, in the expression
24/2^3-1
the order of operations says that the exponent (2^3) should be done first, followed by the division, and finally the subtraction, giving the answer of 2. Without this order of operations, an expression like this one would be ambiguous without parentheses. This "normal" way of writing mathematical expressions is called infix notation.

On the other hand, a mathematical expression written in postfix notation doesn't need an order of operations or parentheses. Postfix expressions are never ambiguous. In postfix notation, the operator is written after the operands. Here are some examples (make sure you understand these before you try to implement the project):

infix postfix
7+1 7 1 +
(6-2)*8 6 2 - 8 *
24/2^3-1 24 2 3 ^ / 1 -

Your assignment is to write a computer program that will convert parenthesized or unparenthesized infix expressions into their equivalent postfix expressions. You will be reading a list of infix expressions from a data file. You should display (to System.out) each infix expression along with its equivalent postfix expression.

The Algorithm

Infix to postfix conversion can be accomplished with a stack. The stack will keep track of the parentheses and operators (plus (+), minus (-), multiply (*), divide (/), exponent (^)). To convert an infix expression into postfix, follow these rules:
  1. Read each token (each operand, operator, or parenthesis) from left to right. Only one pass is required.
  2. If the next token is an operand (represented by a capital letter), immediately append it to the postfix string.
  3. If the next token is an operator, pop and append to the postfix string every operator on the stack until one of the following conditions occurs:
    1. the stack is empty
    2. the top of the stack is a left parenthesis (which stays on the stack)
    3. the operator on top of the stack has a lower precedence than the current operator
    Then push the current operator onto the stack.
  4. If the next token is a left parenthesis, push it onto the stack.
  5. If the next token is a right parenthesis, pop and append to the postfix string all operators on the stack down to the most recently scanned left parenthesis. Then discard this pair of parentheses.
  6. If the next token is a semicolon, then the infix expression has been completely scanned. However, the stack may still contain some operators. (Why?) All remaining operators should be popped and appended to the postfix string.
Here's how the algorithm works on the infix expression (A+B*(C-D))/E; The rule number at the end of each line indicates which rule listed above was used to reach the current state from that of the previous line.

Next token Stack Postfix string Rule
( ( 4
A ( A 2
+ ( + A 3
B ( + AB 2
* ( + * AB 3
( ( + * ( AB 4
C ( + * ( ABC 2
- ( + * ( - ABC 3
D ( + * ( - ABCD 2
) ( + * ABCD- 5
) ABCD-*+ 5
/ / ABCD-*+ 3
E / ABCD-*+E 2
; ABCD-*+E/ 6

The Details

You'll be practicing with Java interfaces in this project, so while you are free to add to the minimum requirements described below, you shouldn't deviate too far from the general paradigm.

To help you get started, I'm supplying several files for you to download:

  1. input.txt. This text file contains some infix expressions to help you get started. The expressions are delimited by semicolons. Of course, you should be testing your code on your own input too! Perform the algorithm on these by hand so you understand how it is supposed to work.
  2. FileReader.java. This lets you read tokens from an input file. Read the javadocs in the file to see how it works. You should test it with the sample input file above to be sure you understand how to use it.
  3. Token.java. This is a Java interface from which you will implement classes that represent various tokens. (The token classes are listed below). Each implementing class will have a toString() method to return the Token in String format and a handle() method to dictate how to process the token when it is encountered. See the javadocs in the file for more explanation.
  4. Semicolon.java. This is a sample of a class that implements the interface above. Only the method prototypes are listed. You'll have to fill in the code yourself.

You are free to alter the code in the above files, but you are required to use them. You are also required to use Javadoc format for all of your documentation for this project.

Here are the classes you'll need to write:

Feel free to add other methods to any of the classes above.

A word about precedence

When dealing with operators, you'll have to develop some way of comparing them to see which has a higher precedence. One easy way to accomplish this is to use an int variable for each of the operators. A higher int means a higher precedence for that operator. You can use the following precedence values: Then, comparing two tokens is just a matter of comparing their int values.

Why do it this way???

As you get into this project, it may seem that it would be quicker to code this algorithm in a straightforward manner without using the interface and all the implementing classes. In fact, it probably would be quicker, but not very well-designed. By using the interface, you control exactly what the stack can (and cannot) hold. Instead of holding generic Strings or Objects, the stack can only contain Tokens. By limiting the stack in this way, you add robustness to the code by preventing the stack from being misused in ways you cannot foresee.

It also allows for the individual tokens to encapsulate their own behavior within their own classes. That way, if you want to modify the program by, say, adding the modulo operator (%), it can be done without touching the code for how division works.

Where do I start???

There are a lot of pieces to this assignment (you'll have at least 12 classes total by the time you're done). So use top-down design to help you manage it all. Here's a good way to approach this:
  1. I'm giving you a bunch of code to begin with. So studying and testing the existing code should be your first priority. Understand the interface. Check out the input. Process the input on paper first so you'll (1) know what the right answers are and (2) have a better grasp on how the algorithm works. Test the FileReader class to be sure it's giving you tokens correctly. Try to simply read the input file and then print it to System.out.

  2. The primary algorithm at the top of this document is handled by Converter. Get a good understanding on exactly what this class is doing on its own and what tasks it is subcontracting out to other methods. Converter is the one that's repeatedly getting tokens, figuring out which one it is, and calling the appropriate handle() method. Think of handle() this way: instead of you writing all of the code in Converter (which would be very messy), you'll simply let each token "handle" itself. So once you've determined that a token is, say, "+", you'll turn it into a Plus object, and tell it to handle() itself. And it will be each token's version of handle() that does the work of pushing on the stack, popping it, or whatever. That makes each token responsible for doing the right thing and makes your code much more modular. This is a key part of the algorithm so you should make sure you understand what this means before going on (and come see me if you don't).

  3. Do one Token at a time. Don't try to take care of all the tokens all at once. Concentrate on being able to read a "+" and get it on the stack. Now can you pop it off and get it back? Can you correctly convert an infix expression that just uses "+" like A+B+C?

Grading

This project is worth 50 points divided up this way: As always, practice good programming skills: Remember to turn in both a paper and an electronic copy of your project.

Administrative statement

Programming assignments, like homework assignments, are individual projects. I encourage you to talk to others about the general nature of the project and ideas about how to pursue it. However, the technical work, the writing, and the inspiration behind these must be substantially your own. If any person besides you contributes in any way to the project, you must credit their work on your homework. Similarly, if you include information that you have gleaned from other published sources, you must cite them as references. Looking at, and/or copying, other people's programs or written work is inappropriate, and will be considered cheating.