Chez Phase 1 (increment 3g): letfn + declare/def-no-init
Closes the last two non-host-interop prelude emit gaps. letfn now analyzes to a :let node flagged :letrec — the binding fns are bound into the env together before any spec is analyzed, so siblings and self resolve. The Chez back end lowers it to letrec*; the Janet back end punts it at emit (its sequential let* can't express the mutual recursion — same interpreter fallback as before, just decided at emit-ir instead of analyze). (def x) with no init (declare) analyzes to a :def with :no-init instead of punting. Chez reserves the var cell via declare-var! (which doesn't clobber an existing root — (do (def x 7) (def x) x) => 7); the Janet back end still punts to the interpreter, which interns a genuinely-unbound var. fallback-zero-test now checks emit-ir too, not just analyze-form, so the real compile-vs-interpret decision is what it asserts (letfn/def-no-init analyze but the Janet back end punts them). letfn stays in must-punt with an updated note. Prelude emit reach 342 -> 348/355 (40-lazy now 13/13); Chez subset 664 -> 672, 0 divergences; emit-test 110 -> 117. Full gate green.
This commit is contained in:
parent
930800f9a6
commit
0f7d2753a8
8 changed files with 119 additions and 31 deletions
|
|
@ -171,8 +171,12 @@
|
|||
(def b (vv b))
|
||||
(string "(" (munge (get b 0)) " " (emit (get b 1)) ")"))
|
||||
|
||||
# letfn lowers to a :let flagged :letrec (mutually-recursive named local fns):
|
||||
# Scheme `letrec*` binds them so each sees its siblings (and itself), which a
|
||||
# sequential let* can't. A plain let uses let* (Clojure let binds sequentially).
|
||||
(defn- emit-let [node]
|
||||
(string "(let* (" (string/join (map emit-binding (vv (get node :bindings))) " ") ") "
|
||||
(def kw (if (get node :letrec) "letrec*" "let*"))
|
||||
(string "(" kw " (" (string/join (map emit-binding (vv (get node :bindings))) " ") ") "
|
||||
(emit (get node :body)) ")"))
|
||||
|
||||
(defn- emit-loop [node]
|
||||
|
|
@ -362,8 +366,13 @@
|
|||
:try (emit-try node)
|
||||
:quote (emit-quoted (get node :form))
|
||||
:fn (emit-fn node)
|
||||
:def (string "(def-var! " (string/format "%j" (get node :ns)) " "
|
||||
(string/format "%j" (get node :name)) " " (emit (get node :init)) ")")
|
||||
# (def name) with no init (declare): reserve the var cell (declare-var!
|
||||
# doesn't clobber an existing root) so a forward reference resolves.
|
||||
:def (if (get node :no-init)
|
||||
(string "(declare-var! " (string/format "%j" (get node :ns)) " "
|
||||
(string/format "%j" (get node :name)) ")")
|
||||
(string "(def-var! " (string/format "%j" (get node :ns)) " "
|
||||
(string/format "%j" (get node :name)) " " (emit (get node :init)) ")"))
|
||||
(errorf "emit: unhandled op %p" (get node :op)))))
|
||||
|
||||
# Wrap emitted top-level forms into a runnable Chez program: load the RT, then
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue