HS test generator: bind run() me: and balanced-brace locals — +2 comparisonOperator
Two related Pattern 1 bugs:
1. The locals capture used `\\{([^}]+)\\}` (greedy non-`}` chars), so
`locals: { that: [1, 2, 3] }` truncated at the first `,` inside `[...]`
and bound `that` to `"[1"`. Switched to balanced-brace extraction +
`split_top_level` so nested arrays/objects survive.
2. `{ me: <X> }` was only forwarded to the SX runtime when X was a single
integer (eval-hs-with-me only accepts numbers). For `me: [1, 2, 3]`
or `me: 1` alongside other locals, `me` was silently dropped, so
`I contain that` couldn't see its receiver. Now any non-numeric `me`
value is bound as a local (`(list (quote me) <val>)`); a numeric
`me` alongside other locals/setups is also bound, so the HS expr
always sees its `me`.
comparisonOperator 79/83 → 81/83 (+2: contains/includes works with arrays).
bind unchanged (43/44).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4341,8 +4341,8 @@
|
||||
(assert= (eval-hs "'Hello World' contains 'missing' ignoring case") false)
|
||||
)
|
||||
(deftest "contains works with arrays"
|
||||
(assert= (eval-hs-locals "I contain that" (list (list (quote that) 1))) true)
|
||||
(assert= (eval-hs-locals "that contains me" (list (list (quote that) "[1"))) true)
|
||||
(assert= (eval-hs-locals "I contain that" (list (list (quote that) 1) (list (quote me) (list 1 2 3)))) true)
|
||||
(assert= (eval-hs-locals "that contains me" (list (list (quote that) (list 1 2 3)) (list (quote me) 1))) true)
|
||||
)
|
||||
(deftest "contains works with css literals"
|
||||
(hs-cleanup!)
|
||||
@@ -4501,8 +4501,8 @@
|
||||
(assert= (eval-hs-locals "foobar does not include foo" (list (list (quote foo) "foo") (list (quote foobar) "foobar"))) false)
|
||||
)
|
||||
(deftest "includes works with arrays"
|
||||
(assert= (eval-hs-locals "I include that" (list (list (quote that) 1))) true)
|
||||
(assert= (eval-hs-locals "that includes me" (list (list (quote that) "[1"))) true)
|
||||
(assert= (eval-hs-locals "I include that" (list (list (quote that) 1) (list (quote me) (list 1 2 3)))) true)
|
||||
(assert= (eval-hs-locals "that includes me" (list (list (quote that) (list 1 2 3)) (list (quote me) 1))) true)
|
||||
)
|
||||
(deftest "includes works with css literals"
|
||||
(hs-cleanup!)
|
||||
|
||||
@@ -1281,16 +1281,48 @@ def generate_eval_only_test(test, idx):
|
||||
hs_expr = extract_hs_expr(m.group(2))
|
||||
opts_str = m.group(3) or ''
|
||||
expected_sx = js_val_to_sx(m.group(4))
|
||||
# Check for { me: X } or { locals: { x: X, y: Y } } in opts
|
||||
me_match = re.search(r'\bme:\s*(\d+)', opts_str)
|
||||
locals_match = re.search(r'locals:\s*\{([^}]+)\}', opts_str)
|
||||
# Check for { me: X } or { locals: { x: X, y: Y } } in opts.
|
||||
# Numeric me uses eval-hs-with-me; other me values get bound as a local.
|
||||
me_num_match = re.search(r'\bme:\s*(\d+)\b', opts_str)
|
||||
me_val_match = re.search(r'\bme:\s*(\[[^\]]*\]|\{[^}]*\}|"[^"]*"|\'[^\']*\')', opts_str)
|
||||
# Locals: balanced-brace extraction so nested arrays/objects don't truncate.
|
||||
locals_idx = opts_str.find('locals:')
|
||||
extra = []
|
||||
if locals_match:
|
||||
for lm in re.finditer(r'(\w+)\s*:\s*([^,}]+)', locals_match.group(1)):
|
||||
extra.append((lm.group(1), js_val_to_sx(lm.group(2).strip())))
|
||||
if me_match and not (window_setups or extra):
|
||||
assertions.append(f' (assert= (eval-hs-with-me "{hs_expr}" {me_match.group(1)}) {expected_sx})')
|
||||
if locals_idx >= 0:
|
||||
open_idx = opts_str.find('{', locals_idx)
|
||||
if open_idx >= 0:
|
||||
depth, in_str, end_idx = 1, None, -1
|
||||
for i in range(open_idx + 1, len(opts_str)):
|
||||
ch = opts_str[i]
|
||||
if in_str:
|
||||
if ch == in_str and opts_str[i - 1] != '\\':
|
||||
in_str = None
|
||||
continue
|
||||
if ch in ('"', "'", '`'):
|
||||
in_str = ch
|
||||
continue
|
||||
if ch == '{':
|
||||
depth += 1
|
||||
elif ch == '}':
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
end_idx = i
|
||||
break
|
||||
if end_idx > open_idx:
|
||||
for kv in split_top_level(opts_str[open_idx + 1:end_idx]):
|
||||
kv = kv.strip()
|
||||
m2 = re.match(r'^(\w+)\s*:\s*(.+)$', kv, re.DOTALL)
|
||||
if m2:
|
||||
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))))
|
||||
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:
|
||||
# If there are other locals/setups but `me: <num>` is present too,
|
||||
# bind it as a local so the HS expression can see it.
|
||||
if me_num_match and not me_val_match:
|
||||
extra.append(('me', me_num_match.group(1)))
|
||||
assertions.append(emit_eval(hs_expr, expected_sx, extra))
|
||||
|
||||
# Pattern 1b: Inline — run("expr", opts).toEqual([...])
|
||||
|
||||
Reference in New Issue
Block a user