All projects

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.

Java CompilersLanguage DesignJava

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, char and string, plus multidimensional array declarations and const values.
  • 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, while and for.
  • Operators — arithmetic, compound assignment (+=, -=, *=, /=), comparison, and the logical and, or, not.
  • I/O — print, println and input, resolved by the runtime library through EASy68K traps.
  • Comments — :D to 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 .dot and 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.