- grammar.ebnf: rewrite the number rule to cover the literal syntaxes the reader now accepts — 0x/0X hex, N (bigint) / M (bigdec) suffixes, ratios a/b, radixed integers (NrXXX, base 2..36), exponents, and the ##Inf/##-Inf/##NaN symbolic floats — noting Jolt reads them as plain Janet numbers. - README: Numbers bullet notes the literal syntaxes read; conformance count. - reader-syntax-spec: drop the stale 'ratio not supported' case; add coverage for hex-uppercase/N/M/ratio/radix/exponent/##Inf/##NaN. - PLAN.md: refresh the stale Current State snapshot for the 3-layer test structure (spec/integration/unit), ~3,920 suite assertions, 218/218 conformance, current source size. jpm test green.
8.5 KiB
Jolt — Complete Implementation Plan
Architecture Goal
Minimal Janet bootstrap → SCI/CLJS Clojure source runs on Jolt.
Three layers:
- Janet runtime: types.janet, reader.janet, evaluator.janet, compiler.janet (~4,200 lines)
- Clojure core: core.janet (~1,400 lines), phm.janet (~200 lines)
- Clojure source (.clj files loadable at runtime): stdlib modules, SCI
Current State
Tests are organized in three layers (see the README "Test" section):
test/spec/ (the black-box contract), test/integration/ (cross-cutting and
external-suite regression batteries), and test/unit/ (white-box component
tests). jpm test is green.
| Metric | Value |
|---|---|
| Spec contract | ~1,000 cases across 24 test/spec/ files |
| Integration batteries | 12 (conformance, SCI bootstrap/runtime, jank, clojure-test-suite, compile-mode, API, systematic-coverage) |
| Unit tests | 9 files (reader, evaluator, types, collections, regex, compiler) |
| Clojure conformance | 218/218 |
| clojure-test-suite | ~3,920 assertions pass (remainder are documented platform/design divergences) |
| Source lines | ~8,600 (src/jolt/*.janet; core.janet ~4,000) |
| SCI source files loading | 9/9 |
| Reader literals | full numeric syntax (N/M/ratio/radix/exponent/##Inf); see doc/grammar.ebnf |
Phase Plan
Phase 0-10: Foundation ✓
| Phase | Description | Status |
|---|---|---|
| 0 | defn bug fix, bare symbol resolution |
✓ |
| 1 | Var/Namespace system, ns form extensions | ✓ |
| 2 | PersistentHashMap implementation | ✓ |
| 3 | Var system: var-get/set/?, alter-var-root, intern, binding | ✓ |
| 4 | deftype/defrecord completion | ✓ |
| 5 | Multimethods + Hierarchy | ✓ |
| 6 | Reader extensions: tagged literals, :jolt/tagged handler | ✓ |
| 7 | LazySeq + PersistentHashSet | ✓ |
| 8 | Protocol system: defprotocol, extend-type, extend-protocol, reify, satisfies? | ✓ |
| 9 | REPL fixes: buffer-based output, collection rendering, cond fix | ✓ |
| 10 | Standard Library: clojure.string, clojure.set, clojure.walk, clojure.zip, clojure.edn, clojure.java_io, jolt.interop, jolt.shell, jolt.http | ✓ |
Phase 11: Fix Pre-existing Failures ✓
types.janet:ns?now accepts both structs and tablescore.janet:commentmacro wired into core-bindingssci/lang_stubs.clj: minimal SCI type stubs for bootstraptest-load-sci.janet: load stubs before SCI source files- Result: SciVar fixed. 1 remaining (deftype with
#?@— Phase 15)
Phase 12: Core Feature Completion ✓
applysupport in evaluator + compilerstrhandles nil correctly- 6 CLJS test files created (~120 assertions)
#()anonymous fn reader with%,%1,%2arg handling
Phase 13: Protocol Completion ✓
- reify dispatch: protocol methods work on reified objects
#()reader macro with gensym-based%arg handling- IFn protocol support in default invocation arm
- clojure.walk loads and
keywordize-keysworks - 4 test sections: reify dispatch, anon fn, extend-type, walk loading
Phase 14: Extend CLJS Ported Tests ✓
cljs-port-2.janetexpanded: 10 sections (12-21), 35→60 assertionscljs-port-5.janetcreated: sections 22-24, destructuring, metadata, fn compositionpr-strcompiler fix: maps to newcore-pr-str(notcore-str)every-predadded to core.janetvar-dynamic?andwith-metatests restored
Phase 15: SCI Bootstrap ✓
- ✅
sci.langnamespace loads completely (all 10 forms, including Var, Type, Namespace deftypes) - ✅ 9 SCI source files load without errors (impl/macros, impl/protocols, impl/types, impl/unrestrict, impl/vars, lang, impl/utils, impl/namespaces, core)
- ✅
prefer-method/remove-method/remove-all-methodspromoted to special forms (fix: auto-deref gave functions toget/put) - ✅ All 5 pre-existing test failures fixed:
cljs-port-1.janet—#{}Janet comment issue replaced with count-based comparisonscljs-port-2.janet—with-metaadded as core binding with table/setprotocljs-port-3b.janet—load-stringmulti-form loader for string.clj and set.cljcljs-port-5.janet—var-dynamic?core binding +^:dynamicdef handler fixphase5-test.janet—remove-methodspecial form fixed to eval-form first arg
- New core infrastructure:
core-with-meta(supports structs/tables via prototype),core-var-dynamic?,load-stringAPI,^:dynamicpropagation indefhandler core-strnow returns"nil"for nil (Clojure-compatible)core-metachecks:jolt/metafor with-meta'd values- Test suite: 317/317 pass, 0 fail
Phase 16: Remaining Core Library + Tests ✅
- ✅
evalimplemented as special form (interpreter + compiler), tested ineval-test.janet(4 assertions) - ✅
&rest destructuring,seqnil handling,vector/listequality verified working - ✅
syntax-quoteconfirmed working with unquote - ✅ 5 new CLJS ported test files (cljs-port-6 through -10): anon fns, symbols/keywords/lists, destructuring, range/concat/partition/sort, seq predicates/complement, when/if-let/doto
- ✅ 16 total CLJS ported test files, 440 assertions across 31 test files
- ✅ 317/317 tests pass, 0 failing scripts
Phase 19: Clojure Conformance Hardening ✅
Added test/conformance.janet — a harness that evaluates (= expected actual)
inside jolt (using jolt's own =, i.e. real Clojure semantics), seeded from
~/src/clojure/test. Drove fixes for a large class of real-Clojure idioms the
bespoke .janet suite never exercised. 80/80 conformance, 46/46 legacy suite,
0 SCI-load failures.
CRITICAL (core semantics):
- Collections as IFn (
([v] i),({m} k),(#{s} x)) via unifiedjolt-invoke - Unified vector representation on Janet tuples (PersistentVector was broken/disconnected)
=/reduce/vec/intonow realize lazy-seqs;into {}/into ()fixed- Lazy
iterate/range(infinite)/single-collmap(over lazy input) - Truly lazy
concat→ self-referentiallazy-cat/lazy-seqwork (fib-seq)
HIGH:
- Unified recursive destructuring (nested seq/map,
:strs/:syms/:or/:as, fn-params) defndocstring/attr-map handling (docstring'd fns were silently param-less)- Clojure
str/pr-str(nil→"", keywords/symbols, collections render readably) update/update-in/assoc-inon immutable maps; multimethod:defaultkey dispatch- defrecord inline protocol methods with field scope
requireloads stdlib namespaces from disk + aliased-ns resolution (s/join)
MED:
- New: peek/pop/subvec/reduce-kv/cycle/partition-all/reductions/dedupe/ keep-indexed/map-indexed/trampoline/format/read-string/fnil + letfn/doseq macros
- Recursive
jolt-equal?(nested sequential + order-independent map equality) - Lazy
filter/take-while/removeover lazy input - REPL: lazy-seq printer walks full chain (capped);
def/defnprint#'ns/name
Gap-closing batch (conformance grew to 157 cases): full atoms; typed
volatiles + delays (deref/force/realized?); quot/rem/mod sign semantics; a regex
subsystem (src/jolt/regex.janet, regex→PEG, #"..." literals, re-find/matches/
seq, string split/replace); sorted-map/sorted-set; Math/* statics; ex-info/
ex-data/ex-message; namespaced keywords; ~40 added core fns (split-at, take-nth,
butlast, filterv/mapv, reduced, min/max-key, find, some-fn, sequential?/ifn?/…)
plus defonce/macroexpand-1/letfn/doseq; and fixes to doto, assoc/update-on-vector,
frequencies, coll?, sort, partition.
Feature batch (conformance now 184): proper char type (\a reads to a
char value, char?/char/int/str, string seq/first/nth yield chars, \a printing);
transducers (map/filter/take/… no-coll arity returns a transducer; transduce/
into-xform/sequence/eduction/reduced; reduce honors reduced); compiler-path
IFn dispatch (jolt-call makes collections callable in :compile? mode; compiled
set/char literals); and a multi-line REPL (accumulates until the form is
balanced).
Deferred: full regex (lookaround/backrefs — the PEG-backed engine covers the common subset). STM (refs/agents/dosync) intentionally skipped.
Phase 17: Optimization
- Compiler improvements: inline small core functions
- PersistentHashMap dynamic bucket growth
- Benchmarks
Phase 18: Standard Library Completion
- Complete EDN reader/writer
- Complete java.io wrappers
- clojure.zip tests
Implementation Order
- ✅ Phases 0-16 (completed)
- Phase 17 (optimization: compiler inlining, PHM bucket growth, benchmarks)
- Phase 18 (stdlib: EDN reader/writer, java.io wrappers, clojure.zip tests)