macroexpand-first analyzer order; one macro path; defmacro/letfn fixes
The analyzer checked special forms before expanding macros, the reverse of the canonical read -> macroexpand -> analyze order (Clojure/CLJS analyze-seq). Move macroexpansion to the front of analyze-list. Knock-on fixes: - letfn was both a (broken) macro expanding to let* AND a primitive special (analyze-letfn, proper letrec*). Macroexpand-first surfaced the macro, breaking mutual recursion; remove the macro, keep letfn a primitive. - defmacro is now compiled by the analyzer (a :set-var-style :defmacro node that defs the expander fn via the fn macro — so destructuring arglists desugar — and marks the var a macro), so a non-top-level (when … (defmacro …)) works. The runtime spine's separate top-level defmacro interception is removed: one path. SCI load 162 -> 202/218.
This commit is contained in:
parent
e6ee17b055
commit
f18ae3bd46
8 changed files with 402 additions and 383 deletions
|
|
@ -225,10 +225,10 @@
|
|||
|
||||
;; Build the fn* form via a template (a reader-list array): cons/list in a macro
|
||||
;; body produce a plist the evaluator can't call as a form.
|
||||
(defmacro letfn [fnspecs & body]
|
||||
(let [binds (reduce (fn [acc spec] (conj (conj acc (first spec)) `(fn* ~@(rest spec))))
|
||||
[] fnspecs)]
|
||||
`(let* [~@binds] ~@body)))
|
||||
;; letfn is a primitive special form (analyze-letfn -> letrec*), not a macro: its
|
||||
;; fns are mutually recursive, which a (let* …) expansion cannot express. Defining
|
||||
;; it as a macro would shadow the special once macroexpansion runs first (the
|
||||
;; canonical order), so it is intentionally NOT a macro here.
|
||||
|
||||
;; Dynamic binding: install a thread-binding frame of var->value (array-map keeps
|
||||
;; var-get happy, unlike a phm), restore on exit.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue