Four runtime/reader gaps that blocked real libraries (hiccup, commonmark):
- reader: a type hint on a code form (^String (to-str x)) was lowered to a
runtime (with-meta (to-str x) {:tag String}), mis-applying the hint to the
call's RESULT and throwing when it's a string/number. A :tag hint on an
evaluated form is compile-time only in Clojure — attach it to the form
instead. Collection literals (^:foo [1 2 3]) still get runtime metadata.
- deftype: register the ctor globally by simple class name (like StringBuilder)
so (Name. ...) interop resolves ns-agnostically. host-new resolved the ctor
against the runtime current ns, which is the caller's, not the defining ns,
once a deftype is used across files.
- protocol dispatch: canonical-host-tag now strips the clojure.lang. prefix too
(clojure.lang.Keyword -> Keyword), and keywords/symbols carry the Named tag,
numbers a Ratio tag. An (extend-protocol P clojure.lang.Keyword ...) was
missing the dispatch, so e.g. hiccup rendered <:html> instead of <html>.
- regex: parse leading Java inline flags ((?s)/(?i)/(?m)) and pass the
equivalent irregex options (single-line/case-insensitive/multi-line); irregex
rejects the inline syntax. Adds a java.util.Iterator shim ((.iterator coll)/
.hasNext/.next) for the run!-style loop hiccup compiles.
|
||
|---|---|---|
| .github/workflows | ||
| bench | ||
| bin | ||
| docs | ||
| host/chez | ||
| img | ||
| jolt-core | ||
| spike/chez | ||
| src/jolt | ||
| test | ||
| tools | ||
| vendor | ||
| .DS_Store | ||
| .gitignore | ||
| .gitmodules | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| clojuredocs-export.edn | ||
| clojuredocs-export.json | ||
| foundational-runtime-handoff.md | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
Jolt
A Clojure implementation on Chez Scheme.
Jolt reads Clojure source, analyzes it to a host-neutral IR, emits Scheme, and
runs it on Chez. The compiler is self-hosted: it is written in Clojure
(jolt-core/) and compiles itself. It ships a Clojure-compatible standard library.
Requirements
Only Chez Scheme (the gate invokes it as
chez). The conformance gate additionally uses Clojure on the JVM as an oracle,
but running jolt does not.
Build
There is no build step. The bootstrap seed (host/chez/seed/{prelude,image}.ss)
is checked in, so a fresh clone runs immediately:
git clone --recurse-submodules https://github.com/jolt-lang/jolt.git
cd jolt
bin/joltc -e '(+ 1 2)' # => 3
After changing a compiler source — the reader (host/chez/reader.ss), the
analyzer/IR/backend (jolt-core/jolt/*.clj), or the clojure.core overlay
(jolt-core/clojure/core/*.clj) — re-mint the seed:
make remint # iterates host/chez/bootstrap.ss to a byte-fixpoint
Run
bin/joltc -e EXPR # evaluate a Clojure expression and print the result
$ bin/joltc -e '(->> (range 10) (filter even?) (map (fn [x] (* x x))) (reduce +))'
120
$ bin/joltc -e '(/ 1 2)'
1/2
Architecture
A small Chez runtime (host/chez/*.ss: value model, persistent collections, seqs,
vars/namespaces, host interop) hosts a portable Clojure overlay (jolt-core/): the
reader/analyzer/IR/backend (jolt-core/jolt/) and clojure.core in
dependency-ordered tiers (jolt-core/clojure/core/NN-*.clj). The stdlib namespaces
(clojure.string/set/walk/edn/pprint/…) are portable Clojure under
src/jolt/clojure/.
bin/joltc loads the checked-in seed and the spine, then compiles and evaluates on
Chez (read → analyze → IR → emit → eval). host/chez/bootstrap.ss rebuilds that
seed from source on pure Chez; the build is a self-hosting fixpoint (a rebuild
reproduces the checked-in seed byte-for-byte).
Differences from Clojure
Jolt targets Clojure semantics but runs on Chez, not the JVM.
- Host platform. No JVM and no Java interop —
import,gen-class,proxyof Java classes, andjava.*are unavailable. A class token resolves to a name; a small set of host classes is recognized forinstance?. - Numbers. The full Scheme numeric tower, matching the JVM: exact integers and
bignums, exact ratios (
(/ 1 2)⇒1/2), and flonum doubles.=is category-aware ((= 3 3.0)⇒false);==is value-equality ((== 3 3.0)⇒true).integer?/int?are exact integers,float?/double?are flonums,ratio?is an exact non-integer. NoBigDecimal(decimal?is always false). - Concurrency.
future/promise/agent/pmaprun on real OS threads over a shared heap, matching JVM semantics (not isolated-heap snapshots). Atoms use a per-atom mutex with JVM-style CAS.clojure.core.asyncprovides blocking channels andgo/<!/>!/alts!/timeout. - Regex. Backed by irregex (vendored), PCRE/Java-style patterns.
- Collections. Immutable persistent vectors (32-way tries), cons lists, and HAMT
maps/sets. Hash-map/hash-set iteration order is unspecified — use
sorted-map/sorted-setwhen order matters. Transients are real mutable scratch collections.
Supported and Clojure-compatible: lazy/infinite sequences, transducers,
destructuring, multimethods with hierarchies, protocols/records
(deftype/defrecord/reify/extend-protocol), metadata, namespaces, runtime
eval/load-string/defmacro, and the reader (#(), #_, #?, tagged literals,
#"…").
Test
make test # the full gate (no Janet)
make corpus # conformance corpus vs the JVM-sourced spec
make unit # host-specific unit cases
make selfhost # bootstrap fixpoint (rebuild == checked-in seed)
make smoke # bin/joltc CLI smoke
make sci # load borkdude/sci's source through joltc (compat stress)
make certify # JVM oracle (skips if clojure is absent)
The conformance corpus (test/chez/corpus.edn) is a host-neutral language spec
whose expected values are sourced from reference JVM Clojure. See
test/conformance/SPEC.md.