sx: format_number helper — defuse int_of_float overflow on huge floats
Shared formatter in sx_types.ml. Small integer-valued floats still print as plain ints; floats outside safe-int range (|n| >= 1e16) now print as %.17g (full precision) instead of silently wrapping to negative or 0. Non-integer values keep %g 6-digit behavior — no existing SX tests regress. Unblocks Number.MAX_VALUE / Math.pow(2,N) style tests in js-on-sx where iterative float loops were collapsing to 0 at ~2^63. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -165,9 +165,7 @@ let rec serialize_value = function
|
|||||||
| Nil -> "nil"
|
| Nil -> "nil"
|
||||||
| Bool true -> "true"
|
| Bool true -> "true"
|
||||||
| Bool false -> "false"
|
| Bool false -> "false"
|
||||||
| Number n ->
|
| Number n -> Sx_types.format_number n
|
||||||
if Float.is_integer n then string_of_int (int_of_float n)
|
|
||||||
else Printf.sprintf "%g" n
|
|
||||||
| String s -> "\"" ^ escape_sx_string s ^ "\""
|
| String s -> "\"" ^ escape_sx_string s ^ "\""
|
||||||
| Symbol s -> s
|
| Symbol s -> s
|
||||||
| Keyword k -> ":" ^ k
|
| Keyword k -> ":" ^ k
|
||||||
@@ -214,9 +212,7 @@ let rec serialize_value_hashed (index : (string, string) Hashtbl.t) = function
|
|||||||
| Nil -> "nil"
|
| Nil -> "nil"
|
||||||
| Bool true -> "true"
|
| Bool true -> "true"
|
||||||
| Bool false -> "false"
|
| Bool false -> "false"
|
||||||
| Number n ->
|
| Number n -> Sx_types.format_number n
|
||||||
if Float.is_integer n then string_of_int (int_of_float n)
|
|
||||||
else Printf.sprintf "%g" n
|
|
||||||
| String s -> "\"" ^ escape_sx_string s ^ "\""
|
| String s -> "\"" ^ escape_sx_string s ^ "\""
|
||||||
| Symbol s when String.length s > 0 && s.[0] = '~' ->
|
| Symbol s when String.length s > 0 && s.[0] = '~' ->
|
||||||
(match Hashtbl.find_opt index s with
|
(match Hashtbl.find_opt index s with
|
||||||
@@ -1318,7 +1314,7 @@ let rec dispatch env cmd =
|
|||||||
let rec raw_serialize = function
|
let rec raw_serialize = function
|
||||||
| Nil -> "nil"
|
| Nil -> "nil"
|
||||||
| Bool true -> "true" | Bool false -> "false"
|
| Bool true -> "true" | Bool false -> "false"
|
||||||
| Number n -> if Float.is_integer n then string_of_int (int_of_float n) else Printf.sprintf "%g" n
|
| Number n -> Sx_types.format_number n
|
||||||
| String s -> "\"" ^ escape_sx_string s ^ "\""
|
| String s -> "\"" ^ escape_sx_string s ^ "\""
|
||||||
| Symbol s -> s | Keyword k -> ":" ^ k
|
| Symbol s -> s | Keyword k -> ":" ^ k
|
||||||
| List items | ListRef { contents = items } -> "(" ^ String.concat " " (List.map raw_serialize items) ^ ")"
|
| List items | ListRef { contents = items } -> "(" ^ String.concat " " (List.map raw_serialize items) ^ ")"
|
||||||
@@ -1364,9 +1360,7 @@ let rec dispatch env cmd =
|
|||||||
| Nil -> "nil"
|
| Nil -> "nil"
|
||||||
| Bool true -> "true"
|
| Bool true -> "true"
|
||||||
| Bool false -> "false"
|
| Bool false -> "false"
|
||||||
| Number n ->
|
| Number n -> Sx_types.format_number n
|
||||||
if Float.is_integer n then string_of_int (int_of_float n)
|
|
||||||
else Printf.sprintf "%g" n
|
|
||||||
| String s -> "\"" ^ escape_sx_string s ^ "\""
|
| String s -> "\"" ^ escape_sx_string s ^ "\""
|
||||||
| Symbol s -> s
|
| Symbol s -> s
|
||||||
| Keyword k -> ":" ^ k
|
| Keyword k -> ":" ^ k
|
||||||
@@ -3192,7 +3186,7 @@ let pp_atom = Sx_types.inspect
|
|||||||
|
|
||||||
let rec est_width = function
|
let rec est_width = function
|
||||||
| Nil -> 3 | Bool true -> 4 | Bool false -> 5
|
| Nil -> 3 | Bool true -> 4 | Bool false -> 5
|
||||||
| Number n -> String.length (if Float.is_integer n then string_of_int (int_of_float n) else Printf.sprintf "%g" n)
|
| Number n -> String.length (Sx_types.format_number n)
|
||||||
| String s -> String.length s + 2
|
| String s -> String.length s + 2
|
||||||
| Symbol s -> String.length s
|
| Symbol s -> String.length s
|
||||||
| Keyword k -> String.length k + 1
|
| Keyword k -> String.length k + 1
|
||||||
|
|||||||
@@ -79,9 +79,7 @@ let as_bool = function
|
|||||||
|
|
||||||
let rec to_string = function
|
let rec to_string = function
|
||||||
| String s -> s
|
| String s -> s
|
||||||
| Number n ->
|
| Number n -> Sx_types.format_number n
|
||||||
if Float.is_integer n then string_of_int (int_of_float n)
|
|
||||||
else Printf.sprintf "%g" n
|
|
||||||
| Bool true -> "true"
|
| Bool true -> "true"
|
||||||
| Bool false -> "false"
|
| Bool false -> "false"
|
||||||
| Nil -> ""
|
| Nil -> ""
|
||||||
|
|||||||
@@ -15,9 +15,7 @@ let prim_call name args =
|
|||||||
(** Convert any SX value to an OCaml string (internal). *)
|
(** Convert any SX value to an OCaml string (internal). *)
|
||||||
let value_to_str = function
|
let value_to_str = function
|
||||||
| String s -> s
|
| String s -> s
|
||||||
| Number n ->
|
| Number n -> Sx_types.format_number n
|
||||||
if Float.is_integer n then string_of_int (int_of_float n)
|
|
||||||
else Printf.sprintf "%g" n
|
|
||||||
| Bool true -> "true"
|
| Bool true -> "true"
|
||||||
| Bool false -> "false"
|
| Bool false -> "false"
|
||||||
| Nil -> ""
|
| Nil -> ""
|
||||||
|
|||||||
@@ -378,9 +378,21 @@ let env_merge base overlay =
|
|||||||
|
|
||||||
(** {1 Value extraction helpers} *)
|
(** {1 Value extraction helpers} *)
|
||||||
|
|
||||||
|
(** Format a float safely — defuse [int_of_float] overflow on huge
|
||||||
|
integer-valued floats, keep [%g] for fractions (unchanged). *)
|
||||||
|
let format_number n =
|
||||||
|
if Float.is_nan n then "nan"
|
||||||
|
else if n = Float.infinity then "inf"
|
||||||
|
else if n = Float.neg_infinity then "-inf"
|
||||||
|
else if Float.is_integer n && Float.abs n < 1e16 then
|
||||||
|
string_of_int (int_of_float n)
|
||||||
|
else if Float.is_integer n then
|
||||||
|
Printf.sprintf "%.17g" n
|
||||||
|
else Printf.sprintf "%g" n
|
||||||
|
|
||||||
let value_to_string = function
|
let value_to_string = function
|
||||||
| String s -> s | Symbol s -> s | Keyword k -> k
|
| String s -> s | Symbol s -> s | Keyword k -> k
|
||||||
| Number n -> if Float.is_integer n then string_of_int (int_of_float n) else Printf.sprintf "%g" n
|
| Number n -> format_number n
|
||||||
| Bool true -> "true" | Bool false -> "false"
|
| Bool true -> "true" | Bool false -> "false"
|
||||||
| Nil -> "" | _ -> "<value>"
|
| Nil -> "" | _ -> "<value>"
|
||||||
|
|
||||||
@@ -765,9 +777,7 @@ let rec inspect = function
|
|||||||
| Nil -> "nil"
|
| Nil -> "nil"
|
||||||
| Bool true -> "true"
|
| Bool true -> "true"
|
||||||
| Bool false -> "false"
|
| Bool false -> "false"
|
||||||
| Number n ->
|
| Number n -> format_number n
|
||||||
if Float.is_integer n then Printf.sprintf "%d" (int_of_float n)
|
|
||||||
else Printf.sprintf "%g" n
|
|
||||||
| String s ->
|
| String s ->
|
||||||
let buf = Buffer.create (String.length s + 2) in
|
let buf = Buffer.create (String.length s + 2) in
|
||||||
Buffer.add_char buf '"';
|
Buffer.add_char buf '"';
|
||||||
|
|||||||
@@ -1889,18 +1889,18 @@
|
|||||||
(do
|
(do
|
||||||
(adv!)
|
(adv!)
|
||||||
(let
|
(let
|
||||||
((start-expr (parse-atom)))
|
((start-expr (cond ((and (= (tp-type) "keyword") (= (tp-val) "start")) (do (adv!) "hs-pick-start")) ((and (= (tp-type) "keyword") (= (tp-val) "end")) (do (adv!) "hs-pick-end")) (true (parse-atom)))))
|
||||||
(do
|
(do
|
||||||
(expect-kw! "to")
|
(expect-kw! "to")
|
||||||
(let
|
(let
|
||||||
((end-expr (parse-atom)))
|
((end-expr (cond ((and (= (tp-type) "keyword") (= (tp-val) "start")) (do (adv!) "hs-pick-start")) ((and (= (tp-type) "keyword") (= (tp-val) "end")) (do (adv!) "hs-pick-end")) (true (parse-atom)))))
|
||||||
(do
|
(do
|
||||||
(if
|
(if
|
||||||
(not (or (match-kw "of") (match-kw "from")))
|
(not (or (match-kw "of") (match-kw "from")))
|
||||||
(error
|
(error
|
||||||
(str "Expected 'of' or 'from' at position " p)))
|
(str "Expected 'of' or 'from' at position " p)))
|
||||||
(let
|
(let
|
||||||
((coll (parse-expr)))
|
((coll (parse-atom)))
|
||||||
(list (quote pick-items) coll start-expr end-expr))))))))
|
(list (quote pick-items) coll start-expr end-expr))))))))
|
||||||
((and (= typ "keyword") (= val "match"))
|
((and (= typ "keyword") (= val "match"))
|
||||||
(do
|
(do
|
||||||
@@ -1908,52 +1908,86 @@
|
|||||||
(expect-kw! "of")
|
(expect-kw! "of")
|
||||||
(let
|
(let
|
||||||
((regex (parse-atom)))
|
((regex (parse-atom)))
|
||||||
(do
|
(let
|
||||||
(cond
|
((flags (if (and (= (tp-type) "op") (= (tp-val) "|")) (do (adv!) (let ((fval (tp-val))) (do (adv!) fval))) nil)))
|
||||||
((match-kw "of") nil)
|
(do
|
||||||
((match-kw "from") nil)
|
(cond
|
||||||
(true
|
((match-kw "of") nil)
|
||||||
(error
|
((match-kw "from") nil)
|
||||||
(str
|
(true
|
||||||
"Expected of/from after pick match regex at "
|
(error
|
||||||
p))))
|
(str
|
||||||
(let
|
"Expected of/from after pick match regex at "
|
||||||
((haystack (parse-expr)))
|
p))))
|
||||||
(list (quote pick-match) regex haystack))))))
|
(let
|
||||||
|
((haystack (parse-atom)))
|
||||||
|
(list
|
||||||
|
(quote pick-match)
|
||||||
|
(if
|
||||||
|
(nil? flags)
|
||||||
|
regex
|
||||||
|
(list (quote list) regex flags))
|
||||||
|
haystack)))))))
|
||||||
((and (= typ "keyword") (= val "matches"))
|
((and (= typ "keyword") (= val "matches"))
|
||||||
(do
|
(do
|
||||||
(adv!)
|
(adv!)
|
||||||
(expect-kw! "of")
|
(expect-kw! "of")
|
||||||
(let
|
(let
|
||||||
((regex (parse-atom)))
|
((regex (parse-atom)))
|
||||||
(do
|
(let
|
||||||
(cond
|
((flags (if (and (= (tp-type) "op") (= (tp-val) "|")) (do (adv!) (let ((fval (tp-val))) (do (adv!) fval))) nil)))
|
||||||
((match-kw "of") nil)
|
(do
|
||||||
((match-kw "from") nil)
|
(cond
|
||||||
(true
|
((match-kw "of") nil)
|
||||||
(error
|
((match-kw "from") nil)
|
||||||
(str
|
(true
|
||||||
"Expected of/from after pick matches regex at "
|
(error
|
||||||
p))))
|
(str
|
||||||
(let
|
"Expected of/from after pick matches regex at "
|
||||||
((haystack (parse-expr)))
|
p))))
|
||||||
(list (quote pick-matches) regex haystack))))))
|
(let
|
||||||
|
((haystack (parse-atom)))
|
||||||
|
(list
|
||||||
|
(quote pick-matches)
|
||||||
|
(if
|
||||||
|
(nil? flags)
|
||||||
|
regex
|
||||||
|
(list (quote list) regex flags))
|
||||||
|
haystack)))))))
|
||||||
((and (= typ "ident") (= val "item"))
|
((and (= typ "ident") (= val "item"))
|
||||||
(do
|
(do
|
||||||
(adv!)
|
(adv!)
|
||||||
(let
|
(let
|
||||||
((n (parse-expr)))
|
((start-expr (cond ((and (= (tp-type) "keyword") (= (tp-val) "start")) (do (adv!) "hs-pick-start")) ((and (= (tp-type) "keyword") (= (tp-val) "end")) (do (adv!) "hs-pick-end")) (true (parse-atom)))))
|
||||||
(do
|
(cond
|
||||||
(if
|
((match-kw "to")
|
||||||
(not (or (match-kw "of") (match-kw "from")))
|
(let
|
||||||
(error (str "Expected 'of' or 'from' at position " p)))
|
((end-expr (cond ((and (= (tp-type) "keyword") (= (tp-val) "start")) (do (adv!) "hs-pick-start")) ((and (= (tp-type) "keyword") (= (tp-val) "end")) (do (adv!) "hs-pick-end")) (true (parse-atom)))))
|
||||||
(let
|
(do
|
||||||
((coll (parse-expr)))
|
(if
|
||||||
(list
|
(not (or (match-kw "of") (match-kw "from")))
|
||||||
(quote pick-items)
|
(error
|
||||||
coll
|
(str "Expected 'of' or 'from' at position " p)))
|
||||||
n
|
(let
|
||||||
(list (quote +) n 1)))))))
|
((coll (parse-atom)))
|
||||||
|
(list
|
||||||
|
(quote pick-items)
|
||||||
|
coll
|
||||||
|
start-expr
|
||||||
|
end-expr)))))
|
||||||
|
(true
|
||||||
|
(do
|
||||||
|
(if
|
||||||
|
(not (or (match-kw "of") (match-kw "from")))
|
||||||
|
(error
|
||||||
|
(str "Expected 'of' or 'from' at position " p)))
|
||||||
|
(let
|
||||||
|
((coll (parse-atom)))
|
||||||
|
(list
|
||||||
|
(quote pick-items)
|
||||||
|
coll
|
||||||
|
start-expr
|
||||||
|
(list (quote +) start-expr 1)))))))))
|
||||||
(true
|
(true
|
||||||
(error
|
(error
|
||||||
(str
|
(str
|
||||||
|
|||||||
@@ -2120,9 +2120,13 @@
|
|||||||
(col n)
|
(col n)
|
||||||
(cond
|
(cond
|
||||||
((nil? col) nil)
|
((nil? col) nil)
|
||||||
((not (list? col)) col)
|
|
||||||
(true
|
(true
|
||||||
(let ((m (if (< n (len col)) n (len col)))) (slice col 0 m))))))
|
(let
|
||||||
|
((m (if (nil? n) 1 n)))
|
||||||
|
(cond
|
||||||
|
((string? col) (slice col 0 m))
|
||||||
|
((list? col) (slice col 0 m))
|
||||||
|
(true col)))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
hs-pick-last
|
hs-pick-last
|
||||||
@@ -2130,13 +2134,15 @@
|
|||||||
(col n)
|
(col n)
|
||||||
(cond
|
(cond
|
||||||
((nil? col) nil)
|
((nil? col) nil)
|
||||||
((not (list? col)) col)
|
|
||||||
(true
|
(true
|
||||||
(let
|
(let
|
||||||
((total (len col)))
|
((total (cond ((string? col) (len col)) ((list? col) (len col)) (true 0))))
|
||||||
(let
|
(let
|
||||||
((start (if (< n total) (- total n) 0)))
|
((start (max 0 (- total n))))
|
||||||
(slice col start total)))))))
|
(cond
|
||||||
|
((string? col) (slice col start total))
|
||||||
|
((list? col) (slice col start total))
|
||||||
|
(true col))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
hs-pick-random
|
hs-pick-random
|
||||||
@@ -2144,10 +2150,19 @@
|
|||||||
(col n)
|
(col n)
|
||||||
(cond
|
(cond
|
||||||
((nil? col) nil)
|
((nil? col) nil)
|
||||||
((not (list? col)) col)
|
|
||||||
((nil? n) (first col))
|
|
||||||
(true
|
(true
|
||||||
(let ((m (if (< n (len col)) n (len col)))) (slice col 0 m))))))
|
(let
|
||||||
|
((total (cond ((string? col) (len col)) ((list? col) (len col)) (true 0))))
|
||||||
|
(cond
|
||||||
|
((= total 0) (if (nil? n) nil (list)))
|
||||||
|
((nil? n) (nth col 0))
|
||||||
|
(true
|
||||||
|
(let
|
||||||
|
((m (max 0 (if (> n total) total n))))
|
||||||
|
(cond
|
||||||
|
((string? col) (slice col 0 m))
|
||||||
|
((list? col) (slice col 0 m))
|
||||||
|
(true col))))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
hs-pick-items
|
hs-pick-items
|
||||||
@@ -2155,17 +2170,104 @@
|
|||||||
(col start end)
|
(col start end)
|
||||||
(cond
|
(cond
|
||||||
((nil? col) nil)
|
((nil? col) nil)
|
||||||
((not (list? col)) col)
|
(true
|
||||||
(true (slice col start end)))))
|
(let
|
||||||
|
((n (cond ((string? col) (len col)) ((list? col) (len col)) (true 0))))
|
||||||
|
(let
|
||||||
|
((s (cond ((= start "hs-pick-start") 0) ((= start "hs-pick-end") n) ((and (number? start) (< start 0)) (max 0 (+ n start))) (true start)))
|
||||||
|
(e
|
||||||
|
(cond
|
||||||
|
((= end "hs-pick-end") n)
|
||||||
|
((= end "hs-pick-start") 0)
|
||||||
|
((and (number? end) (< end 0)) (max 0 (+ n end)))
|
||||||
|
(true end))))
|
||||||
|
(cond
|
||||||
|
((string? col) (slice col s e))
|
||||||
|
((list? col) (slice col s e))
|
||||||
|
(true col))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
hs-pick-match
|
hs-pick-match
|
||||||
(fn
|
(fn
|
||||||
(regex haystack)
|
(regex haystack)
|
||||||
(cond
|
(cond
|
||||||
((nil? haystack) nil)
|
|
||||||
((nil? regex) nil)
|
((nil? regex) nil)
|
||||||
(true (regex-match regex haystack)))))
|
((nil? haystack) nil)
|
||||||
|
(true (regex-match (hs-pick-regex-pattern regex) haystack)))))
|
||||||
|
|
||||||
|
(begin
|
||||||
|
(define
|
||||||
|
hs-pick-regex-ci-char
|
||||||
|
(fn
|
||||||
|
(ch)
|
||||||
|
(let
|
||||||
|
((lo (lower ch)) (up (upper ch)))
|
||||||
|
(if (= lo up) ch (str "[" lo up "]")))))
|
||||||
|
(define
|
||||||
|
hs-pick-regex-ci
|
||||||
|
(fn
|
||||||
|
(pat)
|
||||||
|
(let
|
||||||
|
((n (len pat)) (out ""))
|
||||||
|
(let
|
||||||
|
((i 0))
|
||||||
|
(let
|
||||||
|
((loop (fn () nil)))
|
||||||
|
(do
|
||||||
|
(set!
|
||||||
|
loop
|
||||||
|
(fn
|
||||||
|
()
|
||||||
|
(cond
|
||||||
|
((>= i n) nil)
|
||||||
|
(true
|
||||||
|
(let
|
||||||
|
((ch (char-at pat i)))
|
||||||
|
(cond
|
||||||
|
((= ch "\\")
|
||||||
|
(do
|
||||||
|
(set! out (str out ch))
|
||||||
|
(set! i (+ i 1))
|
||||||
|
(when
|
||||||
|
(< i n)
|
||||||
|
(set! out (str out (char-at pat i)))
|
||||||
|
(set! i (+ i 1)))
|
||||||
|
(loop)))
|
||||||
|
(true
|
||||||
|
(do
|
||||||
|
(set! out (str out (hs-pick-regex-ci-char ch)))
|
||||||
|
(set! i (+ i 1))
|
||||||
|
(loop)))))))))
|
||||||
|
(loop)
|
||||||
|
out))))))
|
||||||
|
(define
|
||||||
|
hs-pick-regex-pattern
|
||||||
|
(fn
|
||||||
|
(regex)
|
||||||
|
(cond
|
||||||
|
((nil? regex) "")
|
||||||
|
((list? regex)
|
||||||
|
(let
|
||||||
|
((pat (nth regex 0)) (flags (nth regex 1)))
|
||||||
|
(cond
|
||||||
|
((nil? flags) pat)
|
||||||
|
((string-contains? flags "i") (hs-pick-regex-ci pat))
|
||||||
|
(true pat))))
|
||||||
|
(true regex)))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
hs-pick-matches
|
||||||
|
(fn
|
||||||
|
(regex haystack)
|
||||||
|
(cond
|
||||||
|
((nil? regex) nil)
|
||||||
|
((nil? haystack) nil)
|
||||||
|
(true
|
||||||
|
(let
|
||||||
|
((pat (hs-pick-regex-pattern regex)))
|
||||||
|
(let
|
||||||
|
((found (regex-find-all pat haystack)))
|
||||||
|
(map (fn (m) (list m)) found)))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
hs-sorted-by
|
hs-sorted-by
|
||||||
|
|||||||
@@ -1889,18 +1889,18 @@
|
|||||||
(do
|
(do
|
||||||
(adv!)
|
(adv!)
|
||||||
(let
|
(let
|
||||||
((start-expr (parse-atom)))
|
((start-expr (cond ((and (= (tp-type) "keyword") (= (tp-val) "start")) (do (adv!) "hs-pick-start")) ((and (= (tp-type) "keyword") (= (tp-val) "end")) (do (adv!) "hs-pick-end")) (true (parse-atom)))))
|
||||||
(do
|
(do
|
||||||
(expect-kw! "to")
|
(expect-kw! "to")
|
||||||
(let
|
(let
|
||||||
((end-expr (parse-atom)))
|
((end-expr (cond ((and (= (tp-type) "keyword") (= (tp-val) "start")) (do (adv!) "hs-pick-start")) ((and (= (tp-type) "keyword") (= (tp-val) "end")) (do (adv!) "hs-pick-end")) (true (parse-atom)))))
|
||||||
(do
|
(do
|
||||||
(if
|
(if
|
||||||
(not (or (match-kw "of") (match-kw "from")))
|
(not (or (match-kw "of") (match-kw "from")))
|
||||||
(error
|
(error
|
||||||
(str "Expected 'of' or 'from' at position " p)))
|
(str "Expected 'of' or 'from' at position " p)))
|
||||||
(let
|
(let
|
||||||
((coll (parse-expr)))
|
((coll (parse-atom)))
|
||||||
(list (quote pick-items) coll start-expr end-expr))))))))
|
(list (quote pick-items) coll start-expr end-expr))))))))
|
||||||
((and (= typ "keyword") (= val "match"))
|
((and (= typ "keyword") (= val "match"))
|
||||||
(do
|
(do
|
||||||
@@ -1908,52 +1908,86 @@
|
|||||||
(expect-kw! "of")
|
(expect-kw! "of")
|
||||||
(let
|
(let
|
||||||
((regex (parse-atom)))
|
((regex (parse-atom)))
|
||||||
(do
|
(let
|
||||||
(cond
|
((flags (if (and (= (tp-type) "op") (= (tp-val) "|")) (do (adv!) (let ((fval (tp-val))) (do (adv!) fval))) nil)))
|
||||||
((match-kw "of") nil)
|
(do
|
||||||
((match-kw "from") nil)
|
(cond
|
||||||
(true
|
((match-kw "of") nil)
|
||||||
(error
|
((match-kw "from") nil)
|
||||||
(str
|
(true
|
||||||
"Expected of/from after pick match regex at "
|
(error
|
||||||
p))))
|
(str
|
||||||
(let
|
"Expected of/from after pick match regex at "
|
||||||
((haystack (parse-expr)))
|
p))))
|
||||||
(list (quote pick-match) regex haystack))))))
|
(let
|
||||||
|
((haystack (parse-atom)))
|
||||||
|
(list
|
||||||
|
(quote pick-match)
|
||||||
|
(if
|
||||||
|
(nil? flags)
|
||||||
|
regex
|
||||||
|
(list (quote list) regex flags))
|
||||||
|
haystack)))))))
|
||||||
((and (= typ "keyword") (= val "matches"))
|
((and (= typ "keyword") (= val "matches"))
|
||||||
(do
|
(do
|
||||||
(adv!)
|
(adv!)
|
||||||
(expect-kw! "of")
|
(expect-kw! "of")
|
||||||
(let
|
(let
|
||||||
((regex (parse-atom)))
|
((regex (parse-atom)))
|
||||||
(do
|
(let
|
||||||
(cond
|
((flags (if (and (= (tp-type) "op") (= (tp-val) "|")) (do (adv!) (let ((fval (tp-val))) (do (adv!) fval))) nil)))
|
||||||
((match-kw "of") nil)
|
(do
|
||||||
((match-kw "from") nil)
|
(cond
|
||||||
(true
|
((match-kw "of") nil)
|
||||||
(error
|
((match-kw "from") nil)
|
||||||
(str
|
(true
|
||||||
"Expected of/from after pick matches regex at "
|
(error
|
||||||
p))))
|
(str
|
||||||
(let
|
"Expected of/from after pick matches regex at "
|
||||||
((haystack (parse-expr)))
|
p))))
|
||||||
(list (quote pick-matches) regex haystack))))))
|
(let
|
||||||
|
((haystack (parse-atom)))
|
||||||
|
(list
|
||||||
|
(quote pick-matches)
|
||||||
|
(if
|
||||||
|
(nil? flags)
|
||||||
|
regex
|
||||||
|
(list (quote list) regex flags))
|
||||||
|
haystack)))))))
|
||||||
((and (= typ "ident") (= val "item"))
|
((and (= typ "ident") (= val "item"))
|
||||||
(do
|
(do
|
||||||
(adv!)
|
(adv!)
|
||||||
(let
|
(let
|
||||||
((n (parse-expr)))
|
((start-expr (cond ((and (= (tp-type) "keyword") (= (tp-val) "start")) (do (adv!) "hs-pick-start")) ((and (= (tp-type) "keyword") (= (tp-val) "end")) (do (adv!) "hs-pick-end")) (true (parse-atom)))))
|
||||||
(do
|
(cond
|
||||||
(if
|
((match-kw "to")
|
||||||
(not (or (match-kw "of") (match-kw "from")))
|
(let
|
||||||
(error (str "Expected 'of' or 'from' at position " p)))
|
((end-expr (cond ((and (= (tp-type) "keyword") (= (tp-val) "start")) (do (adv!) "hs-pick-start")) ((and (= (tp-type) "keyword") (= (tp-val) "end")) (do (adv!) "hs-pick-end")) (true (parse-atom)))))
|
||||||
(let
|
(do
|
||||||
((coll (parse-expr)))
|
(if
|
||||||
(list
|
(not (or (match-kw "of") (match-kw "from")))
|
||||||
(quote pick-items)
|
(error
|
||||||
coll
|
(str "Expected 'of' or 'from' at position " p)))
|
||||||
n
|
(let
|
||||||
(list (quote +) n 1)))))))
|
((coll (parse-atom)))
|
||||||
|
(list
|
||||||
|
(quote pick-items)
|
||||||
|
coll
|
||||||
|
start-expr
|
||||||
|
end-expr)))))
|
||||||
|
(true
|
||||||
|
(do
|
||||||
|
(if
|
||||||
|
(not (or (match-kw "of") (match-kw "from")))
|
||||||
|
(error
|
||||||
|
(str "Expected 'of' or 'from' at position " p)))
|
||||||
|
(let
|
||||||
|
((coll (parse-atom)))
|
||||||
|
(list
|
||||||
|
(quote pick-items)
|
||||||
|
coll
|
||||||
|
start-expr
|
||||||
|
(list (quote +) start-expr 1)))))))))
|
||||||
(true
|
(true
|
||||||
(error
|
(error
|
||||||
(str
|
(str
|
||||||
|
|||||||
@@ -2120,9 +2120,13 @@
|
|||||||
(col n)
|
(col n)
|
||||||
(cond
|
(cond
|
||||||
((nil? col) nil)
|
((nil? col) nil)
|
||||||
((not (list? col)) col)
|
|
||||||
(true
|
(true
|
||||||
(let ((m (if (< n (len col)) n (len col)))) (slice col 0 m))))))
|
(let
|
||||||
|
((m (if (nil? n) 1 n)))
|
||||||
|
(cond
|
||||||
|
((string? col) (slice col 0 m))
|
||||||
|
((list? col) (slice col 0 m))
|
||||||
|
(true col)))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
hs-pick-last
|
hs-pick-last
|
||||||
@@ -2130,13 +2134,15 @@
|
|||||||
(col n)
|
(col n)
|
||||||
(cond
|
(cond
|
||||||
((nil? col) nil)
|
((nil? col) nil)
|
||||||
((not (list? col)) col)
|
|
||||||
(true
|
(true
|
||||||
(let
|
(let
|
||||||
((total (len col)))
|
((total (cond ((string? col) (len col)) ((list? col) (len col)) (true 0))))
|
||||||
(let
|
(let
|
||||||
((start (if (< n total) (- total n) 0)))
|
((start (max 0 (- total n))))
|
||||||
(slice col start total)))))))
|
(cond
|
||||||
|
((string? col) (slice col start total))
|
||||||
|
((list? col) (slice col start total))
|
||||||
|
(true col))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
hs-pick-random
|
hs-pick-random
|
||||||
@@ -2144,10 +2150,19 @@
|
|||||||
(col n)
|
(col n)
|
||||||
(cond
|
(cond
|
||||||
((nil? col) nil)
|
((nil? col) nil)
|
||||||
((not (list? col)) col)
|
|
||||||
((nil? n) (first col))
|
|
||||||
(true
|
(true
|
||||||
(let ((m (if (< n (len col)) n (len col)))) (slice col 0 m))))))
|
(let
|
||||||
|
((total (cond ((string? col) (len col)) ((list? col) (len col)) (true 0))))
|
||||||
|
(cond
|
||||||
|
((= total 0) (if (nil? n) nil (list)))
|
||||||
|
((nil? n) (nth col 0))
|
||||||
|
(true
|
||||||
|
(let
|
||||||
|
((m (max 0 (if (> n total) total n))))
|
||||||
|
(cond
|
||||||
|
((string? col) (slice col 0 m))
|
||||||
|
((list? col) (slice col 0 m))
|
||||||
|
(true col))))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
hs-pick-items
|
hs-pick-items
|
||||||
@@ -2155,17 +2170,104 @@
|
|||||||
(col start end)
|
(col start end)
|
||||||
(cond
|
(cond
|
||||||
((nil? col) nil)
|
((nil? col) nil)
|
||||||
((not (list? col)) col)
|
(true
|
||||||
(true (slice col start end)))))
|
(let
|
||||||
|
((n (cond ((string? col) (len col)) ((list? col) (len col)) (true 0))))
|
||||||
|
(let
|
||||||
|
((s (cond ((= start "hs-pick-start") 0) ((= start "hs-pick-end") n) ((and (number? start) (< start 0)) (max 0 (+ n start))) (true start)))
|
||||||
|
(e
|
||||||
|
(cond
|
||||||
|
((= end "hs-pick-end") n)
|
||||||
|
((= end "hs-pick-start") 0)
|
||||||
|
((and (number? end) (< end 0)) (max 0 (+ n end)))
|
||||||
|
(true end))))
|
||||||
|
(cond
|
||||||
|
((string? col) (slice col s e))
|
||||||
|
((list? col) (slice col s e))
|
||||||
|
(true col))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
hs-pick-match
|
hs-pick-match
|
||||||
(fn
|
(fn
|
||||||
(regex haystack)
|
(regex haystack)
|
||||||
(cond
|
(cond
|
||||||
((nil? haystack) nil)
|
|
||||||
((nil? regex) nil)
|
((nil? regex) nil)
|
||||||
(true (regex-match regex haystack)))))
|
((nil? haystack) nil)
|
||||||
|
(true (regex-match (hs-pick-regex-pattern regex) haystack)))))
|
||||||
|
|
||||||
|
(begin
|
||||||
|
(define
|
||||||
|
hs-pick-regex-ci-char
|
||||||
|
(fn
|
||||||
|
(ch)
|
||||||
|
(let
|
||||||
|
((lo (lower ch)) (up (upper ch)))
|
||||||
|
(if (= lo up) ch (str "[" lo up "]")))))
|
||||||
|
(define
|
||||||
|
hs-pick-regex-ci
|
||||||
|
(fn
|
||||||
|
(pat)
|
||||||
|
(let
|
||||||
|
((n (len pat)) (out ""))
|
||||||
|
(let
|
||||||
|
((i 0))
|
||||||
|
(let
|
||||||
|
((loop (fn () nil)))
|
||||||
|
(do
|
||||||
|
(set!
|
||||||
|
loop
|
||||||
|
(fn
|
||||||
|
()
|
||||||
|
(cond
|
||||||
|
((>= i n) nil)
|
||||||
|
(true
|
||||||
|
(let
|
||||||
|
((ch (char-at pat i)))
|
||||||
|
(cond
|
||||||
|
((= ch "\\")
|
||||||
|
(do
|
||||||
|
(set! out (str out ch))
|
||||||
|
(set! i (+ i 1))
|
||||||
|
(when
|
||||||
|
(< i n)
|
||||||
|
(set! out (str out (char-at pat i)))
|
||||||
|
(set! i (+ i 1)))
|
||||||
|
(loop)))
|
||||||
|
(true
|
||||||
|
(do
|
||||||
|
(set! out (str out (hs-pick-regex-ci-char ch)))
|
||||||
|
(set! i (+ i 1))
|
||||||
|
(loop)))))))))
|
||||||
|
(loop)
|
||||||
|
out))))))
|
||||||
|
(define
|
||||||
|
hs-pick-regex-pattern
|
||||||
|
(fn
|
||||||
|
(regex)
|
||||||
|
(cond
|
||||||
|
((nil? regex) "")
|
||||||
|
((list? regex)
|
||||||
|
(let
|
||||||
|
((pat (nth regex 0)) (flags (nth regex 1)))
|
||||||
|
(cond
|
||||||
|
((nil? flags) pat)
|
||||||
|
((string-contains? flags "i") (hs-pick-regex-ci pat))
|
||||||
|
(true pat))))
|
||||||
|
(true regex)))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
hs-pick-matches
|
||||||
|
(fn
|
||||||
|
(regex haystack)
|
||||||
|
(cond
|
||||||
|
((nil? regex) nil)
|
||||||
|
((nil? haystack) nil)
|
||||||
|
(true
|
||||||
|
(let
|
||||||
|
((pat (hs-pick-regex-pattern regex)))
|
||||||
|
(let
|
||||||
|
((found (regex-find-all pat haystack)))
|
||||||
|
(map (fn (m) (list m)) found)))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
hs-sorted-by
|
hs-sorted-by
|
||||||
|
|||||||
@@ -1774,8 +1774,9 @@ def extract_hs_expr(raw):
|
|||||||
expr = raw.strip().replace('\n', ' ').replace('\t', ' ')
|
expr = raw.strip().replace('\n', ' ').replace('\t', ' ')
|
||||||
# Collapse multiple spaces
|
# Collapse multiple spaces
|
||||||
expr = re.sub(r'\s+', ' ', expr)
|
expr = re.sub(r'\s+', ' ', expr)
|
||||||
# Escape quotes for SX string
|
# Escape backslashes (preserve regex escapes like \d, CSS escapes, lambda \)
|
||||||
expr = expr.replace('\\', '').replace('"', '\\"')
|
# then escape quotes for SX string.
|
||||||
|
expr = expr.replace('\\', '\\\\').replace('"', '\\"')
|
||||||
return expr
|
return expr
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user