js-on-sx: switch/case/default/break

Parser: jp-parse-switch-stmt + jp-parse-switch-cases + jp-parse-switch-body.
AST: (js-switch discr (("case" val body) ("default" nil body) ...)).

Transpile: wraps body in (call/cc (fn (__break__) ...)). Each case
clause becomes (when (or __matched__ (js-strict-eq discr val))
(set! __matched__ true) body). Fall-through works naturally via
__matched__. Default appended as (when (not __matched__) body).

363/365 unit (+6), 148/148 slice unchanged.
This commit is contained in:
2026-04-23 21:04:22 +00:00
parent 275d2ecbae
commit 9d3e54029a
4 changed files with 167 additions and 0 deletions

View File

@@ -181,6 +181,8 @@ Append-only record of completed iterations. Loop writes one line per iteration:
- 2026-04-23 — **String.prototype extensions + Object/Array builtins.** Strings: added `includes`, `startsWith`, `endsWith`, `trim`, `trimStart`, `trimEnd`, `repeat`, `padStart`, `padEnd`, `toString`, `valueOf` to `js-string-method` dispatch and corresponding `js-get-prop` string-branch keys. Helpers: `js-string-repeat` (tail-recursive concat), `js-string-pad` + `js-string-pad-build`. Object: `Object.keys / .values / .entries / .assign / .freeze` (freeze is a no-op — we don't track sealed state). Array: `Array.isArray` (backed by `list?`), `Array.of` (varargs → list). Wired into `js-global`. 17 new unit tests, **357/359** (340→+17). Conformance unchanged. Gotcha: SX's `keys` primitive returns most-recently-inserted-first, so `Object.keys({a:1, b:2})` comes back `["b", "a"]`. Test assertion has to check `.length` rather than the literal pair. If spec order matters for a real app, Object.keys would need its own ordered traversal.
- 2026-04-23 — **switch / case / default.** Parser: new `jp-parse-switch-stmt` (expect `switch (expr) { cases }`), `jp-parse-switch-cases` (walks clauses: `case val:`, `default:`), `jp-parse-switch-body` (collects stmts until next `case`/`default`/`}`). AST: `(js-switch discr (("case" val body-stmts) ("default" nil body-stmts) ...))`. Transpile: wraps body in `(call/cc (fn (__break__) (let ((__discr__ …) (__matched__ false)) …)))`. Each case clause becomes `(when (or __matched__ (js-strict-eq __discr__ val)) (set! __matched__ true) body-stmts)` — implements JS fall-through naturally (once a case matches, all following cases' `when` fires via `__matched__`). Default is a separate `(when (not __matched__) default-body)` appended at the end. `break` inside a case body already transpiles to `(__break__ nil)` and jumps out via the `call/cc`. 6 new unit tests (match, no-match default, fall-through stops on break, string discriminant, empty-body fall-through chain), **363/365** (357→+6). Conformance unchanged.
## Phase 3-5 gotchas
Worth remembering for later phases: