Cloji
A lightweight interpreter for a Lisp-like language inspired by Clojure.
It supports expressions for definitions, function declarations, control flow, object/array operations, destructuring, and more.
Getting Started
Installation
npm install cloji
Usage
import { exec } from 'cloji'
const scope = exec('(def x 10) (+ x 5)')
console.log(scope.result)
Variables
Def (ident, value) Define variable.
(def a 1) ;; define a
Set (ident, value) Set variable value.
(set a 2) ;; update a
It works inside fuction body. Variables are readable only inside the scope of the function where it was declared.
(defn f []
(def a 1) a)
Functions
(def f (fn [x] x))
(f 5) ;; => 5
(defn square [x] (* x x))
(square 4) ;; => 16
Unary
(def f (fn x (+ x 5)))
(f 5) ;; => 10
(defn square x (* x x))
(square 4) ;; => 16
Member call (methods)
(def a "a")
(.concat a "b") ;; => "ab"
Parser level
;; this is a comment
Parsed but not executed
(## this is a comment)
Print
(print "Hello" "World")
;; Logs: Hello World
Conditionals
(if true 1 2) ;; => 1
(cond
(= x 1) "eq"
(> x 1) "gt"
:else "default")
Object
{:name "john" :age 20}
{:name "peter" &user} ;; spread `user` into the object
Arrays
[1 2 3]
[1 2 &arr]
Destructuring
An identifier preceded by &
is a rest/spread token
(def {name age} user)
(def {name &rest} user)
(def [head &tail] arr)
Destructiring function params
(defn func [p1 &rest] rest)
(func 3 4 5) ;; return [4 5]
Utilities
Array (arrayLike, mapperFn). Works like JS Array.from()
(array {:length 3} (fn [_ idx] idx)) ;; => [0 1 2]
aget (object, &keys)
(aget {:name "john"} "name") ;; "john"
(aget {:name "john"} :name) ;; "john"
(aget {:name "john" :pets ["cat" "dog"]} :pets 0) ;; "cat"
aset (object &keys value)
(aset {:name "john"} :name "peter")
(aset {:name "john" :pets ["cat" "dog"]} :pets 0 "pig") ;; {:name "john" :pets ["pig" "dog"]}
-> (-> x &sexprs)
(-> [1 2 3]
(.map (fn [x] (* x 2)))
(.filter (fn [x] (> x 2)))) ;; => [6, 7]
;; (def x [1 2 3])
;; (set x (x.map (fn [x] (* x 2))))
;; (set x (x.filter (fn [x] (> x 2))))
Operators
(+ 3 4) ;; => 7
(?? nil 5) ;; => 5
Available operators
+
-
*
/
=
not=
<
>
<=
>=
and
or
not
?? ;; coalesce
JavaScript integration
Passing JS vars
exec(`(Date.now)`, { Date })
Classes
Class functions are in the cloji/lib/class
package. There can be found class
defclass
new
import * as cls 'cloji/lib/class'
exec(`(new Date)`, { ...cls, Date })
defclass
(defclass Animal
(defn construct [animal name]
(set animal.name name)))
Using script tag
First include the tag
module to register cloji
tag globally
require('cloji/lib/tag')
Now you can execute Cloji code inside js/ts/mjs files. JS variables can be passed using string template literal interpolation
cloji`
(def { new } ${require("cloji/lib/class")})
(def { Animal } ${require("./animal")})
(def animal (new Animal "striker"))
(print animal.name)
Check /spec/scripts for working example
Packing/unpacking AST
Useful to store or send compiled code to be executed latter
import { parse } from 'cloji/lib/parser'
import { pack, unpack } from 'cloji/lib/pack'
const ast = parse(`["white" "brown" void]`)
const packed = pack(ast)
const unpacked = unpack(packed)
License
MIT
Contributing
Feel free to open issues or submit PRs to extend the language, fix bugs, or improve performance.