E Compiler is a program that transforms computer code, written in the E programming language, into bytecode by using ANTLR and Jasmin for reasons of optimization as well as a lower overhead.
- Variables:
int a;
andfloat a = 5.3;
- Operators:
a + b
,a <= b
,a && b
, anda >> b
- Outputs:
print("Hello World.");
- Branches:
if (x) { a = 5; } else { a = 7; }
- Loops:
int i = 0; while (i < 3) { println(i); i = i + 1; }
- Functions:
void foo(int a, int b) { print(a * b); }
andfoo(7, 8);
- Built-in functions:
toInt(3.7);
- Arrays:
int[] a = new int[5];
- Structures:
struct Point { int x; int y; }
andPoint p = new Point(1, 2);
- Macros:
#define noMain
- Imports:
use(e.io.reader)
andString r = reader.read(): String;
- Inline assembler:
invoke "static" "java/lang/System/nanoTime"() "J";
- And much more
Since Jasmin doesn't exist as an official Maven repository, it must be installed locally. Download the latest version and move it to the root directory of this cloned repository:
$ mkdir lib
$ cd lib
$ mvn install:install-file -Dfile=jasmin.jar -DgroupId=net.sourceforge -DartifactId=jasmin -Dversion=2.4.0 -Dpackaging=jar
Then type mvn install
in the terminal to create and install the appropriate JAR file. The target directory now contains two JARs, with the dependencies embedded in one JAR.
The file main.e contains the most used features of E and demonstrates the calculation of a distance between two points.
Just type java -cp target/e_compiler-0.0.1-SNAPSHOT-jar-with-dependencies.jar com.runekrauss.compiler.Main
to compile and run this file. The output should be 2
.
Generally, the created JAR file can be executed by java -jar *.jar
.
Let's assume the following code exists:
print(3 + 2 * 4);
The corresponding abstract syntax tree, generated by ANTLR, looks like this:
graph TB
A[program]---B[command]
B---C[statement]
B---D[";"]
C---E[print]
E---F["print("]
E---G[expression]
E---H[")"]
G---I[expression]
G---J[+]
G---K[expression]
I---L[3]
K---M[expression]
K---N[*]
K---O[expression]
M---P[2]
O---Q[4]
Subsequently, the following assembler-like instructions are generated by post-order traversal:
.class public e_test_main
.super java/lang/Object
.method public static main([Ljava/lang/String;)V
.limit stack 4
.limit locals 1
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc 3
ldc 2
ldc 4
imul
iadd
invokevirtual java/io/PrintStream/print(I)V
return
.end method
Now the code can be translated into a class file using Jasmin. Then, it can be interpreted and executed in the JVM. This process is also automated in the main program.
To work with this project, it can be imported into the IDE IntelliJ IDEA.
Let's assume that a rule test should be implemented. When the grammar is changed, the code must be modified:
$ cd grammar
$ antlr -package com.runekrauss.parser -o ../src/main/java/com/runekrauss/parser/ -no-listener -visitor E.g4
Now look into the EBaseVisitor
class, where you will find a method called visitTest
:
public T visitTest(EParser.TestContext ctx) { return visitChildren(ctx); }
This method must be overwritten by the EVisitor
class with the appropriate actions to traverse the tree. To view the corresponding tree, you can use the TestRig tool as follows:
$ cd target
$ java -cp classes:../lib/antlr.jar org.antlr.v4.gui.TestRig com.runekrauss.parser.E program -gui ../e/test/main.e
To test the code, the array inside the method provideCodeExpectedOutput
of the class CompilerTest
must be extended.
To run the unit tests, there is the command mvn test
.
The E Compiler documentation becomes available by typing the following commands in the terminal:
$ cd java
$ javadoc -d doc com.runekrauss.compiler
E Compiler is licensed under the terms of the MIT license.
Special thanks to Berthold Hoffmann for supporting this project.