Phase 7A: add try-catch codegen + 8 tests

ober

57995702ac2529f511160800ca6eb131b6f2449b

diff --git a/lib/jerboa/wasm/codegen.sls b/lib/jerboa/wasm/codegen.sls
index 0d409f2..f3bc196 100644
--- a/lib/jerboa/wasm/codegen.sls
+++ b/lib/jerboa/wasm/codegen.sls
@@ -1113,6 +1113,29 @@
               (bv-concat-list (map (lambda (a) (compile-expr a ctx)) (cdr args)))
               (bytevector wasm-opcode-throw) (encode-u32-leb128 (car args)))]
 
+           ;; (try-catch tag-idx body catch-var handler)
+           ;; Emits legacy exceptions try/catch: evaluates body; if an exception
+           ;; with tag-idx is thrown, binds the first payload value to catch-var
+           ;; and evaluates handler.  Both branches must produce an i32 result.
+           [(try-catch)
+            (let* ([tag-idx  (car args)]
+                   [body     (cadr args)]
+                   [cvar     (caddr args)]
+                   [handler  (cadddr args)]
+                   ;; Allocate a local for the caught value
+                   [cvar-idx (context-add-local! ctx cvar)])
+              (bv-concat
+                ;; try block returning i32
+                (bytevector wasm-opcode-try wasm-type-i32)
+                (compile-expr body ctx)
+                ;; catch: pops the payload (first i32) into local cvar
+                (bytevector wasm-opcode-catch)
+                (encode-u32-leb128 tag-idx)
+                (bytevector wasm-opcode-local-set)
+                (encode-u32-leb128 cvar-idx)
+                (compile-expr handler ctx)
+                (bytevector wasm-opcode-end)))]
+
            ;; -- GC: struct operations --
            ;; (struct.new type-idx field-exprs...)
            [(struct.new)
diff --git a/tests/test-slang-wasm.ss b/tests/test-slang-wasm.ss
index e56adca..2af48ab 100644
--- a/tests/test-slang-wasm.ss
+++ b/tests/test-slang-wasm.ss
@@ -710,6 +710,88 @@
   (check-pred bytevector? wasm)
   (check (> (bytevector-length wasm) 30) => #t))
 
+;; Helper: check whether a bytevector contains a given byte value
+(define (bv-contains? bv byte)
+  (let loop ([i 0])
+    (cond
+      [(>= i (bytevector-length bv)) #f]
+      [(= (bytevector-u8-ref bv i) byte) #t]
+      [else (loop (+ i 1))])))
+
+;; try-catch compiles to a valid WASM bytevector
+(let ([wasm (compile-program
+              (append
+                value-memory-forms
+                value-global-forms
+                value-tag-forms
+                '((define-tag 0)
+                  (define (safe-add a b)
+                    (try-catch 0
+                      (if (= b 0) (throw 0 0) (+ a b))
+                      exn
+                      -1)))))])
+  (check-pred bytevector? wasm)
+  (check (> (bytevector-length wasm) 30) => #t))
+
+;; try-catch binary contains the try opcode (#x06)
+(let ([wasm (compile-program
+              (append
+                value-memory-forms
+                value-global-forms
+                value-tag-forms
+                '((define-tag 0)
+                  (define (safe-add a b)
+                    (try-catch 0
+                      (if (= b 0) (throw 0 0) (+ a b))
+                      exn
+                      -1)))))])
+  (check (bv-contains? wasm #x06) => #t))
+
+;; try-catch binary contains the catch opcode (#x07)
+(let ([wasm (compile-program
+              (append
+                value-memory-forms
+                value-global-forms
+                value-tag-forms
+                '((define-tag 0)
+                  (define (safe-add a b)
+                    (try-catch 0
+                      (if (= b 0) (throw 0 0) (+ a b))
+                      exn
+                      -1)))))])
+  (check (bv-contains? wasm #x07) => #t))
+
+;; try-catch with constant handler compiles (minimal form)
+(let ([wasm (compile-program
+              (append
+                value-memory-forms
+                value-global-forms
+                value-tag-forms
+                '((define-tag 0)
+                  (define (try-const x)
+                    (try-catch 0
+                      x
+                      _err
+                      0)))))])
+  (check-pred bytevector? wasm)
+  (check (bv-contains? wasm #x06) => #t))
+
+;; try-catch catch-var is accessible in handler expression
+;; (handler uses the caught value — compiles without error)
+(let ([wasm (compile-program
+              (append
+                value-memory-forms
+                value-global-forms
+                value-tag-forms
+                '((define-tag 0)
+                  (define (recover x)
+                    (try-catch 0
+                      (throw 0 x)
+                      caught
+                      caught)))))])
+  (check-pred bytevector? wasm)
+  (check (bv-contains? wasm #x07) => #t))
+
 ;; ================================================================
 ;; Tail Call Patterns
 ;; ================================================================