Phase 3: Var system — var-get, var-set, var?, alter-var-root, find-var, alter-meta!, reset-meta!

- types.janet: find-var (ctx-based var resolution), alter-meta!, reset-meta!
- evaluator.janet: 10 new special-form dispatch arms for var ops
- core.janet: 6 new Clojure-level wrappers in core-bindings
- core-meta: add var-aware branch (was struct-only)
- core-binding macro: use array-map instead of hash-map (PHM incompatibility)
- 8 new tests (17: var system, 18: var metadata) — all pass
- 317 total tests, 0 failures
This commit is contained in:
Yogthos 2026-06-02 20:43:24 -04:00
parent ad895945a0
commit 8e86c7c2ec
8 changed files with 177 additions and 51 deletions

View file

@ -467,7 +467,8 @@
(defn core-meta [x]
"Returns the metadata of x, or nil."
(if (struct? x) (get x :meta) nil))
(if (var? x) (var-meta x)
(if (struct? x) (get x :meta) nil)))
(def core-comp
(fn [& fs]
@ -780,8 +781,17 @@
(defn core-push-thread-bindings [b] (push-thread-bindings b))
(defn core-pop-thread-bindings [] (pop-thread-bindings))
(defn core-var-get [v] (var-get v))
(defn core-var-set [v val] (var-set v val))
(defn core-var? [x] (var? x))
(defn core-alter-var-root [v f & args] (apply alter-var-root v f args))
(defn core-alter-meta! [v f & args] (apply alter-meta! v f args))
(defn core-reset-meta! [v meta] (reset-meta! v meta))
(defn core-binding
"Macro: (binding [var val ...] body...)"
"Macro: (binding [var val ...] body...)
Uses array-map (plain struct) to store binding frame
to avoid PHM get() incompatibility with var-get."
[bindings & body]
(def frame-pairs @[])
(var i 0)
@ -792,7 +802,7 @@
(array/push frame-pairs (in bindings (+ i 1)))
(+= i 2)))
(def hm-form (array/insert frame-pairs 0
{:jolt/type :symbol :ns nil :name "hash-map"}))
{:jolt/type :symbol :ns nil :name "array-map"}))
@[{:jolt/type :symbol :ns nil :name "let*"}
[{:jolt/type :symbol :ns nil :name "frame"} hm-form]
@[{:jolt/type :symbol :ns nil :name "push-thread-bindings"}
@ -1155,6 +1165,12 @@
"avoid-method-too-large" core-avoid-method-too-large
"qualified-symbol?" core-qualified-symbol?
"meta" core-meta
"var-get" core-var-get
"var-set" core-var-set
"var?" core-var?
"alter-var-root" core-alter-var-root
"alter-meta!" core-alter-meta!
"reset-meta!" core-reset-meta!
"binding" core-binding
"push-thread-bindings" core-push-thread-bindings
"pop-thread-bindings" core-pop-thread-bindings

View file

@ -15,7 +15,10 @@
(= name "recur") (= name "throw") (= name "try")
(= name "set!") (= name "var") (= name "locking")
(= name "instance?") (= name "defmulti") (= name "defmethod")
(= name "deftype") (= name "new") (= name ".")))
(= name "deftype") (= name "new") (= name ".")
(= name "var-get") (= name "var-set") (= name "var?")
(= name "alter-var-root") (= name "find-var") (= name "intern")
(= name "alter-meta!") (= name "reset-meta!")))
(var eval-form nil)
@ -568,8 +571,21 @@
(def new-v (ns-intern ns (target-sym :name) val))
val)))))
"var" (let [target-sym (in form 1)
v (resolve-var ctx bindings target-sym)]
(if v v (error (string "Unable to resolve var: " (sym-name-str target-sym) " in var"))))
v (resolve-var ctx bindings target-sym)]
(if v v (error (string "Unable to resolve var: " (sym-name-str target-sym) " in var"))))
"var-get" (var-get (eval-form ctx bindings (in form 1)))
"var-set" (var-set (eval-form ctx bindings (in form 1))
(eval-form ctx bindings (in form 2)))
"var?" (var? (eval-form ctx bindings (in form 1)))
"alter-var-root" (alter-var-root (eval-form ctx bindings (in form 1))
(eval-form ctx bindings (in form 2)))
"find-var" (find-var ctx (eval-form ctx bindings (in form 1)))
"alter-meta!" (let [v (eval-form ctx bindings (in form 1))
f (eval-form ctx bindings (in form 2))
args (map |(eval-form ctx bindings $) (tuple/slice form 3))]
(apply alter-meta! v f args))
"reset-meta!" (reset-meta! (eval-form ctx bindings (in form 1))
(eval-form ctx bindings (in form 2)))
"locking" (eval-form ctx bindings (in form 2))
"instance?" (let [type-sym (in form 1)
val (eval-form ctx bindings (in form 2))]

View file

@ -112,6 +112,19 @@
(let [new-val (f (v :root) ;args)]
(put v :root new-val)))
(defn alter-meta!
"Atomically update a var's metadata via (apply f args)."
[v f & args]
(let [new-meta (apply f (var-meta v) args)]
(put v :meta new-meta)
new-meta))
(defn reset-meta!
"Reset a var's metadata to the given value."
[v meta]
(put v :meta meta)
meta)
(defn with-meta
"Return a new var with updated metadata. The original var is unchanged."
[v meta]
@ -319,3 +332,18 @@
[ctx]
(let [ns (ctx-find-ns ctx (ctx-current-ns ctx))]
(ns :imports)))
(defn find-var
"Resolve a symbol to a var in the current context.
Looks in current namespace first, then clojure.core."
[ctx sym-s]
(let [name (sym-s :name)
ns-sym (sym-s :ns)]
(if ns-sym
(let [ns (ctx-find-ns ctx ns-sym)]
(ns-find ns name))
(let [current-ns (ctx-find-ns ctx (ctx-current-ns ctx))
v (ns-find current-ns name)]
(if v v
(let [core-ns (ctx-find-ns ctx "clojure.core")]
(ns-find core-ns name)))))))