/EasyCompiler

Compiler for "Easy", a small programming language compiling to Java Bytecode.

Primary LanguageJavaMIT LicenseMIT

Easy Compiler

This project is a compiler for Easy, a simple programming language. The compiler's output is Jasmin assembler, which can be assembled into Java Bytecode, i.e. the programs can be executed on the JVM.

Features

  • Compilation of Easy sourcecode into Jasmin assembler, which can be translated into Java Bytecode (the bytecode representation of an Easy program equals a Java class and behaves like it)
  • Translation of each statement in the sourcecode into one clear block of Jasmin assembler, beginning with a comment containing its identifier and original line number
  • Calculation and limitation of maximum depth of stack and count of local variables during compilation
  • Type-checking right after parsing
  • Helpful error messages, if applicable with exact line and position in the sourcecode (for parsing- and type-checking-errors)
  • Optional liveness-analysis per function
    • detection of unused arguments
    • detection of unused variable declarations
    • detection of unused variable values (limitation: false alerts in loops, if a variable is read in the loop's condition)
    • determination of required number of registers

Project Setup

In this section, we describe how to setup this project locally.

VSCode using Dev-Container

If you use VSCode, you should use our dev-container. It sets up a working environment with all dependencies for you.

Other IDEs without a Dev-Container

Install java 17 and gradle 1.7. Simply run gradle build to install most dependencies.

Jasmin is not available on MavenCentral yet. Please download Jasmin 2.4 from SourceForge and put the jar into /libs.

Build the Easy Compiler

We use Gradle for dependency-management, building and testing the compiler. Simply run gradle build to create a runnable jar at /build/libs/EasyCompiler.jar.

Use the Easy Compiler

Gradle creates the EasyCompiler.jar which compiles an .easy file into a .j file containing Jasmin assembler code. In the following example, we describe how to compile an Easy program into Java bytecode.

  1. Create the file hello_world.easy at root and copy the following program.
    none <- main() {
      println("Hello world!");
    }
    
  2. Run the Easy compiler with java -jar build/libs/EasyCompiler.jar -compile hello_world.easy. It creates hello_world.j which contains a Jasmin assembler representation of our Easy code.
  3. Run java -jar libs/jasmin.jar hello_world.j to transform hello_world.j into hello_world.class, which contains Java bytecode.
  4. Finally, run java hello_world to start the compiled program, which should output Hello world!.

Testing

We test analyses and other testable features using input files, and test code generation by running the Easy Compiler and running the compiled programs on the JVM.

It was planned to use a mocked filesystem for generated Jasmin-files and class-files during tests. Unfortunately we cannot call jasmin.jar and the produced class-files via Runtime.getRuntime.exec, as the command starts a new process. An in-memory filesystem would work great, if we could use all functions in one process.

As a trade-off, gradle deletes the generated files after running the tests.