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
This commit is contained in:
Yogthos 2026-06-21 12:01:04 -04:00
parent 750ce05716
commit 48e2ef5910
53 changed files with 418 additions and 675 deletions

View file

@ -1,4 +1,4 @@
;; clojure.core — kernel tier (stage just above the Janet seed).
;; clojure.core — kernel tier (stage just above the host primitives).
;;
;; These are the structural fns the self-hosted compiler itself uses
;; (jolt.analyzer): second/peek/subvec/mapv/update. Because the compiler must be
@ -6,12 +6,11 @@
;; before it is built. So this tier is loaded FIRST and, in compile mode, is
;; bootstrap-compiled directly into clojure.core (not routed through the
;; self-hosted pipeline, which would need these to already exist — the
;; circularity that previously forced `second` to stay in Janet). With this tier
;; in place the analyzer is built against the Clojure definitions and the Janet
;; primitives are gone.
;; circularity that previously forced `second` to stay a host primitive). With this tier
;; in place the analyzer is built against the Clojure definitions.
;;
;; Constraint: depend only on core-renames primitives (first/next/nth/count/conj/
;; vec/map/apply/assoc/get/…, all hardwired to the Janet seed) and on each other.
;; vec/map/apply/assoc/get/…, all hardwired to host primitives) and on each other.
(defn second [coll] (first (next coll)))

View file

@ -157,8 +157,8 @@
(defmacro declare [& syms]
`(do ~@(map (fn* [s] `(def ~s)) syms)))
;; destructure — Clojure's binding-vector expander, ported from the Janet seed
;; (was core-destructure). Turns a binding vector that may contain destructuring
;; destructure — Clojure's binding-vector expander.
;; Turns a binding vector that may contain destructuring
;; patterns into a plain binding vector (alternating symbol / init-form) built from
;; nth/nthnext/get, so the COMPILER only ever sees plain symbols (analyze-bindings
;; rejects patterns). `let` consumes it directly; `loop`/`fn` reuse it transitively

View file

@ -1,5 +1,5 @@
;; clojure.core — seq tier. Pure-Clojure leaf sequence fns on top of the kernel
;; tier (00-kernel) and the Janet seed. Loaded after the kernel tier; in compile
;; tier (00-kernel) and the host primitives. Loaded after the kernel tier; in compile
;; mode these self-host through the now-built analyzer (interpreted otherwise).
;;
;; Migration rule for adding fns here: the fn must (1) NOT be in

View file

