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,335 +0,0 @@
1
- use super::*;
2
-
3
- pub(crate) struct PromiseSetupPolicy<'a> {
4
- pub(crate) non_callable_message: &'a str,
5
- pub(crate) host_suspension_message: &'a str,
6
- pub(crate) async_message: &'a str,
7
- }
8
-
9
- impl Runtime {
10
- fn reject_promise_from_setup_error(
11
- &mut self,
12
- target: PromiseKey,
13
- error: MustardError,
14
- ) -> MustardResult<()> {
15
- match error {
16
- MustardError::Message {
17
- kind: DiagnosticKind::Runtime,
18
- message,
19
- ..
20
- } if message == super::super::INTERNAL_CALLBACK_THROW_MARKER => {
21
- let rejection = self.pending_internal_exception.take().ok_or_else(|| {
22
- MustardError::runtime("missing internal callback exception state")
23
- })?;
24
- self.reject_promise(target, rejection)
25
- }
26
- other => self.reject_promise_from_error(target, other),
27
- }
28
- }
29
-
30
- pub(crate) fn promise_settler(&self, target: PromiseKey, rejected: bool) -> Value {
31
- Value::BuiltinFunction(if rejected {
32
- BuiltinFunction::PromiseRejectFunction(target)
33
- } else {
34
- BuiltinFunction::PromiseResolveFunction(target)
35
- })
36
- }
37
-
38
- pub(crate) fn promise_thenable_handler(&self, value: &Value) -> MustardResult<Option<Value>> {
39
- match value {
40
- Value::Object(_) | Value::Array(_) => {
41
- let then =
42
- self.get_property(value.clone(), Value::String("then".to_string()), false)?;
43
- if is_callable(&then) {
44
- Ok(Some(then))
45
- } else {
46
- Ok(None)
47
- }
48
- }
49
- _ => Ok(None),
50
- }
51
- }
52
-
53
- pub(crate) fn call_promise_setup_callback(
54
- &mut self,
55
- target: PromiseKey,
56
- callback: Value,
57
- this_arg: Value,
58
- args: &[Value],
59
- policy: PromiseSetupPolicy<'_>,
60
- ) -> MustardResult<()> {
61
- let result = match callback {
62
- Value::Closure(closure) => {
63
- let closure = self
64
- .closures
65
- .get(closure)
66
- .cloned()
67
- .ok_or_else(|| MustardError::runtime("closure not found"))?;
68
- let function = self
69
- .program
70
- .functions
71
- .get(closure.function_id)
72
- .ok_or_else(|| MustardError::runtime("function not found"))?;
73
- if function.is_async {
74
- Err(MustardError::runtime(policy.async_message))
75
- } else {
76
- let env = self.new_env(Some(closure.env))?;
77
- let outcome = self.insert_promise(PromiseState::Pending)?;
78
- let base_depth = self.frames.len();
79
- self.push_frame(closure.function_id, env, args, this_arg, Some(outcome))?;
80
- if let Err(error) =
81
- self.run_until_frame_depth(base_depth, policy.host_suspension_message)
82
- {
83
- self.frames.truncate(base_depth);
84
- self.suspended_host_call = None;
85
- self.pending_resume_behavior = ResumeBehavior::Value;
86
- return self.reject_promise_from_setup_error(target, error);
87
- }
88
- match self.promise_outcome(outcome)? {
89
- Some(PromiseOutcome::Rejected(rejection)) => {
90
- self.reject_promise(target, rejection)
91
- }
92
- Some(PromiseOutcome::Fulfilled(_)) | None => Ok(()),
93
- }
94
- }
95
- }
96
- Value::BuiltinFunction(function) => {
97
- self.call_builtin(function, this_arg, args).map(|_| ())
98
- }
99
- Value::HostFunction(capability) => {
100
- match self.call_callable(Value::HostFunction(capability), this_arg, args) {
101
- Ok(RunState::Completed(_) | RunState::StartedAsync(_)) => Ok(()),
102
- Ok(RunState::PushedFrame) => Err(MustardError::runtime(
103
- "promise setup callback unexpectedly pushed a frame",
104
- )),
105
- Ok(RunState::Suspended { .. }) => {
106
- Err(MustardError::runtime(policy.host_suspension_message))
107
- }
108
- Err(error) => Err(error),
109
- }
110
- }
111
- _ => Err(MustardError::runtime(policy.non_callable_message)),
112
- };
113
- match result {
114
- Ok(()) => Ok(()),
115
- Err(error) => self.reject_promise_from_setup_error(target, error),
116
- }
117
- }
118
-
119
- pub(crate) fn construct_promise(&mut self, args: &[Value]) -> MustardResult<Value> {
120
- let executor = args.first().cloned().unwrap_or(Value::Undefined);
121
- if !is_callable(&executor) {
122
- return Err(MustardError::runtime(
123
- "TypeError: Promise constructor expects a callable executor",
124
- ));
125
- }
126
- let target = self.insert_promise(PromiseState::Pending)?;
127
- let resolve = self.promise_settler(target, false);
128
- let reject = self.promise_settler(target, true);
129
- self.call_promise_setup_callback(
130
- target,
131
- executor,
132
- Value::Undefined,
133
- &[resolve, reject],
134
- PromiseSetupPolicy {
135
- non_callable_message: "TypeError: Promise constructor expects a callable executor",
136
- host_suspension_message:
137
- "TypeError: Promise executors do not support synchronous host suspensions",
138
- async_message:
139
- "TypeError: Promise executors must be synchronous in the supported surface",
140
- },
141
- )?;
142
- Ok(Value::Promise(target))
143
- }
144
-
145
- fn promise_receiver(&self, value: Value, method: &str) -> MustardResult<PromiseKey> {
146
- match value {
147
- Value::Promise(key) => Ok(key),
148
- _ => Err(MustardError::runtime(format!(
149
- "TypeError: Promise.prototype.{method} called on incompatible receiver",
150
- ))),
151
- }
152
- }
153
-
154
- fn collect_iterable_values(&mut self, iterable: Value) -> MustardResult<Vec<Value>> {
155
- let iterator = self.create_iterator(iterable)?;
156
- let mut values = Vec::new();
157
- loop {
158
- let (value, done) = self.iterator_next(iterator.clone())?;
159
- if done {
160
- break;
161
- }
162
- values.push(value);
163
- }
164
- Ok(values)
165
- }
166
-
167
- pub(crate) fn call_promise_then(
168
- &mut self,
169
- this_value: Value,
170
- args: &[Value],
171
- ) -> MustardResult<Value> {
172
- let promise = self.promise_receiver(this_value, "then")?;
173
- let target = self.insert_promise(PromiseState::Pending)?;
174
- let on_fulfilled = args.first().cloned().filter(is_callable);
175
- let on_rejected = args.get(1).cloned().filter(is_callable);
176
- self.attach_promise_reaction(
177
- promise,
178
- PromiseReaction::Then {
179
- target,
180
- on_fulfilled,
181
- on_rejected,
182
- },
183
- )?;
184
- Ok(Value::Promise(target))
185
- }
186
-
187
- pub(crate) fn call_promise_catch(
188
- &mut self,
189
- this_value: Value,
190
- args: &[Value],
191
- ) -> MustardResult<Value> {
192
- let promise = self.promise_receiver(this_value, "catch")?;
193
- let target = self.insert_promise(PromiseState::Pending)?;
194
- let on_rejected = args.first().cloned().filter(is_callable);
195
- self.attach_promise_reaction(
196
- promise,
197
- PromiseReaction::Then {
198
- target,
199
- on_fulfilled: None,
200
- on_rejected,
201
- },
202
- )?;
203
- Ok(Value::Promise(target))
204
- }
205
-
206
- pub(crate) fn call_promise_finally(
207
- &mut self,
208
- this_value: Value,
209
- args: &[Value],
210
- ) -> MustardResult<Value> {
211
- let promise = self.promise_receiver(this_value, "finally")?;
212
- let target = self.insert_promise(PromiseState::Pending)?;
213
- let callback = args.first().cloned().filter(is_callable);
214
- self.attach_promise_reaction(promise, PromiseReaction::Finally { target, callback })?;
215
- Ok(Value::Promise(target))
216
- }
217
-
218
- pub(crate) fn call_promise_all(&mut self, args: &[Value]) -> MustardResult<Value> {
219
- let target = self.insert_promise(PromiseState::Pending)?;
220
- let values =
221
- self.collect_iterable_values(args.first().cloned().unwrap_or(Value::Undefined))?;
222
- if values.is_empty() {
223
- let array = Value::Array(self.insert_array(Vec::new(), IndexMap::new())?);
224
- self.resolve_promise(target, array)?;
225
- return Ok(Value::Promise(target));
226
- }
227
- self.promises
228
- .get_mut(target)
229
- .ok_or_else(|| MustardError::runtime("promise missing"))?
230
- .driver = Some(PromiseDriver::All {
231
- remaining: values.len(),
232
- values: vec![None; values.len()],
233
- });
234
- self.refresh_promise_accounting(target)?;
235
- for (index, value) in values.into_iter().enumerate() {
236
- let promise = self.coerce_to_promise(value)?;
237
- self.attach_promise_reaction(
238
- promise,
239
- PromiseReaction::Combinator {
240
- target,
241
- index,
242
- kind: PromiseCombinatorKind::All,
243
- },
244
- )?;
245
- }
246
- Ok(Value::Promise(target))
247
- }
248
-
249
- pub(crate) fn call_promise_race(&mut self, args: &[Value]) -> MustardResult<Value> {
250
- let target = self.insert_promise(PromiseState::Pending)?;
251
- for value in
252
- self.collect_iterable_values(args.first().cloned().unwrap_or(Value::Undefined))?
253
- {
254
- let promise = self.coerce_to_promise(value)?;
255
- self.attach_promise_reaction(
256
- promise,
257
- PromiseReaction::Combinator {
258
- target,
259
- index: 0,
260
- kind: PromiseCombinatorKind::Race,
261
- },
262
- )?;
263
- }
264
- Ok(Value::Promise(target))
265
- }
266
-
267
- pub(crate) fn call_promise_any(&mut self, args: &[Value]) -> MustardResult<Value> {
268
- let target = self.insert_promise(PromiseState::Pending)?;
269
- let values =
270
- self.collect_iterable_values(args.first().cloned().unwrap_or(Value::Undefined))?;
271
- if values.is_empty() {
272
- let error = self.make_aggregate_error(Vec::new())?;
273
- self.reject_promise(
274
- target,
275
- PromiseRejection {
276
- value: error,
277
- span: None,
278
- traceback: self.traceback_snapshots(),
279
- },
280
- )?;
281
- return Ok(Value::Promise(target));
282
- }
283
- self.promises
284
- .get_mut(target)
285
- .ok_or_else(|| MustardError::runtime("promise missing"))?
286
- .driver = Some(PromiseDriver::Any {
287
- remaining: values.len(),
288
- reasons: vec![None; values.len()],
289
- });
290
- self.refresh_promise_accounting(target)?;
291
- for (index, value) in values.into_iter().enumerate() {
292
- let promise = self.coerce_to_promise(value)?;
293
- self.attach_promise_reaction(
294
- promise,
295
- PromiseReaction::Combinator {
296
- target,
297
- index,
298
- kind: PromiseCombinatorKind::Any,
299
- },
300
- )?;
301
- }
302
- Ok(Value::Promise(target))
303
- }
304
-
305
- pub(crate) fn call_promise_all_settled(&mut self, args: &[Value]) -> MustardResult<Value> {
306
- let target = self.insert_promise(PromiseState::Pending)?;
307
- let values =
308
- self.collect_iterable_values(args.first().cloned().unwrap_or(Value::Undefined))?;
309
- if values.is_empty() {
310
- let array = Value::Array(self.insert_array(Vec::new(), IndexMap::new())?);
311
- self.resolve_promise(target, array)?;
312
- return Ok(Value::Promise(target));
313
- }
314
- self.promises
315
- .get_mut(target)
316
- .ok_or_else(|| MustardError::runtime("promise missing"))?
317
- .driver = Some(PromiseDriver::AllSettled {
318
- remaining: values.len(),
319
- results: vec![None; values.len()],
320
- });
321
- self.refresh_promise_accounting(target)?;
322
- for (index, value) in values.into_iter().enumerate() {
323
- let promise = self.coerce_to_promise(value)?;
324
- self.attach_promise_reaction(
325
- promise,
326
- PromiseReaction::Combinator {
327
- target,
328
- index,
329
- kind: PromiseCombinatorKind::AllSettled,
330
- },
331
- )?;
332
- }
333
- Ok(Value::Promise(target))
334
- }
335
- }
@@ -1,356 +0,0 @@
1
- use super::*;
2
- use regex::{Captures, Regex, RegexBuilder};
3
-
4
- struct CompiledRegExp {
5
- flags: RegExpFlagsState,
6
- regex: Regex,
7
- }
8
-
9
- impl Runtime {
10
- pub(crate) fn construct_regexp(&mut self, args: &[Value]) -> MustardResult<Value> {
11
- let pattern_arg = args.first().cloned().unwrap_or(Value::Undefined);
12
- let flags_arg = args.get(1).cloned().unwrap_or(Value::Undefined);
13
- let (pattern, flags) = match pattern_arg {
14
- Value::Object(object) if self.is_regexp_object(object) => {
15
- let regex = self.regexp_object(object)?.clone();
16
- if matches!(flags_arg, Value::Undefined) {
17
- (regex.pattern, regex.flags)
18
- } else {
19
- (regex.pattern, self.to_string(flags_arg)?)
20
- }
21
- }
22
- value => {
23
- let pattern = if matches!(value, Value::Undefined) {
24
- String::new()
25
- } else {
26
- self.to_string(value)?
27
- };
28
- let flags = if matches!(flags_arg, Value::Undefined) {
29
- String::new()
30
- } else {
31
- self.to_string(flags_arg)?
32
- };
33
- (pattern, flags)
34
- }
35
- };
36
- self.make_regexp_value(pattern, flags)
37
- }
38
-
39
- fn regexp_receiver(&self, value: Value, method: &str) -> MustardResult<ObjectKey> {
40
- match value {
41
- Value::Object(key) if self.is_regexp_object(key) => Ok(key),
42
- _ => Err(MustardError::runtime(format!(
43
- "TypeError: RegExp.prototype.{method} called on incompatible receiver",
44
- ))),
45
- }
46
- }
47
-
48
- pub(crate) fn regexp_match_array_value(
49
- &mut self,
50
- input: &str,
51
- matched: &RegExpMatchData,
52
- ) -> MustardResult<Value> {
53
- let mut groups = IndexMap::new();
54
- for (name, value) in &matched.named_groups {
55
- groups.insert(
56
- name.clone(),
57
- value.clone().map_or(Value::Undefined, Value::String),
58
- );
59
- }
60
- let mut properties = IndexMap::from([
61
- (
62
- "index".to_string(),
63
- Value::Number(matched.start_index as f64),
64
- ),
65
- ("input".to_string(), Value::String(input.to_string())),
66
- ]);
67
- if !groups.is_empty() {
68
- properties.insert(
69
- "groups".to_string(),
70
- Value::Object(self.insert_object(groups, ObjectKind::Plain)?),
71
- );
72
- }
73
- let mut elements = Vec::with_capacity(matched.captures.len() + 1);
74
- elements.push(Value::String(
75
- input[matched.start_byte..matched.end_byte].to_string(),
76
- ));
77
- elements.extend(
78
- matched
79
- .captures
80
- .iter()
81
- .map(|value| value.clone().map_or(Value::Undefined, Value::String)),
82
- );
83
- Ok(Value::Array(self.insert_array(elements, properties)?))
84
- }
85
-
86
- pub(crate) fn call_regexp_exec(
87
- &mut self,
88
- this_value: Value,
89
- args: &[Value],
90
- ) -> MustardResult<Value> {
91
- let regex = self.regexp_receiver(this_value, "exec")?;
92
- let input = self.to_string(args.first().cloned().unwrap_or(Value::Undefined))?;
93
- let Some(matched) = self.first_regexp_match(regex, &input)? else {
94
- return Ok(Value::Null);
95
- };
96
- self.regexp_match_array_value(&input, &matched)
97
- }
98
-
99
- pub(crate) fn call_regexp_test(
100
- &mut self,
101
- this_value: Value,
102
- args: &[Value],
103
- ) -> MustardResult<Value> {
104
- let regex = self.regexp_receiver(this_value, "test")?;
105
- let input = self.to_string(args.first().cloned().unwrap_or(Value::Undefined))?;
106
- Ok(Value::Bool(
107
- self.first_regexp_match(regex, &input)?.is_some(),
108
- ))
109
- }
110
-
111
- pub(crate) fn make_regexp_value(
112
- &mut self,
113
- pattern: String,
114
- flags: String,
115
- ) -> MustardResult<Value> {
116
- self.validate_regexp_flags(&flags)?;
117
- self.compile_regexp(&pattern, &flags)?;
118
- let object = self.insert_object(
119
- IndexMap::new(),
120
- ObjectKind::RegExp(RegExpObject {
121
- pattern,
122
- flags,
123
- last_index: 0,
124
- }),
125
- )?;
126
- Ok(Value::Object(object))
127
- }
128
-
129
- fn validate_regexp_flags(&self, flags: &str) -> MustardResult<RegExpFlagsState> {
130
- let mut state = RegExpFlagsState {
131
- global: false,
132
- ignore_case: false,
133
- multiline: false,
134
- dot_all: false,
135
- unicode: false,
136
- sticky: false,
137
- };
138
- let mut seen = HashSet::new();
139
- for flag in flags.chars() {
140
- if !seen.insert(flag) {
141
- return Err(MustardError::runtime(format!(
142
- "SyntaxError: duplicate regular expression flag `{flag}`",
143
- )));
144
- }
145
- match flag {
146
- 'g' => state.global = true,
147
- 'i' => state.ignore_case = true,
148
- 'm' => state.multiline = true,
149
- 's' => state.dot_all = true,
150
- 'u' => state.unicode = true,
151
- 'y' => state.sticky = true,
152
- _ => {
153
- return Err(MustardError::runtime(format!(
154
- "SyntaxError: unsupported regular expression flag `{flag}`",
155
- )));
156
- }
157
- }
158
- }
159
- Ok(state)
160
- }
161
-
162
- fn compile_regexp(&self, pattern: &str, flags: &str) -> MustardResult<CompiledRegExp> {
163
- let flags = self.validate_regexp_flags(flags)?;
164
- let mut builder = RegexBuilder::new(pattern);
165
- builder.case_insensitive(flags.ignore_case);
166
- builder.multi_line(flags.multiline);
167
- builder.dot_matches_new_line(flags.dot_all);
168
- // The Rust engine operates over UTF-8 strings, so keep Unicode mode
169
- // enabled even without the JS `u` flag. This preserves the supported
170
- // text-regexp subset while avoiding non-UTF-8 byte classes.
171
- builder.unicode(true);
172
- let regex = builder.build().map_err(|error| {
173
- MustardError::runtime(format!("SyntaxError: invalid regular expression: {error}"))
174
- })?;
175
- Ok(CompiledRegExp { flags, regex })
176
- }
177
-
178
- pub(crate) fn is_regexp_object(&self, key: ObjectKey) -> bool {
179
- self.objects
180
- .get(key)
181
- .is_some_and(|object| matches!(object.kind, ObjectKind::RegExp(_)))
182
- }
183
-
184
- pub(crate) fn is_date_object(&self, key: ObjectKey) -> bool {
185
- self.objects
186
- .get(key)
187
- .is_some_and(|object| matches!(object.kind, ObjectKind::Date(_)))
188
- }
189
-
190
- pub(crate) fn date_object(&self, key: ObjectKey) -> MustardResult<&DateObject> {
191
- match &self
192
- .objects
193
- .get(key)
194
- .ok_or_else(|| MustardError::runtime("object missing"))?
195
- .kind
196
- {
197
- ObjectKind::Date(date) => Ok(date),
198
- _ => Err(MustardError::runtime("date missing")),
199
- }
200
- }
201
-
202
- pub(crate) fn regexp_object(&self, key: ObjectKey) -> MustardResult<&RegExpObject> {
203
- match &self
204
- .objects
205
- .get(key)
206
- .ok_or_else(|| MustardError::runtime("object missing"))?
207
- .kind
208
- {
209
- ObjectKind::RegExp(regex) => Ok(regex),
210
- _ => Err(MustardError::runtime("regexp missing")),
211
- }
212
- }
213
-
214
- pub(crate) fn regexp_object_mut(&mut self, key: ObjectKey) -> MustardResult<&mut RegExpObject> {
215
- match &mut self
216
- .objects
217
- .get_mut(key)
218
- .ok_or_else(|| MustardError::runtime("object missing"))?
219
- .kind
220
- {
221
- ObjectKind::RegExp(regex) => Ok(regex),
222
- _ => Err(MustardError::runtime("regexp missing")),
223
- }
224
- }
225
-
226
- fn regexp_match_data_from_captures(
227
- &self,
228
- compiled: &CompiledRegExp,
229
- text: &str,
230
- captures: &Captures<'_>,
231
- ) -> MustardResult<RegExpMatchData> {
232
- let matched = captures
233
- .get(0)
234
- .ok_or_else(|| MustardError::runtime("regex match missing full capture"))?;
235
- let named_groups = compiled
236
- .regex
237
- .capture_names()
238
- .enumerate()
239
- .skip(1)
240
- .filter_map(|(index, name)| {
241
- name.map(|name| {
242
- (
243
- name.to_string(),
244
- captures
245
- .get(index)
246
- .map(|capture| capture.as_str().to_string()),
247
- )
248
- })
249
- })
250
- .collect::<IndexMap<_, _>>();
251
- Ok(RegExpMatchData {
252
- start_byte: matched.start(),
253
- end_byte: matched.end(),
254
- start_index: byte_index_to_char_index(text, matched.start()),
255
- end_index: byte_index_to_char_index(text, matched.end()),
256
- captures: (1..captures.len())
257
- .map(|index| {
258
- captures
259
- .get(index)
260
- .map(|capture| capture.as_str().to_string())
261
- })
262
- .collect(),
263
- named_groups,
264
- })
265
- }
266
-
267
- fn first_regexp_match_with_compiled(
268
- &self,
269
- compiled: &CompiledRegExp,
270
- text: &str,
271
- start_index: usize,
272
- ) -> MustardResult<Option<RegExpMatchData>> {
273
- self.check_cancellation()?;
274
- let start_byte = char_index_to_byte_index(text, start_index);
275
- let Some(captures) = compiled.regex.captures_at(text, start_byte) else {
276
- return Ok(None);
277
- };
278
- let matched = captures
279
- .get(0)
280
- .ok_or_else(|| MustardError::runtime("regex match missing full capture"))?;
281
- if compiled.flags.sticky && matched.start() != start_byte {
282
- return Ok(None);
283
- }
284
- self.regexp_match_data_from_captures(compiled, text, &captures)
285
- .map(Some)
286
- }
287
-
288
- pub(crate) fn first_regexp_match_from_state(
289
- &self,
290
- regex: &RegExpObject,
291
- text: &str,
292
- start_index: usize,
293
- ) -> MustardResult<Option<RegExpMatchData>> {
294
- let compiled = self.compile_regexp(&regex.pattern, &regex.flags)?;
295
- self.first_regexp_match_with_compiled(&compiled, text, start_index)
296
- }
297
-
298
- fn first_regexp_match(
299
- &mut self,
300
- regex_key: ObjectKey,
301
- text: &str,
302
- ) -> MustardResult<Option<RegExpMatchData>> {
303
- let regex = self.regexp_object(regex_key)?.clone();
304
- let flags = self.validate_regexp_flags(&regex.flags)?;
305
- let start_index = if flags.global || flags.sticky {
306
- regex.last_index
307
- } else {
308
- 0
309
- };
310
- let matched = self.first_regexp_match_from_state(&regex, text, start_index)?;
311
- if flags.global || flags.sticky {
312
- let next_index = matched.as_ref().map_or(0, |matched| {
313
- if matched.start_byte == matched.end_byte {
314
- advance_char_index(text, matched.start_index)
315
- } else {
316
- matched.end_index
317
- }
318
- });
319
- self.regexp_object_mut(regex_key)?.last_index = next_index;
320
- self.refresh_object_accounting(regex_key)?;
321
- }
322
- Ok(matched)
323
- }
324
-
325
- pub(crate) fn collect_regexp_matches_from_state(
326
- &self,
327
- regex: &RegExpObject,
328
- text: &str,
329
- all: bool,
330
- ) -> MustardResult<Vec<RegExpMatchData>> {
331
- let compiled = self.compile_regexp(&regex.pattern, &regex.flags)?;
332
- let mut matches = Vec::new();
333
- let mut start_index = 0usize;
334
- loop {
335
- let Some(matched) =
336
- self.first_regexp_match_with_compiled(&compiled, text, start_index)?
337
- else {
338
- break;
339
- };
340
- let next_index = if matched.start_byte == matched.end_byte {
341
- advance_char_index(text, matched.start_index)
342
- } else {
343
- matched.end_index
344
- };
345
- matches.push(matched);
346
- if !all {
347
- break;
348
- }
349
- if next_index < start_index {
350
- break;
351
- }
352
- start_index = next_index;
353
- }
354
- Ok(matches)
355
- }
356
- }