Monkey
The programming language that lives in books
What is Monkey?
Monkey is a programming language that you can build yourself by reading through Writing An Interpreter In Go and Writing A Compiler In Go.
There is no official implementation of Monkey — it lives in these books and it's up to you, the reader, to implement it. First as a tree-walking interpreter, then as a bytecode compiler and virtual machine.
Here is what Monkey looks like:
// Integers & arithmetic expressions...
let version = 1 + (50 / 2) - (8 * 3);
// ... and strings
let name = "The Monkey programming language";
// ... booleans
let isMonkeyFastNow = true;
// ... arrays & hash maps
let people = [{"name": "Anna", "age": 24}, {"name": "Bob", "age": 99}];
It also has functions!
// User-defined functions...
let getName = fn(person) { person["name"]; };
getName(people[0]); // => "Anna"
getName(people[1]); // => "Bob"
// and built-in functions
puts(len(people)) // prints: 2
And it has conditionals, implicit and explicit returns and recursive functions, which means we can do this in Monkey:
let fibonacci = fn(x) {
if (x == 0) {
0
} else {
if (x == 1) {
return 1;
} else {
fibonacci(x - 1) + fibonacci(x - 2);
}
}
};
But the crown jewel in every Monkey implementation are closures:
// `newAdder` returns a closure that makes use of the free variables `a` and `b`:
let newAdder = fn(a, b) {
fn(c) { a + b + c };
};
// This constructs a new `adder` function:
let adder = newAdder(1, 2);
adder(8); // => 11
The Monkey Canon
Writing An Interpreter In Go was published in 2016, its latest version (1.6) was released in 2019.
The first book in the Monkey Canon defines the syntax of Monkey and describes its implementation as a tree-walking interpreter with the following features:
- Integers, booleans, strings, arrays, hash maps
- A REPL
- Arithmetic expressions
- Let statements
- First-class and higher-order functions
- Built-in functions
- Recursion
- Closures
The Lost Chapter: A Macro System For Monkey was published in 2017 as a free (to read online or download) addition to Writing An Interpreter In Go.
It can be thought of as Writing An Interpreter In Go's fifth chapter, since builds directly upon the previous four chapters and extends the Monkey interpreter as it stands at the end of the book.
The Lost Chapter: A Macro System For Monkey adds a fully-working Lisp-style macro system to Monkey, that's close to the way Elixir's macro system works.
Writing A Compiler In Go was published in 2018 and its latest version (1.1) in came out in 2019.
This book is the sequel to Writing An Interpreter In Go and while it does not change its syntax and does not add any features, it changes the implementation of Monkey from a tree-walking interpreter into a bytecode compiler and virtual machine.
At the end of the book, Monkey looks and behaves exactly as it did at the end of Writing An Interpreter In Go, except that it's 3x faster!
Monkeys In The Wild
Some readers love to take little detours when following along with the books: a different implementation language, new features, a little twist on the syntax.
And since the code that's presented in the books is MIT licensed, everybody's free to turn their Monkey implementation into whatever their imagination comes up with and showcase it to the rest of the world.
Collected here are some of those interesting and cool Monkey implementations readers have shared with me.
Did you also built your own version of Monkey? Add it to this list by opening a pull request here!
- An implementation of Monkey in Elixir
- RustRust implementation of Monkey programming language from the book Writing an Interpreter in Go. This interpreter is also compiled into WebAssembly format so it can run on browser.
- C++Mico (“Monkey” in catalan). Monkey language implementation done with C++. Including a ton of extensions:
- Modules
- Mutability
- For-loops
- Intervals
- GoA step-by-step walk-through where each commit is a fully working part.
- prologic's monkey-langGo
A Monkey implementation in Go that deviates from the book implementations by adding more built-in functions, objects, modules, more operators, assignments -- with the goal of building a self-hosting Monkey implementation.
- TypescriptA complete Monkey implementation in Typescript
- A full Monkey implementation of the bytecode compiler and VM from Writing A Compiler In Go in C#
- DartA fully working interpreter for the Monkey programming language as known from the book Writing an Interpreter in Go, written in Dart.
- RustMonkey programming language written in Rust. Including compilation to WASM and an online playground
- This repository started life as the implementation written by gaufung, but it has now diverged significantly in terms of both features and implementation. Including:
- File I/O
- A standard library
- Postfix operators
- Function argument defaults
- A Monkey implementation in ISO C11 with additional features:
- The
%
modulo operator - The logical
&&
and||
operators - While-loops
- A built-in
type()
function to determine the type of a Monkey value
- The
- Rust
- C#This repository contains a C# port of the Monkey programming language from the Writing an interpreter in Go book. Its code closely resembles that presented in the book, but written in idiomatic C# for the .NET Core runtime. Using the instructions below, the interpreter runs on both Windows, Mac, and Linux.
- Go
- Go
The geo programming language based on Monkey.
Special features:
- All functions are curried
- Scopes lives on blocks, no name clashes
- Pipe operator: the result of one expression becomes the last argument on a subsequent function call expression
- Unicode support
- Dart
- A Monkey implementation in C++ with its own mark-and-sweep garbage collector
- A Monkey implementation in C++ consistent with the book.
The SharpBASIC interpreter/compiler is based on Thorsten Ball's Monkey language. But SharpBASIC is quite different and should be thought of as sort of a BASIC/Pascal hybrid, but more powerful than BASIC and less verbose than Pascal.
The language is in its early stages. The interpreter works quite well and there are many working examples in the
/ex
folder.The main characteristic of SharpBASIC is that all definition blocks are marked by the keywords
OF..END
, while all execution blocks are marked by the keywordsDO..END
. Look at theCONST
,TYPES
,IF
,SELECT
,FOR
,WHILE
andFUNCTION
examples to see how block statements are defined.See the
info.txt
file contained in the archive for information on how to use SharpBASIC.- Noprianto's monkey.pyPython
A really neat single-file implementation of Monkey in Python with less than 2000 lines of code. It is compatible with Python 2 and Python 3, starting from Python 2.3. The tests are also in one file and can be found here in monkey_tests.py.
- Noprianto's Monkey.java/Monkey.jarJava
This is a simple implementation of the Monkey interpreter in Java. It is compatible with Java 5.0 or later (developed in Java 8, with -source 1.5 -target 1.5, compilation/run test in Java 5.0, 8, and 13). Released as single-file source code, the code is not beautiful, but should work. A compiled Java archive (Monkey.jar) is also available for those who don’t want to compile themselves. Monkey.jar file is also compatible with Java 5.0.
Need to run Monkey on older systems? Monkey.java can even be compiled in Java 5.0 running on Windows 95 (which was released in 1995). Monkey.jar (released version, compiled in Java 8) can also run on Windows 95. Running latest or released-25-years-ago operating systems? No problem, Monkey is always there.
- An implementation of Monkey in V
- Anthony Davis' langurGo
Langur is based on Monkey, but underwent many changes and has features you won't find in Monkey. Some of it's features include:
- arbitrary precision decimal floating point for numbers
- integer basex notation for any base from 2 to 36, such as
2x1010_0010
or-11x123A
- highly expressive
switch
expressions - semi-integrated regex
- ISO 8601 date/time literals
- duration literals
- automated for loop over a list, hash, or string, or over a range
- first-order functions, including closures
- immutable or mutable declarations
- chained string interpolation modifiers
- both truncating and floor division operators; also remainder and modulus, exponent and root
- optional database operators (null propagating)
- catching exceptions without explicit try blocks
Anthony Davis also wrote a book about how he implemented some of langur's features in a virtual machine that's similar to the one in Writing A Compiler In Go. You can find the book and a free sample at opcodebook.com.
- Interpreter for the Monkey programming language, in C. With the best name for a Monkey implementation yet.
- NimAn implementation of Monkey in Nim
Ape is an offspring of Monkey, evolved to be more procedural with variables, loops, and more.
It's implemented in a single C file as a bytecode compiler and VM, including a mark-and-sweep garbage collector.
- Implementation of Monkey based on the original implementation in Writing An Interpreter In Go and Writing A Compiler In go, except that it has a ton of additional features, such as: logical operators, comments, constants, postfix operators, line numbers in errors, a VS Code syntax highlighting extension (!) and much more.
- A TypeScript implementation of Monkey along with an in-browser editor, interpreter, and AST explorer!
- C++A Monkey implementation in C++, using the cpp-peglib PEG library for the lexer and parser.
- GoLudwig is a Monkey dialect, an offspring, that has some really interesting differences to Monkey:
- No statements. Everything is an expression.
- Structs.
- Type identifiers, a first step towards enforcing a type system.
- For/while loops.
- PythonMonkey implemented in Python, with additional built-in functions, string comparisons, and modulo operator.
- PHPAn implementation of the Monkey interpeter in PHP, with a damn cool logo.
- A really interesting twist on Monkey, barely recognizable as such: strongly, statically typed; lazy evaluation; pattern-matching, records; and more.
- GoGhost is a small, object-oriented, embeddable scripting language built from the foundations of the Monkey interpreter.
- A Monkey implementation written in Typescript by David Brownman. It's got extensive tests and is true-to-spec for now.
- KotlinA complete Monkey implementation, including compiler, written in Kotlin
- CrystalA complete Monkey implementation, including compiler, written in Crystal
- ScalaA complete Monkey implementation, including compiler, written in Scala 3
- RubyA complete Monkey interpreter implementation written in Ruby 3
- PythonA complete Monkey interpreter implementation written in Python
- LuaA complete Monkey interpreter implementation written in Lua
- An implementation of Monkey interpreter in OCaml, including macros.
- JuliaA complete Monkey implementation (interpreter + compiler) in Julia, plus:
- Macros
- Mutable variables
- While loops (with local scopes) and break/continue statements
- A standalone syntax analyzer extracted from the original compiler
- A Julia transpiler which tranpiles Monkey to Julia, and behaves almost the same as a real Monkey
- Unified tests for all three backends (interpreter, compiler and Julia transpiler)
- Noprianto's monkey.singkong/monkeyinterpreter.jarSingkong
Simple implementation of The Monkey Programming Language interpreter in Singkong Programming Language. Singkong is based on Monkey.java, which based on monkey.py. Monkey.singkong is also based on monkey.py.
- SwiftA Monkey implementation written in Swift.
- A Monkey implementation in F# that is mostly functional.
- GoObject-oriented variant inspired by Ruby. It offers “everything is an object”, networking, JSON and more!
- SwiftAn implementation of Monkey in Swift
- RustAn interpreted, dynamically-typed, multi-threaded, general purpose hobby programming language written in Rust. Features:
- Multiple Data Types: char, int, float, string, array, hash-table, bytes and buffer
- Arithmetic, Logical operations
- Variables and Constants
- Control and Looping structures
- Functions and Lambda expressions
- Threads and Multi-threading
- Shell operator to run shell commands within the language statements
- Some basic built-in functions
- Iterators (pseudo iterators)
- Byte code generation, serialization and loading
- Pi is an object-oriented, dynamically-typed programming language built from the foundations of the Monkey interpreter.
- RustA mostly vanilla implementation of Monkey in Rust with a WASM powered web playground
- OCamlAn implementation of Monkey in OCaml, but doesn't require semicolons. That's about it.
- RustAn implementation of the Monkey Interpreter in Rust. Contains the following features
- C-like syntax
- variable bindings
- Integers and booleans
- arithmetic expressions
- built-in functions extended to Arrays and Strings.
- first-class and higher-order functions
- closures
- a string data structure
- an array data structure
- a hash data structure
- An implementation of an interpreter for Monkey in C#
- ScalaAn implementation of an interpreter and compiler for Monkey in Scala
- JavaAn implementation of an interpreter for Monkey in Java
- A Monkey interpreter written in Elm with an online playgound.
- RustMonkey interpreter, compiler and formatter, in Rust, with added features.
- Monkey Lang interpreter implemented in Rust
- RustRust port of the monkey interpreter and the bytecode compiler. It has a few more built-in functions, the unit tests and a way to trace bytecode execution.
- A functional interpreted programming language with a minimalistic design. Tau differs from Monkey under many aspects but it was made possible thanks to Monkey. Additional features from Monkey:
- All missing operators
- Loops
- Nice error messages
- Concurrency
- Pipes (like Go channels)
- Objects
- C/Tau interoperability
- Module/import system
- Another implementation of Monkey in Elixir
- SwiftA straightforward port of the Monkey Language interpreter in Swift. This has been tested and verified in Swift version 5.9.
- JavaAn implementation of the Monkey Language made in Java. Code features most of the test suite from the book as well.
- A cheeky simian-inspired interpreter and compiler duo for the Monkey programming language, written in C++. The language features are consistent with those in the original book implementations, and include full test suites and benchmarks.
- TypeScriptAn implementation of the Monkey Language in TypeScript.
- GoAn implementation of the Monkey Language in Go. Additional Features:
- Syntax sugar for Uniform Function Call Syntax and Trailing Lambda Syntax
- VS Code Language Server Extension
- VS Code Debugger Extension
- Started as Monkey but now is GROL, on github github.com/grol-io/grol with following notable changes:
- redesigned the token/lexer/parser with intern'ing
- no need for custom hash function, go maps of interface can be used for exact equality checks
- removed
let
- removed requirement for
(
)
inif
- use numerical (uint8) enum (and stringer) instead of strings
- use said codes for checking type etc everywhere (or even pointer == check thanks for interning)
- added float, use
+
for concat of arrays and merging of maps, etc… - formatting / normalizing the source code (try it at the web site “run” or cli
-format
) - and more… (see site)
Also features a WASM online version and a Discord bot. - GoThe Cixac programming language is a passion project that I decided to pursue with inspiration and guidance from Thorsten Ball's book. Creating this interpreter is the first (out of three) implementation of my object-oriented dynamic programming language.
- RustRust implementation of the Monkey Programming Language with WASM based Playground.
- TypeScriptA TypeScript implementation of both a tree walking interpreter and bytecode compiler & vm for Monkey. Extended to support new features including for-in loops, arrow functions, additional builtin functions and more.
- Go and C++Pretty much the regular monkey language, with a few customizations:
- Supports range expressions like
0..123
. - Supports variable-length arguments to functions with
fn(a, ...) { }
syntax. - Supports builtin functions to turn a vararg object into an array, like
fn(a, ...) { a + len(toArray(...)) }
. - Support a
contains
builtin that returns a boolean indicating if aHash
object contains a key. - Closures capture the environment by value, not by reference, making it truly functional.
- Implements nicer error reporting, giving contextual information of where the error happened.
- Apart from the interpreter, it implements the bytecode VM and a transpiler to C++, which turns out to be the fastest.
- Saves the repl history using
liner
.
- Supports range expressions like
- A MonkeyLang Interpreter written in C