From f0c4127870f76c1a4127773da88540afeab326fd Mon Sep 17 00:00:00 2001 From: giles Date: Fri, 24 Apr 2026 07:01:34 +0000 Subject: [PATCH] HS: possessive expression via its (+1 test) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two generator changes: (a) `parse_run_locals` for Pattern 2 (`var R = await run(...)`) now recognises `result: ` in the opts dict and binds it to `it` so `run("its foo", {result: {foo: "foo"}})` produces `(eval-hs-locals "its foo" (list (list (quote it) {:foo "foo"})))`. Also adds the same extraction to Pattern 1 (`expect(run(...)).toBe(...)`). (b) `_hs-wrap-body` emitted by the generator no longer shadows `it` to nil — it only binds `event` — so eval-hs-locals's outer `it` binding is visible inside the wrapped body. `eval-hs` still binds `it` nil at its own `fn` wrapper so nothing regresses. Suite hs-upstream-expressions/possessiveExpression: 22/23 → 23/23. Smoke 0-195: 162/195 unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) --- spec/tests/test-hyperscript-behavioral.sx | 6 ++++-- tests/playwright/generate-sx-tests.py | 19 ++++++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/spec/tests/test-hyperscript-behavioral.sx b/spec/tests/test-hyperscript-behavioral.sx index 9847409b..6833966d 100644 --- a/spec/tests/test-hyperscript-behavioral.sx +++ b/spec/tests/test-hyperscript-behavioral.sx @@ -27,8 +27,10 @@ ;; bare expressions (e.g. `foo.foo`) get the expression value back. (define _hs-wrap-body (fn (sx) + ;; Wrap body to capture return via `it`. `event` default is always nil. + ;; `it` is NOT shadowed here — callers (eval-hs-locals) may pre-bind it. (list (quote let) - (list (list (quote it) nil) (list (quote event) nil)) + (list (list (quote event) nil)) (list (quote let) (list (list (quote _ret) sx)) (list (quote if) (list (quote nil?) (quote _ret)) (quote it) (quote _ret)))))) @@ -5679,7 +5681,7 @@ (dom-append (dom-body) _el-pDiv) )) (deftest "can access its properties" - (assert= (eval-hs "its foo") "foo") + (assert= (eval-hs-locals "its foo" (list (list (quote it) {:foo "foo"}))) "foo") ) (deftest "can access multiple basic attributes" (hs-cleanup!) diff --git a/tests/playwright/generate-sx-tests.py b/tests/playwright/generate-sx-tests.py index 072b7676..399d0bc9 100644 --- a/tests/playwright/generate-sx-tests.py +++ b/tests/playwright/generate-sx-tests.py @@ -1760,6 +1760,14 @@ def generate_eval_only_test(test, idx): extra.append((m2.group(1), js_val_to_sx(m2.group(2).strip()))) if me_val_match: extra.append(('me', js_val_to_sx(me_val_match.group(1)))) + # `result: X` (or `it: X`) binds `it` — upstream `run("expr", { result: ... })` + # uses the value as the implicit `it` for possessive expressions like `its foo`. + result_match = re.search( + r'\b(?:result|it):\s*(\[[^\]]*\]|\{[^}]*(?:\{[^}]*\}[^}]*)?\}|"[^"]*"|\'[^\']*\'|[\w.]+)', + opts_str, + ) + if result_match: + extra.append(('it', js_val_to_sx(result_match.group(1)))) if me_num_match and not (window_setups or extra): assertions.append(f' (assert= (eval-hs-with-me "{hs_expr}" {me_num_match.group(1)}) {expected_sx})') else: @@ -1917,6 +1925,13 @@ def generate_eval_only_test(test, idx): args_str) if me_m: pairs.append(('me', js_val_to_sx(me_m.group(1)))) + # `result: ` binds `it` — upstream `run("its X", {result: obj})` + # passes `obj` as the implicit `it` for possessive expressions. + result_m = re.search( + r'\bresult:\s*(\{[^}]*(?:\{[^}]*\}[^}]*)?\}|\[[^\]]*\]|"[^"]*"|\'[^\']*\'|\d+(?:\.\d+)?)', + args_str) + if result_m: + pairs.append(('it', js_val_to_sx(result_m.group(1)))) lm = re.search(r'locals:\s*\{', args_str) if not lm: return pairs @@ -2453,8 +2468,10 @@ output.append(';; mutate `it` (e.g. `pick first 3 of arr; set $test to it`) get output.append(';; bare expressions (e.g. `foo.foo`) get the expression value back.') output.append('(define _hs-wrap-body') output.append(' (fn (sx)') +output.append(' ;; Wrap body to capture return via `it`. `event` default is always nil.') +output.append(' ;; `it` is NOT shadowed here — callers (eval-hs-locals) may pre-bind it.') output.append(' (list (quote let)') -output.append(' (list (list (quote it) nil) (list (quote event) nil))') +output.append(' (list (list (quote event) nil))') output.append(' (list (quote let)') output.append(' (list (list (quote _ret) sx))') output.append(' (list (quote if) (list (quote nil?) (quote _ret)) (quote it) (quote _ret))))))')