mustardscript 0.1.0

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 (99) hide show
  1. package/Cargo.lock +1579 -0
  2. package/Cargo.toml +40 -0
  3. package/LICENSE +201 -0
  4. package/README.md +828 -0
  5. package/SECURITY.md +34 -0
  6. package/crates/mustard/Cargo.toml +31 -0
  7. package/crates/mustard/src/cancellation.rs +28 -0
  8. package/crates/mustard/src/diagnostic.rs +145 -0
  9. package/crates/mustard/src/ir.rs +435 -0
  10. package/crates/mustard/src/lib.rs +21 -0
  11. package/crates/mustard/src/limits.rs +22 -0
  12. package/crates/mustard/src/parser/expressions.rs +723 -0
  13. package/crates/mustard/src/parser/mod.rs +115 -0
  14. package/crates/mustard/src/parser/operators.rs +105 -0
  15. package/crates/mustard/src/parser/patterns.rs +123 -0
  16. package/crates/mustard/src/parser/scope.rs +107 -0
  17. package/crates/mustard/src/parser/statements.rs +298 -0
  18. package/crates/mustard/src/parser/tests/acceptance.rs +339 -0
  19. package/crates/mustard/src/parser/tests/mod.rs +2 -0
  20. package/crates/mustard/src/parser/tests/rejections.rs +107 -0
  21. package/crates/mustard/src/runtime/accounting.rs +613 -0
  22. package/crates/mustard/src/runtime/api.rs +192 -0
  23. package/crates/mustard/src/runtime/async_runtime/mod.rs +5 -0
  24. package/crates/mustard/src/runtime/async_runtime/promises.rs +246 -0
  25. package/crates/mustard/src/runtime/async_runtime/reactions.rs +400 -0
  26. package/crates/mustard/src/runtime/async_runtime/scheduler.rs +224 -0
  27. package/crates/mustard/src/runtime/builtins/arrays.rs +1205 -0
  28. package/crates/mustard/src/runtime/builtins/collections.rs +573 -0
  29. package/crates/mustard/src/runtime/builtins/install.rs +501 -0
  30. package/crates/mustard/src/runtime/builtins/intl.rs +553 -0
  31. package/crates/mustard/src/runtime/builtins/mod.rs +25 -0
  32. package/crates/mustard/src/runtime/builtins/objects.rs +405 -0
  33. package/crates/mustard/src/runtime/builtins/primitives.rs +859 -0
  34. package/crates/mustard/src/runtime/builtins/promises.rs +335 -0
  35. package/crates/mustard/src/runtime/builtins/regexp.rs +356 -0
  36. package/crates/mustard/src/runtime/builtins/strings.rs +803 -0
  37. package/crates/mustard/src/runtime/builtins/support.rs +561 -0
  38. package/crates/mustard/src/runtime/bytecode.rs +123 -0
  39. package/crates/mustard/src/runtime/compiler/assignments.rs +690 -0
  40. package/crates/mustard/src/runtime/compiler/bindings.rs +92 -0
  41. package/crates/mustard/src/runtime/compiler/context.rs +46 -0
  42. package/crates/mustard/src/runtime/compiler/control.rs +342 -0
  43. package/crates/mustard/src/runtime/compiler/expressions.rs +372 -0
  44. package/crates/mustard/src/runtime/compiler/mod.rs +173 -0
  45. package/crates/mustard/src/runtime/compiler/statements.rs +459 -0
  46. package/crates/mustard/src/runtime/conversions/boundary.rs +293 -0
  47. package/crates/mustard/src/runtime/conversions/coercions.rs +217 -0
  48. package/crates/mustard/src/runtime/conversions/errors.rs +118 -0
  49. package/crates/mustard/src/runtime/conversions/mod.rs +14 -0
  50. package/crates/mustard/src/runtime/conversions/operators.rs +334 -0
  51. package/crates/mustard/src/runtime/env.rs +355 -0
  52. package/crates/mustard/src/runtime/exceptions.rs +377 -0
  53. package/crates/mustard/src/runtime/gc.rs +595 -0
  54. package/crates/mustard/src/runtime/mod.rs +318 -0
  55. package/crates/mustard/src/runtime/properties.rs +1762 -0
  56. package/crates/mustard/src/runtime/serialization.rs +127 -0
  57. package/crates/mustard/src/runtime/shared.rs +108 -0
  58. package/crates/mustard/src/runtime/snapshot_validation_tests.rs +93 -0
  59. package/crates/mustard/src/runtime/state.rs +652 -0
  60. package/crates/mustard/src/runtime/tests/async_host.rs +104 -0
  61. package/crates/mustard/src/runtime/tests/collections.rs +50 -0
  62. package/crates/mustard/src/runtime/tests/diagnostics.rs +36 -0
  63. package/crates/mustard/src/runtime/tests/exceptions.rs +122 -0
  64. package/crates/mustard/src/runtime/tests/execution.rs +553 -0
  65. package/crates/mustard/src/runtime/tests/gc.rs +533 -0
  66. package/crates/mustard/src/runtime/tests/mod.rs +56 -0
  67. package/crates/mustard/src/runtime/tests/serialization.rs +170 -0
  68. package/crates/mustard/src/runtime/validation/bytecode.rs +484 -0
  69. package/crates/mustard/src/runtime/validation/mod.rs +14 -0
  70. package/crates/mustard/src/runtime/validation/policy.rs +94 -0
  71. package/crates/mustard/src/runtime/validation/snapshot.rs +406 -0
  72. package/crates/mustard/src/runtime/validation/walk.rs +206 -0
  73. package/crates/mustard/src/runtime/vm.rs +1016 -0
  74. package/crates/mustard/src/span.rs +22 -0
  75. package/crates/mustard/src/structured.rs +107 -0
  76. package/crates/mustard-bridge/Cargo.toml +17 -0
  77. package/crates/mustard-bridge/src/codec.rs +46 -0
  78. package/crates/mustard-bridge/src/dto.rs +99 -0
  79. package/crates/mustard-bridge/src/lib.rs +12 -0
  80. package/crates/mustard-bridge/src/operations.rs +142 -0
  81. package/crates/mustard-node/Cargo.toml +24 -0
  82. package/crates/mustard-node/build.rs +3 -0
  83. package/crates/mustard-node/src/lib.rs +236 -0
  84. package/crates/mustard-sidecar/Cargo.toml +21 -0
  85. package/crates/mustard-sidecar/src/lib.rs +134 -0
  86. package/crates/mustard-sidecar/src/main.rs +36 -0
  87. package/dist/index.js +20 -0
  88. package/dist/install.js +117 -0
  89. package/dist/lib/cancellation.js +124 -0
  90. package/dist/lib/errors.js +46 -0
  91. package/dist/lib/executor.js +555 -0
  92. package/dist/lib/policy.js +292 -0
  93. package/dist/lib/progress.js +356 -0
  94. package/dist/lib/runtime.js +109 -0
  95. package/dist/lib/structured.js +286 -0
  96. package/dist/native-loader.js +227 -0
  97. package/index.d.ts +23 -0
  98. package/mustard.d.ts +220 -0
  99. package/package.json +97 -0
