Stage2 task2 tier4b (#15)
* core: Stage 2 Task 2 tier 4b — compile ns (+ use/import/refer-clojure) ns becomes a macro (00-syntax) expanding to (do (in-ns 'name) (require …) (use …) (import …) (refer-clojure …)) — matching Clojure, where require is a fn and ns expands to require calls. use/import/refer-clojure join the namespace ops as ctx-capturing clojure.core fns (use-impl/import-impl/ refer-clojure-impl, interned by install-stateful-fns!). Removed the ns interpreter special arm; dropped ns/use/import from host_iface special-names and loader stateful-head?. Placement matters: ns lives in the FIRST overlay tier (00-syntax), because the self-hosted analyzer build is triggered while 10-seq loads and processes jolt.analyzer's own (ns …) form — ns must exist by then. Its body resolves fn/map/reduce/cond at expansion time, by which point all of 00-syntax has loaded. The bootstrap compiler (compiler.janet) still PUNTS ns/in-ns/require/ use/import (it compiles analyzer.clj/ir.clj, whose ns forms must fall back to the interpreter that expands the macro) — only the self-hosted analyzer compiles them. use-impl fixes a latent bug: it now refers the used ns's vars into the CURRENT ns (the old special interned a ns into itself, a no-op). ns/use/import now compile + interpret as plain invokes. fallback-zero: ns off must-punt, onto must-compile. Gate green: conformance 267x3, fallback-zero 38/4, bootstrap-fixpoint stage1==2==3, self-host, staged-bootstrap, sci-bootstrap, clojure-test-suite >=4034/67, namespace, deps-loader, cli, aot, embedded-stdlib, uberscript, features 78/78, all unit + spec. * test/core: cover ns form + :use; fix use-impl spec parsing Checking test coverage for the namespace tiers surfaced a real bug nothing exercised: use-impl checked (array? s) to find a spec's ns symbol, but a vector spec like [src.x] is a pvec/tuple, not a Janet array — so (:use ...) and standalone (use ...) errored. Fixed to coerce (pvec->array) then take the head when the spec is indexed (matching require-impl). Added coverage that was missing: - conformance (x3 modes): the (ns …) form itself + :use refers. - namespaces-spec: (ns …) form, :use, standalone use. The :use/(use …) path had ZERO tests before, which is why the bug slipped through earlier tiers. EBNF needs no change — it's a reader/syntax grammar with no special-forms enumeration; ns/require/protocol syntax is unchanged (the destructuring note was already updated in jolt-f79). Gate green: conformance 269x3, fallback-zero 38/4, self-host, sci-bootstrap, bootstrap-fixpoint, staged-bootstrap, clojure-test-suite >=4034/67, namespace, all unit + spec (namespaces 27/27). --------- Co-authored-by: Yogthos <yogthos@gmail.com>
This commit is contained in:
parent
e8c19c351f
commit
63d92cd122
8 changed files with 90 additions and 69 deletions
|
|
@ -36,6 +36,31 @@
|
||||||
nil
|
nil
|
||||||
`(if ~(first clauses) ~(nth clauses 1) (cond ~@(drop 2 clauses)))))
|
`(if ~(first clauses) ~(nth clauses 1) (cond ~@(drop 2 clauses)))))
|
||||||
|
|
||||||
|
;; ns is sugar over the namespace-op fns (in-ns/require/use/import/refer-clojure,
|
||||||
|
;; all ctx-capturing clojure.core fns) — matching Clojure, where require is a fn and
|
||||||
|
;; the ns macro expands its clauses into require calls. Each spec is quoted
|
||||||
|
;; individually and passed as data; non-list clauses (docstring, attr-map,
|
||||||
|
;; :gen-class, …) are ignored. So ns compiles to a plain (do …) of invokes.
|
||||||
|
;; MUST live in this first tier: the self-hosted analyzer build (triggered while
|
||||||
|
;; 10-seq loads) processes jolt.analyzer's own (ns …) form, so ns has to exist by
|
||||||
|
;; then. Its body resolves fn/map/reduce/cond at EXPANSION time, by which point all
|
||||||
|
;; of 00-syntax has loaded, so using them here is fine.
|
||||||
|
(defmacro ns [nm & clauses]
|
||||||
|
(let [calls (reduce
|
||||||
|
(fn [acc clause]
|
||||||
|
(if (seq? clause)
|
||||||
|
(let [head (first clause) args (rest clause)]
|
||||||
|
(cond
|
||||||
|
(= head :require) (conj acc `(require ~@(map (fn [s] `(quote ~s)) args)))
|
||||||
|
(= head :use) (conj acc `(use ~@(map (fn [s] `(quote ~s)) args)))
|
||||||
|
(= head :import) (conj acc `(import ~@(map (fn [s] `(quote ~s)) args)))
|
||||||
|
(= head :refer-clojure)
|
||||||
|
(conj acc `(refer-clojure ~@(map (fn [s] `(quote ~s)) args)))
|
||||||
|
:else acc))
|
||||||
|
acc))
|
||||||
|
[] clauses)]
|
||||||
|
`(do (in-ns (quote ~nm)) ~@calls)))
|
||||||
|
|
||||||
;; Threading: a list form threads x in as the first (->) or last (->>) arg; a bare
|
;; Threading: a list form threads x in as the first (->) or last (->>) arg; a bare
|
||||||
;; symbol becomes (form x). Recursive; the expand-once cache makes that free.
|
;; symbol becomes (form x). Recursive; the expand-once cache makes that free.
|
||||||
(defmacro -> [x & forms]
|
(defmacro -> [x & forms]
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@
|
||||||
"defmethod" "prefer-method" "remove-method" "remove-all-methods"
|
"defmethod" "prefer-method" "remove-method" "remove-all-methods"
|
||||||
"get-method" "methods"
|
"get-method" "methods"
|
||||||
"satisfies?" "instance?" "set!" "var" "var-get"
|
"satisfies?" "instance?" "set!" "var" "var-get"
|
||||||
"var-set" "var?" "ns" "create-ns" "remove-ns"
|
"var-set" "var?" "ns" "in-ns" "require" "create-ns" "remove-ns"
|
||||||
"find-ns" "all-ns" "the-ns" "find-var" "intern" "resolve"
|
"find-ns" "all-ns" "the-ns" "find-var" "intern" "resolve"
|
||||||
"ns-resolve" "ns-aliases" "ns-imports" "ns-interns"
|
"ns-resolve" "ns-aliases" "ns-imports" "ns-interns"
|
||||||
"alter-var-root" "alter-meta!" "reset-meta!" "locking" "new"
|
"alter-var-root" "alter-meta!" "reset-meta!" "locking" "new"
|
||||||
|
|
|
||||||
|
|
@ -772,6 +772,50 @@
|
||||||
(ctx-set-current-ns ctx ns-name)
|
(ctx-set-current-ns ctx ns-name)
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
|
(defn use-impl
|
||||||
|
"(use '[ns ...] ...) — refer ALL public vars of each used ns into the CURRENT ns.
|
||||||
|
A fn; quoted specs arrive evaluated. Each spec is a ns symbol or a [ns & opts]
|
||||||
|
vector (a pvec/tuple, not a Janet array — coerce, then take the head as the ns)."
|
||||||
|
[ctx & specs]
|
||||||
|
(def target-ns (ctx-find-ns ctx (ctx-current-ns ctx)))
|
||||||
|
(each s specs
|
||||||
|
(let [spec (if (pvec? s) (pv->array s) s)
|
||||||
|
ns-sym (if (indexed? spec) (in spec 0) spec)
|
||||||
|
src-name (sym-name-str ns-sym)]
|
||||||
|
(maybe-require-ns ctx src-name)
|
||||||
|
(let [source-ns (ctx-find-ns ctx src-name)]
|
||||||
|
(loop [[sym v] :pairs (source-ns :mappings)]
|
||||||
|
(let [nv (ns-intern target-ns sym (var-get v))]
|
||||||
|
(when (get v :macro) (put nv :macro true)))))))
|
||||||
|
nil)
|
||||||
|
|
||||||
|
(defn import-impl
|
||||||
|
"(import 'pkg.Class ...) — register the short class name as an alias of the fully
|
||||||
|
qualified name in the current ns. A fn; quoted class symbols arrive evaluated."
|
||||||
|
[ctx & class-specs]
|
||||||
|
(def ns (ctx-find-ns ctx (ctx-current-ns ctx)))
|
||||||
|
(each class-spec class-specs
|
||||||
|
(let [class-name (if (and (struct? class-spec) (= :symbol (class-spec :jolt/type)))
|
||||||
|
(class-spec :name) (string class-spec))
|
||||||
|
last-dot (do (var idx -1) (var pos 0)
|
||||||
|
(while (< pos (length class-name))
|
||||||
|
(when (= (class-name pos) 46) (set idx pos)) (++ pos))
|
||||||
|
idx)
|
||||||
|
short-name (if (>= last-dot 0) (string/slice class-name (+ last-dot 1)) class-name)]
|
||||||
|
(ns-import ns short-name class-name)))
|
||||||
|
nil)
|
||||||
|
|
||||||
|
(defn refer-clojure-impl
|
||||||
|
"(refer-clojure :exclude [a b]) — currently only :exclude is honored: unmap the
|
||||||
|
excluded names from the current ns. A fn; quoted args arrive evaluated."
|
||||||
|
[ctx & args]
|
||||||
|
(when (and (>= (length args) 2) (= (in args 0) :exclude))
|
||||||
|
(let [ns (ctx-find-ns ctx (ctx-current-ns ctx))
|
||||||
|
excl (in args 1)]
|
||||||
|
(each sym excl
|
||||||
|
(ns-unmap ns (if (and (struct? sym) (= :symbol (sym :jolt/type))) (sym :name) (string sym))))))
|
||||||
|
nil)
|
||||||
|
|
||||||
(defn install-stateful-fns!
|
(defn install-stateful-fns!
|
||||||
"Intern ctx-capturing closures for the stateful primitives into clojure.core, so
|
"Intern ctx-capturing closures for the stateful primitives into clojure.core, so
|
||||||
both the interpreter and the compiler reach them as ordinary fns. Called by
|
both the interpreter and the compiler reach them as ordinary fns. Called by
|
||||||
|
|
@ -789,6 +833,9 @@
|
||||||
(fn [proto-name methods-map] (make-reified-impl ctx proto-name methods-map)))
|
(fn [proto-name methods-map] (make-reified-impl ctx proto-name methods-map)))
|
||||||
(ns-intern core "require" (fn [& specs] (require-impl ctx ;specs)))
|
(ns-intern core "require" (fn [& specs] (require-impl ctx ;specs)))
|
||||||
(ns-intern core "in-ns" (fn [sym] (in-ns-impl ctx sym)))
|
(ns-intern core "in-ns" (fn [sym] (in-ns-impl ctx sym)))
|
||||||
|
(ns-intern core "use" (fn [& specs] (use-impl ctx ;specs)))
|
||||||
|
(ns-intern core "import" (fn [& specs] (import-impl ctx ;specs)))
|
||||||
|
(ns-intern core "refer-clojure" (fn [& args] (refer-clojure-impl ctx ;args)))
|
||||||
core)
|
core)
|
||||||
|
|
||||||
# Dispatch a special form by its string name.
|
# Dispatch a special form by its string name.
|
||||||
|
|
@ -932,69 +979,9 @@
|
||||||
# A (re)defined macro invalidates any cached expansions.
|
# A (re)defined macro invalidates any cached expansions.
|
||||||
(table/clear macro-cache)
|
(table/clear macro-cache)
|
||||||
(var-get v)))
|
(var-get v)))
|
||||||
"ns" (let [raw-name (in form 1)
|
# ns is now a macro (clojure.core, 30-macros) expanding to in-ns + require/use/
|
||||||
name-sym (unwrap-meta-name raw-name)
|
# import/refer-clojure calls — all ctx-capturing fns — so it compiles. No
|
||||||
ns-name (sym-name-str name-sym)
|
# special-form arm; an (ns ...) head falls through to the macro-expansion path.
|
||||||
clauses (tuple/slice form 2)]
|
|
||||||
(ctx-set-current-ns ctx ns-name)
|
|
||||||
(ctx-find-ns ctx ns-name)
|
|
||||||
(var result nil)
|
|
||||||
(var i 0)
|
|
||||||
(let [clen (length clauses)]
|
|
||||||
(while (< i clen)
|
|
||||||
(let [clause (in clauses i)
|
|
||||||
head (if (and (array? clause) (> (length clause) 0)) (first clause) nil)]
|
|
||||||
(if (nil? head)
|
|
||||||
(do (set result clause) (++ i))
|
|
||||||
(match head
|
|
||||||
:require (let [specs (tuple/slice clause 1)
|
|
||||||
slen (length specs)]
|
|
||||||
(var j 0)
|
|
||||||
(while (< j slen)
|
|
||||||
(let [s (in specs j)]
|
|
||||||
(when s (eval-require ctx s)))
|
|
||||||
(++ j))
|
|
||||||
(set i (+ i 1)))
|
|
||||||
:use (let [specs (tuple/slice clause 1)
|
|
||||||
slen (length specs)]
|
|
||||||
(var j 0)
|
|
||||||
(while (< j slen)
|
|
||||||
(let [s (in specs j)
|
|
||||||
ns-sym (if (array? s) (in s 0) s)
|
|
||||||
ns-name (sym-name-str ns-sym)
|
|
||||||
source-ns (ctx-find-ns ctx ns-name)
|
|
||||||
target-ns (ctx-find-ns ctx ns-name)]
|
|
||||||
(loop [[sym v] :pairs (source-ns :mappings)]
|
|
||||||
(ns-intern target-ns sym (var-get v))))
|
|
||||||
(++ j))
|
|
||||||
(set i (+ i 1)))
|
|
||||||
:refer-clojure (let [spec (in clause 1)]
|
|
||||||
(when (and (array? spec) (= (first spec) :exclude))
|
|
||||||
(let [ns (ctx-find-ns ctx ns-name)]
|
|
||||||
(each sym (tuple/slice spec 1)
|
|
||||||
(ns-unmap ns (if (struct? sym) (sym :name) sym)))))
|
|
||||||
(set i (+ i 1)))
|
|
||||||
:import (let [specs (tuple/slice clause 1)
|
|
||||||
slen (length specs)]
|
|
||||||
(var j 0)
|
|
||||||
(while (< j slen)
|
|
||||||
(let [class-spec (in specs j)
|
|
||||||
class-name (if (struct? class-spec) (class-spec :name) (string class-spec))
|
|
||||||
last-dot (do
|
|
||||||
(var idx -1)
|
|
||||||
(var pos 0)
|
|
||||||
(while (< pos (length class-name))
|
|
||||||
(if (= (class-name pos) 46) (set idx pos))
|
|
||||||
(++ pos))
|
|
||||||
idx)
|
|
||||||
short-name (if (>= last-dot 0)
|
|
||||||
(string/slice class-name (+ last-dot 1))
|
|
||||||
class-name)]
|
|
||||||
(ns-import (ctx-find-ns ctx ns-name) short-name class-name))
|
|
||||||
(++ j))
|
|
||||||
(set i (+ i 1)))
|
|
||||||
(do (set result clause) (++ i)))))))
|
|
||||||
result)
|
|
||||||
# require / in-ns are now ordinary clojure.core fns (install-stateful-fns!) —
|
# require / in-ns are now ordinary clojure.core fns (install-stateful-fns!) —
|
||||||
# no special-form arm; they compile + interpret as plain invokes.
|
# no special-form arm; they compile + interpret as plain invokes.
|
||||||
"all-ns" (all-ns ctx)
|
"all-ns" (all-ns ctx)
|
||||||
|
|
|
||||||
|
|
@ -82,9 +82,10 @@
|
||||||
# ns-management forms dispatched by the interpreter (not core vars)
|
# ns-management forms dispatched by the interpreter (not core vars)
|
||||||
"create-ns" "remove-ns" "find-ns" "all-ns" "the-ns" "resolve"
|
"create-ns" "remove-ns" "find-ns" "all-ns" "the-ns" "resolve"
|
||||||
"ns-resolve" "ns-aliases" "ns-imports" "ns-interns"
|
"ns-resolve" "ns-aliases" "ns-imports" "ns-interns"
|
||||||
# require/in-ns are now clojure.core fns (compile as plain invokes).
|
# ns/require/in-ns/use/import/refer-clojure are now clojure.core
|
||||||
"read-string" "macroexpand-1" "defonce" "ns"
|
# fns/macros (compile as plain invokes / expand to them).
|
||||||
"import" "use" "refer" "defrecord"
|
"read-string" "macroexpand-1" "defonce"
|
||||||
|
"refer" "defrecord"
|
||||||
# defprotocol/extend-type/extend-protocol/reify now expand to plain
|
# defprotocol/extend-type/extend-protocol/reify now expand to plain
|
||||||
# def + protocol-dispatch/register-method/make-reified invokes.
|
# def + protocol-dispatch/register-method/make-reified invokes.
|
||||||
"gen-class"
|
"gen-class"
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
# host-coupled set (Stage 2 jolt-eaa): forms move off this list as they gain a
|
# host-coupled set (Stage 2 jolt-eaa): forms move off this list as they gain a
|
||||||
# compile path; syntax-quote already compiles via the analyzer's `handled` set.
|
# compile path; syntax-quote already compiles via the analyzer's `handled` set.
|
||||||
(defn- stateful-head? [head-name]
|
(defn- stateful-head? [head-name]
|
||||||
(or (= head-name "defmacro") (= head-name "ns")
|
(or (= head-name "defmacro")
|
||||||
(= head-name "deftype") (= head-name "defmulti") (= head-name "defmethod")
|
(= head-name "deftype") (= head-name "defmulti") (= head-name "defmethod")
|
||||||
(= head-name "set!")
|
(= head-name "set!")
|
||||||
(= head-name ".") (= head-name "new")
|
(= head-name ".") (= head-name "new")
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,8 @@
|
||||||
|
|
||||||
### ---- HIGH: aliased namespace calls ----
|
### ---- HIGH: aliased namespace calls ----
|
||||||
["require :as alias" "\"1,2,3\"" "(do (require (quote [clojure.string :as s])) (s/join \",\" [1 2 3]))"]
|
["require :as alias" "\"1,2,3\"" "(do (require (quote [clojure.string :as s])) (s/join \",\" [1 2 3]))"]
|
||||||
|
["ns form + alias" "\"HI\"" "(do (ns my.a (:require [clojure.string :as s])) (s/upper-case \"hi\"))"]
|
||||||
|
["ns :use refers" "42" "(do (ns src.u) (def helper 42) (ns dst.u (:use [src.u])) helper)"]
|
||||||
|
|
||||||
### ---- MED: missing core fns ----
|
### ---- MED: missing core fns ----
|
||||||
["peek vec" "3" "(peek [1 2 3])"]
|
["peek vec" "3" "(peek [1 2 3])"]
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@
|
||||||
# compiles over an INTERNED var; the built-in dynamic vars aren't interned yet,
|
# compiles over an INTERNED var; the built-in dynamic vars aren't interned yet,
|
||||||
# so it's exercised end-to-end in the state spec instead.)
|
# so it's exercised end-to-end in the state spec instead.)
|
||||||
"(require (quote [clojure.string :as s]))" "(in-ns (quote foo.bar))"
|
"(require (quote [clojure.string :as s]))" "(in-ns (quote foo.bar))"
|
||||||
|
"(ns foo.bar (:require [clojure.string :as s]))"
|
||||||
"(defprotocol P (m [x]))" "(extend-type Long P (m [x] x))"
|
"(defprotocol P (m [x]))" "(extend-type Long P (m [x] x))"
|
||||||
"(reify P (m [this] 1))" "(var map)"])
|
"(reify P (m [this] 1))" "(var map)"])
|
||||||
|
|
||||||
|
|
@ -55,7 +56,7 @@
|
||||||
# (require/in-ns/protocols/binding now compile). The remaining frozen/uncompiled
|
# (require/in-ns/protocols/binding now compile). The remaining frozen/uncompiled
|
||||||
# set keeps the harness honest in the punt direction.
|
# set keeps the harness honest in the punt direction.
|
||||||
(def must-punt
|
(def must-punt
|
||||||
["(ns foo.bar)" "(defmacro m [x] x)" "(deftype T [a])"
|
["(defmacro m [x] x)" "(deftype T [a])"
|
||||||
"(set! *warn-on-reflection* true)" "(letfn [(f [n] (g n)) (g [n] (f n))] (f 1))"])
|
"(set! *warn-on-reflection* true)" "(letfn [(f [n] (g n)) (g [n] (f n))] (f 1))"])
|
||||||
|
|
||||||
(var fails @[])
|
(var fails @[])
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,11 @@
|
||||||
|
|
||||||
(defspec "namespaces / ns operations"
|
(defspec "namespaces / ns operations"
|
||||||
["in-ns switches" "true" "(do (in-ns 'my.ns) (symbol? 'x))"]
|
["in-ns switches" "true" "(do (in-ns 'my.ns) (symbol? 'x))"]
|
||||||
|
# ns is a macro over in-ns/require/use/import (Stage 2 jolt-eaa): the form sets
|
||||||
|
# the current ns and processes its clauses.
|
||||||
|
["ns form + alias" "\"HI\"" "(do (ns my.app (:require [clojure.string :as s])) (s/upper-case \"hi\"))"]
|
||||||
|
["ns :use refers all" "9" "(do (ns src.lib) (def helper 9) (ns dst.app (:use [src.lib])) helper)"]
|
||||||
|
["standalone use" "7" "(do (ns src.l2) (def k 7) (in-ns 'dst.a2) (use '[src.l2]) k)"]
|
||||||
["ns-name" "true" "(do (require (quote [clojure.string])) (= 'clojure.string (ns-name (find-ns 'clojure.string))))"]
|
["ns-name" "true" "(do (require (quote [clojure.string])) (= 'clojure.string (ns-name (find-ns 'clojure.string))))"]
|
||||||
["find-ns existing" "true" "(some? (find-ns 'clojure.core))"]
|
["find-ns existing" "true" "(some? (find-ns 'clojure.core))"]
|
||||||
["find-ns missing" "nil" "(find-ns 'does.not.exist)"]
|
["find-ns missing" "nil" "(find-ns 'does.not.exist)"]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue