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).
This commit is contained in:
Yogthos 2026-06-18 16:49:35 -04:00
parent eb26ad0401
commit 32e2d8bd58
6 changed files with 247 additions and 10 deletions

54
test/chez/_ns.janet Normal file
View file

@ -0,0 +1,54 @@
# jolt-yxqm — namespace value model (find-ns/ns-name/all-ns/resolve/ns-publics/
# in-ns/*ns* …). TDD harness: drives bin/jolt-chez -e per case (fresh subprocess
# = per-case isolation), checks the LAST printed line == expected. Expected
# values are the JVM-canonical reference (build/jolt), captured up front.
#
# janet test/chez/_ns.janet
(def jolt-bin (or (os/getenv "JOLT_BIN") "bin/jolt-chez"))
# [label expr expected-last-line]
(def cases
[["find-ns existing" "(some? (find-ns 'clojure.core))" "true"]
["find-ns missing" "(nil? (find-ns 'does.not.exist))" "true"]
["resolve native-op +" "(var? (resolve '+))" "true"]
["resolve undefined" "(nil? (resolve 'totally-undefined-xyz))" "true"]
["ns-publics has def" "(do (def npv 1) (some? (get (ns-publics 'user) 'npv)))" "true"]
["ns-map has def" "(do (def nmv 1) (some? (get (ns-map 'user) 'nmv)))" "true"]
["ns-aliases is a map" "(map? (ns-aliases 'clojure.core))" "true"]
["ns-interns is a map" "(map? (ns-interns 'user))" "true"]
["ns-interns count pos" "(do (def q 1) (pos? (count (ns-interns 'user))))" "true"]
["all-ns count pos" "(pos? (count (all-ns)))" "true"]
["ns-name *ns* = user" "(= (ns-name *ns*) (ns-name (find-ns 'user)))" "true"]
["in-ns returns ns str" "(str (in-ns 'jolt.test-ns-b))" "jolt.test-ns-b"]
["in-ns updates *ns*" "(do (in-ns 'jolt.test-ns-a) (str *ns*))" "jolt.test-ns-a"]
["ns-unmap clears var" "(do (def nuv 1) (ns-unmap 'user 'nuv) (nil? (resolve 'nuv)))" "true"]
["in-ns no error" "(do (in-ns 'my.ns) (symbol? 'x))" "true"]
["str ns-name *ns*" "(str (ns-name *ns*))" "user"]
["find-var qualified" "(var? (find-var 'clojure.core/map))" "true"]
["the-ns ns-name" "(= 'user (ns-name (the-ns 'user)))" "true"]
["create-ns ns-name" "(= 'foo.bar (ns-name (create-ns 'foo.bar)))" "true"]])
(defn run-capture [expr]
(def proc (os/spawn [jolt-bin "-e" expr] :p {:out :pipe :err :pipe}))
(def out (ev/read (proc :out) 0x100000))
(def err (ev/read (proc :err) 0x100000))
(def code (os/proc-wait proc))
(def lines (filter (fn [l] (not (empty? l)))
(string/split "\n" (string/trim (if out (string out) "")))))
[code (if (empty? lines) "" (last lines)) (if err (string err) "")])
(var pass 0)
(def fails @[])
(each [label expr expected] cases
(def [code got err] (run-capture expr))
(cond
(not= code 0) (array/push fails [label (string "exit " code "; err: " (string/trim err))])
(= got expected) (++ pass)
(array/push fails [label (string "want `" expected "`, got `" got "`")])))
(printf "\n_ns parity [%s]: %d/%d passed" jolt-bin pass (length cases))
(when (> (length fails) 0)
(printf "%d FAIL(s):" (length fails))
(each [l m] fails (printf " FAIL [%s] %s" l m)))
(flush)
(os/exit (if (empty? fails) 0 1))

View file

@ -49,10 +49,8 @@
"keys evaluate before their values, pairwise" true
"source order with a nil value (phm form)" true
"close on throw" true
"ns-name of *ns*" true
"str of *ns*" true
"*ns* user" true
"str of a var" true
# *ns* now a namespace value (jolt-yxqm): str/ns-name of *ns* + the var str
# case ("ns-name of *ns*" / "str of *ns*" / "*ns* user" / "str of a var") pass.
# *clojure-version* / *unchecked-math* now bound by dynamic-vars.ss (inc 3r)
# (str [##Inf]) wants "[Infinity]" but the Chez -e printer (jolt-pr-str, which
# str falls back to for collections) renders inf as "inf" — str needs a
@ -189,8 +187,11 @@
# matching Clojure, which also un-allowlists pr-str-of-var/defn) and inc J
# (jolt-snry: scalar natives — UUID random-uuid/parse-uuid/uuid?, format/printf,
# tagged-literal, bigint) 1951.
# jolt-yxqm (namespace value model — find-ns/ns-name/all-ns/resolve/ns-publics/
# in-ns/*ns* over the var-table + a jns ns value; native-op var cells so
# (resolve '+) is a var; *ns* bound to the user ns) 1969.
# Strided runs scale down.
(def base-floor (scan-number (or (os/getenv "JOLT_CHEZ_PRELUDE_FLOOR") "1951")))
(def base-floor (scan-number (or (os/getenv "JOLT_CHEZ_PRELUDE_FLOOR") "1969")))
(def floor (if (os/getenv "JOLT_CORPUS_LIMIT") 0 base-floor))
(when (or (> (length diverged) 0) (< pass floor))
(printf "REGRESSION: pass %d < floor %d or %d new divergence(s)" pass floor (length diverged)))