Commit graph

703 commits

Author SHA1 Message Date
Yogthos
5e916433b8 Native SQLite + an HTTP server over FFI (ring-app foundation)
sqlite.ss: jolt.sqlite + jdbc.core (jolt-lang/db's API) over the system
libsqlite3 — open/close, exec, a prepared query returning row maps, text/int/
double parameter binding, last_insert_rowid. The sqlite3 C API is non-variadic
so it binds directly.

http-server.ss: a minimal HTTP/1.1 server over BSD sockets (socket/bind/listen/
accept/recv/send via FFI), one connection at a time on a background accept
thread, synchronous Ring handlers. Parses the request line + headers + a
Content-Length body into a Ring request map (:body a StringReader), formats a
Ring response map back. Exposed as jolt.http.server and, for the example, as
ring-janet.adapter/run-server. macOS and Linux socket-option constants handled.
2026-06-22 02:50:53 -04:00
Yogthos
b251e9166e jolt.http-client (curl-backed) + format width/justify flags
A synchronous HTTP client def-var!'d into jolt.http-client (get/post/put/delete/
head/request -> {:status :headers :body}), with :headers, :body, :query-params,
:content-type and :insecure?. It shells out to the system `curl` rather than a
direct libcurl FFI: on Apple Silicon curl_easy_setopt is variadic and Chez's
fixed-signature foreign-procedure can't place the value arg on the stack where
the ABI expects it, so a direct bind silently drops the option. curl gives the
same native TLS/redirect/gzip with no per-platform C shim.

