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!