From 73dc042ea8e0edc69febae22a0c7e4ea9a3d4efa Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Sun, 14 Jun 2026 12:30:01 -0400 Subject: [PATCH] wat iter-1: special forms + 35 primitives toward asm/lumbda.s parity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Special forms added: let*, letrec, when, unless, case, named-let Primitives added (IDs 25-60): quotient, remainder, min, max, expt even?, odd?, positive?, negative? set-car!, set-cdr!, equal?, eqv? number?, integer?, symbol?, string?, procedure?, boolean? caar, cadr, cdar, cddr, caddr, cadddr reverse, append, apply, error member, memq, assoc, assq list-ref, list-tail, void Plus helpers: equal_p (deep structural), append2, member_eq, assoc_eq. 39/39 wasm tests pass (20 unit + 8 integration + 11 functional). Footer language softened — no more "minimal subset" disclaimer. --- wasm/app/index.html | 10 +- wasm/app/repl.html | 60 +++ wasm/asm/lumbda.wat | 649 ++++++++++++++++++++++++++++- www/playground/asm/lumbda-asm.wasm | Bin 4965 -> 8000 bytes www/playground/index.html | 10 +- 5 files changed, 716 insertions(+), 13 deletions(-) create mode 100644 wasm/app/repl.html diff --git a/wasm/app/index.html b/wasm/app/index.html index 648ccef..c2bc429 100644 --- a/wasm/app/index.html +++ b/wasm/app/index.html @@ -71,11 +71,11 @@ diff --git a/wasm/app/repl.html b/wasm/app/repl.html new file mode 100644 index 0000000..c0f2935 --- /dev/null +++ b/wasm/app/repl.html @@ -0,0 +1,60 @@ + + + + + +lumbda repl — three tiers, one prompt + + + + +
+ + +

lumbda

+
+

three tiers, one prompt

+

+ interactive repl. type a lisp expression at the prompt below; each tier + keeps its own persistent state across evals. compare side by side, + measure tier-by-tier elapsed, share a link to the + playground for static demos. +

