WASM: support variadic lambda parameters (rest args)

ober

6bf99f3544295f7427ceb2d9a28f5b26e33679b2

diff --git a/lib/jerboa/wasm/closure.sls b/lib/jerboa/wasm/closure.sls
index bf45f8b..8b1159b 100644
--- a/lib/jerboa/wasm/closure.sls
+++ b/lib/jerboa/wasm/closure.sls
@@ -28,6 +28,7 @@
   (export
     lambda-lift           ;; (list of forms) -> (list of forms)
     free-variables        ;; (expr bound-set) -> (list of symbols)
+    lambda-params         ;; formals -> (list of param names)
     )
 
   (import (chezscheme))
diff --git a/lib/std/secure/wasm-target.sls b/lib/std/secure/wasm-target.sls
index c54d5f1..59d327d 100644
--- a/lib/std/secure/wasm-target.sls
+++ b/lib/std/secure/wasm-target.sls
@@ -142,19 +142,26 @@
         (symbol->string base) "_"
         (number->string init-counter))))
 
-  ;; Lower parameters: strip type annotations, keep names
+  ;; Lower parameters: strip type annotations, keep names.
+  ;; Handles dotted lists for rest args: (x y . rest) → (x y . rest)
   (define (lower-params params)
-    (map (lambda (p)
-           (cond
-             [(symbol? p) p]
-             ;; (name type) -> name
-             [(and (pair? p) (symbol? (car p))) (car p)]
-             [else p]))
-         ;; Filter out -> return type annotation
-         (let loop ([ps params])
-           (cond [(null? ps) '()]
-                 [(eq? (car ps) '->) '()]
-                 [else (cons (car ps) (loop (cdr ps)))]))))
+    (define (strip-param p)
+      (cond
+        [(symbol? p) p]
+        ;; (name type) -> name
+        [(and (pair? p) (symbol? (car p))) (car p)]
+        [else p]))
+    (let loop ([ps params])
+      (cond
+        [(null? ps) '()]
+        ;; -> return type annotation — stop here
+        [(and (pair? ps) (eq? (car ps) '->)) '()]
+        ;; Dotted tail (rest arg): (x . rest) where rest is a symbol
+        [(symbol? ps) ps]
+        ;; Normal parameter
+        [(pair? ps)
+         (cons (strip-param (car ps)) (loop (cdr ps)))]
+        [else ps])))
 
   ;; ================================================================
   ;; Expression lowering: Slang -> compile-program subset
diff --git a/tests/test-slang-wasm.ss b/tests/test-slang-wasm.ss
index d618d77..4663604 100644
--- a/tests/test-slang-wasm.ss
+++ b/tests/test-slang-wasm.ss
@@ -648,6 +648,34 @@
   (check-pred bytevector? wasm)
   (check (> (bytevector-length wasm) 100) => #t))
 
+;; ================================================================
+;; Variadic Lambda Parameters
+;; ================================================================
+
+(section "Variadic Lambda Parameters")
+
+;; Lambda-params already handles rest args
+(check (lambda-params '(x y)) => '(x y))
+(check (lambda-params '(x . rest)) => '(x rest))
+(check (lambda-params 'args) => '(args))
+(check (lambda-params '((x i32) . rest)) => '(x rest))
+
+;; Free variable analysis handles rest args in lambda
+(let ([fvs (free-variables '(lambda (x . rest) (+ x y rest)) '())])
+  ;; x and rest are bound, y and + are free
+  (check-pred pair? (memq 'y fvs))
+  (check (memq 'x fvs) => #f)
+  (check (memq 'rest fvs) => #f))
+
+;; Lambda lifting of varargs lambda
+(let ([result (lambda-lift
+                '((define (f x)
+                    (let ([g (lambda (y . rest) (+ x y))])
+                      (g 1 2 3)))))])
+  (check-pred pair? result)
+  ;; Should have lifted definitions
+  (check (> (length result) 1) => #t))
+
 ;; Full runtime with UTF-8 string-length compiles to valid WASM
 (let ([wasm (compile-program
               (append