daneelsan/tinylisp
A web-based Lisp interpreter with a "terminal" interface
Play with tinylisp at: https://daneelsan.github.io/tinylisp/
tinylisp is a minimal Lisp interpreter implemented in Zig, using NaN boxing for efficient memory representation. It supports core Lisp features like atoms, lists, conditionals, arithmetic, and closures, along with an interactive REPL and debugging tools for inspecting the heap, stack, and environment.
Read my thoughts at https://daneelsan.github.io/2025/03/20/tinylisp.html
#t
(True):
λ #t
#t
ERR
(Error):
λ (cdr 42)
ERR
+
(Addition):
λ (+ 1 2 3 4)
10
-
(Subtraction):
λ (- 10 2 3)
5
*
(Multiplication):
λ (* 2 3 4)
24
/
(Division):
λ (/ 3.4 4)
0.85
car
:
λ (car '(1 2 3))
1
cdr
:
λ (cdr '(1 2 3))
(2 3)
cons
:
λ (cons 1 2)
(1 . 2)
λ (cons 1 '(2 3))
(1 2 3)
'
(Quoting):
λ '(+ 1 2 3)
(+ 1 2 3)
eval
(Evaluation):
λ (eval '(+ 1 2 3))
6
if
:
λ (if (< 1 2) 'true 'false)
true
and
:
λ (and (< 1 2) (< 2 3))
#t
λ (and (< 2 1) (< 2 3))
()
or
:
λ (or (< 1 2) (< 3 2))
#t
not
:
λ (not (< 1 2))
()
=
(Equality):
λ (= 1.0 1)
#t
λ (= 1.1 1)
()
lambda
:
λ ((lambda (x) (* x x)) 5)
25
define
:
λ (define square (lambda (x) (* x x)))
square
λ (square 5)
25
λ (define x 42)
x
λ (+ x x)
84
echo
:
λ (+ (echo (* 3 4) 5))
>> 12
12
echo-eval
:
λ (echo-eval (+ (* 3 4) 5))
>> ((+ (* 3 4) 5))
<< 17
17
print-env
:
λ (print-env)
(
(echo-eval . «echo-eval»)
(echo . «echo»)
(print-env . «print-env»)
(print-stack . «print-stack»)
(print-heap . «print-heap»)
(define . «define»)
(lambda . «lambda»)
(if . «if»)
(and . «and»)
(or . «or»)
(not . «not»)
(= . «=»)
(> . «>»)
(< . «<»)
(/ . «/»)
(* . «*»)
(- . «-»)
(+ . «+»)
(int . «int»)
(cdr . «cdr»)
(car . «car»)
(cons . «cons»)
(quote . «quote»)
(eval . «eval»)
(#t . #t)
)
()
print-heap
:
λ (print-heap)
------------------- HEAP -------------------
| # | address | symbol |
|-----|----------|-------------------------|
| 0 | 0x0000 | ERR |
| 1 | 0x0004 | #t |
| 2 | 0x0007 | eval |
| 3 | 0x000C | quote |
| 4 | 0x0012 | cons |
| 5 | 0x0017 | car |
| 6 | 0x001B | cdr |
| 7 | 0x001F | int |
| 8 | 0x0023 | + |
| 9 | 0x0025 | - |
| 10 | 0x0027 | * |
| 11 | 0x0029 | / |
| 12 | 0x002B | < |
| 13 | 0x002D | > |
| 14 | 0x002F | = |
| 15 | 0x0031 | not |
| 16 | 0x0035 | or |
| 17 | 0x0038 | and |
| 18 | 0x003C | if |
| 19 | 0x003F | lambda |
| 20 | 0x0046 | define |
| 21 | 0x004D | print-heap |
| 22 | 0x0058 | print-stack |
| 23 | 0x0064 | print-env |
| 24 | 0x006E | echo |
| 25 | 0x0073 | echo-evaluation |
| ... |
--------------------------------------------
()
print-stack
:
λ (print-stack)
------------- STACK ------------
| pointer | tag | ordinal | Expr
|----------|--------|----------|--------------
| 1024 | ATOM | 0x0004 | #t
| 1023 | ATOM | 0x0004 | #t
| 1022 | CONS | 1022 |
| 1021 | NIL | 0 | ()
| 1020 | ATOM | 0x0007 | eval
| 1019 | PRIM | 0 | «eval»
| 1018 | CONS | 1018 |
| 1017 | CONS | 1020 |
| 1016 | ATOM | 0x000C | quote
| 1015 | PRIM | 1 | «quote»
| 1014 | CONS | 1014 |
| 1013 | CONS | 1016 |
| 1012 | ATOM | 0x0012 | cons
| 1011 | PRIM | 2 | «cons»
| 1010 | CONS | 1010 |
| 1009 | CONS | 1012 |
| 1008 | ATOM | 0x0017 | car
| 1007 | PRIM | 3 | «car»
| 1006 | CONS | 1006 |
| 1005 | CONS | 1008 |
| 1004 | ATOM | 0x001B | cdr
| 1003 | PRIM | 4 | «cdr»
| 1002 | CONS | 1002 |
| 1001 | CONS | 1004 |
| 1000 | ATOM | 0x001F | int
| 999 | PRIM | 5 | «int»
| 998 | CONS | 998 |
| 997 | CONS | 1000 |
| 996 | ATOM | 0x0023 | +
| 995 | PRIM | 6 | «+»
| 994 | CONS | 994 |
| 993 | CONS | 996 |
| 992 | ATOM | 0x0025 | -
| 991 | PRIM | 7 | «-»
| 990 | CONS | 990 |
| 989 | CONS | 992 |
| 988 | ATOM | 0x0027 | *
| 987 | PRIM | 8 | «*»
| 986 | CONS | 986 |
| 985 | CONS | 988 |
| 984 | ATOM | 0x0029 | /
| 983 | PRIM | 9 | «/»
| 982 | CONS | 982 |
| 981 | CONS | 984 |
| 980 | ATOM | 0x002B | <
| 979 | PRIM | 10 | «<»
| 978 | CONS | 978 |
| 977 | CONS | 980 |
| 976 | ATOM | 0x002D | >
| 975 | PRIM | 11 | «>»
| 974 | CONS | 974 |
| 973 | CONS | 976 |
| 972 | ATOM | 0x002F | =
| 971 | PRIM | 12 | «=»
| 970 | CONS | 970 |
| 969 | CONS | 972 |
| 968 | ATOM | 0x0031 | not
| 967 | PRIM | 13 | «not»
| 966 | CONS | 966 |
| 965 | CONS | 968 |
| 964 | ATOM | 0x0035 | or
| 963 | PRIM | 14 | «or»
| 962 | CONS | 962 |
| 961 | CONS | 964 |
| 960 | ATOM | 0x0038 | and
| 959 | PRIM | 15 | «and»
| 958 | CONS | 958 |
| 957 | CONS | 960 |
| 956 | ATOM | 0x003C | if
| 955 | PRIM | 16 | «if»
| 954 | CONS | 954 |
| 953 | CONS | 956 |
| 952 | ATOM | 0x003F | lambda
| 951 | PRIM | 17 | «lambda»
| 950 | CONS | 950 |
| 949 | CONS | 952 |
| 948 | ATOM | 0x0046 | define
| 947 | PRIM | 18 | «define»
| 946 | CONS | 946 |
| 945 | CONS | 948 |
| 944 | ATOM | 0x004D | print-heap
| 943 | PRIM | 19 | «print-heap»
| 942 | CONS | 942 |
| 941 | CONS | 944 |
| 940 | ATOM | 0x0058 | print-stack
| 939 | PRIM | 20 | «print-stack»
| 938 | CONS | 938 |
| 937 | CONS | 940 |
| 936 | ATOM | 0x0064 | print-env
| 935 | PRIM | 21 | «print-env»
| 934 | CONS | 934 |
| 933 | CONS | 936 |
| 932 | ATOM | 0x006E | echo
| 931 | PRIM | 22 | «echo»
| 930 | CONS | 930 |
| 929 | CONS | 932 |
| 928 | ATOM | 0x0073 | echo-eval
| 927 | PRIM | 23 | «echo-eval»
| 926 | CONS | 926 |
| 925 | CONS | 928 |
| 924 | ATOM | 0x0058 | print-stack
| 923 | NIL | 0 | ()
| ... |
|------------------------------|
()
Compiled using zig version:
$ zig version
0.14.0
Build the wasm executable using zig build
:
$ zig build
$ ls zig-out/bin
tinylisp.wasm
Additionally, build the local executable using zig build local
:
$ zig build local
$ ls zig-out/bin
tinylisp tinylisp.wasm
Or run the local executable directly using zig build run
:
$ zig build run
λ
Compile to .wasm and add a javascript REPL
Write a blogpost
Expand documentation with more examples
Add tests
Improve parsing to handle multiple expressions separed by new lines
Ctrl+c
to delete the current line
Multiline inputs don't work well with the history explorer (Up Arrow
)
Syntax highlighting
Drop files
Click to copy input/output