js-on-sx: object + array destructuring

Parser: jp-parse-vardecl handles {a, b} obj pattern and [a, , c]
arr pattern (with hole support) in addition to plain idents.
Emits (js-vardecl-obj names rhs) and (js-vardecl-arr names rhs).

Transpile: js-vardecl-forms dispatches on tag. Destructures emit
(define __destruct__ rhs) then (define name (js-get-prop __destruct__
key-or-index)) for each pattern element. Array holes (nil) are skipped.

418/420 unit (+4), 148/148 slice unchanged.
This commit is contained in:
2026-04-23 22:32:24 +00:00
parent dd6375af18
commit 15c310cdc1
4 changed files with 175 additions and 19 deletions

View File

@@ -781,20 +781,102 @@
jp-parse-vardecl
(fn
(st)
(let
((nm (get (jp-peek st) :value)))
(do
(if
(= (get (jp-peek st) :type) "ident")
(jp-advance! st)
(error
(str "Expected ident in var decl, got " (get (jp-peek st) :type))))
(if
(jp-at? st "op" "=")
(do
(cond
((jp-at? st "punct" "{")
(let
((pattern (jp-parse-obj-pattern st)))
(jp-expect! st "op" "=")
(list (quote js-vardecl-obj) pattern (jp-parse-assignment st))))
((jp-at? st "punct" "[")
(let
((pattern (jp-parse-arr-pattern st)))
(jp-expect! st "op" "=")
(list (quote js-vardecl-arr) pattern (jp-parse-assignment st))))
(else
(let
((nm (get (jp-peek st) :value)))
(if
(= (get (jp-peek st) :type) "ident")
(jp-advance! st)
(list (quote js-vardecl) nm (jp-parse-assignment st)))
(list (quote js-vardecl) nm (list (quote js-undef))))))))
(error
(str
"Expected ident in var decl, got "
(get (jp-peek st) :type))))
(if
(jp-at? st "op" "=")
(begin
(jp-advance! st)
(list (quote js-vardecl) nm (jp-parse-assignment st)))
(list (quote js-vardecl) nm (list (quote js-undef)))))))))
(define
jp-parse-obj-pattern
(fn
(st)
(jp-advance! st)
(let
((names (list)))
(jp-parse-obj-pattern-loop st names)
(jp-expect! st "punct" "}")
names)))
(define
jp-parse-obj-pattern-loop
(fn
(st names)
(cond
((jp-at? st "punct" "}") nil)
(else
(begin
(let
((nm (get (jp-peek st) :value)))
(if
(or
(= (get (jp-peek st) :type) "ident")
(= (get (jp-peek st) :type) "string"))
(jp-advance! st)
(error "expected ident in obj pattern"))
(append! names nm))
(cond
((jp-at? st "punct" ",")
(begin (jp-advance! st) (jp-parse-obj-pattern-loop st names)))
(else nil)))))))
(define
jp-parse-arr-pattern
(fn
(st)
(jp-advance! st)
(let
((names (list)))
(jp-parse-arr-pattern-loop st names)
(jp-expect! st "punct" "]")
names)))
(define
jp-parse-arr-pattern-loop
(fn
(st names)
(cond
((jp-at? st "punct" "]") nil)
((jp-at? st "punct" ",")
(begin
(append! names nil)
(jp-advance! st)
(jp-parse-arr-pattern-loop st names)))
(else
(begin
(let
((nm (get (jp-peek st) :value)))
(if
(= (get (jp-peek st) :type) "ident")
(jp-advance! st)
(error "expected ident in arr pattern"))
(append! names nm))
(cond
((jp-at? st "punct" ",")
(begin (jp-advance! st) (jp-parse-arr-pattern-loop st names)))
(else nil)))))))
(define
jp-parse-var-stmt

View File

@@ -1071,6 +1071,16 @@ cat > "$TMPFILE" << 'EPOCHS'
(epoch 2504)
(eval "(js-eval \"var a=[...'abc']; a.length\")")
;; ── Phase 11.destruct: object and array destructuring ──────────
(epoch 2600)
(eval "(js-eval \"var {aa, bb} = {aa:1, bb:2}; aa+bb\")")
(epoch 2601)
(eval "(js-eval \"var [ax, bx, cx] = [1, 2, 3]; ax+bx+cx\")")
(epoch 2602)
(eval "(js-eval \"var [fst, , trd] = [1, 2, 3]; fst+trd\")")
(epoch 2603)
(eval "(js-eval \"var pt = {px: 100}; var {px} = pt; px + 1\")")
EPOCHS
@@ -1648,6 +1658,12 @@ check 2502 "spread in call args" '6'
check 2503 "spread Math.max" '5'
check 2504 "spread string" '3'
# ── Phase 11.destruct ─────────────────────────────────────────
check 2600 "obj destructure" '3'
check 2601 "arr destructure" '6'
check 2602 "arr destructure skip" '4'
check 2603 "obj partial+add" '101'
TOTAL=$((PASS + FAIL))
if [ $FAIL -eq 0 ]; then
echo "$PASS/$TOTAL JS-on-SX tests passed"

View File

@@ -845,12 +845,68 @@
(else
(let
((d (first decls)))
(cons
(list
(js-sym "define")
(js-sym (nth d 1))
(js-transpile (nth d 2)))
(js-vardecl-forms (rest decls))))))))
(cond
((js-tag? d "js-vardecl")
(cons
(list
(js-sym "define")
(js-sym (nth d 1))
(js-transpile (nth d 2)))
(js-vardecl-forms (rest decls))))
((js-tag? d "js-vardecl-obj")
(let
((names (nth d 1))
(rhs (js-transpile (nth d 2)))
(tmp-sym (js-sym "__destruct__")))
(cons
(list (js-sym "define") tmp-sym rhs)
(js-vardecl-obj-forms
names
tmp-sym
(js-vardecl-forms (rest decls))))))
((js-tag? d "js-vardecl-arr")
(let
((names (nth d 1))
(rhs (js-transpile (nth d 2)))
(tmp-sym (js-sym "__destruct__")))
(cons
(list (js-sym "define") tmp-sym rhs)
(js-vardecl-arr-forms
names
tmp-sym
0
(js-vardecl-forms (rest decls))))))
(else (error "js-vardecl-forms: unexpected decl"))))))))
(define
js-vardecl-obj-forms
(fn
(names tmp-sym tail)
(cond
((empty? names) tail)
(else
(cons
(list
(js-sym "define")
(js-sym (first names))
(list (js-sym "js-get-prop") tmp-sym (first names)))
(js-vardecl-obj-forms (rest names) tmp-sym tail))))))
(define
js-vardecl-arr-forms
(fn
(names tmp-sym i tail)
(cond
((empty? names) tail)
((= (first names) nil)
(js-vardecl-arr-forms (rest names) tmp-sym (+ i 1) tail))
(else
(cons
(list
(js-sym "define")
(js-sym (first names))
(list (js-sym "js-get-prop") tmp-sym i))
(js-vardecl-arr-forms (rest names) tmp-sym (+ i 1) tail))))))
(define
js-transpile-if-stmt