Commit graph

11 commits

Author SHA1 Message Date
Yogthos
e434356590 Replace str-render/instance-check set! chains with registries
jolt-str-render-one and instance-check were each extended by a chain of
set!-wrapping closures spread across ~10 and ~5 host files, so the real
behavior of either was scattered and load-order-dependent. Give each a
registry the base file owns: converters.ss/records-interop.ss define the
registry plus a register-* helper, and each extending file registers one arm
instead of capturing %prev and set!-ing the global.

str-render arms are type-disjoint; instance-check arms run newest-first (the
old outermost-wins order) and may return 'pass to defer. The string-token ->
symbol normalization the natives-array arm did for every inner arm moves to
the dispatcher head; array tokens stay strings for that arm to decide.

jolt-ogib.14. Runtime-only shims, no re-mint.
2026-06-23 09:05:36 -04:00
Yogthos
33eff7c7d8 Clean up codebase: rename stdlib layer, strip porting residue, fix tooling
Rename src/jolt -> stdlib (the runtime-loaded layer; jolt-core stays the
seed-baked layer) and update the loader / emit-image / doc paths. Drop dead
code: the spike/ experiments, the duplicate clojuredocs-export.edn (json moves
to tools/), the Janet-era jolt.http binding, and the orphaned
persistent_vector.clj whose ns/path didn't even match.

Strip porting residue from comments and docstrings across host/chez, jolt-core,
stdlib, tests, and docs: internal issue ids, "Phase N" markers, and the "vs
Janet" historical exposition, leaving present-tense descriptions and the real
JVM-Clojure semantic contrasts. Same pass over the corpus suite labels. The seed
is unchanged (docstrings/comments aren't emitted), so the self-host fixpoint and
corpus are untouched.

Port tools/spec_coverage.py off the dead janet probe to bin/joltc and regenerate
coverage.md; drop the dead :host/janet rule from certify.clj and regenerate the
conformance profile. Add docs/host-interop.md (the JVM shims and how to register
your own host class from a library) and a writing-style note in CLAUDE.md.

Stabilize the four racy concurrency corpus cases (future-cancel and agent
send/send-off): give the future a sleeping body and the agent a slow action, so
cancel reliably catches an in-flight future and deref reliably reads the
pre-update snapshot. They certify deterministically now, so drop their :flaky
allowlist entries and the orphaned legend.
2026-06-22 22:18:00 -04:00
Yogthos
c18f8087f0 Real nREPL interrupt + thread-local *ns* (jolt-amzy, jolt-6rld)
Two nREPL divergences the library shakeout surfaced — both have a real host
mechanism on Chez.

Interrupt (jolt-amzy): Chez's engine timer (set-timer + thread-local
timer-interrupt-handler) is polled at procedure-call / loop back-edges, so a
running computation — even a tight loop — can be aborted from another thread.
concurrency.ss adds jolt.host/{make-interrupt, interrupt!, run-interruptible}: an
interrupt token is a shared box; run-interruptible arms a periodic timer whose
handler escapes (call/cc) when the token is set, throwing {:jolt/interrupted true}.
The eval thread is reused, not abandoned. (A thread blocked in a __collect_safe
foreign call only sees it on return — like the JVM not killing native code.)

Thread-local *ns* (jolt-6rld): chez-current-ns is now a Chez thread-parameter, so
each session worker / future has its own current ns (vars stay global, only the
pointer is per-thread). *ns* reads derive from it (dyn-binding.ss), and a bound
*ns* drives chez-current-ns — so (binding [*ns* the-ns] (load-string code))
resolves against the-ns, and concurrent in-ns across threads don't clobber each
other. Single-threaded behaviour is unchanged. All runtime .ss — no re-mint.
2026-06-22 18:57:16 -04:00
Yogthos
ccf93c896a Resolve :use/:only refers, refer-aware syntax-quote, alias-aware resolve
Three related namespace-resolution fixes surfaced porting the clojure.tools.logging
library, all general:

- chez-register-spec! treats a :use :only vector like :require :refer, so a
  (:use [ns :only [names]]) clause actually brings those names in. Before, a bare
  reference to an :only'd name resolved to nil.
