Bracketlang
A lisp-like interpreter for the web :)
How To Use
Basic Syntax
Lisp follows a basic syntax of (FUNCTION ARGUMENTS). For instance, if you wanted to add 3 and 5,
you could write (+ 3 5)
.
To define a variable, you can use the def!
function, like so:
=> (def! x 3)
3
=> x
3
For control flow, there is an if statment with the syntax (if CONDITION TRUE_PATH FALSE_PATH)
.
false
and nil
are false, everything else is true.
=> (if true "hello" "world")
"hello"
=> (if false "hello" "world")
"world"
For creating functions, you can use fn*
:
=> (def! addone (fn* (x) (+ x 1)))
Closure(0x?????)
=> (addone 3)
4
You can also create variadic arguments using &
:
=> (def! test (fn* (& x) (cons "hello" x)))
Closure(0x?????)
=> (test 3 2 3)
("hello" 3 2 3)
For creating loops... don't. Use recursion instead.
Data Types
- List: A list of other data types, expects the first element to be a function if evaluating directly.
- String
- Integer
- Float
- Bool (
true
/false
) - Nil (
nil
) - Vector (
[1 2 3]
) - Dictionary (
{"key1" "value1" "key2" 2}
) - Atom: Allows references and mutability (
(atom 3)
) - Closure
Useful Functions
Add, subtract, multiply, divide, gt, lt, gte, lte, equals all work as expected. Other than those basics, there is also:
(time-ms)
: Get the current time in milliseconds(prn VALUE)
: Print a value to the console(input STRING)
: Get input from the user(load-file STRING)
: Loads a file and evaluates, will not work on web(slurp STRING)
: Loads a file and returns a string, will not work on web. Wins award for best named function.(DATA_TYPE? VALUE)
: Checks if the given value is of the given type(deref ATOM)
: Dereferences an atom, can also use@ATOM
(reset! ATOM VALUE)
: Changes an atom's value, allowing mutability.(swap! ATOM CLOSURE)
: Changes an atom's value to the output of the closure, which is passed the atom's current value.(cons VALUE LIST/VECTOR)
: Adds the value to the list/vector at the front(concat LIST/VECTOR & VALUES)
: Adds the values to the list/vector at the back(nth LIST/VECTOR INTEGER)
: Gets the nth value of a list/vector(count LIST/VECTOR)
: Gets the number of items in a list/vector(first LIST/VECTOR)
: Gets the first value of a list/vector(rest LIST/VECTOR)
: Gets all values but the first of a list/vector(apply CLOSURE/NATIVEFUNCTION & ARGS)
: Applies the function to the rest of the args(map CLOSURE/NATIVEFUNCTION LIST/VECTOR)
: Applies the function to all elements in a list/vector(assoc DICT KEY VALUE KEY VALUE ...)
: Adds keys and values to dictionary(dissoc DICT & KEYS)
: Removes keys from dictionary(get DICT KEY)
: Get a value from a dictionary by key(contains DICT KEY)
: Check if a dictionary contains a key(keys DICT)
: Returns a list of all the keys in a dictionary(values DICT)
: Returns a list of all the values in a dictionary
More Control Flow
This language also has try/catch/throw functionality:
=> (try* (throw "whoops") (fn* (error) (prn error)))
whoops
The let*
special form lets you temporarily set variables
=> (let* (c 3) (+ c 1))
4
The do
special form lets you run multiple blocks of code, only returning the last one.
=> (do 1 2 (prn "hello") 3 4)
hello
4
Macros and Metaprogramming
Now for the fun part.
(read-string STRING)
: Parses a string into an unevaluated AST list(symbol STRING)
: Creates a new symbol from a string(quote VALUE)
: Return a value without evaluating it(quasiquote LIST)
: Return a list of values without evaluating them(unquote VALUE)
: Evaluates a value in a quasiquote. Will not work outside quasiquote.(splice-unquote LIST)
: Evaluates a list in a quasiquote and splices it into the main list. Will not work outside quasiquote.(eval VALUE)
: Evaluate a given value. Will operate in the global environment.(defmacro! SYMBOL CLOSURE)
: Create a closure that will not evaluate its arguments before executing.
...and thats all there is to it! Enjoy!