The lox language interpreter written in Golang.
If you have Go installed, you can build the project with:
go build .
Or you can use make to build the project:
make build
This branch adds a few "unoriginal" features to the original Lox implementation.
Unlike original Lox which considered function definitions as statements, this branch considers function definitions as a expression that resolves to function itself (instance of LoxFunction in runtime). This allows directly providing function as a value to another function.
fun call(callback) {
return callback();
}
call(fun greet() { // Here we can directly declare a function as a value
return "Hello";
});
This is not a huge thing, but it is a nice feature that allows to write functions without name which could be used as lambdas for calling another functions. For example we can write previous code like that:
fun call(callback) {
return callback();
}
call(fun () { // We don't have to provide a name for the function
return "Hello";
});
I also modified the resolver to detect some edge cases like declaring nameless functions as a class method.
With modified lox for statements are not de-sugarized into a while
statement. This is done to allow for writing more complex loops and
supporting break
and continue
statements.
Now you can use break
and continue
statements in loops - both while
and for loops.
Syntax is simple as below:
while (true) {
if (condition) {
break;
}
}
Lox now supports including files. This is useful for writing libraries that can be used in other projects.
include "lib/math.lox";
Include statement can be chained with other statements like if
statement.
That's useful when you want to include files only when some condition is
met.
if (isWindows) {
include "lib/windows.lox";
} else {
include "lib/unix.lox";
}
I'm planning to add more features to the language and to the interpreter. Here is a list of things I want to add:
- Support for
namespace
blocks - Array literals
- Map literals, maybe
- Add more operations to standard library
-
Foreign function calls to dynamic libraries
These are notes I took during reading the book that helped me to write the code.
Here's Lox AST node grammar. Those are simple notations that contains everything needed for implementing parser for given statements.
program → declaration* EOF ;
declaration → classDecl
| funDecl
| varDecl
| statement ;
classDecl → "class" IDENTIFIER ( "<" IDENTIFIER )?
"{" function* "}" ;
funDecl → "fun" function ;
function → IDENTIFIER "(" parameters? ")" block ;
parameters → IDENTIFIER ( "," IDENTIFIER )* ;
statement → exprStmt
| forStmt
| ifStmt
| printStmt
| returnStmt
| whileStmt
| breakStmt
| continueStmt
| includeStmt
| block ;
includeStmt → "include" STRING ";" ;
returnStmt → "return" expression? ";" ;
forStmt → "for" "(" ( varDecl | exprStmt | ";" )
expression? ";"
expression? ")" statement ;
whileStmt → "while" "(" expression ")" statement ;
ifStmt → "if" "(" expression ")" statement
( "else" statement )? ;
block → "{" declaration* "}" ;
exprStmt → expression ";" ;
printStmt → "print" expression ";" ;
varDecl → "var" IDENTIFIER ( "=" expression )? ";" ;
expression → assignment ;
assignment → ( call "." )? IDENTIFIER "=" assignment
| logic_or ;
logic_or → logic_and ( "or" logic_and )* ;
logic_and → equality ( "and" equality )* ;
equality → comparison ( ( "!=" | "==" ) comparison )* ;
comparison → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
term → factor ( ( "-" | "+" ) factor )* ;
factor → unary ( ( "/" | "*" ) unary )* ;
unary → ( "!" | "-" ) unary | call ;
call → primary ( "(" arguments? ")" | "." IDENTIFIER )* ;
arguments → expression ( "," expression )* ;
primary → "true" | "false" | "nil" | "this"
| NUMBER | STRING | IDENTIFIER | "(" expression ")"
| "super" "." IDENTIFIER
| funDecl ;
And the body of the rule translates to code roughly like:
Grammar notation | Code representation |
---|---|
Terminal | Code to match and consume a token |
Nonterminal | Call to that rule’s function |
| |
if or switch statement |
* or + |
while or for loop |
? |
if statement |