glox started as a Go-based interpreter for Lox inspired by @munificent, but it has evolved into a similar yet noticeably distinct language. I highly reccomend checking out the book to learn more about Lox and how my implementation is different!
glox supports first-class functions, lists, classes (with static and instance methods), and extended flow control. It runs on a tree-walk interpreter written entirely in Go, using no external dependencies. The project demonstrates how a small language can grow beyond its origins into something unique.
- "#" for single-line comments and "#[ ... ]#" for block comments
- "not" replaces "!" for logical negation
- If and loop expressions require braces around the body, but do not require parentheses around the conditions
- Only "false" is falsey
- Supports prefix and postfix ++ and --
- Supports "+=", "-=", "*=", and "/=" operators
- Comma operator evaluates left to right and yields the last value
- Supports dynamic lists
- Supports "cycle" (continue) and "break" statements
- Classes support static methods via "class" keyword inside the class body
- Resolver enforces strong semantic checks for early error detection
var x = 10;
x += 2;
print x; # 12
# Single line comment
#[
This is a block comment.
It can span multiple lines.
]#
if x > 10 {
print "big";
} else {
print "small";
}
while x < 20 {
x++;
}
for var i = 0; i < 5; i++ {
if i == 2 { cycle; }
print i;
}
var xs = [1, 2, 3];
append(xs, 4);
print len(xs); # 4
print xs[2]; # 3
xs[0] = 99;
fun greet(name) {
print "Hello, " + name;
}
# Glox also supports anonymous functions:
var add = fun(a, b) {
return a + b;
};
print add(2, 3); # 5
class Vec {
init() {
this.lst = [];
}
class from(lst) {
var vec = Vec();
for var i = 0; i < len(lst); i++ {
vec.push(lst[i]);
}
return vec;
}
get(i) {
return this.lst[i];
}
len() {
return len(this.lst);
}
empty() {
return this.len() == 0;
}
toList() {
return this.lst;
}
map(fn) {
var out = Vec();
for var i = 0; i < this.len(); i++ {
var x = this.get(i);
out.push(fn(x));
}
return out;
}
filter(fn) {
var out = Vec();
for var i = 0; i < this.len(); i++ {
var x = this.get(i);
if fn(x) {
out.push(x);
}
}
return out;
}
pprint() {
print this.lst;
}
# Mutation methods
set(i, x) {
this.lst[i] = x;
}
push(x) {
this.lst = append(this.lst, x);
}
append(xs) {
if type(xs) == "list" {
for var i = 0; i < len(xs); i++ {
this.push(xs[i]);
}
} else if type(xs) == "Vec" {
for var i = 0; i < xs.len(); i++ {
this.push(xs.get(i));
}
} else {
print "Invalid type: " + type(xs);
print "Did you mean to call push()?";
}
}
}
var vec = Vec.from([1, 4, 9, 16]);
vec.map(fun(x) { return x * x; })
.filter(fun(x) { return x > 5 and x < 1024; })
.pprint(); # prints [16, 81, 256]
clock() - returns seconds since Unix epoch as a float
len(list) - returns the number of elements in a list
append(list, val) - returns a new list with val appended at the end
type(obj) - returns the type of an object in string format
Build:
git clone https://github.com/jmann345/glox
cd glox
go build -o glox .
Run a script:
./glox path/to/script.lox
Start the REPL:
./glox
[1] print "hello world";
hello world