support type hints; make metadata coherent
The reader expanded ^X form to (with-meta form X), which evaluated the tag (so
^String errored 'Unable to resolve symbol: String') and, as a param, was no
longer a bare symbol (so the arg bound to nil). Now a keyword/symbol/string hint
on a symbol attaches to the symbol's :meta and the symbol stays bare, so type
hints are transparent in params, lets, and bodies. Map metadata still uses a
runtime with-meta form.
meta now returns a symbol's :meta, and def applies the name's metadata
(^:dynamic, ^:private, ^Type tag, ^{:doc}) to the var, so (meta (var x)) is
consistent. Specs in metadata-spec; grammar note in the ebnf.
README notes regex \p{...} as unsupported (separate from this).
This commit is contained in:
parent
f749ec3c19
commit
761a2b8f72
8 changed files with 94 additions and 39 deletions
|
|
@ -1410,8 +1410,12 @@
|
|||
|
||||
(defn core-meta [x]
|
||||
"Returns the metadata of x, or nil."
|
||||
(if (var? x) (var-meta x)
|
||||
(if (table? x) (or (get x :jolt/meta) (get x :meta)) nil)))
|
||||
(cond
|
||||
(var? x) (var-meta x)
|
||||
# symbols carry reader metadata (type hints etc.) in a :meta field
|
||||
(and (struct? x) (= :symbol (get x :jolt/type))) (get x :meta)
|
||||
(table? x) (or (get x :jolt/meta) (get x :meta))
|
||||
nil))
|
||||
|
||||
(defn core-every-pred [& preds]
|
||||
(fn [& xs]
|
||||
|
|
|
|||
|
|
@ -632,10 +632,16 @@
|
|||
(if (> (length form) 3) (eval-form ctx bindings (in form 3)) nil)))
|
||||
"def" (let [raw-name (in form 1)
|
||||
name-sym (unwrap-meta-name raw-name)
|
||||
# Check for ^:dynamic metadata
|
||||
dynamic? (and (array? raw-name) (> (length raw-name) 0)
|
||||
(sym-name? (first raw-name) "with-meta")
|
||||
(= :dynamic (last raw-name)))
|
||||
# Metadata on the name: keyword/type-hint metadata rides on the
|
||||
# symbol (:meta); a ^{:map} reads as a with-meta form we evaluate.
|
||||
sym-meta (or (and (struct? name-sym) (get name-sym :meta)) {})
|
||||
wm-meta (if (and (array? raw-name) (> (length raw-name) 0)
|
||||
(sym-name? (first raw-name) "with-meta"))
|
||||
(let [mv (protect (eval-form ctx bindings (last raw-name)))]
|
||||
(if (and (mv 0) (or (table? (mv 1)) (struct? (mv 1)))) (mv 1) {}))
|
||||
{})
|
||||
name-meta (merge wm-meta sym-meta)
|
||||
dynamic? (truthy? (get name-meta :dynamic))
|
||||
ns-name (ctx-current-ns ctx)
|
||||
ns (ctx-find-ns ctx ns-name)
|
||||
# Create var first (unbound) so self-referencing defs resolve
|
||||
|
|
@ -644,8 +650,9 @@
|
|||
has-doc (and (> (length form) 3) (string? (in form 2)))
|
||||
val (eval-form ctx bindings (in form (if has-doc 3 2)))]
|
||||
(bind-root v val)
|
||||
(when has-doc
|
||||
(put v :meta (merge (or (get v :meta) {}) {:doc (in form 2)})))
|
||||
(let [extra (if has-doc (merge name-meta {:doc (in form 2)}) name-meta)]
|
||||
(when (not (empty? extra))
|
||||
(put v :meta (merge (or (get v :meta) {}) extra))))
|
||||
(when dynamic?
|
||||
(put v :dynamic true))
|
||||
# def returns the var (Clojure semantics); REPL prints #'ns/name
|
||||
|
|
|
|||
|
|
@ -446,11 +446,30 @@
|
|||
(let [[form final-pos] (read-form s new-pos)]
|
||||
[(array token-sym form) final-pos]))
|
||||
|
||||
(defn- meta-form->map
|
||||
"Normalize a metadata reader form (Clojure semantics): a symbol or string is a
|
||||
:tag, a keyword is {kw true}. Returns a metadata table, or nil if it isn't one
|
||||
of those simple shapes (e.g. a map literal — handled via with-meta instead)."
|
||||
[meta-form]
|
||||
(cond
|
||||
(keyword? meta-form) {meta-form true}
|
||||
(and (struct? meta-form) (= :symbol (meta-form :jolt/type))) {:tag (meta-form :name)}
|
||||
(string? meta-form) {:tag meta-form}
|
||||
nil))
|
||||
|
||||
(defn read-meta [s pos]
|
||||
# pos is at ^
|
||||
(let [[meta-form new-pos] (read-form s (+ pos 1))
|
||||
[form new-pos2] (read-form s new-pos)]
|
||||
[(array (sym "with-meta") form meta-form) new-pos2]))
|
||||
[form new-pos2] (read-form s new-pos)
|
||||
m (meta-form->map meta-form)]
|
||||
(if (and m (struct? form) (= :symbol (form :jolt/type)))
|
||||
# Attach the metadata to the symbol itself and keep it a bare symbol, so
|
||||
# type hints (^String x) and ^:dynamic etc. are transparent in every
|
||||
# position (params, lets, bodies) — the evaluator reads :name and ignores
|
||||
# :meta. This is what makes type hints "parse and otherwise do nothing".
|
||||
[(struct ;(kvs form) :meta (merge (or (form :meta) {}) m)) new-pos2]
|
||||
# Map metadata or non-symbol targets keep the runtime with-meta form.
|
||||
[(array (sym "with-meta") form meta-form) new-pos2])))
|
||||
|
||||
(defn read-until-newline [s pos]
|
||||
(if (or (>= pos (length s)) (= (s pos) 10))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue