jolt/test/spec/namespaces-spec.janet
Dmitri Sotnikov 63d92cd122
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>
2026-06-10 04:34:53 +08:00

37 lines
2.6 KiB
Text

# Specification: namespaces, vars and require.
(use ../support/harness)
(defspec "namespaces / def & vars"
["def + deref" "5" "(do (def x 5) x)"]
["def returns var" "true" "(var? (def y 1))"]
["declare then def" "2" "(do (declare z) (def z 2) z)"]
["var special form" "true" "(var? (var +))"]
["var sugar #'" "true" "(var? #'+)"]
["var-get" "5" "(do (def w 5) (var-get #'w))"]
["defn defines fn" "3" "(do (defn f [x] (inc x)) (f 2))"]
["def with docstring" "7" "(do (def d \"a doc\" 7) d)"]
["dynamic var binding" "2" "(do (def ^:dynamic *x* 1) (binding [*x* 2] *x*))"]
["binding restores" "1" "(do (def ^:dynamic *y* 1) (binding [*y* 9] nil) *y*)"]
["var-set in binding" "5" "(do (def ^:dynamic *z* 1) (binding [*z* 0] (var-set (var *z*) 5) *z*))"])
(defspec "namespaces / ns operations"
["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))))"]
["find-ns existing" "true" "(some? (find-ns 'clojure.core))"]
["find-ns missing" "nil" "(find-ns 'does.not.exist)"]
["resolve var" "true" "(var? (resolve '+))"]
["resolve missing" "nil" "(resolve 'totally-undefined-xyz)"])
(defspec "namespaces / require & refer"
["require :as" "\"AB\"" "(do (require '[clojure.string :as s]) (s/upper-case \"ab\"))"]
["require :refer" "true" "(do (require '[clojure.string :refer [blank?]]) (blank? \"\"))"]
["require :as + :refer" "true" "(do (require '[clojure.string :as s :refer [blank?]]) (and (blank? \"\") (= \"X\" (s/upper-case \"x\"))))"]
["require clojure.set" "#{1 2 3}" "(do (require '[clojure.set :as set]) (set/union #{1 2} #{3}))"]
["require clojure.walk" "{:a 2}" "(do (require '[clojure.walk :as w]) (w/postwalk (fn [x] (if (number? x) (inc x) x)) {:a 1}))"]
["walk keywordize-keys" "{:a 1}" "(do (require '[clojure.walk :as w]) (w/keywordize-keys {\"a\" 1}))"]
["walk stringify-keys" "true" "(do (require '[clojure.walk :as w]) (= {\"a\" 1} (w/stringify-keys {:a 1})))"])