mustardscript 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +62 -20
  2. package/SECURITY.md +1 -1
  3. package/dist/index.js +2 -0
  4. package/dist/lib/executor.js +16 -1
  5. package/dist/lib/policy.js +301 -22
  6. package/dist/lib/progress.js +499 -113
  7. package/dist/lib/runtime.js +109 -40
  8. package/dist/lib/structured.js +327 -11
  9. package/dist/native-loader.js +11 -12
  10. package/index.d.ts +54 -6
  11. package/mustard.d.ts +23 -1
  12. package/package.json +34 -25
  13. package/Cargo.lock +0 -1579
  14. package/Cargo.toml +0 -40
  15. package/crates/mustard/Cargo.toml +0 -31
  16. package/crates/mustard/src/cancellation.rs +0 -28
  17. package/crates/mustard/src/diagnostic.rs +0 -145
  18. package/crates/mustard/src/ir.rs +0 -435
  19. package/crates/mustard/src/lib.rs +0 -21
  20. package/crates/mustard/src/limits.rs +0 -22
  21. package/crates/mustard/src/parser/expressions.rs +0 -723
  22. package/crates/mustard/src/parser/mod.rs +0 -115
  23. package/crates/mustard/src/parser/operators.rs +0 -105
  24. package/crates/mustard/src/parser/patterns.rs +0 -123
  25. package/crates/mustard/src/parser/scope.rs +0 -107
  26. package/crates/mustard/src/parser/statements.rs +0 -298
  27. package/crates/mustard/src/parser/tests/acceptance.rs +0 -339
  28. package/crates/mustard/src/parser/tests/mod.rs +0 -2
  29. package/crates/mustard/src/parser/tests/rejections.rs +0 -107
  30. package/crates/mustard/src/runtime/accounting.rs +0 -613
  31. package/crates/mustard/src/runtime/api.rs +0 -192
  32. package/crates/mustard/src/runtime/async_runtime/mod.rs +0 -5
  33. package/crates/mustard/src/runtime/async_runtime/promises.rs +0 -246
  34. package/crates/mustard/src/runtime/async_runtime/reactions.rs +0 -400
  35. package/crates/mustard/src/runtime/async_runtime/scheduler.rs +0 -224
  36. package/crates/mustard/src/runtime/builtins/arrays.rs +0 -1205
  37. package/crates/mustard/src/runtime/builtins/collections.rs +0 -573
  38. package/crates/mustard/src/runtime/builtins/install.rs +0 -501
  39. package/crates/mustard/src/runtime/builtins/intl.rs +0 -553
  40. package/crates/mustard/src/runtime/builtins/mod.rs +0 -25
  41. package/crates/mustard/src/runtime/builtins/objects.rs +0 -405
  42. package/crates/mustard/src/runtime/builtins/primitives.rs +0 -859
  43. package/crates/mustard/src/runtime/builtins/promises.rs +0 -335
  44. package/crates/mustard/src/runtime/builtins/regexp.rs +0 -356
  45. package/crates/mustard/src/runtime/builtins/strings.rs +0 -803
  46. package/crates/mustard/src/runtime/builtins/support.rs +0 -561
  47. package/crates/mustard/src/runtime/bytecode.rs +0 -123
  48. package/crates/mustard/src/runtime/compiler/assignments.rs +0 -690
  49. package/crates/mustard/src/runtime/compiler/bindings.rs +0 -92
  50. package/crates/mustard/src/runtime/compiler/context.rs +0 -46
  51. package/crates/mustard/src/runtime/compiler/control.rs +0 -342
  52. package/crates/mustard/src/runtime/compiler/expressions.rs +0 -372
  53. package/crates/mustard/src/runtime/compiler/mod.rs +0 -173
  54. package/crates/mustard/src/runtime/compiler/statements.rs +0 -459
  55. package/crates/mustard/src/runtime/conversions/boundary.rs +0 -293
  56. package/crates/mustard/src/runtime/conversions/coercions.rs +0 -217
  57. package/crates/mustard/src/runtime/conversions/errors.rs +0 -118
  58. package/crates/mustard/src/runtime/conversions/mod.rs +0 -14
  59. package/crates/mustard/src/runtime/conversions/operators.rs +0 -334
  60. package/crates/mustard/src/runtime/env.rs +0 -355
  61. package/crates/mustard/src/runtime/exceptions.rs +0 -377
  62. package/crates/mustard/src/runtime/gc.rs +0 -595
  63. package/crates/mustard/src/runtime/mod.rs +0 -318
  64. package/crates/mustard/src/runtime/properties.rs +0 -1762
  65. package/crates/mustard/src/runtime/serialization.rs +0 -127
  66. package/crates/mustard/src/runtime/shared.rs +0 -108
  67. package/crates/mustard/src/runtime/snapshot_validation_tests.rs +0 -93
  68. package/crates/mustard/src/runtime/state.rs +0 -652
  69. package/crates/mustard/src/runtime/tests/async_host.rs +0 -104
  70. package/crates/mustard/src/runtime/tests/collections.rs +0 -50
  71. package/crates/mustard/src/runtime/tests/diagnostics.rs +0 -36
  72. package/crates/mustard/src/runtime/tests/exceptions.rs +0 -122
  73. package/crates/mustard/src/runtime/tests/execution.rs +0 -553
  74. package/crates/mustard/src/runtime/tests/gc.rs +0 -533
  75. package/crates/mustard/src/runtime/tests/mod.rs +0 -56
  76. package/crates/mustard/src/runtime/tests/serialization.rs +0 -170
  77. package/crates/mustard/src/runtime/validation/bytecode.rs +0 -484
  78. package/crates/mustard/src/runtime/validation/mod.rs +0 -14
  79. package/crates/mustard/src/runtime/validation/policy.rs +0 -94
  80. package/crates/mustard/src/runtime/validation/snapshot.rs +0 -406
  81. package/crates/mustard/src/runtime/validation/walk.rs +0 -206
  82. package/crates/mustard/src/runtime/vm.rs +0 -1016
  83. package/crates/mustard/src/span.rs +0 -22
  84. package/crates/mustard/src/structured.rs +0 -107
  85. package/crates/mustard-bridge/Cargo.toml +0 -17
  86. package/crates/mustard-bridge/src/codec.rs +0 -46
  87. package/crates/mustard-bridge/src/dto.rs +0 -99
  88. package/crates/mustard-bridge/src/lib.rs +0 -12
  89. package/crates/mustard-bridge/src/operations.rs +0 -142
  90. package/crates/mustard-node/Cargo.toml +0 -24
  91. package/crates/mustard-node/build.rs +0 -3
  92. package/crates/mustard-node/src/lib.rs +0 -236
  93. package/crates/mustard-sidecar/Cargo.toml +0 -21
  94. package/crates/mustard-sidecar/src/lib.rs +0 -134
  95. package/crates/mustard-sidecar/src/main.rs +0 -36
  96. package/dist/install.js +0 -117
