While digging into test-load-sci, add genuinely-missing clojure.core fns:
doall/dorun/run!/tree-seq, key/val/map-entry?, rand-nth/replicate/
bounded-count/counted?/reversible?/seqable?, nat-int?/pos-int?/neg-int?/
double?/float?/ratio?/decimal?/rational?/numerator/denominator, list*,
special-symbol?/record?, promise/deliver, comparator/completing/halt-when/
ensure-reduced/unreduced, keyword-identical?/object?/tagged-literal/re-groups.
Fix a real pre-existing bug: iterating a map (first/seq/map/filter/reduce/vec/
realize-for-iteration) returned keys or values instead of [k v] entries.
Map literals are Janet structs (not phm), which several paths mishandled.
Now first/seq/realize-for-iteration yield entries for both structs and phms,
matching Clojure — e.g. (vec {:a 1}) => [[:a 1]], (map key m), (reduce f init m).
Tests: conformance 218/218 (+12), features 78/78, jank 120. Advances the SCI
bootstrap further (its clojure.core aggregation map resolves more symbols).
|
||
|---|---|---|
| .beads | ||
| .clj-kondo/.cache/v1/clj | ||
| src/jolt | ||
| test | ||
| vendor | ||
| .gitignore | ||
| .gitmodules | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| clojure-features.clj | ||
| fix-core.janet | ||
| LICENSE | ||
| PLAN.md | ||
| preprocess.janet | ||
| project.janet | ||
| README.md | ||
| test-defprotocol.janet | ||
| test-eval.janet | ||
| test-form15.janet | ||
| test-ivar.janet | ||
| test-load-sci.janet | ||
| test-parse-utils.janet | ||
Jolt
A Clojure interpreter running on Janet. Jolt reads Clojure source, evaluates it with an interpreter written in pure Janet, and ships a Clojure-compatible standard library. The goal is a Janet-hosted SCI runtime — a minimal bootstrap that loads SCI's Clojure source as its standard library.
Build
git clone https://github.com/yogthos/jolt.git
cd jolt
git submodule update --init # pulls vendor/sci
jpm build # compiles build/jolt
Requires Janet ≥ 1.36 and jpm.
Run
build/jolt # start a REPL
build/jolt file.clj [args] # run a file (binds *command-line-args* and *file*)
build/jolt -e EXPR [args] # evaluate EXPR and print the result
build/jolt -h # help
The REPL accumulates multi-line forms until they balance:
user=> (defn fib [n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
#'user/fib
user=> (map fib (range 10))
(0 1 1 2 3 5 8 13 21 34)
Running a file evaluates its top-level forms:
$ echo '(println "hello" (* 6 7))' > hello.clj
$ build/jolt hello.clj
hello 42
Use as a library
(use jolt/api)
(def ctx (init))
(eval-string ctx "(+ 1 2)") # → 3
(eval-string ctx "(map inc [1 2 3])") # → [2 3 4]
(init) returns a context with clojure.core loaded. Each context is isolated; use separate contexts for separate environments.
Host interop
Jolt exposes CLJS-style host interop through . on any Janet table or struct — a field holding a function is called with the receiver as the first argument:
(def obj {:greet (fn [self name] (str "Hello " name))})
(. obj greet "Alice") ; → "Hello Alice"
(.-greet obj) ; field access (reader sugar for (. obj :greet))
Janet's standard library is reachable through jolt.interop (and the jolt.shell / jolt.http helpers built on it):
(require '[jolt.interop :as j])
(j/janet-type [1 2]) ; → :tuple
(j/janet-table-keys {:a 1 :b 2}) ; → [:b :a]
Differences from Clojure
Jolt targets Clojure semantics but runs on Janet, not the JVM. The notable divergences:
- Host platform. No JVM and no Java interop —
import,gen-class,proxyof Java classes, andjava.*are unavailable.instance?recognizes a small set of built-in types (clojure.lang.Atom,Number,String, …). - Numbers. Janet integers and doubles only — no bignums, ratios, or
BigDecimal.(/ 1 3)is0.3333…, large products lose precision, and there are no auto-promoting+'/*'.quot/rem/modfollow Clojure's sign rules.bigint,rational?, andclassare not provided. - Collections. By default Jolt uses immutable persistent data structures: vectors are 32-way branching tries (structural-sharing persistent vectors with O(log₃₂ n)
conj/assoc/nth), lists are persistent singly-linked cons cells (O(1)conj/consprepend with structural sharing), and maps/sets are persistent hash structures. Value equality and sequence operations are Clojure-compatible, but hash-map/hash-set iteration order is unspecified and differs from Clojure — usesorted-map/sorted-setwhen order matters. - Mutable build mode. Jolt can be compiled to use fast Janet-native mutable collections instead, via a build-time flag:
JOLT_MUTABLE=1 jpm build(defaultjpm buildis immutable). In mutable mode vectors and lists share one mutable array representation (soconjmutates in place and appends, andvector?/list?no longer distinguish them) — a performance/looseness trade-off. The default immutable build has full Clojure value semantics. - Concurrency / STM. Single-threaded. No refs,
dosync, agents, orsend;lockingevaluates its body without real locking. Atoms, volatiles, and delays are supported. - Regex. Compiled to Janet's PEG engine (Janet has no regex). Supported: capturing groups (
[whole g1 …]), greedy and lazy quantifiers with backtracking,(?:…), lookahead(?=…)/(?!…), alternation, anchors^ $ \b \B, character classes, and the(?i)flag. Not supported: lookbehind, backreferences (\1), and named groups ((?<name>…)). - Not implemented. Transients (
transient/persistent!), JVM reflection, andproxy. (reifyandextend-protocolwork for Jolt protocols.)
Supported and Clojure-compatible: chars as a distinct type, lazy/infinite sequences, transducers, destructuring, multimethods with hierarchies, protocols/records, metadata, namespaces, and the reader (#(), #_, #?, tagged literals, #"…").
Test
jpm test # full test suite
janet test/conformance.janet # Clojure-conformance battery