This repository implements a small expression language compiler that emits LLVM IR. It uses Jison for parsing/lexing.
The language is not very different than the Dragon language described in https://github.com/ULL-ESIT-PL/dragon2js (the one we have used for the labs "Competenciales") but it is smaller. It still supports:
- basic types:
int(mapped toi32),real(mapped todouble), andbool(mapped toi1) - explicit declarations (
int a,real x,bool b,int[3] v, etc.) - assignments (
a[0] = e) and arithmetic (+,-,*,/) - comparison operators (
<,<=,>,>=,==,!=) returning booleans - logical operators (
&&,||) on booleans - unary minus
- comma operator
- explicit printing with
PRINT(e)/print(e) - arrays with indexing
- block scopes with lexical shadowing
npm installInstall llvm and make sure lli is in your PATH for executing generated IR.
npm run buildEquivalent command:
npx jison src/calc.jison src/calc.l -o src/calc.jsnpm testInput modes:
- From file:
bin/use_calc.js path/to/input.calc - Inline:
bin/use_calc.js --expression 'int a; a = 3, a + 1'
By default the generated LLVM IR is printed to stdout.
Useful options:
--output path/to/output.llwrites IR to file--verboseprints IR even when--outputis used
Scalars and arrays must be declared before use. Supported types are int, real, and bool.
int a
real x
bool b
int[3] v
int[2][3] m
Top-level programs are either:
decls ; expressionexpression
Inside blocks, declarations must appear at the beginning:
{ int b; b = 4, print(b) }
- binary:
+ - * /(onintandrealwith automatic type promotion) - unary:
-e(onint,real, orbool)
< <= > >= == !=(onintandreal)
&& ||
- assignment:
loc = e - comma:
e1, e2(returns value/type ofe2)
print(e) is an expression that prints e and returns it.
If the whole program has no explicit print, the compiler emits an implicit print of the final expression.
Declared variables are emitted as local stack slots (alloca) in @main.
- scalar example:
%.a.addr = alloca i32 - array example:
%.v.addr = alloca [3 x i32] - identifier
mainis safely emitted as%.main.addr(no collision with function@main)
bin/use_calc.js --expression '3-2-1.0' | lli
0.000000Representative IR:
declare i32 @printf(i8*, ...)
@.str.i32 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
@.str.double = private unnamed_addr constant [4 x i8] c"%f\0A\00", align 1
define i32 @main() {
%1 = sub i32 3, 2
%2 = sitofp i32 %1 to double
%3 = fsub double %2, 1.0
%4 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.double, i32 0, i32 0), double %3)
ret i32 0
}bin/use_calc.js --expression 'int a; int b; a = 5, b = 3 + a' | lli
8Representative IR:
define i32 @main() {
%.a.addr = alloca i32
%.b.addr = alloca i32
store i32 5, i32* %.a.addr
%1 = load i32, i32* %.a.addr
%2 = add i32 3, %1
store i32 %2, i32* %.b.addr
%3 = call i32 (i8*, ...) @printf(..., i32 %2)
ret i32 %2
}bin/use_calc.js --expression 'int a; a = 1, { int a; a = 2, print(a) }, print(a)' | lli
2
1bin/use_calc.js --expression 'int[3] a; a[1] = 42, a[1]' | lli
42Representative IR pattern:
%.a.addr = alloca [3 x i32]
%1 = getelementptr inbounds [3 x i32], [3 x i32]* %.a.addr, i32 0, i32 1
store i32 42, i32* %1
%2 = getelementptr inbounds [3 x i32], [3 x i32]* %.a.addr, i32 0, i32 1
%3 = load i32, i32* %2bin/use_calc.js --expression '5 > 3' | lli
1bin/use_calc.js --expression 'bool a; a = (10 <= 20), a && true' | lli
1Representative IR for comparison:
%1 = icmp sgt i32 5, 3
%2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.i32, i32 0, i32 0), i32 %1)
ret i32 %1bin/use_calc.js --expression 'true, false' | lli
0Boolean literals true and false are mapped to i1 in LLVM.
Undeclared variable:
bin/use_calc.js --expression 'a = 1'Type mismatch:
bin/use_calc.js --expression 'int a; a = 1, a = 2.5'Indexing scalar:
bin/use_calc.js --expression 'int a; a = 1, a[0]'bin/use_calc.js --expression 'int a; a = -2' | lli
-2Save IR and execute:
bin/use_calc.js examples/input.calc --output tmp/salida.ll
lli tmp/salida.llCompile (links printf from libc):
clang tmp/salida.ll -o tmp/salida
./tmp/salidaIf needed:
clang tmp/salida.ll -o tmp/salida -Wno-override-module- Go to https://godbolt.org/
- Select
LLVM IRas language. - Paste generated IR.
- Select clang and compile.
- (Optional) enable execution and inspect output.
Go to our tutorial: Hello LLVM
