Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/tylfin/writeingo
Repository covering contents of "Writing An Interpreter in Go" and "Writing a Compiler in Go"
https://github.com/tylfin/writeingo
go
Last synced: 3 days ago
JSON representation
Repository covering contents of "Writing An Interpreter in Go" and "Writing a Compiler in Go"
- Host: GitHub
- URL: https://github.com/tylfin/writeingo
- Owner: tylfin
- Created: 2023-12-07T18:22:06.000Z (11 months ago)
- Default Branch: main
- Last Pushed: 2024-01-28T22:12:41.000Z (10 months ago)
- Last Synced: 2024-01-30T00:30:15.880Z (10 months ago)
- Topics: go
- Language: Go
- Homepage:
- Size: 8.37 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Write in Go
[![Go](https://github.com/tylfin/writeingo/actions/workflows/go.yml/badge.svg)](https://github.com/tylfin/writeingo/actions/workflows/go.yml)
Repository covers the contents of "Writing An Interpreter in Go" and "Writing a Compiler in Go".
## REPL
The monkey language supports a REPL that can be used like so:
```bash
go run main.go
Hello tylfin! This is the Monkey programming language!
Feel free to type in commands
>> let x = 5;
let x = 5;
>> 5 + 4 * (2 + 4) / 5
(5 + ((4 * (2 + 4)) / 5))
>> fn(x, y) { x + y; }
fn(x, y)(x + y)
>> let x 5 2
Woops! We ran into some monkey business here!
parser errors:
expected next token to be =, got INT instead
```## Project Structure
The repository is arranged like:
```bash
├── main.go
├── ast
├── code
├── compiler
├── evaluator
├── lexer
├── object
├── parser
├── repl
├── token
└── vm
```Where:
- `main.go`: Entrypoint to the REPL
- `ast`: Contains the definitions for the Abstract Syntax Tree (AST) nodes. These nodes represent various constructs in the Monkey language such as expressions, statements, etc.
- `code`: Responsible for generating intermediate code or bytecode, which is a lower-level representation of the AST. This is especially relevant if the interpreter includes a bytecode interpreter or virtual machine.
- `compiler`: Contains the logic to compile the AST into an executable form. This may involve generating bytecode for a virtual machine or performing just-in-time compilation.
- `evaluator`: The core component that interprets the AST. This module executes the logic of the language, handling constructs like if-statements, loops, function calls, etc.
- `lexer`: Performs lexical analysis by converting raw input text into a series of tokens. These tokens are the building blocks for the parsing process.
- `object`: Defines the various types of objects that the Monkey language supports. This includes basic data types like integers and strings, as well as functions and their interactions.
- `parser`: Transforms the tokens generated by the lexer into an AST. It follows the grammar rules of the Monkey language, structuring the input into a form that can be easily evaluated or compiled.
- `repl`: Implements the Read-Eval-Print Loop. It's an interactive shell that takes user input, processes it through the interpreter, and outputs the result.
- `token`: Defines the raw tokens that the lexer uses. These tokens represent the basic elements of the Monkey language syntax.
- `vm`: If the interpreter includes a virtual machine, this directory contains its implementation. The VM executes bytecode generated from the Monkey language code.## More on Monkey
Here is how we bind values to names in Monkey:
```bash
let age = 1;
let name = "Monkey";
let result = 10 * (20 / 2);
```Besides integers, booleans and strings, the Monkey interpreter supports arrays and hashes.
Here’s what binding an array of integers to a name looks like:```bash
let myArray = [1, 2, 3, 4, 5];
And here is a hash, where values are associated with keys:
let thorsten = {"name": "Thorsten", "age": 28};
Accessing the elements in arrays and hashes is done with index expressions:
myArray[0] // => 1 thorsten["name"] // => "Thorsten"
```The let statements can also be used to bind functions to names. Here’s a small function that adds two numbers:
```bash
let add = fn(a, b) { return a + b; };
```Implicit return values are also possible,
```bash
let add = fn(a, b) { a + b; };
```And calling a function is as easy as you’d expect:
```bash
add(1, 2);
```A more complex function, such as a fibonacci function that returns the Nth Fibonacci number,
might look like this:```bash
let fibonacci = fn(x) {
if (x == 0) {
0
} else {
if (x == 1) {
1
} else {
fibonacci(x - 1) + fibonacci(x - 2);
}
}
};
```Note the recursive calls to fibonacci itself!
Monkey also supports a special type of functions, called higher order functions. These are functions that take other
functions as arguments. Here is an example:```bash
let twice = fn(f, x) { return f(f(x)); };
let addTwo = fn(x) { return x + 2; };
twice(addTwo, 2); // => 6
```## Benchmark
From the final chapter of Writing a Compiler in Go, my compiled version was 3.05x as fast:
```bash
$ go build -o fibonacci ./benchmark
$ ./fibonacci -engine=eval
engine=eval, result=9227465, duration=12.267151708s
$ ./fibonacci -engine=vm
engine=vm, result=9227465, duration=4.013193583s
```## Book References
- [Writing An Interpreter in Go](https://interpreterbook.com/)
- [Writing a Compiler in Go](https://compilerbook.com/)