|
| 1 | +# Register based VM |
| 2 | + |
| 3 | +A Lisp interpreter with a register-based bytecode virtual machine. |
| 4 | + |
| 5 | +## Features |
| 6 | + |
| 7 | +- **Register-based bytecode VM** - Inspired by Lua's design |
| 8 | +- **Compile-time optimizations** - Constant folding and pure function inlining |
| 9 | +- **Tail call optimization** - Write recursive functions without stack overflow (citation needed) |
| 10 | +- **Macro system** - Compile-time code generation with `defmacro` |
| 11 | +- **Reference counting** - Predictable memory management without garbage collection pauses* |
| 12 | +- **Immutable by default** - Values are immutable, inspired by FP |
| 13 | +- **Fast startup** - Minimal overhead, just feed the VM with code |
| 14 | + |
| 15 | +## Installation |
| 16 | + |
| 17 | +### Building from Source |
| 18 | + |
| 19 | +```bash |
| 20 | +~$ git clone https://github.com/0xhenrique/code-reg.git |
| 21 | +~$ cd reg |
| 22 | +~$ cargo build --release |
| 23 | +``` |
| 24 | + |
| 25 | +## How to use |
| 26 | + |
| 27 | +### Running a Lisp File |
| 28 | + |
| 29 | +```bash |
| 30 | +./target/release/lisp-vm your-program.lisp |
| 31 | +``` |
| 32 | + |
| 33 | +### REPL |
| 34 | + |
| 35 | +```bash |
| 36 | +./target/release/lisp-vm |
| 37 | +``` |
| 38 | + |
| 39 | +Try to experiment a bit with it. Type `:q` or `:quit` to exit. |
| 40 | + |
| 41 | +### CLI Options |
| 42 | + |
| 43 | +```bash |
| 44 | +lisp-vm [OPTIONS] [FILE] |
| 45 | + |
| 46 | +Options: |
| 47 | + --arena Enable arena allocation for cons cells (experimental) |
| 48 | +``` |
| 49 | + |
| 50 | +## Examples |
| 51 | + |
| 52 | +### Basic Arithmetic |
| 53 | + |
| 54 | +```lisp |
| 55 | +; Addition, subtraction, multiplication, division |
| 56 | +(+ 1 2 3) ; => 6 |
| 57 | +(* 4 5) ; => 20 |
| 58 | +(- 10 3) ; => 7 |
| 59 | +(/ 20 4) ; => 5 |
| 60 | +
|
| 61 | +; Comparisons |
| 62 | +(< 5 10) ; => true |
| 63 | +(>= 5 5) ; => true |
| 64 | +(= 2 2) ; => true |
| 65 | +``` |
| 66 | + |
| 67 | +### Variables and Functions |
| 68 | + |
| 69 | +```lisp |
| 70 | +; Define a variable |
| 71 | +(def x 42) |
| 72 | +
|
| 73 | +; Define a function |
| 74 | +(def square (fn (n) (* n n))) |
| 75 | +(square 5) ; => 25 |
| 76 | +
|
| 77 | +; Functions with multiple statements using 'do' |
| 78 | +(def greet (fn (name) |
| 79 | + (do |
| 80 | + (println "Hello") |
| 81 | + name))) |
| 82 | +(greet "World") ; prints "Hello", returns "World" |
| 83 | +``` |
| 84 | + |
| 85 | +### Let Bindings |
| 86 | + |
| 87 | +```lisp |
| 88 | +; Create local bindings |
| 89 | +(let (x 10 y 20) |
| 90 | + (+ x y)) ; => 30 |
| 91 | +
|
| 92 | +; Nested let bindings |
| 93 | +(let (x 5) |
| 94 | + (let (y (* x 2)) |
| 95 | + (+ x y))) ; => 15 |
| 96 | +``` |
| 97 | + |
| 98 | +### Conditionals |
| 99 | + |
| 100 | +```lisp |
| 101 | +; if expressions |
| 102 | +(if (< 5 10) |
| 103 | + "yes" |
| 104 | + "no") ; => "yes" |
| 105 | +
|
| 106 | +; if without else returns nil |
| 107 | +(if false "won't happen") ; => nil |
| 108 | +``` |
| 109 | + |
| 110 | +### Recursion |
| 111 | + |
| 112 | +The VM supports kinda recursion with TCO. This is not battle tested: |
| 113 | + |
| 114 | +```lisp |
| 115 | +; Regular recursion |
| 116 | +(def factorial (fn (n) |
| 117 | + (if (<= n 1) |
| 118 | + 1 |
| 119 | + (* n (factorial (- n 1)))))) |
| 120 | +(factorial 5) ; => 120 |
| 121 | +(factorial 10) ; => 3628800 |
| 122 | +
|
| 123 | +; Tail-recursive function |
| 124 | +(def sum (fn (n acc) |
| 125 | + (if (<= n 0) |
| 126 | + acc |
| 127 | + (sum (- n 1) (+ acc n))))) |
| 128 | +(sum 100 0) ; => 5050 |
| 129 | +(sum 10000 0) ; => 50005000 (no stack overflow!) |
| 130 | +``` |
| 131 | + |
| 132 | +### Lists |
| 133 | + |
| 134 | +```lisp |
| 135 | +; Create a list |
| 136 | +(list 1 2 3 4 5) ; => (1 2 3 4 5) |
| 137 | +
|
| 138 | +; car and cdr (first and rest) |
| 139 | +(car (list 1 2 3)) ; => 1 |
| 140 | +(cdr (list 1 2 3)) ; => (2 3) |
| 141 | +
|
| 142 | +; cons (prepend element) |
| 143 | +(cons 0 (list 1 2 3)) ; => (0 1 2 3) |
| 144 | +
|
| 145 | +; length |
| 146 | +(length (list 1 2 3 4 5)) ; => 5 |
| 147 | +
|
| 148 | +; Quoting |
| 149 | +'(1 2 3) ; => (1 2 3) |
| 150 | +``` |
| 151 | + |
| 152 | +### Higher-Order Functions |
| 153 | + |
| 154 | +```lisp |
| 155 | +; Functions are first-class values |
| 156 | +(def apply-twice (fn (f x) |
| 157 | + (f (f x)))) |
| 158 | +
|
| 159 | +(def add-one (fn (x) (+ x 1))) |
| 160 | +(apply-twice add-one 5) ; => 7 |
| 161 | +
|
| 162 | +; Functions can be returned |
| 163 | +(def make-adder (fn (n) |
| 164 | + (fn (x) (+ x n)))) |
| 165 | +(def add-ten (make-adder 10)) |
| 166 | +(add-ten 5) ; => 15 |
| 167 | +``` |
| 168 | + |
| 169 | +### Macros |
| 170 | + |
| 171 | +Macros enable compile-time code generation: |
| 172 | + |
| 173 | +```lisp |
| 174 | +; Define a macro |
| 175 | +(defmacro unless (cond body) |
| 176 | + (list 'if cond nil body)) |
| 177 | +
|
| 178 | +; Use the macro |
| 179 | +(unless false (println "This prints!")) |
| 180 | +(unless true (println "This does not print")) |
| 181 | +
|
| 182 | +; Macros expand at compile time |
| 183 | +(defmacro when (cond body) |
| 184 | + (list 'if cond body nil)) |
| 185 | +
|
| 186 | +(when (> 5 3) (println "5 is greater than 3")) |
| 187 | +``` |
| 188 | + |
| 189 | +### Built-in Functions |
| 190 | + |
| 191 | +#### Arithmetic |
| 192 | +- `+`, `-`, `*`, `/`, `mod` - Arithmetic operations (support multiple arguments) |
| 193 | + |
| 194 | +#### Comparison |
| 195 | +- `<`, `<=`, `>`, `>=`, `=`, `!=` - Comparison operations |
| 196 | + |
| 197 | +#### Logic |
| 198 | +- `not` - Logical negation |
| 199 | + |
| 200 | +#### List Operations |
| 201 | +- `list` - Create a list |
| 202 | +- `cons` - Prepend element to list |
| 203 | +- `car` - Get first element (`first` or `head`) |
| 204 | +- `cdr` - Get rest of list (`rest` or `tail`) |
| 205 | +- `length` - Get list length |
| 206 | + |
| 207 | +#### Type Predicates |
| 208 | +- `nil?`, `int?`, `float?`, `string?`, `list?`, `fn?`, `symbol?` - Type checking |
| 209 | + |
| 210 | +#### I/O |
| 211 | +- `print` - Print value without newline |
| 212 | +- `println` - Print value with newline |
| 213 | + |
| 214 | +#### Special Forms |
| 215 | +- `def` - Define global variable |
| 216 | +- `fn` - Create anonymous function |
| 217 | +- `if` - Conditional expression |
| 218 | +- `let` - Create local bindings |
| 219 | +- `do` - Execute multiple expressions, return last one |
| 220 | +- `quote` (or `'`) - Quote expression without evaluation |
| 221 | +- `defmacro` - Define compile-time macro |
| 222 | +- `gensym` - Generate unique symbol (for hygienic macros) |
| 223 | + |
| 224 | +## Example Programs |
| 225 | + |
| 226 | +### Fibonacci (Tree Recursion) |
| 227 | + |
| 228 | +```lisp |
| 229 | +(def fib (fn (n) |
| 230 | + (if (<= n 1) |
| 231 | + n |
| 232 | + (+ (fib (- n 1)) (fib (- n 2)))))) |
| 233 | +
|
| 234 | +(println (fib 10)) ; => 55 |
| 235 | +``` |
| 236 | + |
| 237 | +### Sum of List |
| 238 | + |
| 239 | +```lisp |
| 240 | +(def sum-list (fn (lst) |
| 241 | + (if (nil? lst) |
| 242 | + 0 |
| 243 | + (+ (car lst) (sum-list (cdr lst)))))) |
| 244 | +
|
| 245 | +(sum-list (list 1 2 3 4 5)) ; => 15 |
| 246 | +``` |
| 247 | + |
| 248 | +### Map Function |
| 249 | + |
| 250 | +```lisp |
| 251 | +(def map (fn (f lst) |
| 252 | + (if (nil? lst) |
| 253 | + nil |
| 254 | + (cons (f (car lst)) (map f (cdr lst)))))) |
| 255 | +
|
| 256 | +(def double (fn (x) (* x 2))) |
| 257 | +(map double (list 1 2 3 4)) ; => (2 4 6 8) |
| 258 | +``` |
| 259 | + |
| 260 | +## Performance |
| 261 | + |
| 262 | +The VM uses some common optimization techniques: |
| 263 | + |
| 264 | +- **Compile-time constant folding**: Expressions like `(+ 1 2 3)` compile directly to `6` |
| 265 | +- **Pure function inlining**: Pure functions with constant arguments are evaluated at compile time |
| 266 | +- **Register-based bytecode**: Reduced memory traffic |
| 267 | +- **Specialized opcodes**: Direct arithmetic operations bypass function call overhead |
| 268 | +- **Tail call optimization**: Recursive tail calls reuse stack frames |
| 269 | + |
| 270 | +This is a rabbit hole by itself. I'm not expecting to beat something like Lua. |
| 271 | + |
| 272 | +## Acknowledgments |
| 273 | + |
| 274 | +Inspired by: |
| 275 | +- **Lua** - For the register-based VM design |
| 276 | +- **Crafting Interpreters** - For VM implementation techniques |
| 277 | +- **[Tsoding - Lisp in C](https://www.youtube.com/playlist?list=PLpM-Dvs8t0VYbTFO5tBwxG4Q20BJuqXD_)** - The series of videos that inspired me into learning more about VM |
| 278 | +- **A ton of blogs posts, articles, videos and Github issues** - Thank you a lot! |
0 commit comments