Look at Evaluator.java, starting on pg. 407.. Note how this page has a summary of the whole class, with the details on the following pages.
How do we tackle reading a class for the first time?
--read the header comments
--read the signatures of the public methods, to see what services this class offers, and what objects can be made.
Note the inner classes in use. I don't expect you to be knowledgable about inner classes yet. In pa03, the first thing to do is to
pull these classes out to be ordinary public classes without rewriting large parts of them.
Here we see a constructor that takes a string and creates an Evaluator object for it.
Then we call getValue() to do the evaluation and return the integer result.
Look at the main method on pg. 414, see the expected construction of an Evaluator object ev, given the string to evaluate such as “1+2*3”. Then ev.getValue() to do the evaluation, and the result is printed out.
Note: keep this driver code as main for ExprEvaluator, just changing the class name as needed.
We can run Evaluator as follows:
java Evaluator
Enter…
1+2*3
Read 1+2*3
7
Enter…
2^3^2
Read 2^3^2
512
…
Using an input file by redirection: Create file “infile1” say with contents
1+2*3
2^3^2
and run like this:
java Evaluator < infile1
Enter…
Read 1+2*3
7
Enter…
Read 2^3^2
512
or redirect input and output:
java Evaluator < infile1 > outfile1
Note: these work equally well on Windows and UNIX, and this can be done from inside Dr. Java too, in the Interactions box, once the working directory is set right.
Back to pg. 407. We see 3 private static classes dealing with tokens. These are to be moved out of Evaluator into ordinary public classes. It is clear after studying the whole code that Precedence is just a simple helper class that should be hidden away inside Evaluator, so you can leave it there if you want.
Also note the constructor implementation with the string of operator symbols (line 28.) That list of symbols should not be here in the top-level code, but rather inside the token handling code. You are going to change line 36 to private String inString; say, and create a Scanner instead of a StringTokenizer in the tokenizing class.
We see the two stacks defined on lines 34-35.
Now let’s look at the precedence decision we worked so hard on last class. See pg. 413
while( precTable[ lastType ].inputSymbol <=
precTable[ topOp
= opStack.peek() ].topOfStack )
binaryOp( topOp
); // evaluate top of opStack
What a mess! What’s going on here?
lastType is LPAREN or PLUS or MULT or EXP or EOL or…, the input symbol (the one being processed)
topOp is RPAREN or PLUS or MULT or EXP or EOL or …, the symbol at top of stack
precTable[ lastType ] looks up the Precedence object for the input symbol, for example (1, 2) for PLUS
precTable[ lastType ].inputSymbol is the score for this type of symbol as an input symbol (1 for PLUS)
and similarly precTable[topOp].topOfStack is the score for this type of symbol as top-of-stack symbol (2 for PLUS)
Basically this comparison is score_of_lastType <= score_of_topOp
or input_op_score <= top_op_score as we discussed last time.
Examples: Consider a top_op of *, with score of 4, and various ops coming in:
top_op = *, in_op = +, scores are 1 vs. 4, do eval-top (case of unequal precedence: + is weaker than * no matter what)
top_op = * and in_op = ^, scores are 6 vs. 4, don’t do eval-top (case of unequal precedence: ^ is stronger than * no matter what)
top_op = * and in_op = *, scores are 3 vs 4, do eval-top (case of equal precedence, left-to-right assoc op)
Similarly
top_op = ^ and in_op = ^, scores are 6 vs 5, don’t do eval-top (case of equal precedence, right-to-left assoc op)
Object Lifetimes
Objects are created by new (or Class.newInstance) and are guaranteed to live until they are no longer referenced by any program reference variables, directly or indirectly. See pg. 31. In this program, Tokens are created for each input Token, but the Stacks only have Integers on them, so there is no mechanism for storage of a collecton of Tokens. Thus the Tokens become unreferenced, subject to garbage collection.
How many objects does it take to do 2+3? Number of Objects in use at this point
( not counting original i/o objects in main).
p. 414, main: one String to hold "2+3", 1 Evaluator, constructor, pg. 407, creates 2 Stack 4
calls getValue, pg. 410, 1 EvalTokenizer, which wraps one StringTokenizer/Scanner 6
calls tok.getToken(), pg 409: new Token(VALUE, 2) 7
calls processToken, pg. 413, push new Integer (autoboxing) (old Token is no longer in use) 7
call tok.getToken(), new Token(SUM) 8
calls processToken, push new Integer (old Token is no longer in use) 8
calls tok.getToken(), new Token(VALUE, 4) 9
calls processToken() push new Integer (old Token is no longer in use) 9
calls tok.getToken(), new Token(EOL) 10
calls processToken()
calls binaryOp, pop 3 old Integers, push new Integer 8
old Token is no longer in use 7
return to getValue: pop Integer, return, freeing EvalTokenizer (and its Scanner) and Integer 4
return to main, next calc. overwrites str and ev, freeing old Evaluator and its Stacks. 0
We are now done with the coverage of Stacks.
Recall that Stacks and Queues are both specialized lists.
Queues—pg. 227, a “pure” Queue. Put elements in one end and never get to access them until they come out the other end.
Generic queue operations (no API, just ideas)
enqueue--insertion of item at back of queue
dequeue--removal of item from front of queue
getFront--access item at front of queue
Queues are use for dataflow and “workflow”, where tasks flow from one person to another. In cases like this, different programs or different parts of the same program are doing the enqueue and dequeue operations. The queue can save objects that are in transit and allow pacing flexibility at both ends.
What API do we use in Java? All the JDK has is a Queue interface extended from Collection, and implemented by LinkedList.Almost done
with Chap. 6. .