A basic interpreter for a custom Lisp.
First, install bun.
bun start [path] will interpret a file. If a file path is not provided, an interactive REPL will be opened.
Dependencies are only for assisting with local development and can be installed with bun install.
The language is strongly based on Clojure, another Lisp. It has the following grammar:
string: '"' [\x00-\x7F]* '"'
number: [0-9.]+
symbol: [a-zA-Z_+-/*=<>!&?][a-zA-Z0-9_+-/*=<>!&?]*
literal: 'true' | 'false' | 'nil' | string | number | symbol
list: '(' primary* ')'
vector: '[' primary* ']'
primary: literal | symbol | list | vector
program: primary*
Function calls (s-expressions) are written exactly like lists, with the operator as the first element. For example:
(print "Hello world!") ; => "Hello world!"
(+ 2 (- 6 3)) ; => 5
There are various special forms: if, let, do, fn, loop/recur (not implemented yet), quote, defn (not implemented yet) and defmacro.
ifis used for control flow. It has the structure(if cond true_child false_child).- (e.g.
(if true 3 4)returns3)
- (e.g.
letis used for local binding of variables. It has the structure(let bindings body), wherebindingsis a vector with odd elements as names and even elements as their corresponding values.- (e.g.
(let [x 4 y 5] (+ x y))returns9)
- (e.g.
dois used for chaining multiple expressions together, returning the value of the last one. It has the structure(do & expr), meaning it can be followed by any number of expressions.- (e.g.
(do (print "hi") 5)returns 5, but also prints "hi")
- (e.g.
fndeclares an inline function. It has the structure(fn name? params body), whereparamsis a vector of names corresponding to the parameters in order.- (e.g.
(fn mult [a b] (* a b)returns a function that takes 2 arguments and returns their product)
- (e.g.
loopmarks the start of a loop. It has the structure(loop bindings body), wherebindingsis the same as those inlet.recurjumps back to the nearestloop. It has the structure(recur & new-bindings).- (e.g.
(loop [i 1] (if (< i 6) (do (print i) (recur (+ i 1)))))prints out the numbers from 1 to 5)
- (e.g.
quoteescapes evaluation. It has the structure(quote value).- (e.g.
(quote (1 2 3))returns the list(1 2 3), without trying to evaluate this as a function call)
- (e.g.
defndeclares a function in global scope. It has the structure(defn name params body).defmacrodeclares a macro in global scope. It has the structure(defmacro name params body).
Various functions are also included in the standard library. See native.ts/native.s as well as stdlib.clj.
Run bun test <path-to-file> to run a unit test file. Not providing a path runs all tests.
If you only want to run a specific test, add --test-name-pattern="<some test name>".