erlang: try/catch/of/after, Phase 4 complete (+19 tests)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
This commit is contained in:
@@ -122,6 +122,7 @@
|
||||
(= ty "fun") (er-eval-fun node env)
|
||||
(= ty "send") (er-eval-send node env)
|
||||
(= ty "receive") (er-eval-receive node env)
|
||||
(= ty "try") (er-eval-try node env)
|
||||
(= ty "match") (er-eval-match node env)
|
||||
:else (error (str "Erlang eval: unsupported node type '" ty "'"))))))
|
||||
|
||||
@@ -573,6 +574,8 @@
|
||||
(= name "monitor") (er-bif-monitor vs)
|
||||
(= name "demonitor") (er-bif-demonitor vs)
|
||||
(= name "process_flag") (er-bif-process-flag vs)
|
||||
(= name "throw") (raise (er-mk-throw-marker (er-bif-arg1 vs "throw")))
|
||||
(= name "error") (raise (er-mk-error-marker (er-bif-arg1 vs "error")))
|
||||
:else (error
|
||||
(str "Erlang: undefined function '" name "/" (len vs) "'")))))
|
||||
|
||||
@@ -1089,3 +1092,143 @@
|
||||
(do
|
||||
(er-env-restore! env snap)
|
||||
(er-try-receive-clauses clauses msg env (+ i 1))))))))
|
||||
|
||||
;; ── try/of/catch/after ────────────────────────────────────────────
|
||||
;; The outer guard captures any exception so the `after` body is
|
||||
;; guaranteed to run, then re-raises. The inner guard runs the
|
||||
;; expression body, optional `of` clauses on success, and `catch`
|
||||
;; clauses on a thrown/erred/exited condition. If no catch clause
|
||||
;; matches the raised class+pattern, the inner guard's clause
|
||||
;; re-raises by returning nothing (handled via re-raise marker).
|
||||
(define
|
||||
er-eval-try
|
||||
(fn
|
||||
(node env)
|
||||
(let
|
||||
((after-body (get node :after))
|
||||
(saved-exc (list nil))
|
||||
(result-ref (list nil)))
|
||||
(guard
|
||||
(c (:else (do (set-nth! saved-exc 0 c) nil)))
|
||||
(set-nth! result-ref 0 (er-eval-try-inner node env)))
|
||||
(when
|
||||
(> (len after-body) 0)
|
||||
(er-eval-body after-body env))
|
||||
(if
|
||||
(= (nth saved-exc 0) nil)
|
||||
(nth result-ref 0)
|
||||
(raise (nth saved-exc 0))))))
|
||||
|
||||
(define
|
||||
er-eval-try-inner
|
||||
(fn
|
||||
(node env)
|
||||
(let
|
||||
((catch-clauses (get node :catch-clauses))
|
||||
(of-clauses (get node :of-clauses))
|
||||
(caught-ref (list false))
|
||||
(result-ref (list nil))
|
||||
(re-raise-ref (list nil)))
|
||||
(guard
|
||||
(c
|
||||
((er-thrown? c)
|
||||
(er-eval-try-catch
|
||||
catch-clauses "throw" (get c :reason) env
|
||||
caught-ref result-ref re-raise-ref))
|
||||
((er-errored? c)
|
||||
(er-eval-try-catch
|
||||
catch-clauses "error" (get c :reason) env
|
||||
caught-ref result-ref re-raise-ref))
|
||||
((er-exited? c)
|
||||
(er-eval-try-catch
|
||||
catch-clauses "exit" (get c :reason) env
|
||||
caught-ref result-ref re-raise-ref)))
|
||||
(let
|
||||
((r (er-eval-body (get node :exprs) env)))
|
||||
(if
|
||||
(= (len of-clauses) 0)
|
||||
(set-nth! result-ref 0 r)
|
||||
(set-nth!
|
||||
result-ref
|
||||
0
|
||||
(er-eval-of-clauses of-clauses r env 0)))))
|
||||
(when (not (= (nth re-raise-ref 0) nil))
|
||||
(raise (nth re-raise-ref 0)))
|
||||
(nth result-ref 0))))
|
||||
|
||||
;; Try catch-clauses against (Class, Reason). If a clause matches,
|
||||
;; runs its body and writes to result-ref. If none match, queues a
|
||||
;; re-raise marker.
|
||||
(define
|
||||
er-eval-try-catch
|
||||
(fn
|
||||
(clauses class-name reason env caught-ref result-ref re-raise-ref)
|
||||
(er-eval-try-catch-iter
|
||||
clauses class-name reason env 0 caught-ref result-ref re-raise-ref)))
|
||||
|
||||
(define
|
||||
er-eval-try-catch-iter
|
||||
(fn
|
||||
(clauses class-name reason env i caught-ref result-ref re-raise-ref)
|
||||
(if
|
||||
(>= i (len clauses))
|
||||
(set-nth!
|
||||
re-raise-ref
|
||||
0
|
||||
(er-mk-class-marker class-name reason))
|
||||
(let
|
||||
((c (nth clauses i))
|
||||
(snap (er-env-copy env))
|
||||
(clause-class (get (get c :class) :value)))
|
||||
(cond
|
||||
(not (= clause-class class-name))
|
||||
(er-eval-try-catch-iter
|
||||
clauses class-name reason env (+ i 1)
|
||||
caught-ref result-ref re-raise-ref)
|
||||
:else
|
||||
(if
|
||||
(and
|
||||
(er-match! (get c :pattern) reason env)
|
||||
(er-eval-guards (get c :guards) env))
|
||||
(do
|
||||
(set-nth! caught-ref 0 true)
|
||||
(set-nth!
|
||||
result-ref
|
||||
0
|
||||
(er-eval-body (get c :body) env)))
|
||||
(do
|
||||
(er-env-restore! env snap)
|
||||
(er-eval-try-catch-iter
|
||||
clauses class-name reason env (+ i 1)
|
||||
caught-ref result-ref re-raise-ref))))))))
|
||||
|
||||
(define
|
||||
er-mk-class-marker
|
||||
(fn
|
||||
(class-name reason)
|
||||
(cond
|
||||
(= class-name "throw") (er-mk-throw-marker reason)
|
||||
(= class-name "error") (er-mk-error-marker reason)
|
||||
(= class-name "exit") (er-mk-exit-marker reason)
|
||||
:else (er-mk-error-marker reason))))
|
||||
|
||||
(define
|
||||
er-eval-of-clauses
|
||||
(fn
|
||||
(clauses subject env i)
|
||||
(if
|
||||
(>= i (len clauses))
|
||||
(raise
|
||||
(er-mk-error-marker
|
||||
(er-mk-tuple
|
||||
(list (er-mk-atom "try_clause") subject))))
|
||||
(let
|
||||
((c (nth clauses i)) (snap (er-env-copy env)))
|
||||
(if
|
||||
(and
|
||||
(er-match! (get c :pattern) subject env)
|
||||
(er-eval-guards (get c :guards) env))
|
||||
(er-eval-body (get c :body) env)
|
||||
(do
|
||||
(er-env-restore! env snap)
|
||||
(er-eval-of-clauses clauses subject env (+ i 1))))))))
|
||||
|
||||
Reference in New Issue
Block a user