@@ -1,803 +0,0 @@
1
- use super::*;
2
-
3
- impl Runtime {
4
- fn string_value_receiver(&self, value: Value, method: &str) -> MustardResult<String> {
5
- match value {
6
- Value::String(value) => Ok(value),
7
- Value::Object(object) => match &self
8
- .objects
9
- .get(object)
10
- .ok_or_else(|| MustardError::runtime("object missing"))?
11
- .kind
12
- {
13
- ObjectKind::StringObject(value) => Ok(value.clone()),
14
- _ => Err(MustardError::runtime(format!(
15
- "TypeError: String.prototype.{method} called on incompatible receiver",
16
- ))),
17
- },
18
- _ => Err(MustardError::runtime(format!(
19
- "TypeError: String.prototype.{method} called on incompatible receiver",
20
- ))),
21
- }
22
- }
23
-
24
- fn string_receiver(&self, value: Value, method: &str) -> MustardResult<String> {
25
- match value {
26
- Value::String(value) => Ok(value),
27
- Value::Object(object) => match &self
28
- .objects
29
- .get(object)
30
- .ok_or_else(|| MustardError::runtime("object missing"))?
31
- .kind
32
- {
33
- ObjectKind::StringObject(value) => Ok(value.clone()),
34
- ObjectKind::NumberObject(value) => self.to_string(Value::Number(*value)),
35
- ObjectKind::BooleanObject(value) => self.to_string(Value::Bool(*value)),
36
- _ => Err(MustardError::runtime(format!(
37
- "TypeError: String.prototype.{method} called on incompatible receiver",
38
- ))),
39
- },
40
- _ => Err(MustardError::runtime(format!(
41
- "TypeError: String.prototype.{method} called on incompatible receiver",
42
- ))),
43
- }
44
- }
45
-
46
- fn string_search_pattern(
47
- &self,
48
- value: Value,
49
- method: &str,
50
- ) -> MustardResult<StringSearchPattern> {
51
- match value {
52
- Value::Object(object) if self.is_regexp_object(object) => {
53
- Ok(StringSearchPattern::RegExp {
54
- object,
55
- regex: self.regexp_object(object)?.clone(),
56
- })
57
- }
58
- value => {
59
- if is_callable(&value) {
60
- return Err(MustardError::runtime(format!(
61
- "TypeError: String.prototype.{method} does not support callback patterns",
62
- )));
63
- }
64
- Ok(StringSearchPattern::Literal(self.to_string(value)?))
65
- }
66
- }
67
- }
68
-
69
- fn string_callback_replacement(
70
- &mut self,
71
- method: &str,
72
- callback: Value,
73
- input: &str,
74
- matched: &RegExpMatchData,
75
- ) -> MustardResult<String> {
76
- let mut args = vec![Value::String(
77
- input[matched.start_byte..matched.end_byte].to_string(),
78
- )];
79
- args.extend(
80
- matched
81
- .captures
82
- .iter()
83
- .map(|value| value.clone().map_or(Value::Undefined, Value::String)),
84
- );
85
- args.push(Value::Number(matched.start_index as f64));
86
- args.push(Value::String(input.to_string()));
87
- if !matched.named_groups.is_empty() {
88
- let groups = matched
89
- .named_groups
90
- .iter()
91
- .map(|(name, value)| {
92
- (
93
- name.clone(),
94
- value.clone().map_or(Value::Undefined, Value::String),
95
- )
96
- })
97
- .collect::<IndexMap<_, _>>();
98
- let object = self.insert_object(groups, ObjectKind::Plain)?;
99
- args.push(Value::Object(object));
100
- }
101
- let mut roots = vec![callback.clone()];
102
- roots.extend(args.iter().cloned());
103
- let value = self.with_temporary_roots(&roots, |runtime| {
104
- runtime.call_callback(
105
- callback.clone(),
106
- Value::Undefined,
107
- &args,
108
- CallbackCallOptions {
109
- non_callable_message: &format!(
110
- "TypeError: String.prototype.{method} replacement callback is not callable"
111
- ),
112
- host_suspension_message: &format!(
113
- "TypeError: String.prototype.{method} callback replacements do not support host suspensions"
114
- ),
115
- unsettled_message: &format!(
116
- "synchronous String.prototype.{method} callback did not settle"
117
- ),
118
- allow_host_suspension: false,
119
- allow_pending_promise_result: false,
120
- },
121
- )
122
- })?;
123
- self.to_string(value)
124
- }
125
-
126
- pub(crate) fn call_string_trim(&self, this_value: Value) -> MustardResult<Value> {
127
- let value = self.string_receiver(this_value, "trim")?;
128
- Ok(Value::String(value.trim().to_string()))
129
- }
130
-
131
- pub(crate) fn call_string_trim_start(&self, this_value: Value) -> MustardResult<Value> {
132
- let value = self.string_receiver(this_value, "trimStart")?;
133
- Ok(Value::String(value.trim_start().to_string()))
134
- }
135
-
136
- pub(crate) fn call_string_trim_end(&self, this_value: Value) -> MustardResult<Value> {
137
- let value = self.string_receiver(this_value, "trimEnd")?;
138
- Ok(Value::String(value.trim_end().to_string()))
139
- }
140
-
141
- pub(crate) fn call_string_to_string(&self, this_value: Value) -> MustardResult<Value> {
142
- Ok(Value::String(
143
- self.string_value_receiver(this_value, "toString")?,
144
- ))
145
- }
146
-
147
- pub(crate) fn call_string_value_of(&self, this_value: Value) -> MustardResult<Value> {
148
- Ok(Value::String(
149
- self.string_value_receiver(this_value, "valueOf")?,
150
- ))
151
- }
152
-
153
- pub(crate) fn call_string_includes(
154
- &self,
155
- this_value: Value,
156
- args: &[Value],
157
- ) -> MustardResult<Value> {
158
- let value = self.string_receiver(this_value, "includes")?;
159
- let chars = value.chars().collect::<Vec<_>>();
160
- let needle = self
161
- .to_string(args.first().cloned().unwrap_or(Value::Undefined))?
162
- .chars()
163
- .collect::<Vec<_>>();
164
- let position = clamp_index(
165
- self.to_integer(args.get(1).cloned().unwrap_or(Value::Number(0.0)))?,
166
- chars.len(),
167
- );
168
- let haystack = chars[position..].iter().collect::<String>();
169
- Ok(Value::Bool(
170
- haystack.contains(&needle.iter().collect::<String>()),
171
- ))
172
- }
173
-
174
- pub(crate) fn call_string_starts_with(
175
- &self,
176
- this_value: Value,
177
- args: &[Value],
178
- ) -> MustardResult<Value> {
179
- let value = self.string_receiver(this_value, "startsWith")?;
180
- let chars = value.chars().collect::<Vec<_>>();
181
- let needle = self
182
- .to_string(args.first().cloned().unwrap_or(Value::Undefined))?
183
- .chars()
184
- .collect::<Vec<_>>();
185
- let position = clamp_index(
186
- self.to_integer(args.get(1).cloned().unwrap_or(Value::Number(0.0)))?,
187
- chars.len(),
188
- );
189
- Ok(Value::Bool(chars[position..].starts_with(&needle)))
190
- }
191
-
192
- pub(crate) fn call_string_ends_with(
193
- &self,
194
- this_value: Value,
195
- args: &[Value],
196
- ) -> MustardResult<Value> {
197
- let value = self.string_receiver(this_value, "endsWith")?;
198
- let chars = value.chars().collect::<Vec<_>>();
199
- let needle = self
200
- .to_string(args.first().cloned().unwrap_or(Value::Undefined))?
201
- .chars()
202
- .collect::<Vec<_>>();
203
- let end = match args.get(1) {
204
- Some(value) => clamp_index(self.to_integer(value.clone())?, chars.len()),
205
- None => chars.len(),
206
- };
207
- Ok(Value::Bool(chars[..end].ends_with(&needle)))
208
- }
209
-
210
- pub(crate) fn call_string_index_of(
211
- &self,
212
- this_value: Value,
213
- args: &[Value],
214
- ) -> MustardResult<Value> {
215
- let value = self.string_receiver(this_value, "indexOf")?;
216
- let chars = value.chars().collect::<Vec<_>>();
217
- let needle = self
218
- .to_string(args.first().cloned().unwrap_or(Value::Undefined))?
219
- .chars()
220
- .collect::<Vec<_>>();
221
- let position = clamp_index(
222
- self.to_integer(args.get(1).cloned().unwrap_or(Value::Number(0.0)))?,
223
- chars.len(),
224
- );
225
- let index = if needle.is_empty() {
226
- position as f64
227
- } else {
228
- chars[position..]
229
- .windows(needle.len())
230
- .position(|window| window == needle.as_slice())
231
- .map(|offset| (position + offset) as f64)
232
- .unwrap_or(-1.0)
233
- };
234
- Ok(Value::Number(index))
235
- }
236
-
237
- pub(crate) fn call_string_last_index_of(
238
- &self,
239
- this_value: Value,
240
- args: &[Value],
241
- ) -> MustardResult<Value> {
242
- let value = self.string_receiver(this_value, "lastIndexOf")?;
243
- let chars = value.chars().collect::<Vec<_>>();
244
- let needle = self
245
- .to_string(args.first().cloned().unwrap_or(Value::Undefined))?
246
- .chars()
247
- .collect::<Vec<_>>();
248
- let position = match args.get(1) {
249
- Some(value) => clamp_index(self.to_integer(value.clone())?, chars.len()),
250
- None => chars.len(),
251
- };
252
- let index = if needle.is_empty() {
253
- position as f64
254
- } else if needle.len() > chars.len() {
255
- -1.0
256
- } else {
257
- let max_start = position.min(chars.len().saturating_sub(needle.len()));
258
- (0..=max_start)
259
- .rev()
260
- .find(|start| chars[*start..*start + needle.len()] == needle[..])
261
- .map(|index| index as f64)
262
- .unwrap_or(-1.0)
263
- };
264
- Ok(Value::Number(index))
265
- }
266
-
267
- pub(crate) fn call_string_char_at(
268
- &self,
269
- this_value: Value,
270
- args: &[Value],
271
- ) -> MustardResult<Value> {
272
- let value = self.string_receiver(this_value, "charAt")?;
273
- let chars = value.chars().collect::<Vec<_>>();
274
- let index = clamp_index(
275
- self.to_integer(args.first().cloned().unwrap_or(Value::Number(0.0)))?,
276
- chars.len(),
277
- );
278
- Ok(Value::String(
279
- chars
280
- .get(index)
281
- .map(|ch| ch.to_string())
282
- .unwrap_or_default(),
283
- ))
284
- }
285
-
286
- pub(crate) fn call_string_at(&self, this_value: Value, args: &[Value]) -> MustardResult<Value> {
287
- let value = self.string_receiver(this_value, "at")?;
288
- let chars = value.chars().collect::<Vec<_>>();
289
- let index = self.to_integer(args.first().cloned().unwrap_or(Value::Undefined))?;
290
- let index = if index < 0 {
291
- chars.len() as i64 + index
292
- } else {
293
- index
294
- };
295
- if index < 0 || index >= chars.len() as i64 {
296
- Ok(Value::Undefined)
297
- } else {
298
- Ok(Value::String(chars[index as usize].to_string()))
299
- }
300
- }
301
-
302
- pub(crate) fn call_string_slice(
303
- &self,
304
- this_value: Value,
305
- args: &[Value],
306
- ) -> MustardResult<Value> {
307
- let value = self.string_receiver(this_value, "slice")?;
308
- let chars = value.chars().collect::<Vec<_>>();
309
- let start = normalize_relative_bound(
310
- self.to_integer(args.first().cloned().unwrap_or(Value::Number(0.0)))?,
311
- chars.len(),
312
- );
313
- let end = normalize_relative_bound(
314
- match args.get(1) {
315
- Some(value) => self.to_integer(value.clone())?,
316
- None => chars.len() as i64,
317
- },
318
- chars.len(),
319
- );
320
- let end = end.max(start);
321
- Ok(Value::String(chars[start..end].iter().collect()))
322
- }
323
-
324
- pub(crate) fn call_string_substring(
325
- &self,
326
- this_value: Value,
327
- args: &[Value],
328
- ) -> MustardResult<Value> {
329
- let value = self.string_receiver(this_value, "substring")?;
330
- let chars = value.chars().collect::<Vec<_>>();
331
- let start = clamp_index(
332
- self.to_integer(args.first().cloned().unwrap_or(Value::Number(0.0)))?,
333
- chars.len(),
334
- );
335
- let end = match args.get(1) {
336
- Some(value) => clamp_index(self.to_integer(value.clone())?, chars.len()),
337
- None => chars.len(),
338
- };
339
- let (start, end) = if start <= end {
340
- (start, end)
341
- } else {
342
- (end, start)
343
- };
344
- Ok(Value::String(chars[start..end].iter().collect()))
345
- }
346
-
347
- pub(crate) fn call_string_to_lower_case(&self, this_value: Value) -> MustardResult<Value> {
348
- let value = self.string_receiver(this_value, "toLowerCase")?;
349
- Ok(Value::String(value.to_lowercase()))
350
- }
351
-
352
- pub(crate) fn call_string_to_upper_case(&self, this_value: Value) -> MustardResult<Value> {
353
- let value = self.string_receiver(this_value, "toUpperCase")?;
354
- Ok(Value::String(value.to_uppercase()))
355
- }
356
-
357
- pub(crate) fn call_string_repeat(
358
- &self,
359
- this_value: Value,
360
- args: &[Value],
361
- ) -> MustardResult<Value> {
362
- let value = self.string_receiver(this_value, "repeat")?;
363
- let count = self.to_number(args.first().cloned().unwrap_or(Value::Undefined))?;
364
- if !count.is_finite() || count < 0.0 {
365
- return Err(MustardError::runtime("RangeError: Invalid count value"));
366
- }
367
- let count = self.to_integer(Value::Number(count))? as usize;
368
- Ok(Value::String(value.repeat(count)))
369
- }
370
-
371
- pub(crate) fn call_string_concat(
372
- &self,
373
- this_value: Value,
374
- args: &[Value],
375
- ) -> MustardResult<Value> {
376
- let mut value = self.string_receiver(this_value, "concat")?;
377
- for arg in args {
378
- value.push_str(&self.to_string(arg.clone())?);
379
- }
380
- Ok(Value::String(value))
381
- }
382
-
383
- pub(crate) fn call_string_pad_start(
384
- &self,
385
- this_value: Value,
386
- args: &[Value],
387
- ) -> MustardResult<Value> {
388
- self.call_string_pad(this_value, args, true)
389
- }
390
-
391
- pub(crate) fn call_string_pad_end(
392
- &self,
393
- this_value: Value,
394
- args: &[Value],
395
- ) -> MustardResult<Value> {
396
- self.call_string_pad(this_value, args, false)
397
- }
398
-
399
- fn call_string_pad(
400
- &self,
401
- this_value: Value,
402
- args: &[Value],
403
- at_start: bool,
404
- ) -> MustardResult<Value> {
405
- let method = if at_start { "padStart" } else { "padEnd" };
406
- let value = self.string_receiver(this_value, method)?;
407
- let target_len = self.to_integer(args.first().cloned().unwrap_or(Value::Undefined))?;
408
- let target_len = usize::try_from(target_len.max(0)).unwrap_or(usize::MAX);
409
- let value_len = value.chars().count();
410
- if target_len <= value_len {
411
- return Ok(Value::String(value));
412
- }
413
- let fill = self.to_string(args.get(1).cloned().unwrap_or(Value::String(" ".into())))?;
414
- if fill.is_empty() {
415
- return Ok(Value::String(value));
416
- }
417
- let fill_chars = fill.chars().collect::<Vec<_>>();
418
- let pad_len = target_len - value_len;
419
- let padding = fill_chars
420
- .iter()
421
- .copied()
422
- .cycle()
423
- .take(pad_len)
424
- .collect::<String>();
425
- Ok(Value::String(if at_start {
426
- format!("{padding}{value}")
427
- } else {
428
- format!("{value}{padding}")
429
- }))
430
- }
431
-
432
- pub(crate) fn call_string_split(
433
- &mut self,
434
- this_value: Value,
435
- args: &[Value],
436
- ) -> MustardResult<Value> {
437
- let value = self.string_receiver(this_value, "split")?;
438
- let limit = match args.get(1) {
439
- Some(value) => {
440
- let limit = self.to_integer(value.clone())?;
441
- if limit <= 0 {
442
- 0
443
- } else {
444
- usize::try_from(limit).unwrap_or(usize::MAX)
445
- }
446
- }
447
- None => usize::MAX,
448
- };
449
- if limit == 0 {
450
- return Ok(Value::Array(
451
- self.insert_array(Vec::new(), IndexMap::new())?,
452
- ));
453
- }
454
- let pattern = match args.first() {
455
- None | Some(Value::Undefined) => None,
456
- Some(value) => Some(self.string_search_pattern(value.clone(), "split")?),
457
- };
458
- let elements = match pattern {
459
- None => vec![Value::String(value)],
460
- Some(StringSearchPattern::Literal(separator)) => {
461
- split_string_by_pattern(&value, Some(separator.as_str()), limit)
462
- .into_iter()
463
- .map(Value::String)
464
- .collect()
465
- }
466
- Some(StringSearchPattern::RegExp { regex, .. }) => {
467
- let matches = self.collect_regexp_matches_from_state(&regex, &value, true)?;
468
- let mut elements = Vec::new();
469
- let mut last_end = 0usize;
470
- for matched in matches {
471
- if elements.len() >= limit {
472
- break;
473
- }
474
- elements.push(Value::String(
475
- value[last_end..matched.start_byte].to_string(),
476
- ));
477
- if elements.len() >= limit {
478
- break;
479
- }
480
- for capture in matched.captures {
481
- elements.push(capture.map_or(Value::Undefined, Value::String));
482
- if elements.len() >= limit {
483
- break;
484
- }
485
- }
486
- last_end = matched.end_byte;
487
- }
488
- if elements.len() < limit {
489
- elements.push(Value::String(value[last_end..].to_string()));
490
- }
491
- elements
492
- }
493
- };
494
- Ok(Value::Array(self.insert_array(
495
- elements.into_iter().take(limit).collect(),
496
- IndexMap::new(),
497
- )?))
498
- }
499
-
500
- pub(crate) fn call_string_replace(
501
- &mut self,
502
- this_value: Value,
503
- args: &[Value],
504
- ) -> MustardResult<Value> {
505
- let value = self.string_receiver(this_value, "replace")?;
506
- let search = self
507
- .string_search_pattern(args.first().cloned().unwrap_or(Value::Undefined), "replace")?;
508
- let replacement = args.get(1).cloned().unwrap_or(Value::Undefined);
509
- match (search, replacement.clone()) {
510
- (StringSearchPattern::Literal(search), replacement) if is_callable(&replacement) => {
511
- let matched = if search.is_empty() {
512
- Some(RegExpMatchData {
513
- start_byte: 0,
514
- end_byte: 0,
515
- start_index: 0,
516
- end_index: 0,
517
- captures: Vec::new(),
518
- named_groups: IndexMap::new(),
519
- })
520
- } else {
521
- self.literal_match_data(&value, &search, 0)
522
- };
523
- if let Some(matched) = matched {
524
- let replacement =
525
- self.string_callback_replacement("replace", replacement, &value, &matched)?;
526
- let mut result = String::new();
527
- result.push_str(&value[..matched.start_byte]);
528
- result.push_str(&replacement);
529
- result.push_str(&value[matched.end_byte..]);
530
- Ok(Value::String(result))
531
- } else {
532
- Ok(Value::String(value))
533
- }
534
- }
535
- (StringSearchPattern::Literal(search), replacement) => Ok(Value::String(
536
- replace_first_string_match(&value, &search, &self.to_string(replacement)?),
537
- )),
538
- (StringSearchPattern::RegExp { regex, .. }, replacement)
539
- if is_callable(&replacement) =>
540
- {
541
- let all = regex.flags.contains('g');
542
- let matches = self.collect_regexp_matches_from_state(&regex, &value, all)?;
543
- if matches.is_empty() {
544
- return Ok(Value::String(value));
545
- }
546
- let mut result = String::new();
547
- let mut last_end = 0usize;
548
- for matched in &matches {
549
- result.push_str(&value[last_end..matched.start_byte]);
550
- result.push_str(&self.string_callback_replacement(
551
- "replace",
552
- replacement.clone(),
553
- &value,
554
- matched,
555
- )?);
556
- last_end = matched.end_byte;
557
- }
558
- result.push_str(&value[last_end..]);
559
- Ok(Value::String(result))
560
- }
561
- (StringSearchPattern::RegExp { regex, .. }, replacement) => {
562
- let all = regex.flags.contains('g');
563
- let matches = self.collect_regexp_matches_from_state(&regex, &value, all)?;
564
- if matches.is_empty() {
565
- return Ok(Value::String(value));
566
- }
567
- let replacement = self.to_string(replacement)?;
568
- let mut result = String::new();
569
- let mut last_end = 0usize;
570
- for matched in &matches {
571
- result.push_str(&value[last_end..matched.start_byte]);
572
- result.push_str(&expand_regexp_replacement_template(
573
- &replacement,
574
- &value,
575
- matched,
576
- ));
577
- last_end = matched.end_byte;
578
- }
579
- result.push_str(&value[last_end..]);
580
- Ok(Value::String(result))
581
- }
582
- }
583
- }
584
-
585
- pub(crate) fn call_string_replace_all(
586
- &mut self,
587
- this_value: Value,
588
- args: &[Value],
589
- ) -> MustardResult<Value> {
590
- let value = self.string_receiver(this_value, "replaceAll")?;
591
- let search = self.string_search_pattern(
592
- args.first().cloned().unwrap_or(Value::Undefined),
593
- "replaceAll",
594
- )?;
595
- let replacement = args.get(1).cloned().unwrap_or(Value::Undefined);
596
- match search {
597
- StringSearchPattern::Literal(search) if is_callable(&replacement) => {
598
- let mut matches = Vec::new();
599
- if search.is_empty() {
600
- let total = value.chars().count();
601
- for index in 0..=total {
602
- let byte = char_index_to_byte_index(&value, index);
603
- matches.push(RegExpMatchData {
604
- start_byte: byte,
605
- end_byte: byte,
606
- start_index: index,
607
- end_index: index,
608
- captures: Vec::new(),
609
- named_groups: IndexMap::new(),
610
- });
611
- }
612
- } else {
613
- let mut start_index = 0usize;
614
- while let Some(matched) = self.literal_match_data(&value, &search, start_index)
615
- {
616
- start_index = matched.end_index;
617
- matches.push(matched);
618
- }
619
- }
620
- let mut result = String::new();
621
- let mut last_end = 0usize;
622
- for matched in &matches {
623
- result.push_str(&value[last_end..matched.start_byte]);
624
- result.push_str(&self.string_callback_replacement(
625
- "replaceAll",
626
- replacement.clone(),
627
- &value,
628
- matched,
629
- )?);
630
- last_end = matched.end_byte;
631
- }
632
- result.push_str(&value[last_end..]);
633
- Ok(Value::String(result))
634
- }
635
- StringSearchPattern::Literal(search) => Ok(Value::String(replace_all_string_matches(
636
- &value,
637
- &search,
638
- &self.to_string(replacement)?,
639
- ))),
640
- StringSearchPattern::RegExp { regex, .. } => {
641
- if !regex.flags.contains('g') {
642
- return Err(MustardError::runtime(
643
- "TypeError: String.prototype.replaceAll requires a global RegExp",
644
- ));
645
- }
646
- let matches = self.collect_regexp_matches_from_state(&regex, &value, true)?;
647
- if matches.is_empty() {
648
- return Ok(Value::String(value));
649
- }
650
- let mut result = String::new();
651
- let mut last_end = 0usize;
652
- if is_callable(&replacement) {
653
- for matched in &matches {
654
- result.push_str(&value[last_end..matched.start_byte]);
655
- result.push_str(&self.string_callback_replacement(
656
- "replaceAll",
657
- replacement.clone(),
658
- &value,
659
- matched,
660
- )?);
661
- last_end = matched.end_byte;
662
- }
663
- } else {
664
- let replacement = self.to_string(replacement)?;
665
- for matched in &matches {
666
- result.push_str(&value[last_end..matched.start_byte]);
667
- result.push_str(&expand_regexp_replacement_template(
668
- &replacement,
669
- &value,
670
- matched,
671
- ));
672
- last_end = matched.end_byte;
673
- }
674
- }
675
- result.push_str(&value[last_end..]);
676
- Ok(Value::String(result))
677
- }
678
- }
679
- }
680
-
681
- pub(crate) fn call_string_search(
682
- &self,
683
- this_value: Value,
684
- args: &[Value],
685
- ) -> MustardResult<Value> {
686
- let value = self.string_receiver(this_value, "search")?;
687
- let needle = self
688
- .string_search_pattern(args.first().cloned().unwrap_or(Value::Undefined), "search")?;
689
- Ok(Value::Number(match needle {
690
- StringSearchPattern::Literal(needle) => find_string_pattern(&value, &needle, 0)
691
- .map(|index| index as f64)
692
- .unwrap_or(-1.0),
693
- StringSearchPattern::RegExp { regex, .. } => self
694
- .first_regexp_match_from_state(&regex, &value, 0)?
695
- .map(|matched| matched.start_index as f64)
696
- .unwrap_or(-1.0),
697
- }))
698
- }
699
-
700
- pub(crate) fn call_string_match(
701
- &mut self,
702
- this_value: Value,
703
- args: &[Value],
704
- ) -> MustardResult<Value> {
705
- let value = self.string_receiver(this_value, "match")?;
706
- let needle =
707
- self.string_search_pattern(args.first().cloned().unwrap_or(Value::Undefined), "match")?;
708
- match needle {
709
- StringSearchPattern::Literal(needle) => {
710
- let Some(index) = find_string_pattern(&value, &needle, 0) else {
711
- return Ok(Value::Null);
712
- };
713
- let match_array = self.insert_array(
714
- vec![Value::String(needle.clone())],
715
- IndexMap::from([
716
- ("index".to_string(), Value::Number(index as f64)),
717
- ("input".to_string(), Value::String(value)),
718
- ]),
719
- )?;
720
- Ok(Value::Array(match_array))
721
- }
722
- StringSearchPattern::RegExp { object, regex } => {
723
- if regex.flags.contains('g') {
724
- self.regexp_object_mut(object)?.last_index = 0;
725
- self.refresh_object_accounting(object)?;
726
- let matches = self.collect_regexp_matches_from_state(&regex, &value, true)?;
727
- if matches.is_empty() {
728
- return Ok(Value::Null);
729
- }
730
- let array = self.insert_array(
731
- matches
732
- .into_iter()
733
- .map(|matched| {
734
- Value::String(
735
- value[matched.start_byte..matched.end_byte].to_string(),
736
- )
737
- })
738
- .collect(),
739
- IndexMap::new(),
740
- )?;
741
- Ok(Value::Array(array))
742
- } else {
743
- let Some(matched) = self.first_regexp_match_from_state(&regex, &value, 0)?
744
- else {
745
- return Ok(Value::Null);
746
- };
747
- self.regexp_match_array_value(&value, &matched)
748
- }
749
- }
750
- }
751
- }
752
-
753
- pub(crate) fn call_string_match_all(
754
- &mut self,
755
- this_value: Value,
756
- args: &[Value],
757
- ) -> MustardResult<Value> {
758
- let value = self.string_receiver(this_value, "matchAll")?;
759
- let needle = self.string_search_pattern(
760
- args.first().cloned().unwrap_or(Value::Undefined),
761
- "matchAll",
762
- )?;
763
- let matches = match needle {
764
- StringSearchPattern::Literal(needle) => collect_literal_matches(&value, &needle),
765
- StringSearchPattern::RegExp { object, regex } => {
766
- if !regex.flags.contains('g') {
767
- return Err(MustardError::runtime(
768
- "TypeError: String.prototype.matchAll requires a global RegExp",
769
- ));
770
- }
771
- self.regexp_object_mut(object)?.last_index = 0;
772
- self.refresh_object_accounting(object)?;
773
- self.collect_regexp_matches_from_state(&regex, &value, true)?
774
- }
775
- };
776
- let mut values = Vec::with_capacity(matches.len());
777
- for matched in matches {
778
- values.push(self.regexp_match_array_value(&value, &matched)?);
779
- }
780
- let array = self.insert_array(values, IndexMap::new())?;
781
- self.call_array_values(Value::Array(array))
782
- }
783
-
784
- fn literal_match_data(
785
- &self,
786
- value: &str,
787
- needle: &str,
788
- start_index: usize,
789
- ) -> Option<RegExpMatchData> {
790
- let start_index = find_string_pattern(value, needle, start_index)?;
791
- let start_byte = char_index_to_byte_index(value, start_index);
792
- let end_index = start_index + needle.chars().count();
793
- let end_byte = char_index_to_byte_index(value, end_index);
794
- Some(RegExpMatchData {
795
- start_byte,
796
- end_byte,
797
- start_index,
798
- end_index,
799
- captures: Vec::new(),
800
- named_groups: IndexMap::new(),
801
- })
802
- }
803
- }