js-on-sx: optional chaining ?.
Parser: jp-parse-postfix handles op "?." followed by ident / [ / ( emitting (js-optchain-member obj name), (js-optchain-index obj k), or (js-optchain-call callee args). Transpile: each emits (js-optchain-get obj key) or (js-optchain-call fn args). Runtime: js-optchain-get and js-optchain-call short-circuit to js-undefined when receiver is null/undefined. 423/425 unit (+5), 148/148 slice unchanged.
This commit is contained in:
@@ -593,6 +593,42 @@
|
||||
(jp-call-args-loop st args)
|
||||
(jp-expect! st "punct" ")")
|
||||
(jp-parse-postfix st (list (quote js-call) left args)))))
|
||||
((jp-at? st "op" "?.")
|
||||
(do
|
||||
(jp-advance! st)
|
||||
(cond
|
||||
((jp-at? st "punct" "[")
|
||||
(begin
|
||||
(jp-advance! st)
|
||||
(let
|
||||
((k (jp-parse-assignment st)))
|
||||
(jp-expect! st "punct" "]")
|
||||
(jp-parse-postfix
|
||||
st
|
||||
(list (quote js-optchain-index) left k)))))
|
||||
((jp-at? st "punct" "(")
|
||||
(begin
|
||||
(jp-advance! st)
|
||||
(let
|
||||
((args (list)))
|
||||
(jp-call-args-loop st args)
|
||||
(jp-expect! st "punct" ")")
|
||||
(jp-parse-postfix
|
||||
st
|
||||
(list (quote js-optchain-call) left args)))))
|
||||
(else
|
||||
(let
|
||||
((t (jp-peek st)))
|
||||
(if
|
||||
(or
|
||||
(= (get t :type) "ident")
|
||||
(= (get t :type) "keyword"))
|
||||
(do
|
||||
(jp-advance! st)
|
||||
(jp-parse-postfix
|
||||
st
|
||||
(list (quote js-optchain-member) left (get t :value))))
|
||||
(error "expected ident, [ or ( after ?.")))))))
|
||||
((or (jp-at? st "op" "++") (jp-at? st "op" "--"))
|
||||
(let
|
||||
((op (get (jp-peek st) :value)))
|
||||
|
||||
@@ -1750,6 +1750,24 @@
|
||||
|
||||
(define Object {:entries js-object-entries :values js-object-values :freeze js-object-freeze :assign js-object-assign :keys js-object-keys})
|
||||
|
||||
(define
|
||||
js-optchain-get
|
||||
(fn
|
||||
(obj key)
|
||||
(if
|
||||
(or (= obj nil) (js-undefined? obj))
|
||||
js-undefined
|
||||
(js-get-prop obj key))))
|
||||
|
||||
(define
|
||||
js-optchain-call
|
||||
(fn
|
||||
(fn-val args)
|
||||
(if
|
||||
(or (= fn-val nil) (js-undefined? fn-val))
|
||||
js-undefined
|
||||
(js-call-plain fn-val args))))
|
||||
|
||||
(define
|
||||
js-array-spread-build
|
||||
(fn
|
||||
|
||||
@@ -1081,6 +1081,18 @@ cat > "$TMPFILE" << 'EPOCHS'
|
||||
(epoch 2603)
|
||||
(eval "(js-eval \"var pt = {px: 100}; var {px} = pt; px + 1\")")
|
||||
|
||||
;; ── Phase 11.optchain: ?. optional chaining ─────────────────────
|
||||
(epoch 2700)
|
||||
(eval "(js-eval \"var o = {x: 5}; o?.x\")")
|
||||
(epoch 2701)
|
||||
(eval "(js-eval \"var o = null; o?.x\")")
|
||||
(epoch 2702)
|
||||
(eval "(js-eval \"var o = undefined; o?.x\")")
|
||||
(epoch 2703)
|
||||
(eval "(js-eval \"var o = {a:{b:7}}; o?.a?.b\")")
|
||||
(epoch 2704)
|
||||
(eval "(js-eval \"var o = {a:null}; var r = o?.a?.b; r === undefined\")")
|
||||
|
||||
EPOCHS
|
||||
|
||||
|
||||
@@ -1664,6 +1676,13 @@ check 2601 "arr destructure" '6'
|
||||
check 2602 "arr destructure skip" '4'
|
||||
check 2603 "obj partial+add" '101'
|
||||
|
||||
# ── Phase 11.optchain ──────────────────────────────────────────
|
||||
check 2700 "?. obj present" '5'
|
||||
check 2701 "?. obj null" 'undefined'
|
||||
check 2702 "?. obj undef" 'undefined'
|
||||
check 2703 "?. chained" '7'
|
||||
check 2704 "?. null-chain" 'true'
|
||||
|
||||
TOTAL=$((PASS + FAIL))
|
||||
if [ $FAIL -eq 0 ]; then
|
||||
echo "✓ $PASS/$TOTAL JS-on-SX tests passed"
|
||||
|
||||
@@ -111,6 +111,12 @@
|
||||
(js-transpile-postfix (nth ast 1) (nth ast 2)))
|
||||
((js-tag? ast "js-prefix")
|
||||
(js-transpile-prefix (nth ast 1) (nth ast 2)))
|
||||
((js-tag? ast "js-optchain-member")
|
||||
(js-transpile-optchain-member (nth ast 1) (nth ast 2)))
|
||||
((js-tag? ast "js-optchain-index")
|
||||
(js-transpile-optchain-index (nth ast 1) (nth ast 2)))
|
||||
((js-tag? ast "js-optchain-call")
|
||||
(js-transpile-optchain-call (nth ast 1) (nth ast 2)))
|
||||
((js-tag? ast "js-switch")
|
||||
(js-transpile-switch (nth ast 1) (nth ast 2)))
|
||||
((js-tag? ast "js-new")
|
||||
@@ -819,6 +825,30 @@
|
||||
(js-collect-funcdecls (rest stmts))))
|
||||
(else (js-collect-funcdecls (rest stmts))))))
|
||||
|
||||
(define
|
||||
js-transpile-optchain-member
|
||||
(fn
|
||||
(obj-ast name)
|
||||
(list (js-sym "js-optchain-get") (js-transpile obj-ast) name)))
|
||||
|
||||
(define
|
||||
js-transpile-optchain-index
|
||||
(fn
|
||||
(obj-ast key-ast)
|
||||
(list
|
||||
(js-sym "js-optchain-get")
|
||||
(js-transpile obj-ast)
|
||||
(js-transpile key-ast))))
|
||||
|
||||
(define
|
||||
js-transpile-optchain-call
|
||||
(fn
|
||||
(callee-ast args)
|
||||
(list
|
||||
(js-sym "js-optchain-call")
|
||||
(js-transpile callee-ast)
|
||||
(js-transpile-args args))))
|
||||
|
||||
(define
|
||||
js-transpile-stmt-list
|
||||
(fn
|
||||
|
||||
Reference in New Issue
Block a user