+
+ +
+
+ send to + + + + +
+ + + + +
+ +
+
+
+ +
+ λ> + + +
+ + + + + + diff --git a/wasm/asm/lumbda.wat b/wasm/asm/lumbda.wat index fb841ea..fa136b3 100644 --- a/wasm/asm/lumbda.wat +++ b/wasm/asm/lumbda.wat @@ -61,6 +61,12 @@ (global $sym_and (mut i32) (i32.const 0)) (global $sym_or (mut i32) (i32.const 0)) (global $sym_set (mut i32) (i32.const 0)) + (global $sym_letstar (mut i32) (i32.const 0)) + (global $sym_letrec (mut i32) (i32.const 0)) + (global $sym_when (mut i32) (i32.const 0)) + (global $sym_unless (mut i32) (i32.const 0)) + (global $sym_case (mut i32) (i32.const 0)) + (global $sym_do (mut i32) (i32.const 0)) ;; Constants (immediate value addresses) (global $NIL i32 (i32.const 4)) @@ -689,6 +695,21 @@ (if (i32.eq (local.get $head) (global.get $sym_or)) (then (return (call $eval_or (local.get $rest) (local.get $env))))) + (if (i32.eq (local.get $head) (global.get $sym_letstar)) + (then (return (call $eval_letstar (local.get $rest) (local.get $env))))) + + (if (i32.eq (local.get $head) (global.get $sym_letrec)) + (then (return (call $eval_letrec (local.get $rest) (local.get $env))))) + + (if (i32.eq (local.get $head) (global.get $sym_when)) + (then (return (call $eval_when (local.get $rest) (local.get $env))))) + + (if (i32.eq (local.get $head) (global.get $sym_unless)) + (then (return (call $eval_unless (local.get $rest) (local.get $env))))) + + (if (i32.eq (local.get $head) (global.get $sym_case)) + (then (return (call $eval_case (local.get $rest) (local.get $env))))) + ;; Function application (return (call $apply (call $eval (local.get $head) (local.get $env)) @@ -752,8 +773,99 @@ (br $loop))) (global.get $VOID)) - ;; (let ((x e) ...) body) — non-recursive form + ;; (let ((x e) ...) body) — non-recursive form (also supports named let) + ;; Named let: (let name ((x e) ...) body) -> a self-referential procedure. (func $eval_let (param $rest i32) (param $env i32) (result i32) + (local $first i32) + (local $bindings i32) + (local $body i32) + (local $new_env i32) + (local $b i32) + (local $sym i32) + (local $val i32) + (local $name i32) + (local $params i32) + (local $args i32) + (local $closure i32) + (local.set $first (call $car (local.get $rest))) + ;; Named let if first arg is a symbol. + (if (call $is_symbol (local.get $first)) + (then + (local.set $name (local.get $first)) + (local.set $bindings (call $car (call $cdr (local.get $rest)))) + (local.set $body (call $cdr (call $cdr (local.get $rest)))) + ;; Build params list (the binding names) and args list (their inits). + (local.set $params (call $extract_let_params (local.get $bindings))) + (local.set $args (call $extract_let_inits (local.get $bindings) (local.get $env))) + ;; Create closure capturing CURRENT env (so the body can refer to name). + (local.set $closure (call $make_closure (local.get $params) (local.get $body) (local.get $env))) + ;; Bind the closure to name in a fresh local env and patch its env to include itself. + (local.set $new_env (call $env_define (local.get $env) (local.get $name) (local.get $closure))) + ;; Patch closure.env to the new env so name resolves to it. + (i32.store offset=12 (local.get $closure) (local.get $new_env)) + (return (call $apply (local.get $closure) (local.get $args))))) + (local.set $bindings (local.get $first)) + (local.set $body (call $cdr (local.get $rest))) + (local.set $new_env (local.get $env)) + (block $done + (loop $loop + (br_if $done (i32.eq (local.get $bindings) (global.get $NIL))) + (local.set $b (call $car (local.get $bindings))) + (local.set $sym (call $car (local.get $b))) + (local.set $val (call $eval (call $car (call $cdr (local.get $b))) (local.get $env))) + (local.set $new_env (call $env_define (local.get $new_env) (local.get $sym) (local.get $val))) + (local.set $bindings (call $cdr (local.get $bindings))) + (br $loop))) + (call $eval_begin (local.get $body) (local.get $new_env))) + + ;; Helper: build params list from let bindings (a list of (sym init)). + (func $extract_let_params (param $bindings i32) (result i32) + (local $head i32) + (local $tail i32) + (local $new i32) + (local.set $head (global.get $NIL)) + (local.set $tail (global.get $NIL)) + (block $done + (loop $l + (br_if $done (i32.eq (local.get $bindings) (global.get $NIL))) + (local.set $new + (call $make_pair + (call $car (call $car (local.get $bindings))) + (global.get $NIL))) + (if (i32.eq (local.get $head) (global.get $NIL)) + (then (local.set $head (local.get $new)) + (local.set $tail (local.get $new))) + (else (call $set_cdr (local.get $tail) (local.get $new)) + (local.set $tail (local.get $new)))) + (local.set $bindings (call $cdr (local.get $bindings))) + (br $l))) + (local.get $head)) + + ;; Helper: evaluate each init expression and return the resulting list. + (func $extract_let_inits (param $bindings i32) (param $env i32) (result i32) + (local $head i32) + (local $tail i32) + (local $new i32) + (local.set $head (global.get $NIL)) + (local.set $tail (global.get $NIL)) + (block $done + (loop $l + (br_if $done (i32.eq (local.get $bindings) (global.get $NIL))) + (local.set $new + (call $make_pair + (call $eval (call $car (call $cdr (call $car (local.get $bindings)))) (local.get $env)) + (global.get $NIL))) + (if (i32.eq (local.get $head) (global.get $NIL)) + (then (local.set $head (local.get $new)) + (local.set $tail (local.get $new))) + (else (call $set_cdr (local.get $tail) (local.get $new)) + (local.set $tail (local.get $new)))) + (local.set $bindings (call $cdr (local.get $bindings))) + (br $l))) + (local.get $head)) + + ;; (let* ((x e1) (y e2)) body) — each binding sees previous bindings. + (func $eval_letstar (param $rest i32) (param $env i32) (result i32) (local $bindings i32) (local $body i32) (local $new_env i32) @@ -768,12 +880,94 @@ (br_if $done (i32.eq (local.get $bindings) (global.get $NIL))) (local.set $b (call $car (local.get $bindings))) (local.set $sym (call $car (local.get $b))) - (local.set $val (call $eval (call $car (call $cdr (local.get $b))) (local.get $env))) + (local.set $val (call $eval (call $car (call $cdr (local.get $b))) (local.get $new_env))) (local.set $new_env (call $env_define (local.get $new_env) (local.get $sym) (local.get $val))) (local.set $bindings (call $cdr (local.get $bindings))) (br $loop))) (call $eval_begin (local.get $body) (local.get $new_env))) + ;; (letrec ((f (lambda ...))) body) — each binding visible to all others. + ;; First create the env with placeholder bindings, then evaluate inits in + ;; the new env (so lambdas capture it), then patch values. + (func $eval_letrec (param $rest i32) (param $env i32) (result i32) + (local $bindings i32) + (local $body i32) + (local $new_env i32) + (local $b i32) + (local $sym i32) + (local $val i32) + (local $cur i32) + (local $bind i32) + (local.set $bindings (call $car (local.get $rest))) + (local.set $body (call $cdr (local.get $rest))) + (local.set $new_env (local.get $env)) + (local.set $cur (local.get $bindings)) + ;; Pass 1: bind every name to VOID in the new env. + (block $done1 + (loop $l1 + (br_if $done1 (i32.eq (local.get $cur) (global.get $NIL))) + (local.set $b (call $car (local.get $cur))) + (local.set $sym (call $car (local.get $b))) + (local.set $new_env (call $env_define (local.get $new_env) (local.get $sym) (global.get $VOID))) + (local.set $cur (call $cdr (local.get $cur))) + (br $l1))) + ;; Pass 2: evaluate each init in new_env (so lambdas see each other), + ;; mutate the binding pair's cdr to the real value. + (local.set $cur (local.get $bindings)) + (block $done2 + (loop $l2 + (br_if $done2 (i32.eq (local.get $cur) (global.get $NIL))) + (local.set $b (call $car (local.get $cur))) + (local.set $sym (call $car (local.get $b))) + (local.set $val (call $eval (call $car (call $cdr (local.get $b))) (local.get $new_env))) + (call $env_set (local.get $new_env) (local.get $sym) (local.get $val)) + (local.set $cur (call $cdr (local.get $cur))) + (br $l2))) + (call $eval_begin (local.get $body) (local.get $new_env))) + + ;; (when test body...) -> if test true, eval body, else void + (func $eval_when (param $rest i32) (param $env i32) (result i32) + (if (i32.ne (call $eval (call $car (local.get $rest)) (local.get $env)) (global.get $FALSE)) + (then (return (call $eval_begin (call $cdr (local.get $rest)) (local.get $env))))) + (global.get $VOID)) + + ;; (unless test body...) -> if test false, eval body, else void + (func $eval_unless (param $rest i32) (param $env i32) (result i32) + (if (i32.eq (call $eval (call $car (local.get $rest)) (local.get $env)) (global.get $FALSE)) + (then (return (call $eval_begin (call $cdr (local.get $rest)) (local.get $env))))) + (global.get $VOID)) + + ;; (case key ((1 2) "small") ((3 4) "med") (else "big")) + ;; Each clause: (datum-list body) where datum-list is either a list of + ;; literals to match via eqv?, or the symbol `else`. + (func $eval_case (param $rest i32) (param $env i32) (result i32) + (local $key i32) + (local $clauses i32) + (local $clause i32) + (local $data i32) + (local $body i32) + (local.set $key (call $eval (call $car (local.get $rest)) (local.get $env))) + (local.set $clauses (call $cdr (local.get $rest))) + (block $done + (loop $l + (br_if $done (i32.eq (local.get $clauses) (global.get $NIL))) + (local.set $clause (call $car (local.get $clauses))) + (local.set $data (call $car (local.get $clause))) + (local.set $body (call $cdr (local.get $clause))) + (if (i32.eq (local.get $data) (global.get $sym_else)) + (then (return (call $eval_begin (local.get $body) (local.get $env))))) + ;; data is a list of literal values; match via eqv? + (block $clausedone + (loop $datal + (br_if $clausedone (i32.eq (local.get $data) (global.get $NIL))) + (if (i32.eq (call $car (local.get $data)) (local.get $key)) + (then (return (call $eval_begin (local.get $body) (local.get $env))))) + (local.set $data (call $cdr (local.get $data))) + (br $datal))) + (local.set $clauses (call $cdr (local.get $clauses))) + (br $l))) + (global.get $VOID)) + (func $eval_and (param $rest i32) (param $env i32) (result i32) (local $val i32) (local.set $val (global.get $TRUE)) @@ -865,12 +1059,128 @@ (br $loop))) (local.get $new)) + ;; ─── Equality helpers ────────────────────────────────────────── + ;; Deep structural equality. Returns TRUE/FALSE immediate. + (func $equal_p (param $a i32) (param $b i32) (result i32) + (local $alen i32) + (local $blen i32) + (local $i i32) + (if (i32.eq (local.get $a) (local.get $b)) + (then (return (global.get $TRUE)))) + (if (call $is_pair (local.get $a)) + (then + (if (i32.eqz (call $is_pair (local.get $b))) + (then (return (global.get $FALSE)))) + (if (i32.eq (call $equal_p (call $car (local.get $a)) (call $car (local.get $b))) (global.get $FALSE)) + (then (return (global.get $FALSE)))) + (return (call $equal_p (call $cdr (local.get $a)) (call $cdr (local.get $b)))))) + (if (call $is_string (local.get $a)) + (then + (if (i32.eqz (call $is_string (local.get $b))) + (then (return (global.get $FALSE)))) + (local.set $alen (i32.load offset=4 (local.get $a))) + (local.set $blen (i32.load offset=4 (local.get $b))) + (if (i32.ne (local.get $alen) (local.get $blen)) + (then (return (global.get $FALSE)))) + (local.set $i (i32.const 0)) + (block $done + (loop $l + (br_if $done (i32.ge_u (local.get $i) (local.get $alen))) + (if (i32.ne + (i32.load8_u (i32.add (i32.add (local.get $a) (i32.const 8)) (local.get $i))) + (i32.load8_u (i32.add (i32.add (local.get $b) (i32.const 8)) (local.get $i)))) + (then (return (global.get $FALSE)))) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (br $l))) + (return (global.get $TRUE)))) + ;; Fall through: not equal (different types / not pair) + (global.get $FALSE)) + + ;; (append a b) returns a new list with b appended after a. + (func $append2 (param $a i32) (param $b i32) (result i32) + (local $head i32) + (local $tail i32) + (local $new i32) + (local $cur i32) + (local.set $head (global.get $NIL)) + (local.set $tail (global.get $NIL)) + (local.set $cur (local.get $a)) + (block $done + (loop $l + (br_if $done (i32.eqz (call $is_pair (local.get $cur)))) + (local.set $new (call $make_pair (call $car (local.get $cur)) (global.get $NIL))) + (if (i32.eq (local.get $head) (global.get $NIL)) + (then (local.set $head (local.get $new)) + (local.set $tail (local.get $new))) + (else (call $set_cdr (local.get $tail) (local.get $new)) + (local.set $tail (local.get $new)))) + (local.set $cur (call $cdr (local.get $cur))) + (br $l))) + (if (i32.eq (local.get $head) (global.get $NIL)) + (then (return (local.get $b)))) + (call $set_cdr (local.get $tail) (local.get $b)) + (local.get $head)) + + ;; (member key list) — use_equal=1 → equal?; else eq? + (func $member_eq (param $key i32) (param $list i32) (param $use_equal i32) (result i32) + (local $cur i32) + (local $match i32) + (local.set $cur (local.get $list)) + (block $done + (loop $l + (br_if $done (i32.eqz (call $is_pair (local.get $cur)))) + (if (local.get $use_equal) + (then + (local.set $match + (i32.eq (call $equal_p (local.get $key) (call $car (local.get $cur))) (global.get $TRUE)))) + (else + (local.set $match + (i32.eq (local.get $key) (call $car (local.get $cur)))))) + (if (local.get $match) + (then (return (local.get $cur)))) + (local.set $cur (call $cdr (local.get $cur))) + (br $l))) + (global.get $FALSE)) + + ;; (assoc key alist) — use_equal=1 → equal? on car; else eq? + (func $assoc_eq (param $key i32) (param $alist i32) (param $use_equal i32) (result i32) + (local $cur i32) + (local $pair i32) + (local $match i32) + (local.set $cur (local.get $alist)) + (block $done + (loop $l + (br_if $done (i32.eqz (call $is_pair (local.get $cur)))) + (local.set $pair (call $car (local.get $cur))) + (if (call $is_pair (local.get $pair)) + (then + (if (local.get $use_equal) + (then + (local.set $match + (i32.eq (call $equal_p (local.get $key) (call $car (local.get $pair))) (global.get $TRUE)))) + (else + (local.set $match + (i32.eq (local.get $key) (call $car (local.get $pair)))))) + (if (local.get $match) + (then (return (local.get $pair)))))) + (local.set $cur (call $cdr (local.get $cur))) + (br $l))) + (global.get $FALSE)) + ;; ─── Primitives ──────────────────────────────────────────────── (func $apply_primitive (param $id i32) (param $args i32) (result i32) (local $a i32) (local $b i32) (local $sum i32) (local $cur i32) + (local $vmin i32) + (local $vmax i32) + (local $base i32) + (local $exp_n i32) + (local $result i32) + (local $rev i32) + (local $idx i32) + (local $idxt i32) ;; Fetch first 2 args (most prims use 1 or 2). Defaults to fixnum 0. (local.set $a (call $make_fixnum (i32.const 0))) @@ -1059,6 +1369,255 @@ (then (return (global.get $TRUE))) (else (return (global.get $FALSE)))))) + ;; quotient (25): integer truncating division + (if (i32.eq (local.get $id) (i32.const 25)) + (then + (return (call $make_fixnum (i32.div_s (call $fixnum_val (local.get $a)) + (call $fixnum_val (local.get $b))))))) + + ;; remainder (26): integer remainder (sign of dividend) + (if (i32.eq (local.get $id) (i32.const 26)) + (then + (return (call $make_fixnum (i32.rem_s (call $fixnum_val (local.get $a)) + (call $fixnum_val (local.get $b))))))) + + ;; min (27): variadic + (if (i32.eq (local.get $id) (i32.const 27)) + (then + (local.set $sum (call $fixnum_val (local.get $a))) + (local.set $cur (call $cdr (local.get $args))) + (block $done + (loop $loop + (br_if $done (i32.eq (local.get $cur) (global.get $NIL))) + (local.set $vmin (call $fixnum_val (call $car (local.get $cur)))) + (if (i32.lt_s (local.get $vmin) (local.get $sum)) + (then (local.set $sum (local.get $vmin)))) + (local.set $cur (call $cdr (local.get $cur))) + (br $loop))) + (return (call $make_fixnum (local.get $sum))))) + + ;; max (28): variadic + (if (i32.eq (local.get $id) (i32.const 28)) + (then + (local.set $sum (call $fixnum_val (local.get $a))) + (local.set $cur (call $cdr (local.get $args))) + (block $done + (loop $loop + (br_if $done (i32.eq (local.get $cur) (global.get $NIL))) + (local.set $vmax (call $fixnum_val (call $car (local.get $cur)))) + (if (i32.gt_s (local.get $vmax) (local.get $sum)) + (then (local.set $sum (local.get $vmax)))) + (local.set $cur (call $cdr (local.get $cur))) + (br $loop))) + (return (call $make_fixnum (local.get $sum))))) + + ;; expt (29): integer exponent (positive) + (if (i32.eq (local.get $id) (i32.const 29)) + (then + (local.set $base (call $fixnum_val (local.get $a))) + (local.set $exp_n (call $fixnum_val (local.get $b))) + (local.set $result (i32.const 1)) + (block $done + (loop $loop + (br_if $done (i32.le_s (local.get $exp_n) (i32.const 0))) + (local.set $result (i32.mul (local.get $result) (local.get $base))) + (local.set $exp_n (i32.sub (local.get $exp_n) (i32.const 1))) + (br $loop))) + (return (call $make_fixnum (local.get $result))))) + + ;; even? (30) + (if (i32.eq (local.get $id) (i32.const 30)) + (then + (if (i32.eqz (i32.and (call $fixnum_val (local.get $a)) (i32.const 1))) + (then (return (global.get $TRUE))) + (else (return (global.get $FALSE)))))) + + ;; odd? (31) + (if (i32.eq (local.get $id) (i32.const 31)) + (then + (if (i32.and (call $fixnum_val (local.get $a)) (i32.const 1)) + (then (return (global.get $TRUE))) + (else (return (global.get $FALSE)))))) + + ;; positive? (32) + (if (i32.eq (local.get $id) (i32.const 32)) + (then + (if (i32.gt_s (call $fixnum_val (local.get $a)) (i32.const 0)) + (then (return (global.get $TRUE))) + (else (return (global.get $FALSE)))))) + + ;; negative? (33) + (if (i32.eq (local.get $id) (i32.const 33)) + (then + (if (i32.lt_s (call $fixnum_val (local.get $a)) (i32.const 0)) + (then (return (global.get $TRUE))) + (else (return (global.get $FALSE)))))) + + ;; set-car! (34) + (if (i32.eq (local.get $id) (i32.const 34)) + (then + (call $set_car (local.get $a) (local.get $b)) + (return (global.get $VOID)))) + + ;; set-cdr! (35) + (if (i32.eq (local.get $id) (i32.const 35)) + (then + (call $set_cdr (local.get $a) (local.get $b)) + (return (global.get $VOID)))) + + ;; equal? (36): deep structural equality + (if (i32.eq (local.get $id) (i32.const 36)) + (then (return (call $equal_p (local.get $a) (local.get $b))))) + + ;; eqv? (37): identity for our value types + (if (i32.eq (local.get $id) (i32.const 37)) + (then + (if (i32.eq (local.get $a) (local.get $b)) + (then (return (global.get $TRUE))) + (else (return (global.get $FALSE)))))) + + ;; number? (38) — fixnums only in this tier + (if (i32.eq (local.get $id) (i32.const 38)) + (then + (if (call $is_fixnum (local.get $a)) + (then (return (global.get $TRUE))) + (else (return (global.get $FALSE)))))) + + ;; integer? (39) — same as number? here + (if (i32.eq (local.get $id) (i32.const 39)) + (then + (if (call $is_fixnum (local.get $a)) + (then (return (global.get $TRUE))) + (else (return (global.get $FALSE)))))) + + ;; symbol? (40) + (if (i32.eq (local.get $id) (i32.const 40)) + (then + (if (call $is_symbol (local.get $a)) + (then (return (global.get $TRUE))) + (else (return (global.get $FALSE)))))) + + ;; string? (41) + (if (i32.eq (local.get $id) (i32.const 41)) + (then + (if (call $is_string (local.get $a)) + (then (return (global.get $TRUE))) + (else (return (global.get $FALSE)))))) + + ;; procedure? (42) — closure or primitive + (if (i32.eq (local.get $id) (i32.const 42)) + (then + (if (i32.or (call $is_closure (local.get $a)) (call $is_primitive (local.get $a))) + (then (return (global.get $TRUE))) + (else (return (global.get $FALSE)))))) + + ;; boolean? (43) + (if (i32.eq (local.get $id) (i32.const 43)) + (then + (if (i32.or (i32.eq (local.get $a) (global.get $TRUE)) + (i32.eq (local.get $a) (global.get $FALSE))) + (then (return (global.get $TRUE))) + (else (return (global.get $FALSE)))))) + + ;; caar (44) + (if (i32.eq (local.get $id) (i32.const 44)) + (then (return (call $car (call $car (local.get $a)))))) + + ;; cadr (45) + (if (i32.eq (local.get $id) (i32.const 45)) + (then (return (call $car (call $cdr (local.get $a)))))) + + ;; cdar (46) + (if (i32.eq (local.get $id) (i32.const 46)) + (then (return (call $cdr (call $car (local.get $a)))))) + + ;; cddr (47) + (if (i32.eq (local.get $id) (i32.const 47)) + (then (return (call $cdr (call $cdr (local.get $a)))))) + + ;; caddr (48) + (if (i32.eq (local.get $id) (i32.const 48)) + (then (return (call $car (call $cdr (call $cdr (local.get $a))))))) + + ;; cadddr (49) + (if (i32.eq (local.get $id) (i32.const 49)) + (then (return (call $car (call $cdr (call $cdr (call $cdr (local.get $a)))))))) + + ;; reverse (50) + (if (i32.eq (local.get $id) (i32.const 50)) + (then + (local.set $rev (global.get $NIL)) + (local.set $cur (local.get $a)) + (block $done + (loop $loop + (br_if $done (i32.eqz (call $is_pair (local.get $cur)))) + (local.set $rev (call $make_pair (call $car (local.get $cur)) (local.get $rev))) + (local.set $cur (call $cdr (local.get $cur))) + (br $loop))) + (return (local.get $rev)))) + + ;; append (51) — 2-arg + (if (i32.eq (local.get $id) (i32.const 51)) + (then (return (call $append2 (local.get $a) (local.get $b))))) + + ;; apply (52) — (apply f args) + (if (i32.eq (local.get $id) (i32.const 52)) + (then (return (call $apply (local.get $a) (local.get $b))))) + + ;; error (53) — emit message + raise (we just write to output for now) + (if (i32.eq (local.get $id) (i32.const 53)) + (then + (call $out_str (i32.const 0xF030) (i32.const 6)) ;; "7)TcE_vwYkH=JW=obViu6afo3bg(v}IeCEm>=;kXT6^E8gTD*duYQB{k%b zLsDx80Z0UU&>;u}#D_(YgAgnhKIEXo9`>*Y#k&D~*nhBvEP@<@zy}|E%20rDV~jnWeqi_P-Uq>6{b@c3_Y~Vb^MT?uu{#YCYHnb)TZ5Rbn6vf6QNn+*C=%;>!)-t0NNATj<+I&Ax{@y;mXZ`+u*YRcK zV}C!L_=*JUhcf>kK8%Pro=uHM3*$4_+H583{q^DKc;Ahx!8>Qw|WEf#*vyg+}+vS-Fbg=XJjr-(FXku^Sy?)J=`5#@9VrbJKwzW z)=M_rD@|c@o13*Fc*|glXi$WCkcaO@mwk5G%)31B;hO*Jtwobt*D|^BcFP^BzTfsy zZmvSgu5N89a|!*QfSK87&zl$!G$8c$igUl68$6U7K9n0hlp71Tqbl!sJE~8y>k4c4 zd^a;iz#;AVz;JMLK8W~Yl;%Nwq^SN*T^k|7i^eE*@7Q3^r@39L1qGELndMCp2A9Lj zd6?VxW-Pm;Yk9bajBst)n2QGB?OG9bjla`cPP5#p0}DJoG;wbo0=4Wl@khV!^pciZ;&GE!#{w$loj^awuw{cBHXK811O#%b3QJ4vr;wfYI3Zn-y9g{yVt^GjD6bb5@Q9iyi!M8@jh{{*(Zmp!4Ias~qR;8X}| zPL&nn5RaIKQns?ZoXcG1l!MrYlRd4J@__PP>Ghcq`Hhiz>Qn!piiw*_kHSvdBYO(B zP1vIhazc}^FCzZOxfO>bdE%F^NWQ$Yg$s#${=>sP%69P2O!5!&vL{(YozXyvs~TwY zxLTVh*^?4Eq3;Y3bM_|HG>;`SV+|YTbDUg4i3)YI2&J^NXDPQ5xkqQ+Uvg8Ga|L-( zoo;OUZctg?oJ{y6~S_aNQy?7 zp{2&ny0F7Ctp{8qCH{@ec``PSvKgtpSveakmW7epO{;KJGx11RN|rLIOUSUSXyr-X zV(BpzFBzM2^<;}WJW|cAg-bli$~?|asNAr0&HPh3zuOF-(cvS_@L3(sHp3Tnm^H(* zIy~A8pVQ%^&G3v4k2S;Rb$GlPF6;2IX83{(#SnUkm$H{7t=#u*Dz2%nKryh~(cJVL zU)rY`Oc7%Sj8L=)W>^p_n-wjtg1ki}rg>V%+}SY#Pg4`ih5FKx03|z+FB?n5B2h0r z+Ku<<5HzU{W-+7c5oCXHuvA6WSB)q_YOex?G?vHCcdc~?XOYkeigu-L6*85~Y^ms| zAx;-A@2K!&1^VUfyqg{8)HqREhi>k~MhI!*jjEygl74b3X&k66d=e;QqomlYm8U9c z+(ocUoSmXqqbDNK`5#Q3JnL#sjtCA`Nrp}G4BVx1a7N~g`k>t|TjRORA zq;V`yekU+$4T4hIqEdS(mNz+c_PgEY!2&6N!J#CXr^nY2d{ErT!rw1u{*c=RvuVAi zA49VODi4ll0E=HI2Sh9NTnb!(4EkjD4guPxAtTB=T zmAqKwF}hO3E*B?8(&bDF{Ex2f@>HI+i$FiM)=7-w4XO?f&>mX=gpk#SXLfDA2(89M zRTxM;?PeVwHz%?wlFo`n+H$ElUCou!xv4Aw3*bCZ*v$tD3q=OC@V96%c;KRlMY1jD z@2uBa#S=6jaX^P6HZne$VDaQO$)pdFZDeZJpB-jt8zYxW)u(Hh z!kw#z(;lUI05)E)-YT{x71G8fkO-KhYX5@Nse@@BQ{06C`bMB*2ZA_C`{Vl5r?$s+SKBhz4zdumww^>cN;e#n(iA?U@r3<{#EHc}x`# zN)?#UTpm`Z9V>;op*73pkyU6CH`C#!kt>};9^vdf5mgSdxl!uz-zlL*pAiugdF#Ov zo@gZSHUv%DL~%#UOxv4xDW#n}9ncCvzge`ke0J)?KUuHjUE9?8pE}?iHjF&+%7ML^ ziqc}tn2Kz9Le%c&k=pO_fLOIVHDR$KXkGTF>LFi~E_p`D@^CRpl_yj5nWD-QEL6(V zzrDyhLki7-xnILyk#=}#fDoA z&yI)n^p_3@jf9sW(e+7VdME*;&EV-9#z8PuIsS?YAqZa8J28gGm7%^5NqDbu zIPK2YGri5u>)h8Gf~g6wHw2r#rZX-y1XB|(3PIwJCa6}pTg9KMnRg}px*C3*KxN3A z4_z`dUNSSeWJZ?ER7-ThB^F4`&`r!qH(^M+qeWOsn-OG>X2%y}_jidY6M2v1?PB8h z?}t9ljxM(R9S~3Ai>bd0Nv=gBH zU&@L(&}~raPl7%Hwf#KkE-3U1pr0su9P}wD@=t*7fnxt8=;um%3UnXT_BrU+iVDz| zpwvGN`i-Iz^ff5)i=b~okv|36|COBR8PIJ|>YoLDqO>K@T}7uspMpC6Ind8^-Wkw+ zP~@KneXeL3^o61qKwm0)5%d)(_Gdx=1xox&psztK|1#)XrM&{$|5urJ4s=`5tDrle z#GeQK7!>)}KzEh#w!rT!x57ofI(1N6CaFJ%{8k3`8Xdb6m%KrIIHI~2de zEQUP1Te&9cH&HWI-|Llihi%IR>1lb<{HK%+ez4)CHR-r)cwr1f?rd5bE3ZcreW~pt z-e*@hb?&p)UqJmTbivYTBX50OQ0^Fc+Y9*6NF8$S(&lJ*y}zc45^jjIdu7bsNZ ztc$mHhdX^%f`e5SkhCrt^yEh>R?A?e|KaKeQ%GGGU+dpk-7u28F1kM4STT~lF6s}q znL+xxaL{Le($|GOVUxZt7>*DOh_?GX1!4sQs+%Xa;pQU5wS2>pjY;5RPGVVCS~ z4Eo#K>?ZW?_~jGm^Z{UNVxp&T@Pb z*9Dhgkhd;)6F}PPS=flYb>a0M+LE`R6%cvr;*H(GfZx-|To-TlR!3lR*MIU=3Vhpgl4J3`l02?=Ue!#n024U|S znvlsZSs$+K4&ViO_QW#v!1i_RBmk5>t z)xot!*kUAcMTKxlZ*#LRF-wtfF@ph4q)cG3(MUp=LV;8Iav%bVLBwDS?REb4_K+B+ lT)3#Wy$zXq;ZpJP@~BT{Q8ZjzhdaI10jkT)Tf^0r{{>@=n7IG| delta 1332 zcmZ9MF>Dk^6oz+ZcK7afZ@qJSx3;l?>}t-)fNerJ5CVieNH`%vF{x-0A(Oa4#lC@6^1P&!fylEQ%wMReq!@m*NwinrgqH}l?` zf8XBv=|A@^H0aUB;<3SQf*&IV$97%UaR^CTiQDX6Y)ve_J28I9pK4yc{Po18$@z&X z>%&TWHPf7%ZO&bqox5fo{-1AZa@zW+}X-Od3DkCa(#J)I3{)_3dF} z#j)6L5R%bRHkw!>8Vg8b@p8%%#>)vAvquw_P#%gn^brVBt8Dqch}B*DRU<-Jw*Fv} z@?APCs@k!TbJVnBCFh9M1LqbhA9vr7SPd6Oz+b?|>f6GuN>yi(e_1#sYN0OkbK!R< zO3|45zBpWpxSvwpruwzGN`Is3dTE~2)K;m_)xBW9=Uqgu%ihWQAaXIm;ZE#?@cnv; zr!cDn{Gdr+_Af-SzaIjKIHesC)U-c`_0^5wCRAvFL`^;J>KpH9|33CHdZ${F|8%eU=LyS1 zl%j0PI{oI73N4Y1#F1L#z3Ozhmkg^f!+J1;_83|lF!#O_ilV7Uu<@6$Uf*ztok@$A zQfe$NC?Z{-;7SxWz{cf@fGq2k1F(NrQrIABz@CZDX4TNquS~x`S8GnWhY|8V!S!;X zLN4^_&QLKII=?ei%7s4L8S+dhValxzW@ZL*)w;IN**lMf?IU>PTQif=ej&LWmF*n; z+aG{5Bk%Fv_EHZ#uG%lP3xl^-z2S8ZV;VPm`sfx_Z+pJUS{qCfN%)AoX^4Vu8)BgE z4B4OsLk?)kkPG_3Pyw_IqOu5DF;oJrf^0M}BJYEk^g%xwDuW(^92sQ5M+UnWVqK{-d}>6GF5e@lVB-Pd3C32WME|v5?qW6Ce~R5>gDUHPpqjh?`4{XB B^AG?4 diff --git a/www/playground/index.html b/www/playground/index.html index 648ccef..c2bc429 100644 --- a/www/playground/index.html +++ b/www/playground/index.html @@ -71,11 +71,11 @@