Chez Phase 1 (increment 3e): throw/try/catch/finally + ex-info
Emit :throw as jolt-throw (Scheme raise of the raw jolt value, matching the
Janet compiled back end's (error v) — no envelope, so catch binds it directly).
Emit :try as guard (catch; the class is dropped in the IR, so it's catch-all)
plus dynamic-wind for finally. ex-info is a native-op building the tagged jolt
map {:jolt/type :jolt/ex-info :message :data :cause}, so the ex-data/ex-message/
ex-cause tier fns read it over jolt-get for free.
Prelude emit reach 303 -> 334/355 (:throw and :try gaps close). Subset probe
619 -> 632/632 compiled, 0 divergences (throw/try/ex-info pull 13 corpus cases
into the subset). emit-test 94/94 (added 11 throw/try/ex-info cases + uncaught
exits non-zero). Full gate green.
This commit is contained in:
parent
8f26433469
commit
ffa122440a
5 changed files with 87 additions and 12 deletions
|
|
@ -43,7 +43,10 @@
|
|||
"range" "jolt-range" "take" "jolt-take" "drop" "jolt-drop"
|
||||
"keys" "jolt-keys" "vals" "jolt-vals"
|
||||
"even?" "jolt-even?" "odd?" "jolt-odd?" "pos?" "jolt-pos?" "neg?" "jolt-neg?"
|
||||
"zero?" "jolt-zero?" "identity" "jolt-identity"})
|
||||
"zero?" "jolt-zero?" "identity" "jolt-identity"
|
||||
# exceptions (jolt-vcsl): ex-info builds the tagged map; ex-data/ex-message/
|
||||
# ex-cause are pure-over-get Clojure tier fns (no native-op needed).
|
||||
"ex-info" "jolt-ex-info"})
|
||||
|
||||
# Value-position resolution for a clojure.core ref passed AS A VALUE (to map /
|
||||
# filter / reduce / apply). Each native-op already names a usable Scheme
|
||||
|
|
@ -69,7 +72,8 @@
|
|||
"zero?" |(= $ 1) "identity" |(= $ 1)
|
||||
"cons" |(= $ 2) "filter" |(= $ 2) "remove" |(= $ 2) "into" |(= $ 2)
|
||||
"take" |(= $ 2) "drop" |(= $ 2) "map" |(>= $ 2) "apply" |(>= $ 2)
|
||||
"reduce" |(or (= $ 2) (= $ 3)) "range" |(and (>= $ 0) (<= $ 3))})
|
||||
"reduce" |(or (= $ 2) (= $ 3)) "range" |(and (>= $ 0) (<= $ 3))
|
||||
"ex-info" |(or (= $ 2) (= $ 3))})
|
||||
|
||||
# If fnode is a clojure.core (or host) ref to a native-op primitive, return the
|
||||
# Scheme op string — only at an arity where the Scheme op and the jolt fn agree.
|
||||
|
|
@ -168,6 +172,22 @@
|
|||
# (nil when empty — Clojure's rest semantics; list->cseq already does this); recur
|
||||
# carries the rest seq directly, and the named let's init only runs on first
|
||||
# entry, so the coercion isn't re-applied on a recur.
|
||||
# try/catch/finally (jolt-vcsl). throw raises the jolt value RAW (jolt-throw =
|
||||
# Scheme `raise`), mirroring the Janet COMPILED backend (which does `(error v)`,
|
||||
# no :jolt/exception envelope) — so catch binds the value directly, no unwrap.
|
||||
# catch lowers to `guard` with an `else` clause (catch-all: the IR drops the
|
||||
# class), finally to `dynamic-wind`'s after-thunk (runs on success, catch, and
|
||||
# escape — Clojure finally semantics). Both keys are optional on the node.
|
||||
(defn- emit-try [node]
|
||||
(def core
|
||||
(if-let [cs (get node :catch-sym)]
|
||||
(string "(guard (" (munge cs) " (else " (emit (get node :catch-body)) ")) "
|
||||
(emit (get node :body)) ")")
|
||||
(emit (get node :body))))
|
||||
(if-let [fin (get node :finally)]
|
||||
(string "(dynamic-wind (lambda () #f) (lambda () " core ") (lambda () " (emit fin) "))")
|
||||
core))
|
||||
|
||||
(defn- emit-arity-clause [a]
|
||||
(def params (map munge (vv (get a :params))))
|
||||
(def restp (when-let [r (get a :rest)] (munge r)))
|
||||
|
|
@ -309,6 +329,8 @@
|
|||
:let (emit-let node)
|
||||
:loop (emit-loop node)
|
||||
:recur (emit-recur node)
|
||||
:throw (string "(jolt-throw " (emit (get node :expr)) ")")
|
||||
:try (emit-try node)
|
||||
:fn (emit-fn node)
|
||||
:def (string "(def-var! " (string/format "%j" (get node :ns)) " "
|
||||
(string/format "%j" (get node :name)) " " (emit (get node :init)) ")")
|
||||
|
|
|
|||
|
|
@ -20,6 +20,25 @@
|
|||
;; jolt `not`: only nil and false are falsey.
|
||||
(define (jolt-not x) (if (jolt-truthy? x) #f #t))
|
||||
|
||||
;; --- exceptions (jolt-vcsl) --------------------------------------------------
|
||||
;; throw raises the jolt value RAW (no envelope), like the Janet compiled back
|
||||
;; end; catch (emitted as `guard`) binds it directly. Chez `raise` accepts any
|
||||
;; object, so a thrown number/map/ex-info all work; uncaught -> non-zero exit.
|
||||
(define (jolt-throw v) (raise v))
|
||||
;; ex-info builds the tagged map {:jolt/type :jolt/ex-info :message :data :cause}
|
||||
;; — a real jolt-hash-map, so the ex-data/ex-message/ex-cause tier fns read it
|
||||
;; via jolt-get for free. Arity 2 (msg data) or 3 (msg data cause).
|
||||
(define jolt-kw-ex-type (keyword "jolt" "type"))
|
||||
(define jolt-kw-ex-info (keyword "jolt" "ex-info"))
|
||||
(define jolt-kw-message (keyword #f "message"))
|
||||
(define jolt-kw-data (keyword #f "data"))
|
||||
(define jolt-kw-cause (keyword #f "cause"))
|
||||
(define (jolt-ex-info msg data . more)
|
||||
(jolt-hash-map jolt-kw-ex-type jolt-kw-ex-info
|
||||
jolt-kw-message msg
|
||||
jolt-kw-data data
|
||||
jolt-kw-cause (if (null? more) jolt-nil (car more))))
|
||||
|
||||
;; --- var cells: late-bound global roots (Clojure vars) -----------------------
|
||||
;; A var is a mutable cell keyed by "ns/name". A `:def` sets the root; a `:var`
|
||||
;; reference reads it at use time (late binding), so a forward/mutually-recursive
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue