Read metadata as data, consistently (sets, empty lists)
Clojure's reader reads a ^{…} map with the same read() as any value, so a set/
tagged literal in metadata is a value, not a form. jolt's data seam converted a
set-form to a set in the VALUE but left it as the tagged form inside the
METADATA, and dropped metadata on an empty list entirely (a wrong 'interned, =
Clojure' special case in rdr-attach-meta — Clojure's MetaReader withMetas () via
IObj). rdr-form->data now always converts + carries the (recursively converted)
metadata, whether or not the value structure changed; rdr-attach-meta no longer
skips (). Fixes aero's meta-preservation (set/map/vector/empty-list ds round-
trip). All runtime .ss (data seam), no re-mint.
This commit is contained in:
parent
6b99591266
commit
eb64240e29
2 changed files with 25 additions and 13 deletions
|
|
@ -307,7 +307,6 @@
|
||||||
((symbol-t? target)
|
((symbol-t? target)
|
||||||
(make-symbol-t (symbol-t-ns target) (symbol-t-name target)
|
(make-symbol-t (symbol-t-ns target) (symbol-t-name target)
|
||||||
(rdr-merge-meta (symbol-t-meta target) meta)))
|
(rdr-merge-meta (symbol-t-meta target) meta)))
|
||||||
((empty-list-t? target) target)
|
|
||||||
;; Lists/vectors/maps/sets attach metadata to the value itself, as Clojure's
|
;; Lists/vectors/maps/sets attach metadata to the value itself, as Clojure's
|
||||||
;; reader does. Reading DATA (read-string, edn) then preserves it. A list form
|
;; reader does. Reading DATA (read-string, edn) then preserves it. A list form
|
||||||
;; is code: ^Type (expr) is a compile-time hint on the FORM, read off the form
|
;; is code: ^Type (expr) is a compile-time hint on the FORM, read off the form
|
||||||
|
|
@ -607,8 +606,11 @@
|
||||||
(let ((c (rdr-form->data (car xs))))
|
(let ((c (rdr-form->data (car xs))))
|
||||||
(loop (cdr xs) (cons c acc) (or changed (not (eq? c (car xs)))))))))
|
(loop (cdr xs) (cons c acc) (or changed (not (eq? c (car xs)))))))))
|
||||||
|
|
||||||
|
;; carry the reader metadata, converting its nested forms too — a set/tagged
|
||||||
|
;; literal inside a ^{…} map (^{:k #{…}}) must become a value like the rest of
|
||||||
|
;; the data, not stay the tagged set-form.
|
||||||
(define (rdr-carry-meta src dst)
|
(define (rdr-carry-meta src dst)
|
||||||
(let ((m (jolt-meta src))) (if (jolt-nil? m) dst (jolt-with-meta dst m))))
|
(let ((m (jolt-meta src))) (if (jolt-nil? m) dst (jolt-with-meta dst (rdr-form->data m)))))
|
||||||
|
|
||||||
;; tag keyword (:#time/date) -> its *data-readers* reader fn, or #f. The fn's
|
;; tag keyword (:#time/date) -> its *data-readers* reader fn, or #f. The fn's
|
||||||
;; namespace must already be loaded (the loader requires them when a project's
|
;; namespace must already be loaded (the loader requires them when a project's
|
||||||
|
|
@ -664,33 +666,39 @@
|
||||||
(if dfn (jolt-invoke dfn (rdr-tag->symbol tag) inner)
|
(if dfn (jolt-invoke dfn (rdr-tag->symbol tag) inner)
|
||||||
(rdr-make-tagged tag inner))))))))
|
(rdr-make-tagged tag inner))))))))
|
||||||
|
|
||||||
(define (rdr-form->data x)
|
;; rdr-form->data*: convert the VALUE structure (set/tagged/nested forms). The
|
||||||
|
;; wrapper below adds the metadata, so the unchanged branches return x bare.
|
||||||
|
(define (rdr-form->data* x)
|
||||||
(cond
|
(cond
|
||||||
((and (pmap? x) (eq? (jolt-get x rdr-kw-jolt-type) rdr-kw-jolt-tagged))
|
((and (pmap? x) (eq? (jolt-get x rdr-kw-jolt-type) rdr-kw-jolt-tagged))
|
||||||
(rdr-construct-tag (jolt-get x rdr-kw-tag) (rdr-form->data (jolt-get x rdr-kw-form))))
|
(rdr-construct-tag (jolt-get x rdr-kw-tag) (rdr-form->data (jolt-get x rdr-kw-form))))
|
||||||
((rdr-set-form? x)
|
((rdr-set-form? x)
|
||||||
(let ((items (jolt-get x rdr-kw-value)))
|
(let ((items (jolt-get x rdr-kw-value)))
|
||||||
(rdr-carry-meta x
|
(let loop ((i 0) (s empty-pset))
|
||||||
(let loop ((i 0) (s empty-pset))
|
(if (fx>=? i (pvec-count items)) s
|
||||||
(if (fx>=? i (pvec-count items)) s
|
(loop (fx+ i 1) (pset-conj s (rdr-form->data (pvec-nth-d items i jolt-nil))))))))
|
||||||
(loop (fx+ i 1) (pset-conj s (rdr-form->data (pvec-nth-d items i jolt-nil)))))))))
|
|
||||||
((pvec? x)
|
((pvec? x)
|
||||||
(let-values (((items changed) (rdr-conv-each (vector->list (pvec-v x)))))
|
(let-values (((items changed) (rdr-conv-each (vector->list (pvec-v x)))))
|
||||||
(if changed (rdr-carry-meta x (apply jolt-vector items)) x)))
|
(if changed (apply jolt-vector items) x)))
|
||||||
((pmap? x)
|
((pmap? x)
|
||||||
(let ((order (hashtable-ref rdr-map-order x #f)))
|
(let ((order (hashtable-ref rdr-map-order x #f)))
|
||||||
(if order
|
(if order
|
||||||
(let-values (((kvs changed) (rdr-conv-each order)))
|
(let-values (((kvs changed) (rdr-conv-each order)))
|
||||||
(if changed
|
(if changed (rdr-make-map kvs) x))
|
||||||
(let ((m (rdr-make-map kvs))) (rdr-carry-meta x m))
|
|
||||||
x))
|
|
||||||
(let-values (((kvs changed)
|
(let-values (((kvs changed)
|
||||||
(rdr-conv-each (pmap-fold x (lambda (k v a) (cons k (cons v a))) '()))))
|
(rdr-conv-each (pmap-fold x (lambda (k v a) (cons k (cons v a))) '()))))
|
||||||
(if changed (rdr-carry-meta x (apply jolt-hash-map kvs)) x)))))
|
(if changed (apply jolt-hash-map kvs) x)))))
|
||||||
((cseq? x)
|
((cseq? x)
|
||||||
(let-values (((items changed) (rdr-conv-each (seq->list x))))
|
(let-values (((items changed) (rdr-conv-each (seq->list x))))
|
||||||
(if changed (rdr-carry-meta x (apply jolt-list items)) x)))
|
(if changed (apply jolt-list items) x)))
|
||||||
(else x)))
|
(else x)))
|
||||||
|
;; Read DATA always carries metadata, converting its nested forms too — Clojure's
|
||||||
|
;; reader reads a ^{…} map with the same read() as any value, so a set/tagged
|
||||||
|
;; literal in metadata is a value, not a form. Carry it whether or not the value
|
||||||
|
;; itself changed (a set-form in the metadata of an otherwise-unchanged value).
|
||||||
|
(define (rdr-form->data x)
|
||||||
|
(let ((v (rdr-form->data* x)) (m (jolt-meta x)))
|
||||||
|
(if (jolt-nil? m) v (jolt-with-meta v (rdr-form->data m)))))
|
||||||
|
|
||||||
;; --- the two host seams -----------------------------------------------------
|
;; --- the two host seams -----------------------------------------------------
|
||||||
;; clojure.core/read-string: first form, or nil for blank / comment-only input
|
;; clojure.core/read-string: first form, or nil for blank / comment-only input
|
||||||
|
|
|
||||||
|
|
@ -3302,4 +3302,8 @@
|
||||||
{:suite "defn / attr-map" :label "attr-map values are evaluated" :expected "true" :actual "(do (defn m {:val (+ 1 2)} [] 1) (= 3 (:val (meta (var m)))))"}
|
{:suite "defn / attr-map" :label "attr-map values are evaluated" :expected "true" :actual "(do (defn m {:val (+ 1 2)} [] 1) (= 3 (:val (meta (var m)))))"}
|
||||||
{:suite "records / clojure.lang interop" :label "a record's Associative/ILookup methods delegate to the map fns" :expected "[1 9 true true 2]" :actual "(do (defrecord M [a b]) (let [m (->M 1 2)] [(.valAt m :a) (.valAt m :z 9) (= {:a 1 :b 2 :c 3} (into {} (.assoc m :c 3))) (.containsKey m :a) (.count m)]))"}
|
{:suite "records / clojure.lang interop" :label "a record's Associative/ILookup methods delegate to the map fns" :expected "[1 9 true true 2]" :actual "(do (defrecord M [a b]) (let [m (->M 1 2)] [(.valAt m :a) (.valAt m :z 9) (= {:a 1 :b 2 :c 3} (into {} (.assoc m :c 3))) (.containsKey m :a) (.count m)]))"}
|
||||||
{:suite "interop / number classes" :label "Byte/Short bounds + class tokens" :expected "[127 -128 32767 -32768 true]" :actual "[Byte/MAX_VALUE Byte/MIN_VALUE Short/MAX_VALUE Short/MIN_VALUE (= java.lang.Byte java.lang.Byte)]"}
|
{:suite "interop / number classes" :label "Byte/Short bounds + class tokens" :expected "[127 -128 32767 -32768 true]" :actual "[Byte/MAX_VALUE Byte/MIN_VALUE Short/MAX_VALUE Short/MIN_VALUE (= java.lang.Byte java.lang.Byte)]"}
|
||||||
|
{:suite "reader / metadata" :label "a set in metadata is read as a value, not a tagged form" :expected "true" :actual "(= #{:a :b} (:foo (meta (read-string (binding [*print-meta* true] (pr-str (with-meta [1] {:foo #{:a :b}})))))))"}
|
||||||
|
{:suite "reader / metadata" :label "metadata on an empty list is preserved" :expected "true" :actual "(= {:foo 1} (meta (read-string \"^{:foo 1} ()\")))"}
|
||||||
|
{:suite "reader / metadata" :label "a vector in metadata reads as a value" :expected "true" :actual "(= [1 2] (:v (meta (read-string \"^{:v [1 2]} {:a 1}\"))))"}
|
||||||
|
{:suite "reader / metadata" :label "an empty list in metadata reads as a value" :expected "true" :actual "(= () (:e (meta (read-string \"^{:e ()} [1]\"))))"}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue