This assignment highlights the importance of modeling and design in object-oriented programming.

You are given code that implements a simple numerical calculator, which you will gradually enhance. The code benefits from modeling and design because:

This section describes how the code you are given has been designed, and how you will modify both the design and its implementation.

Make sure you understand the aspects of this design. You will want to return to this section when you are ready to proceed with the assignment.

See the menu for provided class documentation.


In the NetBeans lab exercise (see menu to the left), you were given a Console class that presented a window in which input could be entered and results echoed to the display area (see right top).

Console makes use of the JavaFX GUI framework, which you have yet to learn. At this point, however, that does not matter, because Console is designed to be extended by classes that do not have to do any GUI programming — they only have to provide code that handles input in a specific way.

You are given a SimpleCalculator class that extends Console and allows simple arithmetic expressions to be evaluated (see right bottom).

The inheritance relationship between SimpleCalculator and Console is shown in UML as:

In order to evaluate arithmetic expressions, we need to read and return the various basic elements of the expressions, including numbers, operators, and the left and right parentheses.

This process, called parsing, is supported at a rudimentary level by the StreamTokenizer class in the java.io package of the Java Standard Library.

You are given a custom TokenDispenser class that extends the StreamTokenizer library class for the purposes of arithmetic expression evaluation:

SimpleCalculator overrides the handleInput method of the Console class by handing the input to a stack-based shift/reduce expression evaluator, implemented by the evaluate method.

The evaluate method is modeled by the state diagram you produced for the UML lab exercise (see diagram at right; see UML lab exercise in menu to the left).

The evaluator for this state machine is simple: it can only evaluate expressions like "17.4" and "10/3". It cannot evaluate "2*3+4", for example.

In Backus-Naur form (BNF), the evaluator can evaluate expressions of the form NUM [OP NUM] (NUM is a number, OP is an operator, and [...] means "zero or one occurrences of ...").

Use the menu at left to follow an example stack trace on 10/3.

When the evaluation begins, the stack is empty:

The first token, 10, is obtained and shifted onto the stack. The machine state is Number1:

The second token, /, is obtained and shifted onto the stack. The machine state is Operator:

The third token, 3, is obtained and shifted onto the stack. The machine state is Number2:

Since there are no more tokens, the token dispenser indicates this with an EOF indication, the machine goes into state End, and the stack is reduced to the value of 10/3. The value of the expression is left as the only element on the stack:

A simple change to the initial state diagram produces a model for a calculator that can evaluate 2*3+4 as 10. The same change will evaluate 2*3+4*5 as 50.

Note that this new evaluator does not recognize operator precedence. If it did, 2*3+4*5 would evaluate as 26.

In BNF, this evaluator can evaluate expressions of the form NUM [OP NUM]* ([...]* means "zero or more occurrences of ...").

You will create a class called NoPrecedenceCalculator that implements this new evaluation model. It can reuse code by extending SimpleCalculator but it must override the evaluate method:

Operator precedence requires that the * and / operators have higher precedence than + and -.

Enforcing operator precedence requires scanning an expression for consecutive operators, and if the second operator has higher precedence than the first, it must be applied before the first is applied. Thus, 2*3+4*5 evaluates as 26.

Handling precedence in our evaluator does not require any change to the structure of the state machine; it only requires a change in the way the stack is reduced.

You will create a class called PrecedenceCalculator that will inherit from NoPrecedenceCalculator but override the reduce method as shown below.

Operator precedence introduces the possibility that the reducing process needs to iterate. See the menu at left for an example.

When evaluating 2+3*4, the stack will come to look like this:

When the token dispenser indicates no more input, the state machine must reduce the stack:

The stack must be reduced again to finish the input:

Note that only one token, the EOF indicator, triggers these two reductions.

Useful calculators allow parentheses to override the usual precedence rules. For example, 2*(3+4)*5 evaluates as 70.

Handling parentheses in our state diagram requires new states corresponding to the left and right parentheses, and new transitions in and out of them. Thus the evaluate method must be overridden.

Since encountering a right parenthesis triggers stack reductions the reduce action must also be changed.

You will create a class called ParenthesisCalculator that will inherit from PrecedenceCalculator but override the evaluate and reduce methods:

This section walks you through the process of turning the SimpleCalculator into one which handles precedence and parentheses.

At each step, you will modify the simple calculator state diagram that you created for the UML lab exercise.

These diagram modifications are an important part of the modeling and design process, and will be graded along with your code. You must save a copy of the diagram at each step and include it as part of your submission.

Download this Calculator.zip file and use the NetBeans Import Project feature to create the Calculator project.

Your project should have this structure:

Test the SimpleCalculatorTest.java file. You should get a window like:

Try various inputs to observe its behavior. Then close the window by clicking X on the window's title bar. The test cases coded in the SimpleCalculatorTest class will be run and you should get these results in the Test Results window:

In the Output window, you should also see:

-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running calculator.SimpleCalculatorTest
testResults passed
testErrors passed
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
      
In this step you will implement a calculator that can evaluate inputs of arbitrary length, without accounting for operator precedence. You will: This general procedure will be followed for the remaining steps as well.
Since operator precedence is ignored, the stack is reduced when possible. Here is a trace of the stack when evaluating 2 + 3 * 4:

Using Violet, open the State.violet file that you created and submitted for the UML lab exercise:
You need to complete the NoPrecedenceCalculator class, shown below.

As described in the Design section, this class must be a subclass of SimpleCalculator to inherit its features. A class template is given for you in the project:

This class needs to implement the evaluate method, overriding the one in SimpleCalculator. Look at how SimpleCalculator implements the original state diagram, then write your evaluate method to implement your modified diagram. Hints:

Test the NoPrecedenceCalculatorTest.java class, shown below.

As with the simple calculator test, you will first be able to enter some expressions manually. After exiting the calculator, the coded tests will be run and if successful you will get the same results as with the simple calculator.

Don't move to the next step until you've passed this one.

In this step you will implement a calculator that can evaluate inputs of arbitrary length, accounting for operator precedence, but not allowing parentheses.
Since operator precedence is respected, the stack is reduced according to precedence rules. Here is a trace of the stack when evaluating 2 + 3 * 4:

Using Violet:
As described in the Design section, this class must be a subclass of NoPrecedenceCalculator to inherit its features. A class template is given for you in the project:

This class needs to implement the reduce method, overriding the one in SimpleCalculator. Write your reduce method to implement your modified diagram. Hints:

Test the PrecedenceCalculatorTest.java class, shown below.

As with the simple calculator test, you will first be able to enter some expressions manually. After exiting the calculator, the coded tests will be run and if successful you will get the same results as with the previous tests.

Don't move to the next step until you've passed this one.

In the final step you will implement a calculator that handles both operator precedence and parentheses.
The stack is used to both enforce operator precedence and respect parenthesis levels. Here is a trace of the stack when evaluating (2 * (3 + 5)):

Using Violet:
As described in the Design section, this class must be a subclass of PrecedenceCalculator to inherit its features. A class template is given for you in the project:

This class needs to override both the evaluate and reduce methods to implement your modified diagram. Hints:

Test the ParenthesisCalculatorTest.java class, shown below.

As with the previous tests, you will first be able to enter some expressions manually. After exiting the calculator, the coded tests will be run and if successful you will get the same results as with the previous tests.

The files required for this assignment are available from the menu on the left.
When your calculator project is working correctly: Note the general Submission Policy in the menu at left.

Your project will be inspected and run by the lab instructor. For full credit your project must pass the provided tests. Grading criteria: