1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
(** JavaScript String API *)
type t = string
let make _whatever = Js_internal.notImplemented "Js.String" "make"
let fromCharCode code =
let uchar = Uchar.of_int code in
let char_value = Uchar.to_char uchar in
Stdlib.String.make 1 char_value
let fromCharCodeMany _ = Js_internal.notImplemented "Js.String" "fromCharCodeMany"
let fromCodePoint code_point =
let ch = Char.chr code_point in
Stdlib.String.make 1 ch
let fromCodePointMany _ = Js_internal.notImplemented "Js.String" "fromCodePointMany"
let length = Stdlib.String.length
let get str index =
let ch = Stdlib.String.get str index in
Stdlib.String.make 1 ch
let charAt ~index str =
if index < 0 || index >= Stdlib.String.length str then ""
else
let ch = Stdlib.String.get str index in
Stdlib.String.make 1 ch
let charCodeAt ~index:n s =
if n < 0 || n >= Stdlib.String.length s then nan else float_of_int (Stdlib.Char.code (Stdlib.String.get s n))
let codePointAt ~index str =
let str_length = Stdlib.String.length str in
if index >= 0 && index < str_length then
let uchar = Uchar.of_char (Stdlib.String.get str index) in
Some (Uchar.to_int uchar)
else None
let concat ~other:str2 str1 = Stdlib.String.concat "" [ str1; str2 ]
let concatMany ~strings:many original =
let many_list = Stdlib.Array.to_list many in
Stdlib.String.concat "" (original :: many_list)
let endsWith ~suffix ?len str =
let str_length = Stdlib.String.length str in
let end_idx = match len with Some i -> Stdlib.min str_length i | None -> str_length in
let sub_str = Stdlib.String.sub str 0 end_idx in
Stdlib.String.ends_with ~suffix sub_str
let includes ~search ?start str =
let str_length = Stdlib.String.length str in
let search_length = Stdlib.String.length search in
let rec includes_helper idx =
if idx + search_length > str_length then false
else if Stdlib.String.sub str idx search_length = search then true
else includes_helper (idx + 1)
in
let from = match start with None -> 0 | Some f -> f in
includes_helper from
let indexOf ~search ?start str =
let str_length = Stdlib.String.length str in
let search_length = Stdlib.String.length search in
let rec index_helper idx =
if idx + search_length > str_length then -1
else if Stdlib.String.sub str idx search_length = search then idx
else index_helper (idx + 1)
in
let from = match start with None -> 0 | Some f -> f in
index_helper from
let lastIndexOf ~search ?(start = max_int) str =
let len = String.length str in
let rec find_index i =
if i < 0 || i > start then -1
else
let sub_len = min (len - i) (String.length search) in
if String.sub str i sub_len = search then i else find_index (i - 1)
in
find_index (min (len - 1) start)
let localeCompare ~other:_ _ = Js_internal.notImplemented "Js.String" "localeCompare"
let match_ ~regexp str =
let match_next str regex =
match Js_re.exec ~str regex with None -> None | Some result -> Some (Js_re.captures result)
in
let match_all : t -> Js_re.t -> t Js_internal.nullable array Js_internal.nullable =
fun str regex ->
match match_next str regex with
| None -> None
| Some result -> (
match match_next str regex with None -> Some result | Some second -> Some (Stdlib.Array.append result second))
in
if Js_re.global regexp then match_all str regexp else match_next str regexp
let normalize ?(form = `NFC) str =
let normalization =
match form with
| `NFC -> Quickjs.String.NFC
| `NFD -> Quickjs.String.NFD
| `NFKC -> Quickjs.String.NFKC
| `NFKD -> Quickjs.String.NFKD
in
match Quickjs.String.Prototype.normalize normalization str with Some s -> s | None -> str
let repeat ~count str =
let rec repeat' str acc remaining = if remaining <= 0 then acc else repeat' str (str ^ acc) (remaining - 1) in
repeat' str "" count
let replace ~search ~replacement str =
let search_regexp = Str.regexp_string search in
Str.replace_first search_regexp replacement str
let process_replacement ~replacement ~matches ~prefix ~suffix =
let len = String.length replacement in
let buf = Buffer.create len in
let i = ref 0 in
while !i < len do
if replacement.[!i] = '$' && !i + 1 < len then (
let next = replacement.[!i + 1] in
match next with
| '$' ->
Buffer.add_char buf '$';
i := !i + 2
| '&' ->
let matched = Stdlib.Array.get matches 0 |> Option.value ~default:"" in
Buffer.add_string buf matched;
i := !i + 2
| '`' ->
Buffer.add_string buf prefix;
i := !i + 2
| '\'' ->
Buffer.add_string buf suffix;
i := !i + 2
| '0' .. '9' ->
let start_digit = !i + 1 in
let group_num, advance =
if !i + 2 < len then
match replacement.[!i + 2] with
| '0' .. '9' ->
let two_digit = int_of_string (String.sub replacement start_digit 2) in
if two_digit < Array.length matches then (two_digit, 3) else (Char.code next - Char.code '0', 2)
| _ -> (Char.code next - Char.code '0', 2)
else (Char.code next - Char.code '0', 2)
in
if group_num > 0 && group_num < Array.length matches then
let group_value = Stdlib.Array.get matches group_num |> Option.value ~default:"" in
Buffer.add_string buf group_value
else (
Buffer.add_char buf '$';
Buffer.add_char buf next;
if advance = 3 then Buffer.add_char buf replacement.[!i + 2]);
i := !i + advance
| _ ->
Buffer.add_char buf '$';
incr i)
else (
Buffer.add_char buf replacement.[!i];
incr i)
done;
Buffer.contents buf
let replaceByRe ~regexp ~replacement str =
let rec replace_all str =
Js_re.setLastIndex regexp 0;
match Js_re.exec ~str regexp with
| None -> str
| Some result when Stdlib.Array.length (Js_re.captures result) == 0 -> str
| Some result ->
let matches = Js_re.captures result in
let matched_str = Stdlib.Array.get matches 0 |> Option.get in
let prefix = Stdlib.String.sub str 0 (Js_re.index result) in
let suffix_start = Js_re.index result + String.length matched_str in
let suffix = Stdlib.String.sub str suffix_start (String.length str - suffix_start) in
let processed_replacement = process_replacement ~replacement ~matches ~prefix ~suffix in
Js_re.setLastIndex regexp suffix_start;
prefix ^ processed_replacement ^ replace_all suffix
in
let replace_first str =
match Js_re.exec ~str regexp with
| None -> str
| Some result ->
let matches = Js_re.captures result in
let matched_str = Stdlib.Array.get matches 0 |> Option.get in
let prefix = Stdlib.String.sub str 0 (Js_re.index result) in
let suffix_start = Js_re.index result + String.length matched_str in
let suffix = Stdlib.String.sub str suffix_start (String.length str - suffix_start) in
let processed_replacement = process_replacement ~replacement ~matches ~prefix ~suffix in
prefix ^ processed_replacement ^ suffix
in
if Js_re.global regexp then replace_all str else replace_first str
let unsafeReplaceBy0 ~regexp:_ ~f:_ _ = Js_internal.notImplemented "Js.String" "unsafeReplaceBy0"
let unsafeReplaceBy1 ~regexp:_ ~f:_ _ = Js_internal.notImplemented "Js.String" "unsafeReplaceBy1"
let unsafeReplaceBy2 ~regexp:_ ~f:_ _ = Js_internal.notImplemented "Js.String" "unsafeReplaceBy2"
let unsafeReplaceBy3 ~regexp:_ ~f:_ _ = Js_internal.notImplemented "Js.String" "unsafeReplaceBy3"
let search ~regexp str =
let saved_last_index = Js_re.lastIndex regexp in
Js_re.setLastIndex regexp 0;
let result =
if Js_re.test ~str regexp then (
Js_re.setLastIndex regexp 0;
match Js_re.exec ~str regexp with Some result -> Js_re.index result | None -> -1)
else -1
in
Js_re.setLastIndex regexp saved_last_index;
result
let slice ?start ?end_ str =
let str_length = Stdlib.String.length str in
let start = match start with None -> 0 | Some s -> s in
let end_ = match end_ with None -> str_length | Some s -> s in
let start_idx = Stdlib.max 0 (Stdlib.min start str_length) in
let end_idx = Stdlib.max start_idx (Stdlib.min end_ str_length) in
if start_idx >= end_idx then "" else Stdlib.String.sub str start_idx (end_idx - start_idx)
let split ?sep ?limit str =
let sep = Option.value sep ~default:str in
let regexp = Str.regexp_string sep in
let split = if sep <> "" then Str.split_delim else Str.split in
let items = split regexp str |> Stdlib.Array.of_list in
let limit = Option.value limit ~default:(Stdlib.Array.length items) in
match limit with
| limit when limit >= 0 && limit < Stdlib.Array.length items -> Stdlib.Array.sub items 0 limit
| _ -> items
let splitByRe ~regexp ?limit str =
let rev_array arr = arr |> Stdlib.Array.to_list |> Stdlib.List.rev |> Stdlib.Array.of_list in
let rec split_all str acc =
Js_re.setLastIndex regexp 0;
match Js_re.exec ~str regexp with
| Some result when Stdlib.Array.length (Js_re.captures result) = 0 ->
Stdlib.Array.append [| Some str |] acc |> rev_array
| None -> Stdlib.Array.append [| Some str |] acc |> rev_array
| Some result ->
let matches = Js_re.captures result in
let matched_str = Stdlib.Array.get matches 0 |> Option.get in
let prefix = String.sub str 0 (Js_re.index result) in
let suffix_start = Js_re.index result + String.length matched_str in
let suffix = String.sub str suffix_start (String.length str - suffix_start) in
let suffix_matches = Stdlib.Array.append [| Some prefix |] acc in
split_all suffix suffix_matches
in
let split_next str acc =
Js_re.setLastIndex regexp 0;
match Js_re.exec ~str regexp with
| None -> Stdlib.Array.append [| Some str |] acc |> rev_array
| Some result ->
let matches = Js_re.captures result in
let matched_str = Stdlib.Array.get matches 0 |> Option.get in
let index = Js_re.index result in
let prefix = String.sub str 0 index in
let suffix_start = index + String.length matched_str in
let suffix = String.sub str suffix_start (String.length str - suffix_start) in
Stdlib.Array.append [| Some prefix |] (split_all suffix acc)
in
let _ = limit in
if Js_re.global regexp then split_all str [||] else split_next str [||]
let startsWith ~prefix ?(start = 0) str =
let len_prefix = String.length prefix in
let len_str = String.length str in
if start < 0 || start > len_str then false
else
let rec compare_prefix i =
i = len_prefix || (i < len_str && prefix.[i] = str.[start + i] && compare_prefix (i + 1))
in
compare_prefix 0
let substr ?(start = 0) ?len str =
let str_length = Stdlib.String.length str in
let len = match len with None -> str_length | Some s -> s in
let start_idx = max 0 (min start str_length) in
let end_idx = min (start_idx + len) str_length in
if start_idx >= end_idx then "" else Stdlib.String.sub str start_idx (end_idx - start_idx)
let substring ?start ?end_ str =
let str_length = Stdlib.String.length str in
let start = match start with None -> 0 | Some s -> s in
let end_ = match end_ with None -> str_length | Some s -> s in
let start_idx = max 0 (min start str_length) in
let end_idx = max 0 (min end_ str_length) in
if start_idx >= end_idx then Stdlib.String.sub str end_idx (start_idx - end_idx)
else Stdlib.String.sub str start_idx (end_idx - start_idx)
let case_to_utf_8 case_map s =
let rec loop buf s i max =
if i > max then Buffer.contents buf
else
let dec = String.get_utf_8_uchar s i in
let u = Uchar.utf_decode_uchar dec in
(match case_map u with
| `Self -> Buffer.add_utf_8_uchar buf u
| `Uchars us -> List.iter (Buffer.add_utf_8_uchar buf) us);
loop buf s (i + Uchar.utf_decode_length dec) max
in
let buf = Buffer.create (String.length s * 2) in
loop buf s 0 (String.length s - 1)
let toLowerCase s = case_to_utf_8 Uucp.Case.Map.to_lower s
let toLocaleLowerCase _ = Js_internal.notImplemented "Js.String" "toLocaleLowerCase"
let toUpperCase s = case_to_utf_8 Uucp.Case.Map.to_upper s
let toLocaleUpperCase _ = Js_internal.notImplemented "Js.String" "toLocaleUpperCase"
let trim str =
let whitespace = " \t\n\r" in
let is_whitespace c = Stdlib.String.contains whitespace c in
let length = Stdlib.String.length str in
let rec trim_start idx =
if idx >= length then length else if is_whitespace (Stdlib.String.get str idx) then trim_start (idx + 1) else idx
in
let rec trim_end idx =
if idx <= 0 then 0 else if is_whitespace (Stdlib.String.get str (idx - 1)) then trim_end (idx - 1) else idx
in
let start_idx = trim_start 0 in
let end_idx = trim_end length in
if start_idx >= end_idx then "" else Stdlib.String.sub str start_idx (end_idx - start_idx)
let anchor ~name:_ _ = Js_internal.notImplemented "Js.String" "anchor"
let link ~href:_ _ = Js_internal.notImplemented "Js.String" "link"