Home

Intro

The language is work-in-progress, imperative and targets the amd64 instruction set. The compiler sits at around 2000 SLOC plus 500 lines of tests. Its feature set is only a slim percentage of what it ought to be.

I'm currently in the process of rewriting the compiler in C, which might sound like a step backward, but I have some confidence that in the long run it'll be the right choice.

Compile Time Code Execution

Internally the compiler generates bytecode fairly early. The coolest optimization that is already in place is to compile-time execute instructions that don't rely on the "outside world" (e.g. syscalls, out/in instructions, etc.). Instructions that necessitate runtime execution are kept, and those that don't are seeved away and executed. For example, an if statement relies on the existence of a file. It's normal to assume that someone that calls file-open() wants to delay that to runtime. So, unless specified, the compiler won't execute that function at compile time.

All chunks of code that do not rely on user inputs can then be executed and "folded" into their result:

SourceBefore compile-time executionAfter compile-time execution
a = (1 + 4) / 3 + 1
b = a / 1
c = b - 2
"zero: {}" % c
    
mov r0 1
mov r1 4
mov r2 r0
add r2 r1
mov r3 3
mov r4 r2
div r4 r3
mov r5 1
mov r6 r4
add r6 r5
mov r7 1
mov r8 r6
div r8 r7
mov r9 2
mov r10 r8
sub r10 r9
mov r11 &"zero: {}"
mov r13 INT
call TEMPLATE_STR
call PRINT
    
mov r0 &"zero: 0"
call PRINT
    

Register Allocation

The somewhat naive strategy that I'm employing is to forego strictly sequencial instruction generation and the start generating blocks of code that are in the deepest loops. That way, spilling on each iteration is avoided. However, I'm not sure about the benifits because it makes instruction generation that much more complicated, by having to insert code before and after chuncks.

total = 0               # <-- this block is generated last
for i in 0..|10
    "i: {}" % i
    acc = 0             # <-- this block is generated second
    for j in 0..|10
        acc += j        # <-- this block is generated first
    "j x i: {}" % acc
    total += acc
"total: {}" % total

Syntax

Kind of like shell languages, an expression on a line implies call to print:
0.375 * 1.2
f = file-open("todos.txt", "w")
file-append(f, "- Do the thing")
file-close(f)
user = prompt("username: ")
msgnumber = 4
"Hello {}, you have {} messages" % user, msgnumber
0.44999999999999996
true
Hello lenny, you have 4 messages