- syntax-quote (hc-sq-symbol) qualifies a referred name to its SOURCE namespace,
  not the compile namespace — so a macro that syntax-quotes a referred var (e.g.
  clojure.tools.logging/spy reaching clojure.pprint's pprint) expands correctly.
- resolve consults :as aliases and :refers like ns-resolve does; (resolve
  'alias/name) was returning nil because the alias wasn't expanded.
2026-06-22 10:25:35 -04:00
Yogthos
10e3a00777 Host completeness for the ring stack (ring-app)
Gaps surfaced loading ring-core / hiccup / config / tools.logging on Chez:

- clojure.java.io/resource — resolve a named resource against the loader's
  source roots (no classpath), returning a slurp-able File.
- (Object.) constructor — a fresh distinct value (lock / unique sentinel).
- *out* / *err* as dynamic vars over a port-writer, so (binding [*out* *err*] …)
  and #'*out* compile and run (tools.logging, selmer).
- :refer :all now registers a refer-all relation, so an unqualified var from a
  (require '[ns :refer :all]) resolves at compile time — including #'var.
- java.lang.Character interop ((.toString \+) etc.).
- StringReader accepts a char[] ((StringReader. (char-array s))), not just a
  String.
- the \p{L} translation stops just below the UTF-16 surrogate gap (\x{D7FF})
  instead of \x{10FFFF} — a range across the surrogates made irregex's char-set
  construction call integer->char on a surrogate and crash.
2026-06-22 03:20:31 -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
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
1ba19759c8 Chez parity: missing native core fns + ns runtime fns (jolt-cf1q.7)
Native Chez shims for clojure.core fns that live in the Janet seed but had none
on the zero-Janet spine (they resolved to nil -> "not a fn"):

- host/chez/natives-parity.ss: hash / hash-combine / hash-ordered-coll /
  hash-unordered-coll (24-bit masked like core_extra), transient?, rseq (vectors +
  sorted colls), cat (transducer).
- jolt-invoke now dispatches a TRANSIENT vec/map/set as a fn (callable on the JVM):
  ((transient [10 20 30]) 1) -> 20.
- ns.ss: ns-resolve, ns-imports, remove-ns, intern, alias, ns-unalias, refer,
  ns-refers, refer-clojure, alter-meta!, reset-meta!, and a real ns-aliases (was a
  stub returning {}).
- runtime (require ...)/(use ...) now register :as/:refer into the Chez ns tables
  (was a no-op). The Chez analyzer already pre-registers at analyze time, but when
  the JANET analyzer compiled the form (prelude path) the Chez tables stayed empty,
  so ns-aliases/ns-resolve over an alias diverged — this fixes both paths.

Seed unchanged (overlay doesn't reference these at mint time). zero-Janet corpus
2567->2600, prelude 2557->2590, 0 new divergences on either; Janet gate + JVM cert
green. Filed jolt-vgrp for the pre-existing var-get-of-scalar-native-op quirk.

jolt-cf1q.7
2026-06-20 15:46:11 -04:00
Yogthos
24ef2b8d4b Chez inc7: :refer support — Chez analyzer reaches parity with the oracle
(require '[clojure.string :refer [blank?]]) then an unqualified blank? now
resolves. chez-register-spec! registers :refer names (in addition to :as) into a
refer table; hc-resolve-cell's unqualified branch consults it before clojure.core.

Zero-Janet corpus parity 2293 -> 2295 = the Janet-hosted oracle's exact pass
count. The self-hosted Chez compiler (read -> analyze -> emit -> eval, no Janet)
now compiles every corpus case the Janet-hosted compiler does, with 0
divergences. Remaining failures are shared runtime breadth (host interop,
futures, runtime eval) deferred to Phase 4 / jolt-r8ku. Floor 2295.
2026-06-20 02:06:27 -04:00
Yogthos
011e5d6337 Chez inc7: namespace :as aliases on the Chez-hosted analyzer
(require '[clojure.string :as s]) then s/foo crashed "Unknown class s": the
Chez analyzer resolved a qualified symbol's ns literally, and there was no
alias table (ns.ss jolt-ns-aliases was a stub, require a no-op). Add an alias
table + chez-register-spec! (parses [ns :as a]) in ns.ss; hc-resolve-cell now
resolves a qualified ns through it; compile-eval pre-registers a form's
require/use :as aliases before analysis (analysis precedes the runtime require,
so the runtime require staying a no-op is fine). The batched gate runner clears
the alias table between cases.

Zero-Janet corpus parity 2159 -> 2240, 0 divergences. spine-test 35/35.
2026-06-20 01:25:01 -04:00
Yogthos
32e2d8bd58 Chez Phase 2 (inc K): namespace value model (jolt-yxqm)
Reimplement the ctx-coupled seed ns natives over the rt.ss var-table, since
Chez has no ctx. host/chez/ns.ss adds a jns namespace value + a registry and
binds find-ns/the-ns/create-ns/in-ns/all-ns/ns-publics/ns-map/ns-interns/
ns-aliases/resolve/find-var/ns-unmap/*ns* into clojure.core.

The resolve friction: native-ops (+, map, …) are inlined at emit so they have
no var-cell, and (resolve '+) was nil — diverging from Clojure where it's a
var. Added a defined? flag to the var-cell record (set by def-var!/declare-var!,
left false on a lazily-materialised forward ref) and def-var!'d every native-op
name to its value-position proc, so resolve returns the cell iff genuinely
defined. ns-unmap clears the flag. resolve never interns an empty cell
(var-cell-lookup is non-creating).

ns-name is overridden natively in post-prelude (the overlay reads
(get ns :name), nil on a jns record); the printers render a namespace as its
name. *ns* binds to the user ns; in-ns re-binds it. use/require cross-ns
switching stays deferred to Phase 3 (the analyzer bakes a def's target ns at
compile time).

Prelude parity 1951 -> 1969, 0 new divergences; four now-passing allowlist
entries dropped (ns *ns* cases + str-of-a-var). New focused gate test/chez/
_ns.janet (19 cases, expectations from the JVM-canonical build/jolt).
2026-06-18 16:49:35 -04:00