Congratulations, your front-end is complete! You are now only one step away from completing your compiler. The last step of the compiler is to generate binary code for the Java Virtual Machine.
If you have any questions about this assignment please do not hesitate to ask the instructor.
Generating class files is a tedious and not so interesting task. We recommend you to use the Jasmin assembler for this purpose as discussed in Lecture 30. Alternatively, you may choose other available libraries, such as ASM for Java. The main advantage of ASM is using the visitor pattern to generate bytecode.
To concatenate and print out strings, you will need to call methods from the Java library (as there are no bytecodes to do that directly). You only need to handle the cases where you print booleans, integers or strings1). Turns out that System.out.println(…) of the Java library is conveniently overloaded for all these cases.
To call System.out.println(…), you need to invoke the proper method println(…) (with the signature matching what you're trying to print, that is) on the static object System.out, so you need to emit a GETSTATIC bytecode, then emit the code for the expression you're trying to print, then emit an INVOKEVIRTUAL bytecode. System.out is of type java.io.PrintStream.
Concatenation is done using java.lang.StringBuilder. The procedure consists in building a new instance of a StringBuilder, then appending to it whatever you want to concatenate together, then calling toString() on the builder. The append method is overloaded for strings and integers, and you're not asked to be able to concatenate any other type.
We will handle equality in a simple way:
You have to apply lazy evaluation to those two boolean operators (short-circuiting). E.g. make sure your compiled code for expressions such as (true || (1/0 == 1)) doesn't crash (this is the example expression that we give in the code).
Some bytecodes contain a letter indicating to which type of operands they apply (eg. iload which loads integers, if_acmpeq which compares two references for equality or lreturn which returns a
long value). The naming convention is as follows:
(Note that these letters are not type descriptor themselves, they are prefixes of instructions that show the type of operands)
|A||Reference (object or array)|
Note that returning from a
void method is done using the return bytecode (no prefix letter).
To see how you can refer to a type in the JVM (e.g. for method signatures), consult e.g. Jasmin User Guide.
Note the the L in the beginning of class types is not needed when we indicate the class type on which we invoke a method (e.g. the first argument of the
InvokeVirtual abstract bytecode).
Complete your compiler to emit class files. The generated class files should be executable on the Java Virtual Machine: you should be able to run the main class with
java and get the same output as with
javac for the eMiniJava benchmarks that are also valid Java programs.
Your compiler should generate class files (one per class, as in Java, and one for the main object) silently if the compilation is successful, or generate errors and warnings from the previous phases in case there was a problem. The code generation phase should not produce any error.
Since we are not imposing any code structure for your programs, it is extremely important for your compiler to exactly follow a fixed interface as described here. This allows us to uniformly run and test all the projects from different groups of students. Command-line is the primary interface for your users to interact with your compiler. A general form for the command-line interface is as follows:
emjc [options] <source file>
For this phase of assignment, there are seven possible options. If the user does not provide a correct option, the compiler treats it the same as the –help option.
––help: Prints a synopsis of options
––pp: Pretty-prints the input file to the standard output
––lex: Generates output from lexical analysis as described in Assignment 2.
––ast: Generates output from syntactic analysis as described in Assignment 3.
––name: Generates output from name analysis as described in Assignment 4.
––type: Generates output from type analysis as described in Assignment 5.
––cgen: Generates output from code generation
After executing the compiler with the option
––cgen for the source file
filename.emj the compiler silently generates class files for a valid eMiniJava program.
In the case that the program has errors, your compiler front-end should work as before.
It prints out a set of errors (such as unknown identifier) in the following format:
where <line> and <column> indicate the beginning position of the error, and <description> details the error.
The deadline of the this assignment is May 5th at 11:59pm (https://mycourses.rit.edu). Please upload all your files as a single zip file. You should include a readme.txt file in your package to describe how your source code can be compiled and built. Ideally your project should include a user-friendly compiling technique with Makefile, Ant or similar tools.
When grading, we are serious about simple programming errors. You are writing a compiler to check the programs of other people, so your compiler should not have simple errors itself! Your compiler should never crash for an incorrect input. We expect your compiler to give a comprehensive error when the user does not provide a valid input to it.