@ -16,7 +16,7 @@
;; neg? throws on non-numbers via <, as Clojure's Numbers.isNeg does.
(defn neg? [x] (< x 0))
;; even?/odd? stay in the seed: (filter even? ...) is idiomatic-hot and the
;; even?/odd? stay host primitives: (filter even? ...) is idiomatic-hot and the
;; overlay versions cost an extra call layer per element (seq-pipe bench 4x).
;; Variadic bit ops — canonical Clojure arities folding the binary host op
@ -355,9 +355,9 @@
(make-hierarchy) (partition 2 deriv-seq))
h))))
;; --- Stage 3 tier shrink: pure-over-core leaves moved off the Janet seed ----
;; --- Stage 3 tier shrink: pure-over-core leaves moved off the host primitives ----
;; Representation predicates over the overlay's own predicates (no Janet reps).
;; Representation predicates over the overlay's own predicates.
(defn sequential? [x] (or (vector? x) (seq? x)))
(defn associative? [x] (or (map? x) (vector? x)))
(defn counted? [x]
@ -381,10 +381,10 @@
;; realized?: defined on the pending types only (delay/lazy-seq/future read
;; Tagged-value predicates. The constructors (atom/volatile!/...) stay in Janet,
;; but every tagged value carries its kind under :jolt/type (records under
;; :jolt/deftype), reachable via get — which is nil on non-tables — so the
;; predicates are pure over get and move out of the seed.
;; Tagged-value predicates. The constructors (atom/volatile!/...) are host
;; primitives, but every tagged value carries its kind under :jolt/type (records
;; under :jolt/deftype), reachable via get — which is nil on non-tables — so the
;; predicates are pure over get.
(defn atom? [x] (= (get x :jolt/type) :jolt/atom))
(defn volatile? [x] (= (get x :jolt/type) :jolt/volatile))
(defn reader-conditional? [x] (= (get x :jolt/type) :jolt/reader-conditional))
@ -418,7 +418,7 @@
:else (throw (str "pop not supported on: " coll))))
;; doall/dorun: realization boundaries. dorun walks (optionally at most n
;; steps — the Janet seed version ignored n); doall walks then returns coll.
;; steps); doall walks then returns coll.
(defn dorun
([coll]
(loop [s (seq coll)]
@ -791,7 +791,7 @@
(defn numerator [x] (throw (ex-info "numerator requires a ratio (Jolt has no ratios)" {})))
(defn denominator [x] (throw (ex-info "denominator requires a ratio (Jolt has no ratios)" {})))
;; No class hierarchy on the Janet host.
;; No class hierarchy on this host.
(defn supers [x] #{})
;; Like Clojure's munge: rewrite dashes to underscores, preserving the argument's
@ -949,7 +949,7 @@
;; inst-ms itself.
(defn inst-ms* [i] (inst-ms i))
;; Canonical comp — here rather than the seed so each stage is invoked with
;; Canonical comp — here rather than a host primitive so each stage is invoked with
;; jolt call semantics: (comp seq :content) works because the keyword stage
;; goes through IFn dispatch (raw Janet keyword application does not).
(defn comp
@ -1078,7 +1078,7 @@
(let [xf (xform f)]
(xf (reduce xf init coll)))))
;; into stays in the seed: it's perf-wall hot (the into-vec bench pays ~11%
;; into stays a host primitive: it's perf-wall hot (the into-vec bench pays ~11%
;; through the overlay call layers — same lesson as even?/odd? in round 4).
;; eduction is EAGER on jolt (documented divergence, as before): the composed
@ -1116,8 +1116,8 @@
;; print-method / print-dup are real multimethods in the io tier (50-io.clj).
;; JVM proxies don't exist on a Janet host: the read-only surface is inert,
;; the constructive surface throws (matching the prior seed stubs).
;; JVM proxies don't exist on this host: the read-only surface is inert,
;; the constructive surface throws.
(defn proxy-mappings [p] {})
(defn proxy-call-with-super [f p meth] (f))
(defn init-proxy [p mappings] p)

View file

@ -17,7 +17,7 @@
;; and the methods become functions — the algorithm is identical.
;;
;; A sorted-SET stores its elements as keys with a nil value; its ops project the
;; key. ALL the semantics live here in Clojure; the Janet seed keeps only its
;; key. ALL the semantics live here in Clojure; the host keeps only its
;; dispatch branches (conj/assoc/get/seq/count/…), each a one-line call through
;; the value's own :ops table, so the ops travel WITH the value (correct across
;; contexts, forks, and AOT images). The wrapper is minted/read through the host
@ -286,7 +286,7 @@
(defn- ss-disj-many [ss xs] (reduce ss-disj-1 ss xs))
;; --- the ops tables the Janet seed dispatches through ------------------------
;; --- the ops tables the host dispatches through ------------------------
(def ^:private sm-ops
{:count (fn [sm] (sfield sm :cnt))

View file

@ -5,9 +5,9 @@
;; IMPORTANT — only macros NOT used by the self-hosted compiler (jolt-core/jolt/*)
;; or by the earlier overlay tiers belong here; those (and/or/when/when-not/
;; when-let/cond/case/doseq/declare/cond->/->) must stay available before this
;; tier loads, so they remain in Janet for now. Everything here is user-facing.
;; tier loads, so they remain host primitives for now. Everything here is user-facing.
;;
;; Migration: remove the Janet core-X macro fn AND its core-macro-names entry when
;; Migration: remove the host core-X macro fn AND its core-macro-names entry when
;; moving a macro here (defmacro installs the :macro flag itself).
(defmacro comment [& body] nil)

View file

@ -86,7 +86,7 @@
()))
;; --- repeatedly --- ((f) throws on a non-fn; (take n …) throws on a non-number
;; count — both now enforced in the seed (jolt-call / core-take), so the canonical
;; count — both enforced by the host (jolt-call / take), so the canonical
;; CLJ form matches the repeatedly.cljc exception cases.)
(defn repeatedly
([f] (lazy-seq (cons (f) (repeatedly f))))
@ -105,8 +105,8 @@
;; --- partition-all --- (transducer + [n coll] + [n step coll])
;; The collection arities realize EXACTLY n per chunk via a first/rest loop and
;; continue from the advanced cursor (not a re-drop / nthrest), so they realize
;; minimally — matching the Janet pstep the §6.3 laziness counters were written
;; against. (A take/nthrest form is correct but over-realizes.)
;; minimally — the §6.3 laziness counters depend on this.
;; (A take/nthrest form is correct but over-realizes.)
(defn partition-all
([n]
(fn [rf]

View file

@ -7,7 +7,7 @@
bootstrap can compile this namespace via its plain :var path. ctx is an opaque
host handle threaded to the contract fns; the analyzer never inspects it.
Coverage grows toward compiler.janet; unsupported forms throw :jolt/uncompilable
Unsupported forms throw :jolt/uncompilable
so the caller falls back to the interpreter (the hybrid contract).
`env` carries lexical state: {:locals #{names} :recur recur-target-name|nil}.
@ -140,8 +140,8 @@
;; form (the Chez data reader) wraps an arglist vector carrying a return-type hint
;; (^bytes [b] / ^String [x y]). Unwrap to the underlying vector so fn parsing sees
;; the params — the hint is ignored at runtime. Only the (with-meta <vec> _) shape
;; matches, so a real arity clause (head is a vector) and the Janet reader's
;; meta-on-tuple (already a vector) pass through unchanged.
;; matches, so a real arity clause (head is a vector) and a
;; meta-on-vector arglist pass through unchanged.
(defn- strip-arglist-meta [form]
(if (form-list? form)
(let [es (vec (form-elements form))]
@ -212,10 +212,10 @@
;; letfn: (letfn [(name [params] body*)...] body*). The named local fns are
;; MUTUALLY recursive, so bind every name into the env BEFORE analyzing any spec
;; — each spec then resolves its siblings (and itself) as locals. Emitted as a
;; :let flagged :letrec so the back ends know the bindings forward-reference each
;; other: Chez lowers it to `letrec*`; the Janet back end punts to the
;; interpreter (its shared mutable env already gives the letrec semantics that a
;; compiled sequential let* lacks — the reason letfn was uncompilable before).
;; :let flagged :letrec so the back end knows the bindings forward-reference each
;; other: Chez lowers it to `letrec*`. The interpreter's shared mutable env already
;; gives the letrec semantics that a
;; compiled sequential let* lacks — the reason letfn was uncompilable before.
(defn- analyze-letfn [ctx items env]
(let [specs (vec (form-vec-items (nth items 1)))
names (mapv #(form-sym-name (first (vec (form-elements %)))) specs)
@ -252,9 +252,9 @@
(uncompilable "def name with map metadata"))
(if (< (count items) 3)
;; (def name) with no init (declare): intern + reserve the cell so a
;; forward reference resolves. The back ends key on :no-init — Chez
;; def-var!s an unbound placeholder; the Janet back end punts to the
;; interpreter, which interns a genuinely-unbound var.
;; forward reference resolves. The back end keys on :no-init — Chez
;; def-var!s an unbound placeholder; the interpreter interns a
;; genuinely-unbound var.
(let [nm (form-sym-name name-sym) cur (compile-ns ctx)]
(host-intern! ctx cur nm)
{:op :def :ns cur :name nm :no-init true})
@ -297,8 +297,7 @@
;; Host interop method call (jolt-0kf5). `(.method target arg*)` — a head that
;; starts with "." but not ".-" (field access stays punted). Analyzes to a
;; :host-call node; the Janet back end punts it at emit (no interop model -> the
;; interpreter runs it), the Chez back end lowers it to a jolt-host-call dispatch.
;; :host-call node; the Chez back end lowers it to a jolt-host-call dispatch.
(defn- method-head? [nm]
(and (> (count nm) 1)
(= "." (subs nm 0 1))
@ -320,9 +319,8 @@
(not (= "." (subs nm 0 1)))))
;; `(Class. args*)` and `(new Class args*)` -> a :host-new node carrying the class
;; token and the analyzed args. The Janet back end punts it (the interpreter runs
;; the constructor from its class-ctors registry); the Chez back end lowers it to
;; a runtime constructor dispatch (jolt-avt6).
;; token and the analyzed args. The Chez back end lowers it to a runtime
;; constructor dispatch (jolt-avt6).
(defn- analyze-ctor [ctx class args env]
(host-new class (mapv #(analyze ctx % env) args)))
@ -330,10 +328,8 @@
;; A symbol member whose name starts with "-" is a field read; otherwise it is a
;; method (call with the trailing args). Both lower to a :host-call carrying the
;; member name verbatim (the leading "-" survives so the runtime dispatcher reads
;; it as a field). The Janet back end punts :host-call to the interpreter, which
;; re-runs the original `.` form via eval-dot — so Janet behavior is unchanged;
;; the Chez back end dispatches it through record-method-dispatch (jolt-kuic).
;; A non-symbol member (e.g. a keyword) stays punted — the interpreter handles it.
;; it as a field). The Chez back end dispatches it through record-method-dispatch
;; (jolt-kuic).
(defn- analyze-dot [ctx items env]
(when (< (count items) 3)
(throw (str "Malformed (. target member ...) form")))
@ -367,11 +363,8 @@
(if (= :var (:kind r))
(var-ref (:ns r) (:name r))
;; A non-var qualified ref `Class/member` is a host class static
;; (Math/sqrt, Long/MAX_VALUE, System/getenv). The Janet back end
;; punts the :host-static node (the interpreter resolves it from its
;; class-statics registry, exactly as it did when this was an
;; uncompilable); the Chez back end lowers it to a runtime static
;; dispatch (jolt-avt6).
;; (Math/sqrt, Long/MAX_VALUE, System/getenv). The Chez back end
;; lowers it to a runtime static dispatch (jolt-avt6).
(host-static ns nm)))
:else (let [r (resolve-global ctx form)]
(case (:kind r)
@ -445,12 +438,10 @@
(form-map-pairs form)))
(form-set? form) (set-node (mapv #(analyze ctx % env) (form-set-items form)))
(form-list? form) (analyze-list ctx form env)
;; regex literal #"…" -> a :regex IR node (leaf). The Janet back end punts it
;; (interpreter compiles via the seed PEG engine); the Chez back end emits a
;; regex literal #"…" -> a :regex IR node (leaf). The Chez back end emits a
;; jolt-regex value over the vendored irregex.
(form-regex? form) {:op :regex :source (form-regex-source form)}
;; #inst / #uuid literals -> :inst / :uuid IR leaves. Like :regex, the Janet
;; back end punts (the interpreter's data-readers parse them); the Chez back
;; #inst / #uuid literals -> :inst / :uuid IR leaves. The Chez back
;; end emits a runtime inst/uuid value (host/chez/inst-time.ss).
(form-inst? form) {:op :inst :source (form-inst-source form)}
(form-uuid? form) {:op :uuid :source (form-uuid-source form)}

View file

@ -1,15 +1,14 @@
(ns jolt.backend-scheme
"Portable Clojure IR -> Chez Scheme emitter (Chez Phase 3, jolt-cf1q.4).
The no-Janet replacement for host/chez/emit.janet: consumes the same
Consumes the
host-neutral IR (jolt.ir, see jolt-core/jolt/ir.clj) the analyzer produces and
emits Chez Scheme source TEXT. Pure jolt-core (clojure.core + clojure.string
only) so that, once cross-compiled, it runs ON Chez and the analyzer can emit
its own code with no Janet in the loop the bootstrap spine.
its own code the bootstrap spine.
Output is a STRING of Scheme source; `host/compile` on Chez is `(eval (read
...))`. Mirrors emit.janet op-for-op so the value-parity gate holds against the
Janet emitter while the port is in flight.
...))`. Lowers each IR op to Scheme.
INCREMENT 1 (jolt-hg7z): const/local/var/the-var/if/do/let/loop/recur/invoke
(+ native-ops)/fn/def + the escaping/flonum/munge helpers.
@ -17,7 +16,7 @@
quote (emit-quoted, walks the raw reader form via the portable jolt.host form-*
contract same seam the analyzer uses, so it stays host-neutral).
INCREMENT 3 (jolt-me6m): try/throw + host-call + regex/inst/uuid + def-meta +
quoted-symbol-meta. With this the emitter covers every op emit.janet handles.
quoted-symbol-meta. With this the emitter covers every IR op.
emit-quoted now also reconstructs plain jolt VALUES (def/symbol :meta), enabled
by making :meta a portable struct at the host seam (h-sym-meta). Program
assembly + the prelude driver port land with compile-from-source (inc 4+)."
@ -27,8 +26,8 @@
form-literal? form-elements form-vec-items
form-map-pairs form-set-items form-char-code]]))
;; Hot clojure.core primitives lowered to native Scheme, mirroring the Janet
;; backend's native-ops. `=` is the exactness-aware jolt= from values.ss; inc/dec/
;; Hot clojure.core primitives lowered to native Scheme.
;; `=` is the exactness-aware jolt= from values.ss; inc/dec/
;; not are rt shims; mod/rem/quot map to Scheme's (Scheme has all three).
(def ^:private native-ops
{"+" "+" "-" "-" "*" "*" "/" "/"
@ -80,7 +79,7 @@
;; Host interop methods with a Chez RT shim (rt.ss jolt-host-call). A `.method`
;; call on any other method routes to record-method-dispatch (a reify/record
;; protocol method). Keep in sync with emit.janet's supported-host-methods.
;; protocol method).
(def ^:private supported-host-methods #{"isDirectory" "listFiles"})
;; Native-op Scheme procedures that return a genuine Scheme boolean (#t/#f), so an
@ -173,8 +172,7 @@
;; reconstruct each as the matching Chez RT constructor — the runtime value of a
;; quote is just that literal data. The form is walked via the jolt.host form-*
;; contract (the portable seam the analyzer uses), NOT host-native predicates, so
;; this stays host-neutral: on Janet the contract walks Janet reader forms, on
;; Chez it walks Chez reader forms.
;; this stays host-neutral — the contract walks the host's reader forms.
(declare emit-quoted)
(defn- emit-quoted-map [pairs]
;; pairs: a jolt vector of [k-form v-form] pairs (form-map-pairs)

View file

@ -29,14 +29,13 @@
;; A qualified static reference to a host class member, `Class/member` (e.g.
;; Math/sqrt, Long/MAX_VALUE, System/getenv). A leaf node carrying the class and
;; member names. The Janet back end punts it (the interpreter resolves the static
;; from its class-statics registry); the Chez back end lowers a value ref to
;; host-static-ref and a call head to host-static-call (host-static.ss).
;; member names. The Chez back end lowers a value ref to host-static-ref and a
;; call head to host-static-call (host-static.ss).
(defn host-static [class member] {:op :host-static :class class :member member})
;; A host constructor, `(Class. args*)` / `(new Class args*)`. Carries the class
;; name and the analyzed argument nodes. Janet punts (interpreter runs the
;; constructor); Chez lowers to host-new (host-static.ss class-ctor registry).
;; name and the analyzed argument nodes. Chez lowers to host-new (host-static.ss
;; class-ctor registry).
(defn host-new [class args] {:op :host-new :class class :args args})
(defn if-node [test then else] {:op :if :test test :then then :else else})

View file

@ -25,7 +25,7 @@
;; :phm (persistent hash map; NOT raw-get-safe)
;; :any (top), nil (bottom, identity for join).
;; Compound types are small jolt maps, so they compare by value on both the
;; Clojure and the Janet (orchestrator) side. struct/vec/set use distinct keys so
;; Clojure and the host (orchestrator) side. struct/vec/set use distinct keys so
;; a type is recognised by which key it carries.
;; (get t :KEY) is nil for a keyword type and the child for a compound, so a
;; compound is detected by some? — no map?/contains? needed.

View file

@ -1,16 +1,16 @@
(ns jolt.reader
"Portable Clojure reader: source text -> reader forms (Chez Phase 3, jolt-cf1q.4).
The no-Janet replacement for src/jolt/reader.janet. All the lexing/parsing LOGIC
All the lexing/parsing LOGIC
is portable Clojure; form CONSTRUCTION and string->number parsing delegate to the
jolt.host contract (form-make-symbol/char, form-char-from-name, form-scan-number)
a Clojure source file cannot write a {:jolt/type :symbol} literal (it parses as
a tagged reader form), and the concrete form representation is the host's to own.
Same split the analyzer uses for the form-* readers. Once cross-compiled this runs
ON Chez so compile-from-source needs no Janet reader.
ON Chez to drive compile-from-source.
Positions are CHARACTER indices (the Janet reader uses byte indices); for ASCII
source they coincide, and form VALUES are identical either way the parity gate
Positions are CHARACTER indices; for ASCII
source they coincide with byte indices, and form VALUES are identical either way the parity gate
compares values, not positions.
INCREMENT 5a (jolt-50xx): the ATOM layer whitespace/comments, symbols (+ nil/
@ -27,8 +27,8 @@
form-elements form-vec-items form-set-items
form-map-pairs]]))
;; Source access by CHARACTER codepoint, mirroring the Janet reader's byte access
;; (identical for ASCII). cp = codepoint at i; len = character count.
;; Source access by CHARACTER codepoint
;; (identical to byte access for ASCII). cp = codepoint at i; len = character count.
(defn- cp [s i] (int (nth s i)))
(defn- len [s] (count s))
@ -71,8 +71,7 @@
(if (and (< end (len s)) (symbol-char? (cp s end))) (recur s pos (inc end)) end))
(defn- read-keyword* [s pos]
;; pos is at the first colon; ::foo is treated as :foo (no auto-resolution),
;; matching the Janet reader.
;; pos is at the first colon; ::foo is treated as :foo (no auto-resolution).
(let [start (if (and (< (inc pos) (len s)) (= (cp s (inc pos)) 58)) (+ pos 2) (inc pos))
end (read-keyword-name s start start)]
[(keyword (subs s start end)) end]))
@ -182,7 +181,7 @@
;; :form payload=the form a real datum
;; :skip payload=nil a comment (;) or #_ discard — produced nothing
;; :splice payload=items-vector #?@ — contributes 0+ items to the enclosing coll
;; Out-of-band control (vs the Janet reader's :jolt/skip / :jolt/splice sentinel
;; Out-of-band control (rather than :jolt/skip / :jolt/splice sentinel
;; FORMS) keeps it collision-free and host-neutral — no tagged-struct to build or
;; recognize. Collection readers dispatch on kind; read-next-form skips :skip.
(declare read-form)
@ -345,7 +344,7 @@
(defn- read-tagged* [s pos]
;; unknown dispatch -> a tagged literal (#inst, #uuid, #foo). The tag includes
;; the leading # (read-symbol-name starts at #), matching the Janet reader.
;; the leading # (read-symbol-name starts at #).
(let [end (read-symbol-name s pos pos)
tag (subs s pos end)
[form np] (read-next-form s end)]