Java68 Compiler
A complete compiler for Java68, a small imperative language built for it, that takes a program through every classic phase — lexical, syntax and semantic analysis, three-address code and optimization — down to Motorola 68000 assembly ready to run on a simulator.
The problem
Project for the Compilers course at the Universitat de les Illes Balears.
The target is a real machine, not a virtual one: Motorola 68000 assembly that has to assemble and run on the EASy68K simulator. There is no runtime underneath, so everything the language offers — multidimensional arrays, typed function returns, input and output — has to be lowered by hand into registers, addressing modes and an explicit memory layout.
Java68 is the language we designed to be lowered. It is small, but complete enough to write real programs:
- Types —
int,bool,charandstring, plus multidimensionalarraydeclarations andconstvalues. - Structure — global variables and functions, with a single mandatory
main. Functions declare their return type with->and are void without it; parameters are passed by value. - Control flow —
if/elif/else,whileandfor. - Operators — arithmetic, compound assignment (
+=,-=,*=,/=), comparison, and the logicaland,or,not. - I/O —
print,printlnandinput, resolved by the runtime library through EASy68K traps. - Comments —
:Dto the end of the line,:O … :/for a block.
How it works
The compiler is a straight pipeline, and every phase is an independent traversal that writes its own artefact to disk, so a whole translation can be followed step by step:
- Lexical analysis — a scanner generated with JFlex produces a token table with line, column and lexeme.
- Syntax analysis — a parser generated with Java CUP builds the syntax tree, dumped as
.dotand rendered to PDF with Graphviz. - Semantic analysis — a three-part symbol table (description, scope and expansion) and full type checking over that tree.
- Intermediate code — three-address code, plus the variable and procedure tables.
- Optimization — a peephole pass with five rewrites: adjacent jumps, jumps over jumps, dead labels and gotos, label merging, and deferred assignments. The whole set repeats until a full pass changes nothing, because each rewrite tends to expose the next.
- Code generation — MC68000 assembly, emitted unoptimized and optimized. Every block is annotated with the three-address instruction it came from.
Running it
Requires a JDK 17 or newer — plain Java, with the JFlex and Java CUP jars bundled in lib/, no build tool. Open the repository folder in IntelliJ IDEA and run the Compile configuration.
Pick any program from programs/ in the file chooser that opens; the artefacts of every phase land in programs/results/. To run the compiled program, open programs/results/programa_opt.x68 with the EASy68K editor and simulator.
The other two run configurations, Run JFlex and Run Java CUP, regenerate the scanner and the parser after editing lexic.flex or sintactic.cup. Graphviz is optional: without dot on the PATH everything still works, only the PDF render of the syntax tree is skipped.