Skip to content

indocomsoft/jlitec

Repository files navigation

JLite Compiler

Requirements

  • Java 14
  • Bash shell
  • Connection to the internet

This project uses the gradle build system, which can self-bootstrap via gradle/wrapper/graddle-wrapper.jar. As such, all the Makefile does is invoke gradle to build the jar artifact. If gradle has not been set up before, the self-bootstrapping process will download all the requirements, freeing you from having to install anything besides Java 14.

Java 14 Preview Features

This project uses Java 14 features, so you will need to pass --enable-preview if you are running the jar file yourself.

This should be handled by the ./jlitec wrapper script.

How to compile

In your shell, run:

$ make

This actually simply invokes:

$ ./gradlew jar

How to run

In your shell, run:

$ ./jlitec

I recommend trying out list.j, which is an interactive program to manipulate a linked-list. Assuming you have installed GCC ARM cross-compiler and QEMU user binfmt, simply run:

./jlitec arm -O3 test/arm/list.j > /tmp/a.s
arm-linux-gnueabi-gcc /tmp/a.s -o /tmp/a.out --static
/tmp/a.out

Subcommands

To get the subcommands and options available, run:

$ ./jlitec --help

You can also get the details of a particular subcommand, for example:

$ ./jlitec reg --help

Compile to ARM assembly

Use arm to generate ARM assembly. It also takes an optimization flag -O:

  • -O0 is used if not specified, which disables any optimization.
  • -O2 for optimizations on the Lower intermediate representation
  • -O3 for additional peephole optimizations on the generated ARM assembly.

For example:

 $ ./jlitec arm -O3 test/arm/gcd_fixed.j
.cpu cortex-a7
.global main
.type main, %function
main:
STMFD SP!, {R4, LR}
LDR R0, .S0
BL puts
MOV R1, #60
MOV R2, #50
BL Gcd_0
MOV R1, R0
LDR R0, .S1
BL printf
MOV R0, #0
LDMFD SP!, {R4, PC}
.global Gcd_0
.type Gcd_0, %function
Gcd_0:
L1:
CMP R1, R2
BEQ L2
CMP R1, R2
BLE L3
SUB R1, R1, R2
B L1
L3:
SUB R2, R2, R1
B L1
L2:
MOV R0, R1
BX LR
.S0:
.word .S0S
.S1:
.word .S1S
.section .rodata
.S0S:
.asciz "The GCD of 60 and 50 is:"
.section .rodata
.S1S:
.asciz "%d\n"

Lower Intermediate Code

Use lower to generate Lower intermediate code. You can also use the flag --opt to generate Lower intermediate code after optimization.

For example:

$ ./jlitec lower --opt test/arm/gcd_fixed.j
======= Data =======
class Main {
}

class Gcd {
}

======= Methods =======
Void main() {
  // START OF SPILLED TO STACK
  // END OF SPILLED TO STACK
  Int a;
  Int b;
  Gcd _t1;
  Int _t2;
  R0 = "The GCD of 60 and 50 is:";
  CALL puts;
  R1 = 60;
  R2 = 50;
  CALL %Gcd_0;
  _t2 <- R0;
  R0 = "%d\n";
  R1 <- _t2;
  CALL printf;
  return;
}

Int Gcd_0(Gcd this, Int a, Int b) {
  // START OF SPILLED TO STACK
  // END OF SPILLED TO STACK
  a <- R1;
  b <- R2;
L1:
  if (a == b) goto L2;
  if (a <= b) goto L3;
  a = a - b;
  goto L1;
L3:
  b = b - a;
  goto L1;
L2:
  R0 <- a;
  return;
}

Static checks

Use check to perform static check (distinct-name checking and type checking). For example:

$ ./jlitec check test/arm/gcd_fixed.j
Static check succeeded!

Or in the case of failure:

$ ./jlitec check test/type/fail/overload.j
--> test/type/fail/overload.j:4:5
Semantic error: Call with signature `f(null)' on class `C' is ambiguous. Possible method overloads: f(B), f(C)
2 |   Void main(){
3 |     C c;
4 |     c.f(null);
  | ~~~~^^^ ambiguous method call `f(null)'
5 |     return;
6 |   }

IR3 Intermediate Code

Use ir3 to generate the IR3 intermediate code. IR3 is actually not really used much in the compiler and what you probably want is the Lower intermediate code.

For example:

$ ./jlitec ir3 test/arm/gcd_fixed.j
======= CData3 =======
class Main {
}

class Gcd {
}

======= CMtd3 =======
Void main(Main this) {
  Int a;
  Int b;
  Gcd _t1;
  Int _t2;
  a = 60;
  b = 50;
  println("The GCD of 60 and 50 is:");
  _t1 = new Gcd();
  _t2 = %Gcd_0(_t1, a, b);
  println(_t2);
}

Int %Gcd_0(Gcd this, Int a, Int b) {
L1:
  if (a == b) goto L2;
  if (a <= b) goto L3;
  a = a - b;
  goto L4;
L3:
  b = b - a;
L4:
  goto L1;
L2:
  return a;
}

Pretty printing

To pretty print a JLite file, you can use the pretty_print subcommand. For example:

$ ./jlitec pretty_print test/arm/gcd_fixed.j
class Main {
  Void main() {
    Int a;
    Int b;
    a = 60;
    b = 50;
    println("The GCD of 60 and 50 is:");
    println(new Gcd().gcd(a, b));
  }
}
class Gcd {
  Int gcd(Int a, Int b) {
    while (a != b) {
      if (a > b) {
        a = a - b;
      } else {
        b = b - a;
      }
    }
    return a;
  }
}

There are 2 other subcommands available that you may find useful:

  • lex to print out the CUP symbols generated by the lexer.
  • parse_tree to print out the parse tree in JSON format.