core: one alias store (jolt-ark); '.' classified as the special form it is
require-:as wrote the string-keyed :imports table (which resolution reads)
while ns-aliases read the symbol-keyed :aliases table (which nothing wrote)
— so (ns-aliases) was always empty and the alias fn had to write both as a
bridge. :aliases (alias-name string -> ns-name string) is THE store now:
require :as and the alias fn write it, both resolution paths read it first
(falling back to :imports for class imports, which is all that table holds
now), ns-unalias removes one entry, and ns-aliases presents Clojure's
{alias-symbol -> namespace object} shape built from it. ns-resolve's
qualified path goes through the same lookup.
Also: the coverage dashboard's last 'resolvable-not-interned' entry was '.'
— which (resolve '.) returns nil for on the JVM too; the tool now classifies
it as the special form it is, and that category reads ZERO.
7 new unified-alias spec rows (require/alias/ns-unalias round-trips through
both the resolution and introspection views); the white-box namespace test
tracks the accessor rename. Gate exit 0.
This commit is contained in:
parent
7c2d556dc5
commit
e311018d55
6 changed files with 55 additions and 22 deletions
|
|
@ -9,7 +9,7 @@ community examples). jolt interns 564 of them.
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| implemented+tested | 564 | in jolt and exercised by spec/conformance |
|
| implemented+tested | 564 | in jolt and exercised by spec/conformance |
|
||||||
| implemented-untested | 0 | in jolt, no direct test — spec entries will add them |
|
| implemented-untested | 0 | in jolt, no direct test — spec entries will add them |
|
||||||
| resolvable-not-interned | 1 | works in code but invisible to ns introspection (conformance finding) |
|
| resolvable-not-interned | 0 | works in code but invisible to ns introspection (conformance finding) |
|
||||||
| missing-portable | 0 | portable semantics, jolt lacks it — implementation gap |
|
| missing-portable | 0 | portable semantics, jolt lacks it — implementation gap |
|
||||||
| special-form | 15 | specified in §3, not a library var |
|
| special-form | 15 | specified in §3, not a library var |
|
||||||
| dynamic-var | 29 | classification needed: portable default vs host-dependent |
|
| dynamic-var | 29 | classification needed: portable default vs host-dependent |
|
||||||
|
|
|
||||||
|
|
@ -286,7 +286,7 @@
|
||||||
# aliased ref like g/foo wouldn't resolve mid-compile. Same ns h-current-ns uses.
|
# aliased ref like g/foo wouldn't resolve mid-compile. Same ns h-current-ns uses.
|
||||||
(let [cur-name (or (get (ctx :env) :compile-ns) (ctx-current-ns ctx))
|
(let [cur-name (or (get (ctx :env) :compile-ns) (ctx-current-ns ctx))
|
||||||
current-ns (ctx-find-ns ctx cur-name)
|
current-ns (ctx-find-ns ctx cur-name)
|
||||||
aliased-ns (ns-import-lookup current-ns ns)
|
aliased-ns (or (ns-alias-lookup current-ns ns) (ns-import-lookup current-ns ns))
|
||||||
target-ns (ctx-find-ns ctx (or aliased-ns ns))]
|
target-ns (ctx-find-ns ctx (or aliased-ns ns))]
|
||||||
(ns-find target-ns name))
|
(ns-find target-ns name))
|
||||||
(if (get bindings name) nil
|
(if (get bindings name) nil
|
||||||
|
|
@ -374,7 +374,7 @@
|
||||||
(maybe-require-ns ctx ns-name)
|
(maybe-require-ns ctx ns-name)
|
||||||
(when alias
|
(when alias
|
||||||
(let [current-ns (ctx-find-ns ctx (ctx-current-ns ctx))]
|
(let [current-ns (ctx-find-ns ctx (ctx-current-ns ctx))]
|
||||||
(ns-import current-ns alias ns-name)))
|
(ns-add-alias current-ns alias ns-name)))
|
||||||
(when refer-syms
|
(when refer-syms
|
||||||
(let [source-ns (ctx-find-ns ctx ns-name)
|
(let [source-ns (ctx-find-ns ctx ns-name)
|
||||||
target-ns (ctx-find-ns ctx (ctx-current-ns ctx))]
|
target-ns (ctx-find-ns ctx (ctx-current-ns ctx))]
|
||||||
|
|
@ -436,7 +436,7 @@
|
||||||
(if (nil? v) (error (string "Unsupported Thread member: Thread/" name)) v))
|
(if (nil? v) (error (string "Unsupported Thread member: Thread/" name)) v))
|
||||||
(if (not (nil? ns))
|
(if (not (nil? ns))
|
||||||
(let [current-ns (ctx-find-ns ctx (ctx-current-ns ctx))
|
(let [current-ns (ctx-find-ns ctx (ctx-current-ns ctx))
|
||||||
aliased-ns (ns-import-lookup current-ns ns)
|
aliased-ns (or (ns-alias-lookup current-ns ns) (ns-import-lookup current-ns ns))
|
||||||
target-ns (ctx-find-ns ctx (or aliased-ns ns))
|
target-ns (ctx-find-ns ctx (or aliased-ns ns))
|
||||||
v (and target-ns (ns-find target-ns name))]
|
v (and target-ns (ns-find target-ns name))]
|
||||||
(if v (var-get v)
|
(if v (var-get v)
|
||||||
|
|
@ -1026,7 +1026,14 @@
|
||||||
# interns/imports return a jolt MAP (struct), not the live host table — so
|
# interns/imports return a jolt MAP (struct), not the live host table — so
|
||||||
# count/seq/keys work on them, and callers can't mutate the ns through them.
|
# count/seq/keys work on them, and callers can't mutate the ns through them.
|
||||||
(ns-intern core "ns-interns" (fn [&opt x] (table/to-struct ((ns-or-current x) :mappings))))
|
(ns-intern core "ns-interns" (fn [&opt x] (table/to-struct ((ns-or-current x) :mappings))))
|
||||||
(ns-intern core "ns-aliases" (fn [&opt x] ((ns-or-current x) :aliases)))
|
# {alias-symbol -> namespace object}, Clojure's shape, from the string store.
|
||||||
|
(ns-intern core "ns-aliases"
|
||||||
|
(fn [&opt x]
|
||||||
|
(def ns (ns-or-current x))
|
||||||
|
(def out @{})
|
||||||
|
(eachp [a target] (ns :aliases)
|
||||||
|
(put out {:jolt/type :symbol :ns nil :name a} (ctx-find-ns ctx target)))
|
||||||
|
(table/to-struct out)))
|
||||||
(ns-intern core "ns-imports" (fn [&opt x] (table/to-struct ((ns-or-current x) :imports))))
|
(ns-intern core "ns-imports" (fn [&opt x] (table/to-struct ((ns-or-current x) :imports))))
|
||||||
# (ns-resolve ns sym) -> the var or nil. Unqualified syms look in ns's own
|
# (ns-resolve ns sym) -> the var or nil. Unqualified syms look in ns's own
|
||||||
# mappings; ns-qualified syms resolve through ns's aliases. (types/ns-resolve
|
# mappings; ns-qualified syms resolve through ns's aliases. (types/ns-resolve
|
||||||
|
|
@ -1038,8 +1045,9 @@
|
||||||
(def nm (if (struct? sym) (sym :name) (string sym)))
|
(def nm (if (struct? sym) (sym :name) (string sym)))
|
||||||
(def nsp (if (struct? sym) (sym :ns) nil))
|
(def nsp (if (struct? sym) (sym :ns) nil))
|
||||||
(if nsp
|
(if nsp
|
||||||
(let [alias-ns (get (ns :aliases) nsp)]
|
(let [target (or (ns-alias-lookup ns nsp) nsp)
|
||||||
(when alias-ns (ns-find alias-ns nm)))
|
target-ns (ctx-find-ns ctx target)]
|
||||||
|
(when target-ns (ns-find target-ns nm)))
|
||||||
(ns-find ns nm))))
|
(ns-find ns nm))))
|
||||||
(ns-intern core "resolve"
|
(ns-intern core "resolve"
|
||||||
(fn [sym]
|
(fn [sym]
|
||||||
|
|
@ -1185,20 +1193,18 @@
|
||||||
(var nxt (expand-1 cur))
|
(var nxt (expand-1 cur))
|
||||||
(while (not= cur nxt) (set cur nxt) (set nxt (expand-1 cur)))
|
(while (not= cur nxt) (set cur nxt) (set nxt (expand-1 cur)))
|
||||||
cur))
|
cur))
|
||||||
# alias/ns-unalias: alias bookkeeping is currently split (require :as writes
|
# alias bookkeeping is UNIFIED (jolt-ark): :aliases (alias-name string ->
|
||||||
# the string-keyed :imports table that resolution reads; :aliases is the
|
# ns-name string) is the one store, read by resolution and ns-aliases;
|
||||||
# introspection table ns-aliases reads) — write/remove BOTH until unified.
|
# :imports holds class imports only.
|
||||||
(ns-intern core "alias"
|
(ns-intern core "alias"
|
||||||
(fn [alias-sym ns-sym]
|
(fn [alias-sym ns-sym]
|
||||||
(def cur (ctx-find-ns ctx (ctx-current-ns ctx)))
|
(def cur (ctx-find-ns ctx (ctx-current-ns ctx)))
|
||||||
(ns-import cur (alias-sym :name) (ns-sym :name))
|
(ns-add-alias cur (alias-sym :name) (ns-sym :name))
|
||||||
(ns-add-alias cur alias-sym (ctx-find-ns ctx (ns-sym :name)))
|
|
||||||
nil))
|
nil))
|
||||||
(ns-intern core "ns-unalias"
|
(ns-intern core "ns-unalias"
|
||||||
(fn [ns-d alias-sym]
|
(fn [ns-d alias-sym]
|
||||||
(def ns (ns-or-current ns-d))
|
(def ns (ns-or-current ns-d))
|
||||||
(put (ns :imports) (alias-sym :name) nil)
|
(put (ns :aliases) (alias-sym :name) nil)
|
||||||
(put (ns :aliases) alias-sym nil)
|
|
||||||
nil))
|
nil))
|
||||||
# ns-publics: {symbol -> var} (jolt has no private vars, so publics = interns).
|
# ns-publics: {symbol -> var} (jolt has no private vars, so publics = interns).
|
||||||
# Keys are symbol structs (value-hashed), matching Clojure's symbol keys.
|
# Keys are symbol structs (value-hashed), matching Clojure's symbol keys.
|
||||||
|
|
|
||||||
|
|
@ -279,7 +279,7 @@
|
||||||
(or (ns-find ns sym)
|
(or (ns-find ns sym)
|
||||||
(let [qualified? (sym? sym)]
|
(let [qualified? (sym? sym)]
|
||||||
(when qualified?
|
(when qualified?
|
||||||
# qualified symbol: look up via alias
|
# qualified symbol: look up via alias (string-keyed store)
|
||||||
(let [alias-ns (get (ns :aliases) (sym :ns))]
|
(let [alias-ns (get (ns :aliases) (sym :ns))]
|
||||||
(when alias-ns
|
(when alias-ns
|
||||||
(ns-find alias-ns (sym :name))))))))
|
(ns-find alias-ns (sym :name))))))))
|
||||||
|
|
@ -295,9 +295,16 @@
|
||||||
(get (ns :imports) class-name))
|
(get (ns :imports) class-name))
|
||||||
|
|
||||||
(defn ns-add-alias
|
(defn ns-add-alias
|
||||||
"Add an alias from alias-sym to target-ns."
|
"Add an alias: alias-name (string) -> target ns NAME (string). The ONE alias
|
||||||
[ns alias-sym target-ns]
|
store (jolt-ark): resolution and ns-aliases both read it; :imports is class
|
||||||
(put (ns :aliases) alias-sym target-ns))
|
imports only."
|
||||||
|
[ns alias-name target-ns-name]
|
||||||
|
(put (ns :aliases) alias-name target-ns-name))
|
||||||
|
|
||||||
|
(defn ns-alias-lookup
|
||||||
|
"The target ns NAME for alias-name, or nil."
|
||||||
|
[ns alias-name]
|
||||||
|
(get (ns :aliases) alias-name))
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Context
|
# Context
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@
|
||||||
(assert (= "my.app" (ctx-current-ns ctx)) "ns with require sets current namespace")
|
(assert (= "my.app" (ctx-current-ns ctx)) "ns with require sets current namespace")
|
||||||
# Alias should be registered
|
# Alias should be registered
|
||||||
(let [ns (ctx-find-ns ctx "my.app")
|
(let [ns (ctx-find-ns ctx "my.app")
|
||||||
aliased (ns-import-lookup ns "o")]
|
aliased (ns-alias-lookup ns "o")]
|
||||||
(assert (= "other.lib" aliased) "alias o -> other.lib registered")))
|
(assert (= "other.lib" aliased) "alias o -> other.lib registered")))
|
||||||
(print " passed")
|
(print " passed")
|
||||||
|
|
||||||
|
|
@ -55,7 +55,7 @@
|
||||||
(let [ctx (fresh-ctx)]
|
(let [ctx (fresh-ctx)]
|
||||||
(eval-form ctx @{} (parse-string "(require '[other.lib :as o])"))
|
(eval-form ctx @{} (parse-string "(require '[other.lib :as o])"))
|
||||||
(let [ns (ctx-find-ns ctx "user")
|
(let [ns (ctx-find-ns ctx "user")
|
||||||
aliased (ns-import-lookup ns "o")]
|
aliased (ns-alias-lookup ns "o")]
|
||||||
(assert (= "other.lib" aliased) "standalone require registers alias")))
|
(assert (= "other.lib" aliased) "standalone require registers alias")))
|
||||||
(print " passed")
|
(print " passed")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,3 +53,21 @@
|
||||||
"(do (require (quote [clojure.string :as s9])) (try (s9/join nil nil nil) (catch Exception e nil)) (s9/upper-case \"a\"))"]
|
"(do (require (quote [clojure.string :as s9])) (try (s9/join nil nil nil) (catch Exception e nil)) (s9/upper-case \"a\"))"]
|
||||||
["*ns* restored after throw" "\"user\""
|
["*ns* restored after throw" "\"user\""
|
||||||
"(do (require (quote [clojure.walk :as w9])) (try (w9/postwalk nil nil nil) (catch Exception e nil)) (str *ns*))"])
|
"(do (require (quote [clojure.walk :as w9])) (try (w9/postwalk nil nil nil) (catch Exception e nil)) (str *ns*))"])
|
||||||
|
|
||||||
|
# Alias bookkeeping is unified (jolt-ark): one string-keyed :aliases store,
|
||||||
|
# read by resolution AND ns-aliases (which presents Clojure's
|
||||||
|
# {alias-symbol -> namespace} shape); :imports holds class imports only.
|
||||||
|
(defspec "namespaces / unified alias store"
|
||||||
|
["require :as registers the alias" "1"
|
||||||
|
"(do (require (quote [clojure.string :as st1])) (count (filter (fn [[a n]] (= (str a) \"st1\")) (ns-aliases))))"]
|
||||||
|
["aliased call resolves" "\"A\""
|
||||||
|
"(do (require (quote [clojure.string :as st2])) (st2/upper-case \"a\"))"]
|
||||||
|
["alias fn registers + resolves" "\"B\""
|
||||||
|
"(do (require (quote [clojure.string])) (alias (quote st3) (quote clojure.string)) (st3/upper-case \"b\"))"]
|
||||||
|
["alias fn visible to ns-aliases" "true"
|
||||||
|
"(do (require (quote [clojure.string])) (alias (quote st4) (quote clojure.string)) (pos? (count (filter (fn [[a n]] (= (str a) \"st4\")) (ns-aliases)))))"]
|
||||||
|
["ns-unalias removes both views" "[0 false]"
|
||||||
|
"(do (require (quote [clojure.string :as st5])) (ns-unalias (quote user) (quote st5)) [(count (filter (fn [[a n]] (= (str a) \"st5\")) (ns-aliases))) (boolean (resolve (quote st5/upper-case)))])"]
|
||||||
|
["ns-resolve through alias" "true"
|
||||||
|
"(do (require (quote [clojure.string :as st6])) (var? (ns-resolve (quote user) (quote st6/upper-case))))"]
|
||||||
|
["empty ns-aliases is a map" "true" "(map? (ns-aliases (quote clojure.core)))"])
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,9 @@ for f in glob.glob('test/spec/*.janet') + ['test/integration/conformance-test.ja
|
||||||
|
|
||||||
# --- classification ---------------------------------------------------------
|
# --- classification ---------------------------------------------------------
|
||||||
SPECIAL = {'catch','finally','do','def','defmacro','fn','if','let','loop','quote',
|
SPECIAL = {'catch','finally','do','def','defmacro','fn','if','let','loop','quote',
|
||||||
'recur','throw','try','var','new','set!','monitor-enter','monitor-exit'}
|
'recur','throw','try','var','new','set!','monitor-enter','monitor-exit',
|
||||||
|
# '.' is the interop special form — (resolve '.) is nil on the JVM too
|
||||||
|
'.'}
|
||||||
AGENTS = {'agent','send','send-off','send-via','await','await-for','await1',
|
AGENTS = {'agent','send','send-off','send-via','await','await-for','await1',
|
||||||
'agent-error','agent-errors','clear-agent-errors','error-handler',
|
'agent-error','agent-errors','clear-agent-errors','error-handler',
|
||||||
'error-mode','set-agent-send-executor!','set-agent-send-off-executor!',
|
'error-mode','set-agent-send-executor!','set-agent-send-off-executor!',
|
||||||
|
|
@ -105,7 +107,7 @@ community examples). jolt interns {len(jolt & set(core))} of them.
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| implemented+tested | {counts['implemented+tested']} | in jolt and exercised by spec/conformance |
|
| implemented+tested | {counts['implemented+tested']} | in jolt and exercised by spec/conformance |
|
||||||
| implemented-untested | {counts['implemented-untested']} | in jolt, no direct test — spec entries will add them |
|
| implemented-untested | {counts['implemented-untested']} | in jolt, no direct test — spec entries will add them |
|
||||||
| resolvable-not-interned | {len((resolvable - interned) & set(core))} | works in code but invisible to ns introspection (conformance finding) |
|
| resolvable-not-interned | {len((resolvable - interned) & set(core) - SPECIAL)} | works in code but invisible to ns introspection (conformance finding) |
|
||||||
| missing-portable | {counts['missing-portable']} | portable semantics, jolt lacks it — implementation gap |
|
| missing-portable | {counts['missing-portable']} | portable semantics, jolt lacks it — implementation gap |
|
||||||
| special-form | {counts['special-form']} | specified in §3, not a library var |
|
| special-form | {counts['special-form']} | specified in §3, not a library var |
|
||||||
| dynamic-var | {counts['dynamic-var']} | classification needed: portable default vs host-dependent |
|
| dynamic-var | {counts['dynamic-var']} | classification needed: portable default vs host-dependent |
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue