Implementation of the Lox language in OCaml, comprised of a front-end compiler and an interpreter, in roughly 1KLOC !
Big thanks to Bob Nystrom!! You're amazing.
No further work is expected to happen on this project as it is feature-complete. The implementation is not optimal but still useful as a learning material.
Assuming ocaml
(version 4.08.1
) and opam
are installed and setup:
$ opam install .
$ lox run test/hello_world.lox
hello, world
$ printf 'print 4 * 3 - 1;' | lox run
11
$ lox repl
> 1+1;
2.
> ^C
# The last field of an AST node is an internal unique id
$ echo "print 1 + 2;" | lox dump ast
((Print (Binary (Literal (Number 1) 1) Plus (Literal (Number 2) 2) 3) 4))
- Numbers, strings, booleans, nil
- Comments
- Number arithmetic
- Statements & print
- Variables (scope based)
- Conditions (if/else)
- Boolean logic (and, or, not)
- Loops (while, for)
- All errors are reported with line and column number
- Functions (including recursion, closures)
- Object-oriented programming (classes, single inheritance)
- Shebang
- "Native" functions:
- clock()
- readLine()
- parseNumber()
- Basic static analysis:
- Return statement outside of a function body e.g
return 1;
- Variables assigned to themselves e.g
var a = a;
- Same-scope variable shadowing e.g
var a = 1; var a = 2;
- Unused variables, functions, and function arguments
- Return statement outside of a function body e.g
Example:
fun fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 2) + fibonacci(n - 1);
}
for (var i = 0; i < 20; i = i + 1) {
print fibonacci(i);
}
Example of objects:
class Breakfast {}
class BreakfastBuilder {
init() {
this.breakfast = Breakfast();
}
withEggs(eggNumber) {
this.breakfast.eggNumber = eggNumber;
return this;
}
withToasts(toastNumber) {
this.breakfast.toastNumber = toastNumber;
return this;
}
build() {
return this.breakfast;
}
}
var breakfast = BreakfastBuilder().withEggs(2).withToasts(5).build();
print breakfast.eggNumber;
print breakfast.toastNumber;
Requires GNU parallel
make test
You can opam install patdiff
for a better output, by default it uses diff
.
$ make docker
$ docker run -it lox
lox repl
works out of the box with the GNU Readline library if you have it installed:
rlwrap lox repl
will provide command history for free.
- Variable resolution does not distinguish between local/global variables. In practices it means that mutally recursive functions in the global scope are not allowed
- Stricter static analysis, e.g referring to undefined variables (will fail at compile time in some cases instead of at runtime)