format now honours width and the -/0 flags (%-30s, %5d, %05d), not just %.Nf
precision — it was emitting the directive literally.
2026-06-22 02:44:29 -04:00
Yogthos
33664ed5e0 jolt.png: a built-in PNG writer (ray-tracer-multi)
A minimal truecolor PNG encoder over Chez bytevectors — CRC-32 / Adler-32
framing with DEFLATE "stored" blocks, so there's no compressor to carry. Restores
the jolt.png built-in (image/put!/write) the old host provided; def-var!'d into
the jolt.png namespace and loaded in the CLI before the loader's baked-namespace
snapshot, so (require '[jolt.png]) resolves with no source file. Output verified
to decode as a valid PNG (signature, IHDR, CRC-correct chunks, inflatable IDAT).
2026-06-22 02:33:27 -04:00
Yogthos
af163bad59 java.util.HashMap shim + a value in the "No method" error
A mutable Map keyed by jolt values (jolt-hash/=) with put/get/putAll/containsKey/
size/remove/keySet/values/entrySet — enough for libraries that build a fast
lookup table (malli's fast-registry doto's a HashMap then .get's it). The
"No method M for value" dot-dispatch error now includes the value, which makes
a wrong-type interop target far easier to pin down.
2026-06-22 02:28:17 -04:00
Yogthos
2de0543613 More library-compat fixes from porting the examples (markdown, malli)
Reader / loader:
- #?@ splicing reader conditionals now actually splice the matched collection's
  items into the enclosing sequence; the splice flag was read but ignored, so a
  binding vector like [a #?@(:clj [b (.foo b)])] lost its alignment.
- the file loader reads by position and skips a top-level form that reads as
  nothing (a :cljs-only #?, a #_ discard, a trailing comment) instead of
  treating it as EOF — which silently dropped the rest of a large .cljc file.
- jolt's reader feature set now includes :clj (was {:jolt :default}). jolt is a
  Clojure/JVM-compatible host that emulates clojure.lang.* and java.* interop,
  so it reads the :clj branch of a .cljc library, not :cljs. This also lets four
  more reader-conditional corpus cases pass (floor 2726 -> 2730).

Backend:
- munge-name escapes ' (prime) -> _PRIME_; a Clojure symbol like f' otherwise
  emitted a bare ' into Scheme, which is the quote reader macro and unbalanced
  the output.

Host shims:
- clojure.java.io/writer (pass through a StringWriter, file-back a path) and a
  readLine on the string reader, so line-seq over (io/reader …) works (markdown).

A better "unsupported destructuring pattern: <pat>" error message.
2026-06-22 02:25:11 -04:00
Yogthos
7e2704642b deps.edn resolution + a file loader + a project-aware CLI
joltc grew from a single -e expression into a real project runner. require now
loads a namespace's .clj/.cljc from the source roots transitively (load-once),
so a multi-file project works; the corpus/unit gates load compile-eval.ss but
not the loader, so their alias-only require is unchanged.

jolt.deps resolves a deps.edn into ordered source roots — git + local deps only
(no Maven), breadth-first so a top-level pin wins, with aliases (:extra-paths/
:extra-deps/:main-opts) and tasks. Git deps clone into a sha-immutable cache
($JOLT_GITLIBS, else ~/.jolt/gitlibs) by shelling out to git via a new
jolt.host/sh primitive. jolt.main dispatches run -m / -M:alias / -A / repl /
path / a deps.edn task. The launcher passes the user's cwd as JOLT_PWD (the
project dir) since it cd's to the repo root for the runtime's relative loads.
2026-06-22 02:06:05 -04:00
Yogthos
85cec65937 Compiler fixes surfaced porting the examples to Chez
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.
2026-06-22 02:06:05 -04:00
Yogthos
0d4f84e1f2 corpus gate: skip the racy future-cancel cases instead of banking the floor on them
The two future-cancel cases — (future-cancel (future 1)) and the
future-cancelled? variant — depend on whether future-cancel catches a
trivial future in-flight, which is pure thread-scheduling luck. They were
allowlisted but still counted toward `pass` whenever the race resolved
favorably, so the floor (2728) silently assumed both passed. On a fast dev
machine they always pass; on CI's loaded shared runner one races to a
divergence, dropping pass to 2727 and failing the gate.

Skip them like the undelivered-promise case (neither pass nor fail) so the
race can't perturb the count. Floor drops to the deterministic 2726.
2026-06-22 01:29:24 -04:00
Yogthos
dfc34e6e71 spec: set! now supports deftype mutable fields 2026-06-22 01:19:21 -04:00
Yogthos
424ce75cf6 mutable deftype fields: (set! field val) in a method
deftype fields tagged ^:unsynchronized-mutable / ^:volatile-mutable can now be
reassigned in place from a method, as on the JVM. A jrec stores fields as cons
cells, so a new jolt-set-field! mutates the pair with set-cdr!. The deftype macro
rewrites (set! mutable-field v) in a method body to (set! (.-field inst) v), and
the analyzer compiles a (set! (.-field obj) v) target to jolt-set-field! — so
both the rewritten symbol form and an explicit interop (set! (.-root this) v) go
through one path. Field reads remain a snapshot at method entry, which is correct
for the universal read-then-set pattern (a repeated set! of the same field in one
call would read the entry value).

Closes the set!-of-local SCI failures: SCI load 202 -> 205/218.
2026-06-22 01:19:03 -04:00
Yogthos
b9ab750983 spec/ebnf: macroexpand order, set!, letfn primitive, numeric tower
Bring the formal definition in line with this session's language work:
- grammar.ebnf: numbers are a real tower (exact integer / Ratio / double); the M
  suffix reads a real BigDecimal, N an exact integer (drop the stale Janet note).
- 02-reader S5: M is a real java.math.BigDecimal with scale-insensitive equality.
- 03-special-forms: document the read -> macroexpand -> analyze order (macros
  expand before special-form dispatch); special-form heads are not shadowable but
  macros are and value-position locals may be named like a special; set! on a var
  sets the innermost binding (else root); letfn is a primitive with letrec*
  semantics.
2026-06-22 01:03:48 -04:00
Yogthos
212cd0399a special-form heads are not shadowable
Found in a read/eval review: a local named like a special form wrongly took
over operator position. (let [if (fn ...)] (if true 1 2)) returned the fn, but
per spec section 3 (and the reference) special-form heads are not shadowable;
only macros are. Two fixes: drop the (not shadowed) guard on the special-form
branch of analyze-list (so an (if ...) head is always the special), and prefix
a local whose name is a Scheme keyword when emitting (so a value local legally
named if does not shadow the (if ...) the back end emits). Value-position
locals named if/or/case still work.
2026-06-22 01:01:53 -04:00
Yogthos
f18ae3bd46 macroexpand-first analyzer order; one macro path; defmacro/letfn fixes
The analyzer checked special forms before expanding macros, the reverse of the
canonical read -> macroexpand -> analyze order (Clojure/CLJS analyze-seq). Move
macroexpansion to the front of analyze-list. Knock-on fixes:

- letfn was both a (broken) macro expanding to let* AND a primitive special
  (analyze-letfn, proper letrec*). Macroexpand-first surfaced the macro, breaking
  mutual recursion; remove the macro, keep letfn a primitive.
- defmacro is now compiled by the analyzer (a :set-var-style :defmacro node that
  defs the expander fn via the fn macro — so destructuring arglists desugar — and
  marks the var a macro), so a non-top-level (when … (defmacro …)) works. The
  runtime spine's separate top-level defmacro interception is removed: one path.

SCI load 162 -> 202/218.
2026-06-22 00:54:16 -04:00
Yogthos
e6ee17b055 compile (set! *var* val)
The analyzer punted set! as uncompilable. Add it as a special form: (set! sym
val) on a var emits jolt-var-set, which updates the innermost thread binding (or
the root when unbound), returning val. A local target (deftype mutable field) or
an interop (.-field) target stays uncompilable for now. Also defines
*warn-on-reflection* (false) so set! on it resolves. SCI load 186 -> 196/218.
2026-06-22 00:40:46 -04:00
Yogthos
86aa89c832 uniquify duplicate fn params (macro _ _ expanders)
Chez rejects duplicate lambda formals, so any (fn [_ _] ...) failed to compile
— including every macro expander, whose &form/&env slots both expand to _. The
analyzer now renames each earlier duplicate param to a fresh name (Clojure binds
the last occurrence, so the earlier ones are shadowed and unreferenceable). SCI
load 162 -> 186/218.
2026-06-22 00:35:16 -04:00
Yogthos
3721423a64 real chunked seqs for vector seqs
A vector's seq is now a real chunked-seq (chunked-seq? true), matching Clojure/
CLJS. Each vector-seq cell carries its backing vector + element index as two
cseq fields (cvec/ci, no extra allocation vs the old lazy cell), so:
  - chunk-first hands out a 32-element block (a pvec slice), chunk-rest is the
    seq at the next block boundary — the ChunkedSeq contract (chunk-first ++
    chunk-rest == the seq);
  - reduce/transduce take a fast path that walks the backing vector by index in
    a tight loop with no per-element seq cells (reduce over a 1M-vector ~0.4s).
The seq cell stays a cseq, so first/rest/count/printing and the ~26 cseq?
dispatch sites are untouched. The eager chunk-buffer model (chunk-buffer/chunk/
chunk-cons) is preserved for the round-trip case. No seed change (runtime only).
2026-06-22 00:21:05 -04:00
Yogthos
0e4ccc97e0 (type record) returns its class-name string, not a symbol
(type r) returned a symbol user.TyR, so (= (symbol (str (type r))) (type r))
was true; the JVM's type is a Class (not a Symbol) so it's false. jolt models
classes as strings, so a record's type is now its ns-qualified class-name
string — equal to (class r), as on the JVM where type and class coincide for a
record. The symbol-keyed print-method defmethods already fall through to the
default record printing, so they're unaffected. Closes type-of-record.
2026-06-22 00:05:08 -04:00
Yogthos
ab96650fbb real BigDecimal type (bigdec, M literals)
bigdec / 1.5M / 0.0M silently produced doubles. Add a jbigdec value type
{unscaled, scale} over Chez exact integers (host/chez/bigdec.ss): value =
unscaled * 10^-scale. An M-suffix literal reads to a :bigdec form that the back
end lowers to jolt-bigdec-from-string (same IR-leaf path as #inst/#uuid); bigdec
coerces a number/string. Equality is by value (1.0M = 1.00M true, 3M = 3 false),
str drops the M and pr keeps it, class is java.math.BigDecimal, decimal? is true.
Arithmetic contagion isn't modelled (out of scope). The old corpus cases passed
spuriously as doubles; they now exercise a genuine BigDecimal.
2026-06-22 00:01:01 -04:00
Yogthos
7db5fabc8d resolve ^Type hint to canonical class name in var :tag
(def ^String tv ...) left (:tag (meta (var tv))) as the unresolved "String";
the JVM compiler resolves the hint to java.lang.String at def time. Add a
resolve-class-hint host seam (built from the existing class-token table) and
resolve a def's :tag through it in the analyzer. The reader path
(read-string "^String x") stays unresolved, matching the JVM (only the
compiler resolves). Closes ^Type-tag-on-var.
2026-06-21 23:52:47 -04:00
Yogthos
d1c2811d13 *in* is a Reader, not a map
(map? *in*) was true because *in* was a plain map of read-line-fn/read-fn
closures; the JVM *in* is a java.io.Reader so map? is false. A defrecord
doesn't help (records are maps). Make the reader a reify over a new IReader
protocol — a non-map value — and route read/read-line/read+string/line-seq
through its -read-line/-read-form/-read+string methods instead of keyword
access. with-in-str's __string-reader and the stdin *in* both reify it.
Closes *in*-bound + *in*-is-bound.
2026-06-21 23:45:24 -04:00
Yogthos
8ce00d29fd embed a namespace value spliced into a form (~*ns*)
A macro like (defmacro cur-ns [] `(str ~*ns*)) splices the live *ns* value
into its expansion, leaving an opaque jns object as a list element. The
analyzer had no way to carry a runtime value and threw uncompilable — the last
remaining corpus crash. Recognize a jns via the host contract (form-ns-value?)
and emit a :the-ns leaf that reconstructs it by name (intern-ns!) at the call
site, the same IR-leaf pattern as regex/inst/uuid. Closes unquote-*ns*-in-
template; corpus crash count -> 0.

A namespace fast path rather than a general constant pool: it's the only
embedded-value case in the corpus and the common real-world one (libs splice
~*ns*). A general pool can come later if other value types appear.
2026-06-21 23:40:59 -04:00
Yogthos
ccc76fd69f extenders excludes inline defrecord/deftype impls
deftype/defrecord inline protocol methods went through extend-type ->
register-method, so a record implementing a protocol inline showed up in
(extenders P) — the JVM only lists extend/extend-type/extend-protocol
registrations there (inline impls compile into the class). Add
register-inline-method: it registers for dispatch under the record tag but
skips the extender mark. The mark lives inside type-registry so the per-case
corpus prune restores it. Closes corpus lists-extended-type + seq-of-tags.
2026-06-21 23:35:11 -04:00
Yogthos
10d2b992f7 Prune stale corpus allowlist entries (now-passing print-method + misc)
The defmethod/print-method record cases, symbol-hint, and source-order entries no
longer diverge (closed by the defmethod-setup + earlier fixes); drop them. 0 new
divergences, corpus 2720/2741, 20 genuine gaps remain.
2026-06-21 22:49:34 -04:00
Yogthos
632a90cae2 definterface returns the name (not a var); ns-imports returns java.lang defaults
definterface now expands to (do (def name {}) 'name) so (var? (definterface ...))
is false, matching the JVM where it yields the interface Class. ns-imports returns
the 96 auto-imported java.lang classes (short symbol -> canonical name) so
(count (ns-imports 'user)) is 96. Re-minted for the macro change. Corpus 2718->2720.
2026-06-21 22:47:23 -04:00
Yogthos
9e0a930eb4 Typed-array identity + JVM flonum printing (Inc 3) 2026-06-21 22:36:14 -04:00
Yogthos
f2747679e9 Prune now-passing class/atom entries from the corpus allowlist
class number/string/keyword/name + atom?/instance? Atom pass after the class
refinements; drop their stale allowlist entries. Corpus 2705/2741 0 new div.
2026-06-21 20:06:21 -04:00
Yogthos
e3674d17a7 defmethod auto-create copies clojure.core dispatch (fix SCI regression)
The prior fix resolved an unqualified defmethod to clojure.core's multifn, which
broke SCI (it relies on per-ns shadow multimethods — hung loading core_protocols).
Keep the shadow, but when auto-creating it copy the dispatch fn + default from a
same-named clojure.core multifn (e.g. print-method's 2-arg dispatch) instead of
the 1-arg identity that crashed (print-method x w). Also trim the FQN class
tokens to value classes only (the collection interfaces shadowed names SCI uses).

Corpus 2705/2741 0 new div; SCI 162/218 restored; cross-ns + direct print-method
overrides work.
2026-06-21 20:04:13 -04:00
Yogthos
bb6c9eeb29 Class refinements: per-type class names, FQN tokens, instance? built-ins
- (class x) returns per-type JVM class names (Long/Double/Ratio/Character/Atom),
  not a blanket java.lang.Number.
- register fully-qualified class tokens (java.lang.Long, clojure.lang.Keyword,
  clojure.lang.Atom, ...) that self-evaluate to their name, so (= (class 1)
  java.lang.Long) and (instance? clojure.lang.Atom x) resolve.
- instance? recognizes Long/Double/Ratio/Character/Symbol/Atom/IFn built-ins.

Closes class number/string/keyword/name, instance? Atom, atom?. Corpus 2699->2705.
2026-06-21 19:12:40 -04:00
Yogthos
b03da19ba8 defmethod resolves the multifn like a var (find clojure.core's)
(defmethod print-method ...) from the user ns auto-created a stray user/print-method
with identity dispatch -> 'incorrect number of arguments 2' on (print-method x w).
Resolve an unqualified multifn name through current-ns -> :refer -> clojure.core
(like var resolution) before auto-creating. Fixes direct print-method/print-dup
override calls; pairs with the cross-ns defmethod fix.
2026-06-21 18:47:15 -04:00
Yogthos
4d1ec44676 JVM parity: unchecked-char -> char, pr of infinities -> ##Inf
Allowlist review found three addressable divergences:
- unchecked-char returned a number; the JVM returns a char.
- the readable printer (pr-str, coll elements, the -e/REPL printer) rendered
  infinities as Infinity/inf; Clojure's readable form is ##Inf/##-Inf/##NaN
  (str/print still gives Infinity). So (pr-str ##Inf) => ##Inf, (str [##Inf]) =>
  [##Inf], (str ##Inf) => Infinity.

Corpus 2695->2698; allowlist 43->40 (drop the 3 now-passing entries). Re-minted.
2026-06-21 17:55:05 -04:00
Yogthos
518683ccd6 syntax-quote leaves interop forms unqualified (jolt-z1zu)
A macro that syntax-quoted interop — `(.. (StringBuilder.) (.append x)) — had
its .method / Class. / .-field heads qualified to the compile ns (user/.append,
user/StringBuilder.), so they read as 'Unknown class user' at expansion. Like
Clojure, leave interop-head symbols bare in syntax-quote. Fixes any macro
templating interop, not just the one corpus case. Corpus 2694->2695.
2026-06-21 17:49:11 -04:00
Yogthos
f15a4e7747 reduce dispatches to a reify's IReduceInit reduce method (jolt-z1zu)
(reduce f init (reify clojure.lang.IReduceInit (reduce [_ f i] ...))) tried to
seq the reify and threw 'not seqable'. When the coll is a reify carrying a reduce
method, drive the reduction through it. Corpus 2693->2694.
2026-06-21 17:46:20 -04:00
Yogthos
31a453d492 (. obj :kw) is a keyword lookup (JVM parity)
The . special form rejected a non-symbol member; a keyword member now lowers to
an invoke of the keyword on the target ((. {:value 41} :value) => 41, as on the
JVM). Added a form-keyword? contract seam. Corpus 2692->2693. Re-minted.
2026-06-21 17:13:40 -04:00
Yogthos
45596d7239 import: bring a cross-ns deftype/record ctor into the current ns (jolt-c2l1)
(:import [other.ns Type]) was a no-op (import unbound), so (Type. ...) failed
with 'Unknown class'. Bind import to register each named type's ctor closure
under the current ns. Corpus 2691->2692.
2026-06-21 17:11:15 -04:00
Yogthos
547a8c6d17 The .. threading macro analyzes as a macro, not interop (jolt-c2l1 tail)
(.. x m ...) failed: the analyzer classified the .. head as a .method interop
call (method-head? matched any "."-prefixed name) and form-special?
(hc-interop-head?) also flagged it, so it never reached the macro check. Exclude
".." from both (the char after "." being "." means the threading macro, not
.method). Corpus 2690->2691. Re-minted.
2026-06-21 17:08:11 -04:00
Yogthos
e7f5bcb58d assoc! fills nil for a trailing lone key (JVM parity)
JVM assoc! is variadic: with a complete first pair present (>=3 kvs), a trailing
lone key fills nil ((assoc! t :a 1 :b) => {:a 1 :b nil}); a lone key alone (1 kv)
is still a wrong-arity throw. jolt delegated to the strict persistent assoc which
threw on any odd count. Pad a trailing nil for odd kvs >=3. Corpus 2688->2690.
2026-06-21 17:03:10 -04:00
Yogthos
c788e86f1a Cross-ns def/require/use/defmethod through the spine (jolt-c2l1)
The per-form eval passed a FIXED compile-ns to every subform of a top-level do,
so a runtime (ns ...)/(in-ns ...) didn't redirect later defs/refs — defs landed
in the wrong ns and qualified refs hit host-static ("Unknown class"). Thread
the current ns: each subform analyzes in (chez-current-ns), which ns/in-ns move.

That exposed two more gaps, now fixed:
- use refers ALL of a target's public vars (a refer-all table consulted by
  chez-resolve-refer) — was bound to plain require (explicit :refer only).
- defmethod on a QUALIFIED multifn (cf.mm/ext from another ns) resolves in the
  symbol's ns, not the current one (was auto-creating a stray multifn).

Corpus 2684->2688, 0 new divergences; floor raised. No re-mint (runtime shims).
2026-06-21 16:56:46 -04:00
Yogthos
76f8274603 sorted-map entries are real map-entries (jolt-jk23)
sorted-map seq/first/entries built plain [k v] vectors, so map-entry? was false
and key/val threw. Build them via a new jolt.host/map-entry seam (entry-flagged
pvec), matching a regular map's entries. Re-minted.
2026-06-21 16:43:05 -04:00
Yogthos
9ca30a236d CI runs behavior gates; self-host fixpoint is dev-only (jolt-8479)
The self-host byte-fixpoint (make selfhost) only holds on the Chez that minted
the seed — CI's Debian Chez emits byte-different output for some constructs
(isolated to the dedupe re-mint), so it failed there. The checked-in seed RUNS
correctly on any Chez, so CI now runs 'make ci' (corpus/unit/smoke/sci/certify);
'make test' keeps selfhost for local dev. Cross-version emit determinism tracked
in jolt-8479.
2026-06-21 15:46:40 -04:00
Yogthos
7d0e2f2b61 dedupe: add the 0-arg transducer arity (jolt-05i2)
(into [] (dedupe) coll) / (sequence (dedupe) coll) threw an arity error — dedupe
was [coll]-only. Add the 0-arg stateful transducer (tracks [seen? prev] in a
volatile, no sentinel). Re-minted.
2026-06-21 15:42:01 -04:00
Yogthos
f6937dd7df Bind clojure.core/float (= double on Chez)
Chez has no single-float type, so float coerces to a flonum like double.
Corpus 2683->2684; floor raised.
2026-06-21 15:39:21 -04:00
Yogthos
cf0b544baf Host interop fixes: ==/time/subvec/defonce + corpus cleanup
- == 1-arg returns true for any value (Clojure short-circuits before the number
  check), not 'requires numbers'.
- current-time-ms wired to now-millis so the time macro works.
- subvec truncates float/ratio indices via long (Scheme quotient rejects flonums).
- defonce checks bound? not var-get — in a top-level do the name is already an
  unbound interned cell, which var-get throws on.
- drop the line-seq corpus row (used janet/spit, N/A); allowlist char-array
  (needs Class/forName "[C").

Corpus 2678->2683, floor raised. Re-minted. Full gate green; CI green.

jolt-cf1q.7
2026-06-21 15:36:41 -04:00
Yogthos
dd0e0b55cc CI: invoke Chez via a scheme wrapper, not a symlink
Chez derives its boot-file name from argv0, so a symlink named chez looks for a
nonexistent chez.boot (CI failed at the first gate step). Replace the symlink
with a wrapper that exec's scheme, preserving argv0 so the boot files resolve.
2026-06-21 15:32:16 -04:00
Yogthos
39efb29134 Drop N/A janet.* cases from the corpus (jolt-0obq)
Remove the 17 rows that exercised the Janet FFI bridge (interop/janet bridge,
interop/jolt.interop) and the Janet build-time env scrub (host-interop/bake env
scrub, janet.os/setenv) — none exist on any non-Janet host, so they only added
crash noise. Portable interop is covered elsewhere. corpus 2920->2903 rows;
gate 2678/2742 0 new div, certify 0 new/0 stale.
2026-06-21 15:08:14 -04:00
Yogthos
017a1bc8c4 SCI conformance gate (pure Chez)
Re-port the SCI compatibility stress test to joltc: host/chez/run-sci.ss loads
borkdude/sci's own source (vendor/sci, re-vendored) through the spine and
requires its forms to compile+eval. Floor-gated at 160/218 forms (the tail is
genuine host gaps — set! on vars, some macro/def shapes); raise as they close.
Wired into 'make test' (skips if the submodule isn't checked out).

jolt-cf1q.6
2026-06-21 12:06:18 -04:00
Yogthos
48e2ef5910 Scrub dangling Janet references; drop dead Janet-coupled files
Rephrase comments that pointed at deleted Janet files (emit.janet, the seed
sources, 'the Janet back end punts ...') to present-tense descriptions of the
Chez behavior. Comment/docstring-only; the self-host fixpoint is unchanged
(comments don't affect the compiled seed).

Delete five files that were Janet-host shims with no Chez path: clojure.java.io
(provided natively by host/chez/io.ss), and jolt.{nrepl,png,interop,shell}
(the janet.* bridge, os/shell, janet.net — none exist on Chez).

jolt-cf1q.6
2026-06-21 12:01:04 -04:00
Yogthos
750ce05716 Docs + CI for the Chez-only substrate
Rewrite the README, CLAUDE.md build/architecture sections, test/chez/README,
and conformance SPEC for the Janet-free world: bin/joltc + make test, the
self-hosting bootstrap, the frozen JVM-sourced corpus. CI installs Chez + JDK/
Clojure and runs 'make test' (was Janet/jpm).

jolt-cf1q.6
2026-06-21 11:34:48 -04:00
Yogthos
58d03d67be Delete the Janet host — Chez is the sole substrate
Remove the Janet seed (src/jolt/*.janet: reader, value layer, vars/ns, the
tree-walking interpreter, the Janet backend, the optimizing compiler), the
Janet->Scheme cross-compiler (host/chez/{driver,emit,jolt-chez}.janet),
bin/jolt-chez, the jpm build (project.janet) and the Janet test runner
(run-tests.janet), plus the entire Janet test suite. jolt now builds and runs
on Chez alone: bin/joltc off the checked-in seed, bootstrap.ss to rebuild it.

The portable Clojure stays: jolt-core/**, host/chez/**.ss, and the stdlib +
tooling under src/jolt/clojure + src/jolt/jolt (read by the seed build, no
Janet). The gate is 'make test' (self-host, corpus, unit, cli smoke, certify).
Drop the sci and clojure-test-suite submodules (used only by deleted Janet
integration tests); irregex stays.

Filesystem corpus/unit cases that probed project.janet now probe README.md.

jolt-cf1q.6
2026-06-21 11:29:03 -04:00
Yogthos
5c1fdfc336 Pure-Chez test gates (no Janet)
Add a Janet-free gate so correctness can be judged with only Chez + Clojure:
- host/chez/run-corpus.ss: corpus.edn vs JVM expecteds, lifting the per-case ns
  isolation from the old Janet driver; reads corpus.edn via the jolt reader.
- host/chez/run-unit.ss + test/chez/unit.edn: the host-specific unit cases,
  evaluated in-process and compared to baked expecteds.
- host/chez/selfcheck.sh: self-host fixpoint (bootstrap.ss rebuild == checked-in seed).
- host/chez/smoke.sh: real bin/joltc CLI smoke.
- host/chez/remint.sh: re-mint the seed to a byte-fixpoint after a source change.
- Makefile: 'make test' runs the lot; 'make remint' rebuilds the seed.

Numbers match the Janet gate: corpus 2679/2757 0 new div, unit 450/450, certify
0 new/0 stale.

jolt-cf1q.6
2026-06-21 11:22:32 -04:00
Yogthos
9a273e71cd Move the Chez test oracle off Janet
The unit tests in test/chez/_*.janet now drive bin/joltc (the zero-Janet
spine) and judge against baked expected values instead of a live build/jolt
run. Ten of them captured the oracle from build/jolt per case; their values
are now literals (one env-dependent javastatic case became a predicate so it
stays portable). The rest already had literal expecteds with a redundant
build/jolt sanity check, now dropped.

Retire emit-test/emit-parity/reader-parity: they compared the Chez/Clojure
path against a live Janet evaluation, emitter, or reader. That migration check
is done, and run-corpus-zero-janet (Chez analyzer vs the JVM corpus) plus
certify.clj cover correctness now.

Rewrite the README for the current zero-Janet gate.

jolt-5oci
2026-06-21 09:48:49 -04:00