Read this assignment thoroughly before beginning to write your solution. (This assignment is loosely based on the Chapter 1 assignment in Andrew Appel's Modern Compiler Implementation which is on reserve at the library.)
A straight-line program consists of a list of instructions that are to be executed in sequence - i.e., there are no conditional operations, no loops, no jumps.
In this assignment you will be writing an interpreter for a straight-line expression language. The language is very simple: every instruction is an expression - meaning it can be evaluated to an integer. Programs in this language are a sequence of semi-colon terminated expressions with each expression in one of four forms:
Here is a transcript of my solution to the assignment run on three small examples which illustrate the facets of this simple language. Observe that a program evaluates to the value corresponding to the final expression in its list of instructions. If a variable is used before it is defined, a warning is issued, but the value is taken to be zero so that processing can continue.
You need not worry about any syntactic issues in the programming language - the programs are expressed using an abstract syntax formed by using Java class constructors. For instance:
x = 3; x + 7;corresponds to the following Java fragment:
new Program(
new AssignmentExpression("x", new NumberExpression(3)),
new BinaryOperationExpression(
new IdExpression("x"),
Operator.PLUS,
new NumberExpression(7)))
(This example can be found
in ExamplePrograms.)
Write Java code, using the supplied data constructors, to represent these straight-line programs:
gamma = 100; delta = gamma - 1; (gamma * gamma) - (delta * delta);
a = b = c + 5; b = (a = a * b) + 1; a + b;
Add the necessary code to DefinedVariables so that showDefinedVariables prints in alphabetical order the variables that are defined (assigned values) in a program. Do not print duplicates. (See the solution transcript for an illustration.) Important: this is not intended to require a lot of coding on your part - take advantage of generic collections: for instance, you might use a TreeSet.
Finally, and most importantly, write the interpreter in the Evaluate class. Following the model provided by Unparse, you should use recursion over tree structures defining expressions. In order to keep track of the values of variables you will need a symbol table. There are many ways to do this. Again, I recommend taking advantage of generic collections, perhaps using a HashMap.
All the files you need are included in this Eclipse-project archive. The individual Java files can also be found here.
All of your programs should be part of the interpreter package. To compile from the command line use:
javac interpreter/Main.javafrom the directory above the interpreter source directory. (Using Eclipse, you need only import the files by creating a new Java project from existing source.) Likewise for execution, from the command-line use:
java interpreter/Main.javafrom the directory above the interpreter class (binary) directory. From Eclipse, just use the "Run" menu option.
Compiler writing involves substantial programming and you should be careful to follow coding conventions. That is: have conventions, and stick to them. A helpful place to start: Doug Lea's Draft Java Coding Standard. Another potentially useful site in this regard is: at geosoft.no/development/javastyle.html.
Anyone who has taken a course involving a C-style programming language with me has heard me complain about the problem with allowing assignments to be nested as expressions. Implement a test that reports such occurrences as:
a = b = c;
x ? y : zwhich results in the value associated with expression z if x evaluates to zero, and y otherwise.