monkey-lang
Monkey programming language interpreter designed in Writing An Interpreter In Go.
A step-by-step walk-through where each commit is a fully working part.
Read the book and follow along with the commit history.
Status
Still working on a self-hosted Monkey lang (Monkey written in Monkey).
Read and Follow
Read the books and follow along with the following commit history.
(This also happens to be the elapsed days I took to read both books!)
See: Reading Guide
Please note that whilst reading the awesome books I slihtly modified this
version of Monkey-lang in some places. FOr example I opted to have a single
RETURN
Opcode.
Quickstart
$ go get github.com/prologic/monkey-lang/cmd/monkey-lang
$ monkey-lang
Development
To build run make
.
$ go get github.com/prologic/monkey-lang
$ cd $GOPATH/github.com/prologic/monkey-lang
$ make
This is the Monkey programming language!
Feel free to type in commands
>>
To run the tests run make test
You can also execute program files by invoking monkey-lang <filename>
There are also some command-line options:
$ ./monkey-lang -h
Usage: monkey-lang [options] [<filename>]
-c compile input to bytecode
-d enable debug mode
-e string
engine to use (eval or vm) (default "vm")
-i enable interactive mode
-v display version information
Monkey Language
See also: examples
Programs
A Monkey program is simply zero or more statements. Statements don't actually
have to be separated by newlines, only by whitespace. The following is a valid
program (but you'd probably use newlines in theif
block in real life):
s := "world"
print("Hello, " + s)
if (s != "") { t := "The end" print(t) }
// Hello, world
// The end
Between tokens, whitespace and comments
(lines starting with //
or #
through to the end of a line)
are ignored.
Types
Monkey has the following data types: null
, bool
, int
, str
, array
,
hash
, and fn
. The int
type is a signed 64-bit integer, strings are
immutable arrays of bytes, arrays are growable arrays
(use the append()
builtin), and hashes are unordered hash maps.
Trailing commas are NOT allowed after the last element in an array or hash:
Type |
Syntax |
Comments |
null |
null |
|
bool |
true false |
|
int |
0 42 1234 -5 |
-5 is actually 5 with unary - |
str |
"" "foo" "\"quotes\" and a\nline break" |
Escapes: \" \\ \t \r \n \t \xXX |
array |
[] [1, 2] [1, 2, 3] |
|
hash |
{} {"a": 1} {"a": 1, "b": 2} |
|
Variable Bindings
>> a := 10
Artithmetic Expressions
>> a := 10
>> b := a * 2
>> (a + b) / 2 - 3
12
Conditional Expressions
Monkey supports if
and else
:
>> a := 10
>> b := a * 2
>> c := if (b > a) { 99 } else { 100 }
>> c
99
Monkey also supports else if
:
>> test := fn(n) { if (n % 15 == 0) { return "FizzBuzz" } else if (n % 5 == 0) { return "Buzz" } else if (n % 3 == 0) { return "Fizz" } else { return str(n) } }
>> test(1)
"1"
>> test(3)
"Fizz"
>> test(5)
"Buzz"
>> test(15)
"FizzBuzz"
While Loops
Monkey supports only one looping construct, the while
loop:
i := 3
while (i > 0) {
print(i)
i = i - 1
}
// 3
// 2
// 1
Monkey does not have break
or continue
, but you can return <value>
as
one way of breaking out of a loop early inside a function.
Functions and Closures
You can define named or anonymous functions, including functions inside
functions that reference outer variables (closures).
>> multiply := fn(x, y) { x * y }
>> multiply(50 / 2, 1 * 2)
50
>> fn(x) { x + 10 }(10)
20
>> newAdder := fn(x) { fn(y) { x + y } }
>> addTwo := newAdder(2)
>> addTwo(3)
5
>> sub := fn(a, b) { a - b }
>> applyFunc := fn(a, b, func) { func(a, b) }
>> applyFunc(10, 2, sub)
8
NOTE: You cannot have a "bare return" -- it requires a return value.
So if you don't want to return anything
(functions always return at least null
anyway),
just say return null
.
Recursive Functions
Monkey also supports recursive functions including recursive functions defined
in the scope of another function (self-recursion).
>> wrapper := fn() { inner := fn(x) { if (x == 0) { return 2 } else { return inner(x - 1) } } return inner(1) }
>> wrapper()
2
Monkey also does tail
call optimization and turns recursive tail-calls into iteration.
>> fib := fn(n, a, b) { if (n == 0) { return a } if (n == 1) { return b } return fib(n - 1, b, a + b) }
>> fib(35, 0, 1)
9227465
Strings
>> makeGreeter := fn(greeting) { fn(name) { greeting + " " + name + "!" } }
>> hello := makeGreeter("Hello")
>> hello("skatsuta")
Hello skatsuta!
Arrays
>> myArray := ["Thorsten", "Ball", 28, fn(x) { x * x }]
>> myArray[0]
Thorsten
>> myArray[4 - 2]
28
>> myArray[3](2)
4
Hashes
>> myHash := {"name": "Jimmy", "age": 72, true: "yes, a boolean", 99: "correct, an integer"}
>> myHash["name"]
Jimmy
>> myHash["age"]
72
>> myHash[true]
yes, a boolean
>> myHash[99]
correct, an integer
Assignment Expressions
Assignment can assign to a name, an array element by index, or a hash value by key.
When assigning to a name (variable), it always assigns to the scope the variable was defined .
To help with object-oriented programming, obj.foo = bar
is syntactic sugar for obj["foo"] = bar
. They're exactly equivalent.
i := 1
func mutate() {
i = 2
print(i)
}
print(i)
mutate()
print(i)
// 1
// 2
// 2
map = {"a": 1}
func mutate() {
map.a = 2
print(map.a)
}
print(map.a)
mutate()
print(map.a)
// 1
// 2
// 2
lst := [0, 1, 2]
lst[1] = "one"
print(lst)
// [0, "one", 2]
map = {"a": 1, "b": 2}
map["a"] = 3
map.c = 4
print(map)
// {"a": 3, "b": 2, "c": 4}
Binary and unary operators
Monkey supports pretty standard binary and unary operators.
Here they are with their precedence, from highest to lowest
(operators of the same precedence evaluate left to right):
Operators |
Description |
[] obj.keu |
Subscript |
- |
Unary minus |
* / % |
Multiplication, Division, Modulo |
+ - |
Addition, Subtraction |
< <= > >= in |
Comparison |
== != |
Equality |
~ |
Bitwise not |
& |
Bitwise and |
| |
Bitwise or |
|| |
Logical or (short-circuit) |
&& |
Logical and (short-circuit) |
! |
Logical not |
Several of the operators are overloaded. Here are the types they can operate on:
Operator |
Types |
Action |
[] |
str[int] |
fetch nth byte of str (0-based) |
[] |
array[int] |
fetch nth element of array (0-based) |
[] |
hash[str] |
fetch hash value by key str |
- |
int |
negate int |
* |
int * int |
multiply ints |
* |
str * int |
repeat str n times |
* |
int * str |
repeat str n times |
* |
array * int |
repeat array n times, give new array |
* |
int * array |
repeat array n times, give new array |
/ |
int / int |
divide ints, truncated |
% |
int % int |
divide ints, give remainder |
+ |
int + int |
add ints |
+ |
str + str |
concatenate strs, give new string |
+ |
array + array |
concatenate arrays, give new array |
+ |
hash + hash |
merge hashes into new hash, keys in right hash win |
- |
int - int |
subtract ints |
< |
int < int |
true iff left < right |
< |
str < str |
true iff left < right (lexicographical) |
< |
array < array |
true iff left < right (lexicographical, recursive) |
<= > >= |
same as < |
similar to < |
== |
any == any |
deep equality (always false if different type) |
!= |
any != any |
same as not == |
| |
int | int |
Bitwise or |
& |
int & int |
Bitwise and |
~ |
~int |
Bitwise not (1's complement) |
|| |
bool || bool |
true iff either true, right not evaluated if left true |
&& |
bool && bool |
true iff both true, right not evaluated if left false |
! |
!bool |
inverse of bool |
Builtin functions
len(iterable)
Returns the length of the iterable (str
, array
or hash
).
input([prompt])
Reads a line from standard input optionally printing prompt
.
print(value...)
Prints the value
(s) to standard output followed by a newline.
first(array)
Returns the first element of the array
.
last(array)
Returns the last element of the array
.
rest(array)
Returns a new array with the first element of array
removed.
push(array, value)
Returns a new array with value
pushed onto the end of array
.
pop(array)
Returns the last value of the array
or null
if empty.
exit([status])
Exits the program immediately with the optional status
or 0
.
assert(expr, [msg])
Exits the program immediately with a non-zero status if expr
is false
optionally displaying msg
to standard error.
bool(value)
Converts value
to a bool
. If value
is bool
returns the value directly.
Returns true
for non-zero int
(s), false
otherwise. Returns true
for
non-empty str
, array
and hash
values. Returns true
for all other
values except null
which always returns false
.
int(value)
Converts decimal value
str
to int
. If value
is invalid returns null. If
valueis an
int` returns its value directly.
str(value)
Returns the string representation of value
: null
for null,
true
or false
for bool
, decimal for int
(eg: 1234
),
the string itself for str
(not quoted),
the Monkey representation for array and hash (eg: [1, 2]
and {"a": 1}
with keys sorted), and something like <fn name(...) at 0x...>
for functions..
type(value)
Returns a str
denoting the type of value: nil
, bool
, int
, str
, array
, hash
, or fn
.
args()
Returns an array of command-line options passed to the program.
lower(str)
Returns a lowercased version of str
.
upper(str)
Returns an uppercased version of str
.
join(array, sep)
Concatenates str
s in arrat
to form a single str
, with the separator str
between each element.
split(str[, sep])
Splits the str
using given separator sep
, and returns the parts (excluding the separator) as an array
. If sep
is not given or null
, it splits on whitespace.
find(haystack, needle) Returns the index of
needle
strin
haystack
str, or the index of
needleelement in
haystack` array.
Returns -1 if not found.
read(filename)
Reads the contents of the file filename
and returns it as a str
.
write(filename, data)
Writes data
to a file filename
.
Coming soon...
sort(list[, func])
sorts the list in place using a stable sort, and returns nil. Elements in the list must be orderable with <
(int, str, or list of those). If a key function is provided, it must take the element as an argument and return an orderable value to use as the sort key.
Objects
>> Person := fn(name, age) { self := {} self.name = name self.age = age self.str = fn() { return self.name + ", aged " + str(self.age) } return self }
>> p := Person("John", 35)
>> p.str()
"John, aged 35"
License
This work is licensed under the terms of the MIT License.