@@ -0,0 +1,298 @@
1
+ use super::*;
2
+
3
+ impl<'a> Lowerer<'a> {
4
+ pub(super) fn lower_block_stmt(&mut self, block: &BlockStatement<'a>) -> Stmt {
5
+ self.push_scope();
6
+ self.predeclare_block(&block.body);
7
+ let body = block
8
+ .body
9
+ .iter()
10
+ .filter_map(|statement| self.lower_stmt(statement))
11
+ .collect();
12
+ self.pop_scope();
13
+ Stmt::Block {
14
+ span: block.span.into(),
15
+ body,
16
+ }
17
+ }
18
+
19
+ pub(super) fn lower_stmt(&mut self, statement: &Statement<'a>) -> Option<Stmt> {
20
+ match statement {
21
+ Statement::BlockStatement(block) => Some(self.lower_block_stmt(block)),
22
+ Statement::BreakStatement(statement) => Some(Stmt::Break {
23
+ span: statement.span.into(),
24
+ }),
25
+ Statement::ContinueStatement(statement) => Some(Stmt::Continue {
26
+ span: statement.span.into(),
27
+ }),
28
+ Statement::EmptyStatement(statement) => Some(Stmt::Empty {
29
+ span: statement.span.into(),
30
+ }),
31
+ Statement::ExpressionStatement(statement) => Some(Stmt::Expression {
32
+ span: statement.span.into(),
33
+ expression: self.lower_expr(&statement.expression)?,
34
+ }),
35
+ Statement::ForStatement(statement) => {
36
+ let init = match &statement.init {
37
+ Some(ForStatementInit::VariableDeclaration(decl)) => {
38
+ Some(ForInit::VariableDecl {
39
+ kind: self.lower_binding_kind(decl.kind, decl.span)?,
40
+ declarators: decl
41
+ .declarations
42
+ .iter()
43
+ .filter_map(|declarator| self.lower_declarator(declarator))
44
+ .collect(),
45
+ })
46
+ }
47
+ Some(init) => Some(ForInit::Expression(self.lower_for_init_expr(init)?)),
48
+ None => None,
49
+ };
50
+ Some(Stmt::For {
51
+ span: statement.span.into(),
52
+ init,
53
+ test: statement
54
+ .test
55
+ .as_ref()
56
+ .and_then(|test| self.lower_expr(test)),
57
+ update: statement
58
+ .update
59
+ .as_ref()
60
+ .and_then(|expr| self.lower_expr(expr)),
61
+ body: Box::new(self.lower_stmt(&statement.body)?),
62
+ })
63
+ }
64
+ Statement::ForOfStatement(statement) => {
65
+ let head = self.lower_for_loop_head(&statement.left, "for...of")?;
66
+
67
+ let iterable = self.lower_expr(&statement.right)?;
68
+ let body = match &head {
69
+ crate::ir::ForOfHead::Binding { pattern, .. } => {
70
+ self.push_scope();
71
+ self.collect_ir_pattern_bindings(pattern);
72
+ let body = self.lower_stmt(&statement.body);
73
+ self.pop_scope();
74
+ Box::new(body?)
75
+ }
76
+ crate::ir::ForOfHead::Assignment { .. } => {
77
+ Box::new(self.lower_stmt(&statement.body)?)
78
+ }
79
+ };
80
+
81
+ Some(Stmt::ForOf {
82
+ span: statement.span.into(),
83
+ await_each: statement.r#await,
84
+ head,
85
+ iterable,
86
+ body,
87
+ })
88
+ }
89
+ Statement::ForInStatement(statement) => {
90
+ let head = self.lower_for_loop_head(&statement.left, "for...in")?;
91
+ let iterable = self.lower_expr(&statement.right)?;
92
+ let body = match &head {
93
+ crate::ir::ForOfHead::Binding { pattern, .. } => {
94
+ self.push_scope();
95
+ self.collect_ir_pattern_bindings(pattern);
96
+ let body = self.lower_stmt(&statement.body);
97
+ self.pop_scope();
98
+ Box::new(body?)
99
+ }
100
+ crate::ir::ForOfHead::Assignment { .. } => {
101
+ Box::new(self.lower_stmt(&statement.body)?)
102
+ }
103
+ };
104
+
105
+ Some(Stmt::ForIn {
106
+ span: statement.span.into(),
107
+ head,
108
+ object: iterable,
109
+ body,
110
+ })
111
+ }
112
+ Statement::IfStatement(statement) => Some(Stmt::If {
113
+ span: statement.span.into(),
114
+ test: self.lower_expr(&statement.test)?,
115
+ consequent: Box::new(self.lower_stmt(&statement.consequent)?),
116
+ alternate: statement
117
+ .alternate
118
+ .as_ref()
119
+ .and_then(|alternate| self.lower_stmt(alternate))
120
+ .map(Box::new),
121
+ }),
122
+ Statement::ReturnStatement(statement) => Some(Stmt::Return {
123
+ span: statement.span.into(),
124
+ value: statement
125
+ .argument
126
+ .as_ref()
127
+ .and_then(|expr| self.lower_expr(expr)),
128
+ }),
129
+ Statement::SwitchStatement(statement) => Some(Stmt::Switch {
130
+ span: statement.span.into(),
131
+ discriminant: self.lower_expr(&statement.discriminant)?,
132
+ cases: statement
133
+ .cases
134
+ .iter()
135
+ .map(|case| crate::ir::SwitchCase {
136
+ span: case.span.into(),
137
+ test: case.test.as_ref().and_then(|expr| self.lower_expr(expr)),
138
+ consequent: case
139
+ .consequent
140
+ .iter()
141
+ .filter_map(|statement| self.lower_stmt(statement))
142
+ .collect(),
143
+ })
144
+ .collect(),
145
+ }),
146
+ Statement::ThrowStatement(statement) => Some(Stmt::Throw {
147
+ span: statement.span.into(),
148
+ value: self.lower_expr(&statement.argument)?,
149
+ }),
150
+ Statement::TryStatement(statement) => Some(Stmt::Try {
151
+ span: statement.span.into(),
152
+ body: Box::new(self.lower_block_stmt(&statement.block)),
153
+ catch: statement.handler.as_ref().map(|handler| {
154
+ self.push_scope();
155
+ if let Some(param) = &handler.param {
156
+ self.collect_pattern_bindings(&param.pattern);
157
+ }
158
+ self.predeclare_block(&handler.body.body);
159
+ let clause = crate::ir::CatchClause {
160
+ span: handler.span.into(),
161
+ parameter: handler
162
+ .param
163
+ .as_ref()
164
+ .and_then(|param| self.lower_pattern(&param.pattern)),
165
+ body: Box::new(self.lower_block_stmt(&handler.body)),
166
+ };
167
+ self.pop_scope();
168
+ clause
169
+ }),
170
+ finally: statement
171
+ .finalizer
172
+ .as_ref()
173
+ .map(|block| Box::new(self.lower_block_stmt(block))),
174
+ }),
175
+ Statement::VariableDeclaration(decl) => Some(Stmt::VariableDecl {
176
+ span: decl.span.into(),
177
+ kind: self.lower_binding_kind(decl.kind, decl.span)?,
178
+ declarators: decl
179
+ .declarations
180
+ .iter()
181
+ .filter_map(|declarator| self.lower_declarator(declarator))
182
+ .collect(),
183
+ }),
184
+ Statement::WhileStatement(statement) => Some(Stmt::While {
185
+ span: statement.span.into(),
186
+ test: self.lower_expr(&statement.test)?,
187
+ body: Box::new(self.lower_stmt(&statement.body)?),
188
+ }),
189
+ Statement::DoWhileStatement(statement) => Some(Stmt::DoWhile {
190
+ span: statement.span.into(),
191
+ body: Box::new(self.lower_stmt(&statement.body)?),
192
+ test: self.lower_expr(&statement.test)?,
193
+ }),
194
+ Statement::FunctionDeclaration(function) => Some(Stmt::FunctionDecl {
195
+ span: function.span.into(),
196
+ function: self.lower_function(function, false)?,
197
+ }),
198
+ Statement::DebuggerStatement(statement) => {
199
+ self.unsupported(
200
+ "debugger statements are not supported",
201
+ Some(statement.span.into()),
202
+ );
203
+ None
204
+ }
205
+ Statement::LabeledStatement(statement) => {
206
+ self.unsupported(
207
+ "labeled statements are not supported in v1",
208
+ Some(statement.span.into()),
209
+ );
210
+ None
211
+ }
212
+ statement if statement.is_module_declaration() => {
213
+ self.unsupported(
214
+ "module syntax is not supported",
215
+ Some(statement.span().into()),
216
+ );
217
+ None
218
+ }
219
+ Statement::WithStatement(statement) => {
220
+ self.unsupported("with is not supported", Some(statement.span.into()));
221
+ None
222
+ }
223
+ Statement::ClassDeclaration(class) => {
224
+ self.unsupported("classes are not supported in v1", Some(class.span.into()));
225
+ None
226
+ }
227
+ statement => {
228
+ self.unsupported(
229
+ format!("unsupported statement form: {statement:?}"),
230
+ Some(statement.span().into()),
231
+ );
232
+ None
233
+ }
234
+ }
235
+ }
236
+
237
+ fn lower_for_loop_head(
238
+ &mut self,
239
+ left: &ForStatementLeft<'a>,
240
+ loop_name: &str,
241
+ ) -> Option<crate::ir::ForOfHead> {
242
+ match left {
243
+ ForStatementLeft::VariableDeclaration(decl) => {
244
+ if decl.declarations.len() != 1 {
245
+ self.unsupported(
246
+ format!("{loop_name} currently requires exactly one let or const binding"),
247
+ Some(decl.span.into()),
248
+ );
249
+ return None;
250
+ }
251
+ let declarator = &decl.declarations[0];
252
+ if declarator.init.is_some() {
253
+ self.unsupported(
254
+ format!("{loop_name} binding initializers are not supported"),
255
+ Some(declarator.span.into()),
256
+ );
257
+ return None;
258
+ }
259
+ Some(crate::ir::ForOfHead::Binding {
260
+ kind: self.lower_binding_kind(decl.kind, decl.span)?,
261
+ pattern: self.lower_pattern(&declarator.id)?,
262
+ })
263
+ }
264
+ _ => Some(crate::ir::ForOfHead::Assignment {
265
+ target: self.lower_for_of_assignment_target(left)?,
266
+ }),
267
+ }
268
+ }
269
+
270
+ pub(super) fn lower_binding_kind(
271
+ &mut self,
272
+ kind: VariableDeclarationKind,
273
+ span: oxc_span::Span,
274
+ ) -> Option<BindingKind> {
275
+ match kind {
276
+ VariableDeclarationKind::Let => Some(BindingKind::Let),
277
+ VariableDeclarationKind::Const => Some(BindingKind::Const),
278
+ _ => {
279
+ self.unsupported("only let and const are supported", Some(span.into()));
280
+ None
281
+ }
282
+ }
283
+ }
284
+
285
+ pub(super) fn lower_declarator(
286
+ &mut self,
287
+ declarator: &VariableDeclarator<'a>,
288
+ ) -> Option<Declarator> {
289
+ Some(Declarator {
290
+ span: declarator.span.into(),
291
+ pattern: self.lower_pattern(&declarator.id)?,
292
+ initializer: declarator
293
+ .init
294
+ .as_ref()
295
+ .and_then(|expr| self.lower_expr(expr)),
296
+ })
297
+ }
298
+ }
@@ -0,0 +1,339 @@
1
+ use crate::{
2
+ compile,
3
+ ir::{ArrayElement, AssignOp, CallArgument, Expr, Stmt},
4
+ };
5
+
6
+ #[test]
7
+ fn parses_basic_function_and_if() {
8
+ let program = compile(
9
+ r#"
10
+ const add = (a, b) => {
11
+ if (a > b) {
12
+ return a + b;
13
+ }
14
+ return a ?? b;
15
+ };
16
+ "#,
17
+ )
18
+ .expect("program should compile");
19
+
20
+ assert_eq!(program.script.body.len(), 1);
21
+ match &program.script.body[0] {
22
+ Stmt::VariableDecl { declarators, .. } => {
23
+ assert_eq!(declarators.len(), 1);
24
+ }
25
+ other => panic!("unexpected stmt: {other:?}"),
26
+ }
27
+ }
28
+
29
+ #[test]
30
+ fn allows_shadowed_require() {
31
+ compile("const require = () => 1; require();").expect("shadowed require should compile");
32
+ }
33
+
34
+ #[test]
35
+ fn allows_shadowed_function_identifier() {
36
+ compile("const Function = (value) => value; Function(1);")
37
+ .expect("shadowed Function should compile");
38
+ }
39
+
40
+ #[test]
41
+ fn allows_shadowed_arguments_identifier() {
42
+ compile("const arguments = [1]; arguments[0];").expect("shadowed arguments should compile");
43
+ }
44
+
45
+ #[test]
46
+ fn parses_array_for_of_with_const_binding() {
47
+ let program = compile(
48
+ r#"
49
+ let total = 0;
50
+ for (const value of [1, 2, 3]) {
51
+ total += value;
52
+ }
53
+ total;
54
+ "#,
55
+ )
56
+ .expect("for...of over arrays should compile");
57
+
58
+ assert!(matches!(program.script.body[1], Stmt::ForOf { .. }));
59
+ }
60
+
61
+ #[test]
62
+ fn parses_for_of_with_assignment_targets() {
63
+ let program = compile(
64
+ r#"
65
+ let value = 0;
66
+ const boxes = [{ current: 0 }, { current: 0 }];
67
+ let index = 0;
68
+ for (value of [1, 2]) {
69
+ index += value;
70
+ }
71
+ for (boxes[index - 3].current of [3, 4]) {
72
+ index += 1;
73
+ }
74
+ value + boxes[0].current + boxes[1].current + index;
75
+ "#,
76
+ )
77
+ .expect("for...of assignment-target headers should compile");
78
+
79
+ assert!(matches!(program.script.body[3], Stmt::ForOf { .. }));
80
+ assert!(matches!(program.script.body[4], Stmt::ForOf { .. }));
81
+ }
82
+
83
+ #[test]
84
+ fn parses_for_in_with_binding_and_assignment_targets() {
85
+ let program = compile(
86
+ r#"
87
+ let total = 0;
88
+ const boxes = [{ current: 0 }, { current: 0 }];
89
+ let index = 0;
90
+ for (const key in { beta: 2, alpha: 1 }) {
91
+ total += key.length;
92
+ }
93
+ for (boxes[index].current in [3, 4]) {
94
+ index += 1;
95
+ }
96
+ total + boxes[0].current + boxes[1].current + index;
97
+ "#,
98
+ )
99
+ .expect("for...in should compile");
100
+
101
+ assert!(matches!(program.script.body[3], Stmt::ForIn { .. }));
102
+ assert!(matches!(program.script.body[4], Stmt::ForIn { .. }));
103
+ }
104
+
105
+ #[test]
106
+ fn parses_for_await_of_inside_async_functions() {
107
+ compile(
108
+ r#"
109
+ async function run(values, boxRef) {
110
+ for await (const value of values) {
111
+ boxRef.total += value;
112
+ }
113
+ for await (boxRef.current of values) {
114
+ boxRef.total += boxRef.current;
115
+ }
116
+ return boxRef.total;
117
+ }
118
+ "#,
119
+ )
120
+ .expect("for await...of should compile inside async functions");
121
+ }
122
+
123
+ #[test]
124
+ fn parses_default_parameters_and_default_destructuring() {
125
+ compile(
126
+ r#"
127
+ function wrap(value = 1, { label = "ok" } = {}) {
128
+ return [value, label];
129
+ }
130
+ wrap();
131
+ "#,
132
+ )
133
+ .expect("default parameters and destructuring should compile");
134
+ }
135
+
136
+ #[test]
137
+ fn parses_destructuring_assignment_targets_and_update_expressions() {
138
+ compile(
139
+ r#"
140
+ let value = 0;
141
+ let boxRef = { current: 1 };
142
+ [value, boxRef.current] = [2, 3];
143
+ ({ value, current: boxRef.current = 4 } = { value: 5 });
144
+ ++value;
145
+ boxRef.current--;
146
+ "#,
147
+ )
148
+ .expect("destructuring assignment and update expressions should compile");
149
+ }
150
+
151
+ #[test]
152
+ fn parses_sequence_and_exponentiation_expressions() {
153
+ compile(
154
+ r#"
155
+ let total = 0;
156
+ const value = (total = total + 1, total = total + 2, 2 ** 3 ** 2);
157
+ [value, total];
158
+ "#,
159
+ )
160
+ .expect("sequence expressions and exponentiation should compile");
161
+ }
162
+
163
+ #[test]
164
+ fn parses_remainder_and_exponent_assignment_expressions() {
165
+ compile(
166
+ r#"
167
+ let left = 10;
168
+ let right = 2;
169
+ left %= 3;
170
+ right **= 3;
171
+ [left, right];
172
+ "#,
173
+ )
174
+ .expect("compound remainder and exponent assignments should compile");
175
+ }
176
+
177
+ #[test]
178
+ fn parses_in_operator_expressions() {
179
+ compile(
180
+ r#"
181
+ const object = { alpha: undefined };
182
+ const array = [1, 2];
183
+ ["alpha" in object, 1 in array, "push" in array];
184
+ "#,
185
+ )
186
+ .expect("in operator expressions should compile");
187
+ }
188
+
189
+ #[test]
190
+ fn parses_instanceof_expressions() {
191
+ compile(
192
+ r#"
193
+ function Box() {}
194
+ [([] instanceof Array), (new Map() instanceof Object), ({} instanceof Box)];
195
+ "#,
196
+ )
197
+ .expect("instanceof should compile for the documented constructor surface");
198
+ }
199
+
200
+ #[test]
201
+ fn parses_object_literals_with_computed_keys_methods_and_spread() {
202
+ compile(
203
+ r#"
204
+ const key = "value";
205
+ const extra = [3];
206
+ extra.label = "ok";
207
+ ({
208
+ [key]: 1,
209
+ total(step) {
210
+ return this[key] + step;
211
+ },
212
+ ...null,
213
+ ...extra,
214
+ beta: 4,
215
+ });
216
+ "#,
217
+ )
218
+ .expect("object literal computed keys, methods, and spread should compile");
219
+ }
220
+
221
+ #[test]
222
+ fn parses_sparse_array_literals() {
223
+ compile(
224
+ r#"
225
+ const values = [1, , 3];
226
+ [values.length, values[1], 1 in values];
227
+ "#,
228
+ )
229
+ .expect("sparse array literals should compile");
230
+ }
231
+
232
+ #[test]
233
+ fn lowers_array_spread_into_ir() {
234
+ let program = compile("[1, ...values, 3];").expect("array spread should compile");
235
+
236
+ let Stmt::Expression { expression, .. } = &program.script.body[0] else {
237
+ panic!("unexpected stmt: {:?}", program.script.body[0]);
238
+ };
239
+ let Expr::Array { elements, .. } = expression else {
240
+ panic!("unexpected expr: {expression:?}");
241
+ };
242
+
243
+ assert!(matches!(elements[0], ArrayElement::Value(_)));
244
+ assert!(matches!(elements[1], ArrayElement::Spread { .. }));
245
+ assert!(matches!(elements[2], ArrayElement::Value(_)));
246
+ }
247
+
248
+ #[test]
249
+ fn lowers_spread_call_arguments_into_ir() {
250
+ let program = compile("run(1, ...values, 3);").expect("spread call arguments should compile");
251
+
252
+ let Stmt::Expression { expression, .. } = &program.script.body[0] else {
253
+ panic!("unexpected stmt: {:?}", program.script.body[0]);
254
+ };
255
+ let Expr::Call { arguments, .. } = expression else {
256
+ panic!("unexpected expr: {expression:?}");
257
+ };
258
+
259
+ assert!(matches!(arguments[0], CallArgument::Value(_)));
260
+ assert!(matches!(arguments[1], CallArgument::Spread { .. }));
261
+ assert!(matches!(arguments[2], CallArgument::Value(_)));
262
+ }
263
+
264
+ #[test]
265
+ fn lowers_spread_constructor_arguments_into_ir() {
266
+ let program =
267
+ compile("new Box(1, ...values, 3);").expect("spread constructor arguments should compile");
268
+
269
+ let Stmt::Expression { expression, .. } = &program.script.body[0] else {
270
+ panic!("unexpected stmt: {:?}", program.script.body[0]);
271
+ };
272
+ let Expr::New { arguments, .. } = expression else {
273
+ panic!("unexpected expr: {expression:?}");
274
+ };
275
+
276
+ assert!(matches!(arguments[0], CallArgument::Value(_)));
277
+ assert!(matches!(arguments[1], CallArgument::Spread { .. }));
278
+ assert!(matches!(arguments[2], CallArgument::Value(_)));
279
+ }
280
+
281
+ #[test]
282
+ fn lowers_spread_optional_call_arguments_into_ir() {
283
+ let program =
284
+ compile("maybeRun?.(1, ...values, 3);").expect("optional spread call should compile");
285
+
286
+ let Stmt::Expression { expression, .. } = &program.script.body[0] else {
287
+ panic!("unexpected stmt: {:?}", program.script.body[0]);
288
+ };
289
+ let Expr::Call {
290
+ arguments,
291
+ optional,
292
+ ..
293
+ } = expression
294
+ else {
295
+ panic!("unexpected expr: {expression:?}");
296
+ };
297
+
298
+ assert!(*optional);
299
+ assert!(matches!(arguments[0], CallArgument::Value(_)));
300
+ assert!(matches!(arguments[1], CallArgument::Spread { .. }));
301
+ assert!(matches!(arguments[2], CallArgument::Value(_)));
302
+ }
303
+
304
+ #[test]
305
+ fn parses_logical_assignment_operators_into_ir() {
306
+ let program = compile(
307
+ r#"
308
+ let truthy = 1;
309
+ let falsy = 0;
310
+ truthy ||= 2;
311
+ falsy &&= 3;
312
+ "#,
313
+ )
314
+ .expect("logical assignment operators should parse");
315
+
316
+ match &program.script.body[2] {
317
+ Stmt::Expression {
318
+ expression:
319
+ Expr::Assignment {
320
+ operator: AssignOp::OrAssign,
321
+ ..
322
+ },
323
+ ..
324
+ } => {}
325
+ other => panic!("expected ||= assignment expression, got {other:?}"),
326
+ }
327
+
328
+ match &program.script.body[3] {
329
+ Stmt::Expression {
330
+ expression:
331
+ Expr::Assignment {
332
+ operator: AssignOp::AndAssign,
333
+ ..
334
+ },
335
+ ..
336
+ } => {}
337
+ other => panic!("expected &&= assignment expression, got {other:?}"),
338
+ }
339
+ }
@@ -0,0 +1,2 @@
1
+ mod acceptance;
2
+ mod rejections;