HS: pick regex + indices (+13 tests)

Implements cluster 19 — pick command extensions for hs-upstream-pick suite
(11/24 → 24/24, +13):

- Parser:
  - pick items/item EXPR to EXPR supports `start` and `end` keywords
  - pick match / pick matches accept `| <flag>` syntax after regex
  - pick item N without `to` still works (single-item slice)
- Runtime:
  - hs-pick-items / hs-pick-first / hs-pick-last now handle strings
    (not just lists) via slice
  - hs-pick-items resolves `start`/`end` sentinel strings and negative
    indices (len + N) at runtime
  - hs-pick-matches added (wraps regex-find-all, each match as a list)
  - hs-pick-regex-pattern handles (list pat flags) form; `i` flag
    transforms pattern to case-insensitive by replacing alpha chars with
    [aA] character classes (Re.Pcre has no inline-flag support)
- Generator:
  - extract_hs_expr now decodes JS string escape sequences (\" -> ",
    \\ -> \) instead of stripping all backslashes, then re-escapes for
    SX. Preserves regex escapes (\d, \s), CSS escapes, and lambda `\`
    syntax for String.raw template literals while still producing
    correct output for regular JS strings.

Smoke (0-195): 170/195 unchanged (no regressions).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-24 15:08:23 +00:00
parent b45a69b7a4
commit 4be90bf21f
2 changed files with 43 additions and 8 deletions

View File

@@ -4183,7 +4183,7 @@
(assert= (map (fn (x) (get x "name")) (eval-hs "set arr to [{name: \"b\", age: 30}, {name: \"a\", age: 20}, {name: \"c\", age: 25}] then return arr sorted by its age")) (list "a" "c" "b"))
)
(deftest "split by on null returns null"
(eval-hs "set x to null then return x split by ','n")
(eval-hs "set x to null then return x split by ','\\n")
)
(deftest "the result inside where refers to previous command result, not current element"
(assert= (eval-hs "get 3 then set arr to [1, 2, 3, 4, 5] then return arr where it > the result") (list 4 5))
@@ -9135,13 +9135,13 @@
(assert= (eval-hs-locals "pick item 2 from arr set $test to it" (list (list (quote arr) (list 10 11 12 13 14 15 16)))) (list 12))
)
(deftest "can pick a single regex match"
(assert= (eval-hs-locals "pick match of \"d+\" from haystack set window.test to it" (list (list (quote haystack) "The 32 quick brown foxes jumped 12 times over the 149 lazy dogs"))) (list "32"))
(assert= (eval-hs-locals "pick match of \"\\d+\" from haystack set window.test to it" (list (list (quote haystack) "The 32 quick brown foxes jumped 12 times over the 149 lazy dogs"))) (list "32"))
)
(deftest "can pick a single regex match w/ a flag"
(assert= (eval-hs-locals "pick match of \"t.e\" | i from haystack set window.test to it" (list (list (quote haystack) "The 32 quick brown foxes jumped 12 times over the 149 lazy dogs"))) (list "The"))
)
(deftest "can pick all regex matches"
(assert= (eval-hs-locals "pick matches of \"d+\" from haystack set window.test to it" (list (list (quote haystack) "The 32 quick brown foxes jumped 12 times over the 149 lazy dogs"))) (list (list "32") (list "12") (list "149")))
(assert= (eval-hs-locals "pick matches of \"\\d+\" from haystack set window.test to it" (list (list (quote haystack) "The 32 quick brown foxes jumped 12 times over the 149 lazy dogs"))) (list (list "32") (list "12") (list "149")))
)
(deftest "can pick first n items"
(assert= (eval-hs-locals "pick first 3 of arr set $test to it" (list (list (quote arr) (list 10 20 30 40 50)))) (list 10 20 30))
@@ -9159,7 +9159,7 @@
(assert= (eval-hs-locals "pick last 2 of arr set $test to it" (list (list (quote arr) (list 10 20 30 40 50)))) (list 40 50))
)
(deftest "can pick match using 'of' syntax"
(assert= (eval-hs-locals "pick match of \"d+\" of haystack set window.test to it" (list (list (quote haystack) "The 32 quick brown foxes"))) (list "32"))
(assert= (eval-hs-locals "pick match of \"\\d+\" of haystack set window.test to it" (list (list (quote haystack) "The 32 quick brown foxes"))) (list "32"))
)
(deftest "can pick random item"
(assert (not (nil? (eval-hs-locals "pick random of arr set $test to it" (list (list (quote arr) (list 10 20 30)))))))
@@ -9186,7 +9186,7 @@
(assert= (eval-hs-locals "pick items 0 to -4 from arr set $test to it" (list (list (quote arr) (list 10 11 12 13 14 15 16)))) (list 10 11 12))
)
(deftest "does not hang on zero-length regex matches"
(assert (not (nil? (eval-hs-locals "pick matches of \"d*\" from haystack set window.test to it" (list (list (quote haystack) "a1b"))))))
(assert (not (nil? (eval-hs-locals "pick matches of \"\\d*\" from haystack set window.test to it" (list (list (quote haystack) "a1b"))))))
)
(deftest "pick first from null returns null"
(eval-hs "set x to null then pick first 3 from x then return it")
@@ -9195,7 +9195,7 @@
(eval-hs "set x to null then pick last 2 from x then return it")
)
(deftest "pick match from null returns null"
(eval-hs "set x to null then pick match of \"d+\" from x then return it")
(eval-hs "set x to null then pick match of \"\\d+\" from x then return it")
)
(deftest "pick random from null returns null"
(eval-hs "set x to null then pick random from x then return it")