Fix home-stepper paren bugs, harden defisland multi-body, add SX Tools essay

Two paren bugs in home-stepper.sx caused the home page to render blank:

1. Line 222 had one extra ) that prematurely closed the letrec bindings
   list — rebuild-preview and do-back became body expressions instead
   of bindings, making them undefined in scope.

2. Lines 241-308 were outside the let/letrec scope entirely — the outer
   let closed at line 240, so freeze-scope, cookie restore, source
   parsing, and the entire div rendering tree had no access to signals
   or letrec functions.

Also hardens defisland to wrap multi-expression bodies in (begin ...),
matching the Python-side fix from 9f0c541. Both spec/evaluator.sx and
the OCaml transpiled sx_ref.ml are updated.

Adds SX Tools essay under Applications — the revised plan for structural
tree reading/editing tools for .sx files, motivated by this exact bug.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-25 17:52:16 +00:00
parent 1777f631a5
commit c18f46278f
6 changed files with 223 additions and 8 deletions

View File

@@ -327,9 +327,9 @@ and defcomp_kwarg args key default =
and parse_comp_params params_expr =
(let params = ref ((List [])) in let param_types = (Dict (Hashtbl.create 0)) in let has_children = ref ((Bool false)) in let in_key = ref ((Bool false)) in (let () = ignore ((List.iter (fun p -> ignore ((if sx_truthy ((let _and = (prim_call "=" [(type_of (p)); (String "list")]) in if not (sx_truthy _and) then _and else (let _and = (prim_call "=" [(len (p)); (Number 3.0)]) in if not (sx_truthy _and) then _and else (let _and = (prim_call "=" [(type_of ((first (p)))); (String "symbol")]) in if not (sx_truthy _and) then _and else (let _and = (prim_call "=" [(type_of ((nth (p) ((Number 1.0))))); (String "keyword")]) in if not (sx_truthy _and) then _and else (prim_call "=" [(keyword_name ((nth (p) ((Number 1.0))))); (String "as")])))))) then (let name = (symbol_name ((first (p)))) in let ptype = (nth (p) ((Number 2.0))) in (let type_val = (if sx_truthy ((prim_call "=" [(type_of (ptype)); (String "symbol")])) then (symbol_name (ptype)) else ptype) in (if sx_truthy ((Bool (not (sx_truthy (!has_children))))) then (let () = ignore ((params := sx_append_b !params name; Nil)) in (sx_dict_set_b param_types name type_val)) else Nil))) else (if sx_truthy ((prim_call "=" [(type_of (p)); (String "symbol")])) then (let name = (symbol_name (p)) in (if sx_truthy ((prim_call "=" [name; (String "&key")])) then (in_key := (Bool true); Nil) else (if sx_truthy ((prim_call "=" [name; (String "&rest")])) then (has_children := (Bool true); Nil) else (if sx_truthy ((prim_call "=" [name; (String "&children")])) then (has_children := (Bool true); Nil) else (if sx_truthy (!has_children) then Nil else (if sx_truthy (!in_key) then (params := sx_append_b !params name; Nil) else (params := sx_append_b !params name; Nil))))))) else Nil)))) (sx_to_list params_expr); Nil)) in (List [!params; !has_children; param_types])))
(* sf-defisland *)
(* sf-defisland — multi-expression bodies wrapped in (begin ...) *)
and sf_defisland args env =
(let name_sym = (first (args)) in let params_raw = (nth (args) ((Number 1.0))) in let body = (last (args)) in let comp_name = (strip_prefix ((symbol_name (name_sym))) ((String "~"))) in let parsed = (parse_comp_params (params_raw)) in let params = (first (parsed)) in let has_children = (nth (parsed) ((Number 1.0))) in (let island = (make_island (comp_name) (params) (has_children) (body) (env)) in (let () = ignore ((env_bind env (sx_to_string (symbol_name (name_sym))) island)) in island)))
(let name_sym = (first (args)) in let params_raw = (nth (args) ((Number 1.0))) in let body_exprs = (slice (args) ((Number 2.0))) in let body = (if sx_truthy ((prim_call "=" [(len (body_exprs)); (Number 1.0)])) then (first (body_exprs)) else (prim_call "cons" [(make_symbol ((String "begin"))); body_exprs])) in let comp_name = (strip_prefix ((symbol_name (name_sym))) ((String "~"))) in let parsed = (parse_comp_params (params_raw)) in let params = (first (parsed)) in let has_children = (nth (parsed) ((Number 1.0))) in (let island = (make_island (comp_name) (params) (has_children) (body) (env)) in (let () = ignore ((env_bind env (sx_to_string (symbol_name (name_sym))) island)) in island)))
(* sf-defmacro *)
and sf_defmacro args env =