Skip to content

A single-pass compiler for a toy language with custom byte code intermediate representation and a stack-based virtual machine.

Notifications You must be signed in to change notification settings

andreev-io/Monkey-Compiler

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

28 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Monkey Compiler in Rust

This project is a single-pass custom bytecode compiler and a stack-based virtual machine that runs code written in Monkey. Under the hood, the parser uses Pratt parsing.

The language supports first-class functions (with arguments), anonymous functions, closures, return statements, if clauses, strings, integers, booleans, and arrays.

Run cargo run --release to bring up a REPL.

The following are examples of some amazing Monkey programs:

>>> let fibonacci = fn(x) {
        if (x == 0) { 
            0
        } else {
            if (x == 1) {
                return 1;
            } else {
                fibonacci(x - 1) + fibonacci(x - 2);
            }
        } 
};

fibonacci(35);
>>> let wrapper = fn() {
        let countDown = fn(x) {
            if (x == 0) {
                return 0;
            } else {
                countDown(x - 1);
            } 
        };
        countDown(1);
};
wrapper();

Arrays and indexing:

>>> let x = [1, 2, fn(x) { x }(5)]; 
>>> x[2]

5

Nulls and truthy values:

>>> !!fn() {}();
false

>>> !!"hi"
true

Strings:

>>> let x = "good";
>>> let y = "bye";
>>> let goodbye = fn(x, y) { if (x+y == "goodbye") { 1 } else { 0 }};
>>> goodbye(x, y)

1

As shown above, the language supports anonymous functions and function literals. The language doesn't have garbage collection and relies on Rust's ownership semantics for memory management.

As far as the virtual machine goes, it correctly cleans up the call frame whenever a closure is exited.

>>> let v = [1, 2, 3]; 

let superfunc = fn(a) { 
    let b = a; 
    b[0] + b[1] 
}; 

3 == superfunc(v)

When superfunc is called, b is created by cloning the array a, but is deallocated when superfunc returns.

let factorial = fn(x) {
    if (x == 0) {
        1
    } else {
        x * factorial(x-1)
    }
}

factorial(5)

Note that we could've allocated anything within the recursively called function, and the values would be successfully deallocated on return.

About

A single-pass compiler for a toy language with custom byte code intermediate representation and a stack-based virtual machine.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages