Here is the starter code for hw12: hw12.zip
Grading notes:
The files in question are
submissions@knuth:~/submissionsFall13Files/handbuiltScripts/cs60_wk12_files/xxin/hw12/src/junit-4.10.jar and
.../hamcrest-core-1.3.jar
Then,
cp ~/*.jar .
javac -cp .:./junit-4.10.jar *.java
javac -cp .:./junit-4.10.jar *.java
javac -cp .:./junit-4.10.jar QuantityTest.java
javac -cp .:./junit-4.10.jar TokenizerTest.java
javac -cp .:./junit-4.10.jar ParserTest.java
javac -cp .:./junit-4.10.jar EvaluatorTest.java
java -cp .:./junit-4.10.jar org.junit.runner.JUnitCore QuantityTest
java -cp .:./junit-4.10.jar org.junit.runner.JUnitCore TokenizerTest
java -cp .:./junit-4.10.jar org.junit.runner.JUnitCore ParserTest
java -cp .:./junit-4.10.jar org.junit.runner.JUnitCore EvaluatorTest
if in the src package, go up one directory and run
java -cp .:./src/junit-4.10.jar org.junit.runner.JUnitCore src.TokenizerTest
and so on...
This homework asks you to use Java to implement the Unicalc application that you completed in Scheme in weeks 2 and 3. You will use the OpenList data structure as the fundamental building block. OpenList is designed to implement Racket-like list structures. Because Java is organized around creating and combining data structures, we will Java-ize Unicalc in four parts:
Why so much code provided? I'd prefer to write the application myself... .
Please do! With the support code we are balancing two valid concerns: on
one hand, there is a deep understanding that only a from-scratch implementation
can provide. On the other hand, we want to provide enough examples and framework to build
familiarity with what are certainly intricate - and important! -
computational ideas. There is also an example languages: the MiniMath
language that we showed briefly in class (and is not fully working, though it does
give an example of all of the important ideas!)
Here is a link to that MiniMath example code, in a zip file...
Feel free to use as much - or as little - of the support code as you wish. One important thing we do ask if you implement from scratch is to use (or replicate) our toString conventions, so that we can check your results efficiently! Also, please adhere to the interfaces we provide (names of functions, parameters, return types, etc).
The goal of this assignment is that you have hands-on experience with (and, we hope, insights into) the processing that is part of implementing all computer languages. Using the starter code or building from scratch, we believe, can both provide that foundation.
Please submit the following files zipped in an archive named hw12.zip:
Because the .java files you write this week will be interdependent, keep them all in a single folder/directory named hw12 That way, java will be able to find classes as it needs to. Also, it will be easy to zip up that directory to submit.
+10 points
This is the implementation - really a port - of Unicalc's functionality into a Quantity class in Java.
The implementation will be in the Quantity class.
In order to
ensure that toString and other important methods are uniform,
there is a starter file for Quantity.java
Notes on Quantity
{ 9.8, [meter], [second, second], 0.1 }
The above Quantity object represents 9.8 meters per second squared with an
uncertainty of 0.1.
Note that when a Quantity is printed, it is surrounded by curly braces.
{ 42.0, [], [], 0.0 }
Here, we are illustrating the provided toString function, along with the
OpenList class . The curly braces are used to help
distinguish objects of type Quantity from objects of type OpenList.
cancel( [m,s], [kg,s] ) returns [m]
cancel( [m,s], [kg,m,s] ) returns []
cancel( [m,s], [kg] ) returns [m,s]
cancel( [m,s], [] ) returns [m,s]
So, cancel does not treat its input lists the same! It's trying
to cancel elements from the first input, L that are present
in the second input, M.
String fL = (String)L.first;
String fM = (String)M.first;
From there, you can test conditions such as fL.equals(fM) and
fL.compareTo(fM). Based on those tests, your code will return
the appropriate value.
private double m; // the multiplier
private double u; // the uncertainty
private OpenList N; // the numerator, an OpenList of Strings (units)
private OpenList D; // the denominator, also an OpenList of Strings (units)
public Quantity(double m) // constructor for a "plain-number" QL
public Quantity(double m, OpenList N, OpenList D) // constructor with 0.0 unc.
public Quantity(double m, OpenList N, OpenList D, double u) // "standard" constructor
public String toString() // for printing objects of type Quantity
public static OpenList getSmallDB() // gets a pre-prepared database of units
public boolean equals( Object o ) // the equals method for any Object
public boolean equals( Quantity q ) // the equals method for comparing Quantity objects
public static void pl( Object o ) // printing for lazy (or sane) folks!
public static void pl() // ditto!
public static void main(String[] a) // main method
// a constructor with a different order of inputs...
public Quantity(double m, double u, OpenList N, OpenList D)
// These are ALMOST translations from earlier weeks' Unicalc code
//
// Note that norm_unit, norm, and add take in a DB, rather than using a global DB
// cancel removes elements from L, if they're in M - it's NOT symmetric!
public static OpenList cancel( OpenList L, OpenList M )
// simplify cancels common units from N to D and vice-versa
public static Quantity simplify(Quantity Q);
// multiplies two Quantity lists
public static Quantity multiply(Quantity Q1, Quantity Q2);
// raises Q to the pth power (p ≥ 0)
public static Quantity power(Quantity Q, int p)
// divides two Quantity lists
public static Quantity divide(Quantity Q1, Quantity Q2);
// returns the normalized QL equivalent to unit
public static Quantity norm_unit(String unit, OpenList DB);
// returns the norm_unit QL equivalent to Q
public static Quantity norm(Quantity Q, OpenList DB);
// adds two Quantity lists *after* normalizing them
public static Quantity add(Quantity Q1, Quantity Q2, OpenList DB);
// If the units (afer normalizing) do not match, return the following
// valid Quantity: { 0.0, ["error:add"], [], 0.0 }
// negation (we can implement subtraction via add and negate)
public static Quantity negate(Quantity Q);
Presuming the numerators and denominators are sorted The constructors of Quantity should make sure that the numerator and denominator units are in alphabetical (sorted) order. Our starter file provides this functionality.
In addition, the file QuantityTest.java
has a set of tests that you'll be able to compile and run
once Quantity's methods are written.
+5 points
A Tokenizer.java file provided for you in the code at the top-level assignment page. You will want to improve it so that the Unicalc language's tokens are found correctly whether or not spaces are inserted around the operators. Other things are possible here, too, if you decide to add your own features to the language, but for the required part of the language, this file will require the fewest changes.
Notes on the tokenizer:
+15 points
This problem asks you to implement a Parser for the Unicalc language, whose grammar and
parse trees are are summaried in this picture.
More detail on some fo these are below... .
Notes on this grammar
Symbols listed in red are terminal symbols. Similarly, doubles, ints and strings are also terminals.
The rule for Q uses the asterisk, not as "times," but meaning "zero or more of the preceding symbol." It also uses the plus sign, not as addition, but meaning "one or more of the preceding symbol." This Q rule is already implemented in the provided Parser.java file. D, I, and V are also already implemented. You will only need to implement rules U and above.
Parser.java implements a recursive-descent parser for part of the above grammar; your task is to extend it to the rest of the grammar.
Reminders about parsing:
Suggested strategy for Parser One way to build up to the full grammar is to implement one nonterminal method at a time and then test it to be sure that your rule works. The parse trees above can be used in the order they're presented you implement the rules in a similar order:
Warning! if you use the order suggested above, BE SURE
you are changing the start symbol that is called in the parse method! Also, be sure that
the lowest-implemented method calls Q until methods lower in the grammar can
replace it. To be even more concrete, here is a summary of the symbols you'll need to
change as you implement - and test - these Parser methods one at a time:
public ParseTree E() // be sure to change parse to call E and change E to call Q and E (not P and E)
public ParseTree P() // be sure to change E to call P and change P to call Q and P (not K and P)
public ParseTree K() // be sure to change P to call K and change K to call Q and I (not U and I)
public ParseTree U() // be sure to change K to call U and change U to call Q and E (as it should)
public ParseTree S() // be sure to change parse to call S and change S to call E and V, as appropriate
+12 points
This part completes the unicalc language. Here, you should build/extend the Evaluator class that will convert parse trees into objects of type Quantity.
What does everything evaluate to? Well, every valid statement in the Unicalc language evaluates to an object of type Quantity, that is, a quantity list. For example, the parse tree which is simply a list of a Quantity naturally evaluates to that Quantity object.
Notes on the Evaluator:
// DB is the unit database in which the expression is evaluated, if needed
// when you implement def, this should change the line
// OpenList DB = Quantity.getSmallDB(); to the line
// OpenList DB = this.env;
OpenList DB = Quantity.getSmallDB();
Then, you can use DB throughout the following code (to norm or add or
elsewhere) and it will be easy to change what DB you're using!
def x 42 m # defines x to be 42 m
18 m + x # asks, what is 18 m plus x
def x 42 m
The tokens are < def, x, 42, m >
The parseTree is [def, x, [{ 42.0, [m], [], 0.0 }]]
The value is { 42.0, [m], [], 0.0 }
The current env is
[
[x, { 42.0, [m], [], 0.0 }] // NOTE: no longer empty!
]
18 m + x
The tokens are < 18, m, +, x >
The parseTree is [+, [{ 18.0, [m], [], 0.0 }], [{ 1.0, [x], [], 0.0 }]]
The value is { 60.0, [m], [], 0.0 } // CORRECT RESULT!
The current env is
[
[x, { 42.0, [m], [], 0.0 }]
]
def mile 5280 foot
The current env is
[
[mile, { 5280.0, [foot], [], 0.0 }]
]
def league 0.125 mile
The current env is
[
[league, { 0.125, [mile], [], 0.0 }],
[mile, { 5280.0, [foot], [], 0.0 }]
]
def mile 1760 yard
The current env is
[
[mile, { 1760.0, [yard], [], 0.0 }],
[league, { 0.125, [mile], [], 0.0 }],
[mile, { 5280.0, [foot], [], 0.0 }]
]
where we'd redefined "mile" and updated env at the top;
assoc will "do the right thing" by taking that topmost match.
The test cases, below, show more examples of this use of the environment... .
We have provided JUnit test cases that should enable you to test your methods.
The following examples are identical to those tested in the JUnit test cases, but you can run them interactively by running the method main in Evaluator.java Here are a pretty extensive list of examples, including the printout of the input line, the tokens, parse tree, value, and - when of interest - the environment association list in this.env each time. They're the same as the parse trees above, but now with the evaluation results.
Assignment 10's tests including evaluation and (when applicable) the this.env environment.
These are the tests we will use in grading the code, so if it passes all of these - perfect!
Personalizing For up to +3 (or more) points, improve the unicalc-language with whatever feature you'd like... . Here are the rules, however:
Functions! For up to an additional +8 points, provide an implementation of functions in the Parser and Evaluator of the unicalc language. How to design and implement this is 100% up to you.
Warning: If you try this, be sure not to edit the only copy of your unicalc language files! -- implementing functions is tricky, and it's happened that people have broken their language in the attempt to do so!
Be sure to work on a copy of your files -- and then, if you submit this, please place those new files into a folder named extra and zip that folder. There is a spot in the submission site to upload extra.zip for this extra-credit challenge.
Be sure to include a comment at the top of your new Evaluator.java on how we could best test your code - give the graders a couple of examples to try!
