;; restart-demo.sx — Classic CL condition system demo ;; ;; Demonstrates resumable exceptions via restarts. ;; The `safe-divide` function signals a division-by-zero condition ;; and offers two restarts: ;; :use-zero — return 0 as the result ;; :retry — call safe-divide again with a corrected divisor ;; ;; Depends on: lib/common-lisp/runtime.sx already loaded. ;; ── safe-divide ──────────────────────────────────────────────────────────── ;; ;; Divides numerator by denominator. ;; When denominator is 0, signals division-by-zero with two restarts. (define safe-divide (fn (n d) (if (= d 0) (cl-restart-case (fn () (cl-signal (cl-make-condition "division-by-zero" "operation" "/" "operands" (list n d))) (error "division by zero — no restart invoked")) (list "use-zero" (list) (fn () 0)) (list "retry" (list "d") (fn (d2) (safe-divide n d2)))) (/ n d)))) ;; ── tests ───────────────────────────────────────────────────────────────── (define passed 0) (define failed 0) (define failures (list)) (define check (fn (label got expected) (if (= got expected) (set! passed (+ passed 1)) (begin (set! failed (+ failed 1)) (set! failures (append failures (list (str "FAIL [" label "]: got=" (inspect got) " expected=" (inspect expected))))))))) (define reset-stacks! (fn () (set! cl-handler-stack (list)) (set! cl-restart-stack (list)))) ;; Normal division (reset-stacks!) (check "10 / 2 = 5" (safe-divide 10 2) 5) ;; Invoke use-zero restart (reset-stacks!) (check "10 / 0 -> use-zero" (cl-handler-bind (list (list "division-by-zero" (fn (c) (cl-invoke-restart "use-zero")))) (fn () (safe-divide 10 0))) 0) ;; Invoke retry restart with a corrected denominator (reset-stacks!) (check "10 / 0 -> retry with 2" (cl-handler-bind (list (list "division-by-zero" (fn (c) (cl-invoke-restart "retry" 2)))) (fn () (safe-divide 10 0))) 5) ;; Nested calls: outer handles the inner divide-by-zero (reset-stacks!) (check "nested: 20 / (0->4) = 5" (cl-handler-bind (list (list "division-by-zero" (fn (c) (cl-invoke-restart "retry" 4)))) (fn () (let ((r1 (safe-divide 20 0))) r1))) 5) ;; handler-case — unwinding version (reset-stacks!) (check "handler-case: catches division-by-zero" (cl-handler-case (fn () (safe-divide 9 0)) (list "division-by-zero" (fn (c) "caught!"))) "caught!") ;; Verify use-zero is idempotent (two uses) (reset-stacks!) (check "two use-zero invocations" (cl-handler-bind (list (list "division-by-zero" (fn (c) (cl-invoke-restart "use-zero")))) (fn () (+ (safe-divide 10 0) (safe-divide 3 0)))) 0) ;; No restart needed for normal division (reset-stacks!) (check "no restart needed for 8/4" (safe-divide 8 4) 2) ;; ── summary ──────────────────────────────────────────────────────────────── (define demo-passed passed) (define demo-failed failed) (define demo-failures failures)