@tishlang/tish 1.6.0 → 1.7.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.
- package/Cargo.toml +1 -0
- package/bin/tish +0 -0
- package/crates/js_to_tish/src/error.rs +2 -8
- package/crates/js_to_tish/src/transform/expr.rs +101 -130
- package/crates/js_to_tish/src/transform/stmt.rs +25 -22
- package/crates/tish/Cargo.toml +1 -1
- package/crates/tish/src/cli_help.rs +76 -29
- package/crates/tish/src/main.rs +85 -54
- package/crates/tish/tests/cargo_example_compile.rs +3 -1
- package/crates/tish/tests/integration_test.rs +197 -47
- package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
- package/crates/tish/tests/shortcircuit.rs +19 -4
- package/crates/tish_ast/src/ast.rs +12 -14
- package/crates/tish_build_utils/src/lib.rs +31 -6
- package/crates/tish_builtins/src/array.rs +52 -21
- package/crates/tish_builtins/src/construct.rs +2 -8
- package/crates/tish_builtins/src/globals.rs +30 -15
- package/crates/tish_builtins/src/lib.rs +5 -5
- package/crates/tish_builtins/src/math.rs +5 -3
- package/crates/tish_builtins/src/string.rs +71 -19
- package/crates/tish_bytecode/src/chunk.rs +0 -1
- package/crates/tish_bytecode/src/compiler.rs +164 -60
- package/crates/tish_bytecode/src/opcode.rs +13 -4
- package/crates/tish_bytecode/src/peephole.rs +2 -2
- package/crates/tish_compile/src/codegen.rs +921 -299
- package/crates/tish_compile/src/infer.rs +69 -19
- package/crates/tish_compile/src/lib.rs +15 -5
- package/crates/tish_compile/src/resolve.rs +112 -69
- package/crates/tish_compile/src/types.rs +10 -14
- package/crates/tish_compile_js/src/codegen.rs +34 -13
- package/crates/tish_compile_js/src/tests_jsx.rs +30 -6
- package/crates/tish_compiler_wasm/src/lib.rs +16 -13
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +39 -48
- package/crates/tish_core/src/json.rs +5 -3
- package/crates/tish_core/src/lib.rs +1 -1
- package/crates/tish_core/src/uri.rs +9 -6
- package/crates/tish_core/src/value.rs +92 -28
- package/crates/tish_cranelift/src/link.rs +6 -9
- package/crates/tish_cranelift/src/lower.rs +14 -8
- package/crates/tish_eval/src/eval.rs +389 -142
- package/crates/tish_eval/src/lib.rs +10 -6
- package/crates/tish_eval/src/natives.rs +95 -38
- package/crates/tish_eval/src/promise.rs +14 -8
- package/crates/tish_eval/src/timers.rs +28 -19
- package/crates/tish_eval/src/value.rs +10 -3
- package/crates/tish_fmt/src/lib.rs +29 -13
- package/crates/tish_lexer/src/lib.rs +217 -63
- package/crates/tish_lexer/src/token.rs +6 -6
- package/crates/tish_llvm/src/lib.rs +15 -8
- package/crates/tish_lsp/src/main.rs +41 -43
- package/crates/tish_native/src/build.rs +1 -6
- package/crates/tish_native/src/lib.rs +48 -19
- package/crates/tish_opt/src/lib.rs +67 -50
- package/crates/tish_parser/src/lib.rs +36 -11
- package/crates/tish_parser/src/parser.rs +172 -87
- package/crates/tish_runtime/src/http.rs +15 -6
- package/crates/tish_runtime/src/http_fetch.rs +24 -14
- package/crates/tish_runtime/src/lib.rs +224 -168
- package/crates/tish_runtime/src/promise.rs +1 -5
- package/crates/tish_runtime/src/ws.rs +45 -20
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
- package/crates/tish_ui/src/jsx.rs +41 -22
- package/crates/tish_ui/src/lib.rs +2 -2
- package/crates/tish_vm/src/vm.rs +309 -112
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +8 -3
- package/crates/tish_wasm/src/lib.rs +38 -28
- package/crates/tishlang_cargo_bindgen/Cargo.toml +25 -0
- package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +52 -0
- package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
- package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -0
- package/package.json +1 -1
- package/platform/darwin-arm64/tish +0 -0
- package/platform/darwin-x64/tish +0 -0
- package/platform/linux-arm64/tish +0 -0
- package/platform/linux-x64/tish +0 -0
- package/platform/win32-x64/tish.exe +0 -0
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
//! Code generation: AST -> Rust source.
|
|
2
2
|
|
|
3
|
+
use crate::resolve::is_builtin_native_spec;
|
|
4
|
+
use crate::types::{RustType, TypeContext};
|
|
3
5
|
use std::borrow::Cow;
|
|
4
6
|
use std::collections::{HashMap, HashSet};
|
|
5
7
|
use std::path::Path;
|
|
6
|
-
use tishlang_ast::{
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
use tishlang_ast::{
|
|
9
|
+
ArrayElement, ArrowBody, BinOp, CallArg, CompoundOp, DestructElement, DestructPattern, Expr,
|
|
10
|
+
FunParam, Literal, LogicalAssignOp, MemberProp, ObjectProp, Program, Span, Statement, UnaryOp,
|
|
11
|
+
};
|
|
9
12
|
|
|
10
13
|
/// Tracks variable usage for move/clone optimization.
|
|
11
14
|
/// A variable can be moved instead of cloned if it's at its last use.
|
|
@@ -41,7 +44,12 @@ impl UsageAnalyzer {
|
|
|
41
44
|
self.analyze_expr(e);
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
|
-
Statement::If {
|
|
47
|
+
Statement::If {
|
|
48
|
+
cond,
|
|
49
|
+
then_branch,
|
|
50
|
+
else_branch,
|
|
51
|
+
..
|
|
52
|
+
} => {
|
|
45
53
|
self.analyze_expr(cond);
|
|
46
54
|
self.analyze_statement(then_branch);
|
|
47
55
|
if let Some(e) = else_branch {
|
|
@@ -49,7 +57,13 @@ impl UsageAnalyzer {
|
|
|
49
57
|
}
|
|
50
58
|
}
|
|
51
59
|
Statement::Block { statements, .. } => self.analyze_statements(statements),
|
|
52
|
-
Statement::For {
|
|
60
|
+
Statement::For {
|
|
61
|
+
init,
|
|
62
|
+
cond,
|
|
63
|
+
update,
|
|
64
|
+
body,
|
|
65
|
+
..
|
|
66
|
+
} => {
|
|
53
67
|
if let Some(i) = init {
|
|
54
68
|
self.analyze_statement(i);
|
|
55
69
|
}
|
|
@@ -69,7 +83,12 @@ impl UsageAnalyzer {
|
|
|
69
83
|
self.analyze_expr(cond);
|
|
70
84
|
self.analyze_statement(body);
|
|
71
85
|
}
|
|
72
|
-
Statement::Switch {
|
|
86
|
+
Statement::Switch {
|
|
87
|
+
expr,
|
|
88
|
+
cases,
|
|
89
|
+
default_body,
|
|
90
|
+
..
|
|
91
|
+
} => {
|
|
73
92
|
self.analyze_expr(expr);
|
|
74
93
|
for (case_expr, stmts) in cases {
|
|
75
94
|
if let Some(e) = case_expr {
|
|
@@ -82,7 +101,12 @@ impl UsageAnalyzer {
|
|
|
82
101
|
}
|
|
83
102
|
}
|
|
84
103
|
Statement::Throw { value, .. } => self.analyze_expr(value),
|
|
85
|
-
Statement::Try {
|
|
104
|
+
Statement::Try {
|
|
105
|
+
body,
|
|
106
|
+
catch_body,
|
|
107
|
+
finally_body,
|
|
108
|
+
..
|
|
109
|
+
} => {
|
|
86
110
|
self.analyze_statement(body);
|
|
87
111
|
if let Some(c) = catch_body {
|
|
88
112
|
self.analyze_statement(c);
|
|
@@ -153,14 +177,17 @@ impl UsageAnalyzer {
|
|
|
153
177
|
}
|
|
154
178
|
}
|
|
155
179
|
}
|
|
156
|
-
Expr::ArrowFunction { body, .. } => {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
}
|
|
180
|
+
Expr::ArrowFunction { body, .. } => match body {
|
|
181
|
+
ArrowBody::Expr(e) => self.analyze_expr(e),
|
|
182
|
+
ArrowBody::Block(s) => self.analyze_statement(s),
|
|
183
|
+
},
|
|
162
184
|
Expr::Assign { value, .. } => self.analyze_expr(value),
|
|
163
|
-
Expr::Conditional {
|
|
185
|
+
Expr::Conditional {
|
|
186
|
+
cond,
|
|
187
|
+
then_branch,
|
|
188
|
+
else_branch,
|
|
189
|
+
..
|
|
190
|
+
} => {
|
|
164
191
|
self.analyze_expr(cond);
|
|
165
192
|
self.analyze_expr(then_branch);
|
|
166
193
|
self.analyze_expr(else_branch);
|
|
@@ -183,23 +210,37 @@ impl UsageAnalyzer {
|
|
|
183
210
|
*self.use_counts.entry(name.to_string()).or_insert(0) += 1;
|
|
184
211
|
self.analyze_expr(value);
|
|
185
212
|
}
|
|
186
|
-
Expr::PostfixInc { name, .. }
|
|
213
|
+
Expr::PostfixInc { name, .. }
|
|
214
|
+
| Expr::PostfixDec { name, .. }
|
|
215
|
+
| Expr::PrefixInc { name, .. }
|
|
216
|
+
| Expr::PrefixDec { name, .. } => {
|
|
187
217
|
*self.use_counts.entry(name.to_string()).or_insert(0) += 1;
|
|
188
218
|
}
|
|
189
219
|
Expr::MemberAssign { object, value, .. } => {
|
|
190
220
|
self.analyze_expr(object);
|
|
191
221
|
self.analyze_expr(value);
|
|
192
222
|
}
|
|
193
|
-
Expr::IndexAssign {
|
|
223
|
+
Expr::IndexAssign {
|
|
224
|
+
object,
|
|
225
|
+
index,
|
|
226
|
+
value,
|
|
227
|
+
..
|
|
228
|
+
} => {
|
|
194
229
|
self.analyze_expr(object);
|
|
195
230
|
self.analyze_expr(index);
|
|
196
231
|
self.analyze_expr(value);
|
|
197
232
|
}
|
|
198
233
|
Expr::Await { operand, .. } => self.analyze_expr(operand),
|
|
199
|
-
Expr::JsxElement {
|
|
234
|
+
Expr::JsxElement {
|
|
235
|
+
props, children, ..
|
|
236
|
+
} => {
|
|
200
237
|
for p in props {
|
|
201
238
|
match p {
|
|
202
|
-
tishlang_ast::JsxProp::Attr {
|
|
239
|
+
tishlang_ast::JsxProp::Attr {
|
|
240
|
+
value: tishlang_ast::JsxAttrValue::Expr(e),
|
|
241
|
+
..
|
|
242
|
+
}
|
|
243
|
+
| tishlang_ast::JsxProp::Spread(e) => self.analyze_expr(e),
|
|
203
244
|
_ => {}
|
|
204
245
|
}
|
|
205
246
|
}
|
|
@@ -240,7 +281,10 @@ pub struct CompileError {
|
|
|
240
281
|
|
|
241
282
|
impl CompileError {
|
|
242
283
|
fn new(msg: impl Into<String>, span: Option<Span>) -> Self {
|
|
243
|
-
Self {
|
|
284
|
+
Self {
|
|
285
|
+
message: msg.into(),
|
|
286
|
+
span,
|
|
287
|
+
}
|
|
244
288
|
}
|
|
245
289
|
}
|
|
246
290
|
|
|
@@ -262,21 +306,45 @@ fn program_uses_async(program: &Program) -> bool {
|
|
|
262
306
|
match s {
|
|
263
307
|
Statement::FunDecl { async_, .. } if *async_ => true,
|
|
264
308
|
Statement::Block { statements, .. } => statements.iter().any(stmt_has_async),
|
|
265
|
-
Statement::If {
|
|
266
|
-
|
|
309
|
+
Statement::If {
|
|
310
|
+
then_branch,
|
|
311
|
+
else_branch,
|
|
312
|
+
..
|
|
313
|
+
} => {
|
|
314
|
+
stmt_has_async(then_branch)
|
|
315
|
+
|| else_branch
|
|
316
|
+
.as_ref()
|
|
317
|
+
.is_some_and(|s| stmt_has_async(s.as_ref()))
|
|
267
318
|
}
|
|
268
|
-
Statement::While { body, .. }
|
|
319
|
+
Statement::While { body, .. }
|
|
320
|
+
| Statement::For { body, .. }
|
|
321
|
+
| Statement::ForOf { body, .. }
|
|
269
322
|
| Statement::DoWhile { body, .. } => stmt_has_async(body),
|
|
270
|
-
Statement::Switch {
|
|
271
|
-
cases
|
|
323
|
+
Statement::Switch {
|
|
324
|
+
cases,
|
|
325
|
+
default_body,
|
|
326
|
+
..
|
|
327
|
+
} => {
|
|
328
|
+
cases
|
|
329
|
+
.iter()
|
|
330
|
+
.any(|(_, stmts)| stmts.iter().any(stmt_has_async))
|
|
272
331
|
|| default_body
|
|
273
332
|
.as_ref()
|
|
274
333
|
.is_some_and(|stmts| stmts.iter().any(stmt_has_async))
|
|
275
334
|
}
|
|
276
|
-
Statement::Try {
|
|
335
|
+
Statement::Try {
|
|
336
|
+
body,
|
|
337
|
+
catch_body,
|
|
338
|
+
finally_body,
|
|
339
|
+
..
|
|
340
|
+
} => {
|
|
277
341
|
stmt_has_async(body)
|
|
278
|
-
|| catch_body
|
|
279
|
-
|
|
342
|
+
|| catch_body
|
|
343
|
+
.as_ref()
|
|
344
|
+
.is_some_and(|s| stmt_has_async(s.as_ref()))
|
|
345
|
+
|| finally_body
|
|
346
|
+
.as_ref()
|
|
347
|
+
.is_some_and(|s| stmt_has_async(s.as_ref()))
|
|
280
348
|
}
|
|
281
349
|
_ => false,
|
|
282
350
|
}
|
|
@@ -287,14 +355,16 @@ fn program_uses_async(program: &Program) -> bool {
|
|
|
287
355
|
Expr::Binary { left, right, .. } => expr_has_await(left) || expr_has_await(right),
|
|
288
356
|
Expr::Unary { operand, .. } | Expr::TypeOf { operand, .. } => expr_has_await(operand),
|
|
289
357
|
Expr::Call { callee, args, .. } => {
|
|
290
|
-
expr_has_await(callee)
|
|
291
|
-
|
|
292
|
-
|
|
358
|
+
expr_has_await(callee)
|
|
359
|
+
|| args.iter().any(|a| match a {
|
|
360
|
+
CallArg::Expr(e) | CallArg::Spread(e) => expr_has_await(e),
|
|
361
|
+
})
|
|
293
362
|
}
|
|
294
363
|
Expr::New { callee, args, .. } => {
|
|
295
|
-
expr_has_await(callee)
|
|
296
|
-
|
|
297
|
-
|
|
364
|
+
expr_has_await(callee)
|
|
365
|
+
|| args.iter().any(|a| match a {
|
|
366
|
+
CallArg::Expr(e) | CallArg::Spread(e) => expr_has_await(e),
|
|
367
|
+
})
|
|
298
368
|
}
|
|
299
369
|
Expr::Member { object, prop, .. } => {
|
|
300
370
|
expr_has_await(object)
|
|
@@ -305,32 +375,48 @@ fn program_uses_async(program: &Program) -> bool {
|
|
|
305
375
|
}
|
|
306
376
|
}
|
|
307
377
|
Expr::Index { object, index, .. } => expr_has_await(object) || expr_has_await(index),
|
|
308
|
-
Expr::Conditional {
|
|
309
|
-
|
|
378
|
+
Expr::Conditional {
|
|
379
|
+
cond,
|
|
380
|
+
then_branch,
|
|
381
|
+
else_branch,
|
|
382
|
+
..
|
|
383
|
+
} => expr_has_await(cond) || expr_has_await(then_branch) || expr_has_await(else_branch),
|
|
384
|
+
Expr::NullishCoalesce { left, right, .. } => {
|
|
385
|
+
expr_has_await(left) || expr_has_await(right)
|
|
310
386
|
}
|
|
311
|
-
Expr::NullishCoalesce { left, right, .. } => expr_has_await(left) || expr_has_await(right),
|
|
312
387
|
Expr::Array { elements, .. } => elements.iter().any(|el| match el {
|
|
313
388
|
ArrayElement::Expr(e) | ArrayElement::Spread(e) => expr_has_await(e),
|
|
314
389
|
}),
|
|
315
390
|
Expr::Object { props, .. } => props.iter().any(|p| match p {
|
|
316
391
|
ObjectProp::KeyValue(_, e) | ObjectProp::Spread(e) => expr_has_await(e),
|
|
317
392
|
}),
|
|
318
|
-
Expr::Assign { value, .. }
|
|
319
|
-
| Expr::
|
|
393
|
+
Expr::Assign { value, .. }
|
|
394
|
+
| Expr::CompoundAssign { value, .. }
|
|
395
|
+
| Expr::LogicalAssign { value, .. }
|
|
396
|
+
| Expr::MemberAssign { value, .. }
|
|
397
|
+
| Expr::IndexAssign { value, .. } => expr_has_await(value),
|
|
320
398
|
Expr::ArrowFunction { body, .. } => match body {
|
|
321
399
|
ArrowBody::Expr(e) => expr_has_await(e),
|
|
322
400
|
ArrowBody::Block(s) => stmt_has_async(s),
|
|
323
401
|
},
|
|
324
402
|
Expr::TemplateLiteral { exprs, .. } => exprs.iter().any(expr_has_await),
|
|
325
|
-
Expr::JsxElement {
|
|
403
|
+
Expr::JsxElement {
|
|
404
|
+
props, children, ..
|
|
405
|
+
} => {
|
|
326
406
|
props.iter().any(|p| match p {
|
|
327
|
-
tishlang_ast::JsxProp::Attr {
|
|
407
|
+
tishlang_ast::JsxProp::Attr {
|
|
408
|
+
value: tishlang_ast::JsxAttrValue::Expr(e),
|
|
409
|
+
..
|
|
410
|
+
}
|
|
411
|
+
| tishlang_ast::JsxProp::Spread(e) => expr_has_await(e),
|
|
328
412
|
_ => false,
|
|
329
|
-
}) || children
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
children.iter().any(|c| matches!(c, tishlang_ast::JsxChild::Expr(e) if expr_has_await(e)))
|
|
413
|
+
}) || children
|
|
414
|
+
.iter()
|
|
415
|
+
.any(|c| matches!(c, tishlang_ast::JsxChild::Expr(e) if expr_has_await(e)))
|
|
333
416
|
}
|
|
417
|
+
Expr::JsxFragment { children, .. } => children
|
|
418
|
+
.iter()
|
|
419
|
+
.any(|c| matches!(c, tishlang_ast::JsxChild::Expr(e) if expr_has_await(e))),
|
|
334
420
|
_ => false,
|
|
335
421
|
}
|
|
336
422
|
}
|
|
@@ -340,21 +426,42 @@ fn program_uses_async(program: &Program) -> bool {
|
|
|
340
426
|
Statement::VarDecl { init, .. } => init.as_ref().is_some_and(expr_has_await),
|
|
341
427
|
Statement::VarDeclDestructure { init, .. } => expr_has_await(init),
|
|
342
428
|
Statement::ExprStmt { expr, .. } => expr_has_await(expr),
|
|
343
|
-
Statement::If {
|
|
344
|
-
|
|
345
|
-
|
|
429
|
+
Statement::If {
|
|
430
|
+
cond,
|
|
431
|
+
then_branch,
|
|
432
|
+
else_branch,
|
|
433
|
+
..
|
|
434
|
+
} => {
|
|
435
|
+
expr_has_await(cond)
|
|
436
|
+
|| stmt_has_await(then_branch)
|
|
437
|
+
|| else_branch
|
|
438
|
+
.as_ref()
|
|
439
|
+
.is_some_and(|s| stmt_has_await(s.as_ref()))
|
|
346
440
|
}
|
|
347
441
|
Statement::While { cond, body, .. } => expr_has_await(cond) || stmt_has_await(body),
|
|
348
|
-
Statement::For {
|
|
442
|
+
Statement::For {
|
|
443
|
+
init,
|
|
444
|
+
cond,
|
|
445
|
+
update,
|
|
446
|
+
body,
|
|
447
|
+
..
|
|
448
|
+
} => {
|
|
349
449
|
init.as_ref().is_some_and(|s| stmt_has_await(s.as_ref()))
|
|
350
450
|
|| cond.as_ref().is_some_and(expr_has_await)
|
|
351
451
|
|| update.as_ref().is_some_and(expr_has_await)
|
|
352
452
|
|| stmt_has_await(body)
|
|
353
453
|
}
|
|
354
|
-
Statement::ForOf { iterable, body, .. } =>
|
|
454
|
+
Statement::ForOf { iterable, body, .. } => {
|
|
455
|
+
expr_has_await(iterable) || stmt_has_await(body)
|
|
456
|
+
}
|
|
355
457
|
Statement::Return { value, .. } => value.as_ref().is_some_and(expr_has_await),
|
|
356
458
|
Statement::FunDecl { body, .. } => stmt_has_await(body),
|
|
357
|
-
Statement::Switch {
|
|
459
|
+
Statement::Switch {
|
|
460
|
+
expr,
|
|
461
|
+
cases,
|
|
462
|
+
default_body,
|
|
463
|
+
..
|
|
464
|
+
} => {
|
|
358
465
|
expr_has_await(expr)
|
|
359
466
|
|| cases.iter().any(|(c, stmts)| {
|
|
360
467
|
c.as_ref().is_some_and(expr_has_await) || stmts.iter().any(stmt_has_await)
|
|
@@ -365,23 +472,38 @@ fn program_uses_async(program: &Program) -> bool {
|
|
|
365
472
|
}
|
|
366
473
|
Statement::DoWhile { body, cond, .. } => stmt_has_await(body) || expr_has_await(cond),
|
|
367
474
|
Statement::Throw { value, .. } => expr_has_await(value),
|
|
368
|
-
Statement::Try {
|
|
475
|
+
Statement::Try {
|
|
476
|
+
body,
|
|
477
|
+
catch_body,
|
|
478
|
+
finally_body,
|
|
479
|
+
..
|
|
480
|
+
} => {
|
|
369
481
|
stmt_has_await(body)
|
|
370
|
-
|| catch_body
|
|
371
|
-
|
|
482
|
+
|| catch_body
|
|
483
|
+
.as_ref()
|
|
484
|
+
.is_some_and(|s| stmt_has_await(s.as_ref()))
|
|
485
|
+
|| finally_body
|
|
486
|
+
.as_ref()
|
|
487
|
+
.is_some_and(|s| stmt_has_await(s.as_ref()))
|
|
372
488
|
}
|
|
373
489
|
Statement::Import { .. } | Statement::Export { .. } => false,
|
|
374
490
|
_ => false,
|
|
375
491
|
}
|
|
376
492
|
}
|
|
377
|
-
program
|
|
493
|
+
program
|
|
494
|
+
.statements
|
|
495
|
+
.iter()
|
|
496
|
+
.any(|s| stmt_has_async(s) || stmt_has_await(s))
|
|
378
497
|
}
|
|
379
498
|
|
|
380
499
|
pub fn compile(program: &Program) -> Result<String, CompileError> {
|
|
381
500
|
compile_with_project_root(program, None)
|
|
382
501
|
}
|
|
383
502
|
|
|
384
|
-
pub fn compile_with_project_root(
|
|
503
|
+
pub fn compile_with_project_root(
|
|
504
|
+
program: &Program,
|
|
505
|
+
project_root: Option<&Path>,
|
|
506
|
+
) -> Result<String, CompileError> {
|
|
385
507
|
compile_with_features(program, project_root, &[])
|
|
386
508
|
}
|
|
387
509
|
|
|
@@ -416,16 +538,28 @@ pub fn compile_project_full(
|
|
|
416
538
|
> {
|
|
417
539
|
use crate::resolve;
|
|
418
540
|
let root = project_root.unwrap_or_else(|| entry_path.parent().unwrap_or(Path::new(".")));
|
|
419
|
-
let modules = resolve::resolve_project(entry_path, project_root)
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
541
|
+
let modules = resolve::resolve_project(entry_path, project_root).map_err(|e| CompileError {
|
|
542
|
+
message: e,
|
|
543
|
+
span: None,
|
|
544
|
+
})?;
|
|
545
|
+
resolve::detect_cycles(&modules).map_err(|e| CompileError {
|
|
546
|
+
message: e,
|
|
547
|
+
span: None,
|
|
548
|
+
})?;
|
|
549
|
+
let merged = resolve::merge_modules(modules).map_err(|e| CompileError {
|
|
550
|
+
message: e,
|
|
551
|
+
span: None,
|
|
552
|
+
})?;
|
|
553
|
+
let native_modules =
|
|
554
|
+
resolve::resolve_native_modules(&merged, root).map_err(|e| CompileError {
|
|
555
|
+
message: e,
|
|
556
|
+
span: None,
|
|
557
|
+
})?;
|
|
427
558
|
let native_build = resolve::compute_native_build_artifacts(&merged, root, &native_modules)
|
|
428
|
-
.map_err(|e| CompileError {
|
|
559
|
+
.map_err(|e| CompileError {
|
|
560
|
+
message: e,
|
|
561
|
+
span: None,
|
|
562
|
+
})?;
|
|
429
563
|
let mut all_features: Vec<String> = features.to_vec();
|
|
430
564
|
for f in resolve::extract_native_import_features(&merged) {
|
|
431
565
|
if !all_features.contains(&f) {
|
|
@@ -463,7 +597,11 @@ pub fn compile_with_native_modules(
|
|
|
463
597
|
native_init: &std::collections::HashMap<String, crate::resolve::NativeModuleInit>,
|
|
464
598
|
optimize: bool,
|
|
465
599
|
) -> Result<String, CompileError> {
|
|
466
|
-
let program = if optimize {
|
|
600
|
+
let program = if optimize {
|
|
601
|
+
tishlang_opt::optimize(program)
|
|
602
|
+
} else {
|
|
603
|
+
program.clone()
|
|
604
|
+
};
|
|
467
605
|
// Type-inference pass: fills in `type_ann` on unannotated VarDecl nodes where
|
|
468
606
|
// the type is unambiguous (literals, arithmetic of typed vars, etc.).
|
|
469
607
|
let program = crate::infer::infer_program(&program);
|
|
@@ -668,17 +806,23 @@ impl Codegen {
|
|
|
668
806
|
/// Escape Rust reserved keywords by prefixing with r#
|
|
669
807
|
fn escape_ident(name: &str) -> Cow<'_, str> {
|
|
670
808
|
// Rust standard library macros that conflict with variable names
|
|
671
|
-
const RUST_MACROS: &[&str] = &[
|
|
809
|
+
const RUST_MACROS: &[&str] = &[
|
|
810
|
+
"line",
|
|
811
|
+
"column",
|
|
812
|
+
"file",
|
|
813
|
+
"module_path",
|
|
814
|
+
"stringify",
|
|
815
|
+
"concat",
|
|
816
|
+
];
|
|
672
817
|
if RUST_MACROS.contains(&name) {
|
|
673
818
|
return Cow::Owned(format!("r#{}", name));
|
|
674
819
|
}
|
|
675
820
|
const RUST_KEYWORDS: &[&str] = &[
|
|
676
|
-
"as", "async", "await", "break", "const", "continue", "crate", "dyn",
|
|
677
|
-
"
|
|
678
|
-
"
|
|
679
|
-
"
|
|
680
|
-
"
|
|
681
|
-
"final", "macro", "override", "priv", "try", "typeof", "unsized",
|
|
821
|
+
"as", "async", "await", "break", "const", "continue", "crate", "dyn", "else", "enum",
|
|
822
|
+
"extern", "false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod",
|
|
823
|
+
"move", "mut", "pub", "ref", "return", "self", "Self", "static", "struct", "super",
|
|
824
|
+
"trait", "true", "type", "unsafe", "use", "where", "while", "abstract", "become",
|
|
825
|
+
"box", "do", "final", "macro", "override", "priv", "try", "typeof", "unsized",
|
|
682
826
|
"virtual", "yield",
|
|
683
827
|
];
|
|
684
828
|
if RUST_KEYWORDS.contains(&name) {
|
|
@@ -717,20 +861,20 @@ impl Codegen {
|
|
|
717
861
|
if !Self::needs_clone(expr) {
|
|
718
862
|
return false;
|
|
719
863
|
}
|
|
720
|
-
|
|
864
|
+
|
|
721
865
|
// Check for last-use optimization on simple identifiers
|
|
722
866
|
if let Expr::Ident { name, .. } = expr {
|
|
723
867
|
// Don't optimize RefCell-wrapped vars (they're borrowed, not owned)
|
|
724
868
|
if self.refcell_wrapped_vars.contains(name.as_ref()) {
|
|
725
869
|
return true;
|
|
726
870
|
}
|
|
727
|
-
|
|
871
|
+
|
|
728
872
|
// Inside a loop, any variable used in an init (e.g. "let x = outerVar") must be cloned:
|
|
729
873
|
// the loop body runs multiple times, so we cannot move on the first iteration.
|
|
730
874
|
if !self.loop_stack.is_empty() {
|
|
731
875
|
return true;
|
|
732
876
|
}
|
|
733
|
-
|
|
877
|
+
|
|
734
878
|
// Check if this is the last use
|
|
735
879
|
if let Some(ref mut analyzer) = self.usage_analyzer {
|
|
736
880
|
if analyzer.is_last_use(name.as_ref()) {
|
|
@@ -738,7 +882,7 @@ impl Codegen {
|
|
|
738
882
|
}
|
|
739
883
|
}
|
|
740
884
|
}
|
|
741
|
-
|
|
885
|
+
|
|
742
886
|
true
|
|
743
887
|
}
|
|
744
888
|
|
|
@@ -806,7 +950,7 @@ impl Codegen {
|
|
|
806
950
|
/// Returns Some(true) for ascending, Some(false) for descending, None if not detected
|
|
807
951
|
fn detect_numeric_sort_comparator(expr: &Expr) -> Option<bool> {
|
|
808
952
|
use tishlang_ast::ArrowBody;
|
|
809
|
-
|
|
953
|
+
|
|
810
954
|
if let Expr::ArrowFunction { params, body, .. } = expr {
|
|
811
955
|
if params.len() != 2 {
|
|
812
956
|
return None;
|
|
@@ -819,7 +963,7 @@ impl Codegen {
|
|
|
819
963
|
}
|
|
820
964
|
_ => return None,
|
|
821
965
|
};
|
|
822
|
-
|
|
966
|
+
|
|
823
967
|
// Body must be a single expression that's a subtraction
|
|
824
968
|
let body_expr = match body {
|
|
825
969
|
ArrowBody::Expr(e) => e.as_ref(),
|
|
@@ -831,10 +975,24 @@ impl Codegen {
|
|
|
831
975
|
}
|
|
832
976
|
}
|
|
833
977
|
};
|
|
834
|
-
|
|
835
|
-
if let Expr::Binary {
|
|
978
|
+
|
|
979
|
+
if let Expr::Binary {
|
|
980
|
+
left,
|
|
981
|
+
op: BinOp::Sub,
|
|
982
|
+
right,
|
|
983
|
+
..
|
|
984
|
+
} = body_expr
|
|
985
|
+
{
|
|
836
986
|
// Check for a - b (ascending) or b - a (descending)
|
|
837
|
-
if let (
|
|
987
|
+
if let (
|
|
988
|
+
Expr::Ident {
|
|
989
|
+
name: left_name, ..
|
|
990
|
+
},
|
|
991
|
+
Expr::Ident {
|
|
992
|
+
name: right_name, ..
|
|
993
|
+
},
|
|
994
|
+
) = (left.as_ref(), right.as_ref())
|
|
995
|
+
{
|
|
838
996
|
if left_name.as_ref() == param_a && right_name.as_ref() == param_b {
|
|
839
997
|
return Some(true); // ascending
|
|
840
998
|
}
|
|
@@ -916,31 +1074,61 @@ impl Codegen {
|
|
|
916
1074
|
self.writeln("(Arc::from(\"error\"), Value::Function(Rc::new(|args: &[Value]| { tish_console_error(args); Value::Null }))),");
|
|
917
1075
|
self.indent -= 1;
|
|
918
1076
|
self.writeln("]))));");
|
|
919
|
-
self.writeln(
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
self.writeln(
|
|
923
|
-
|
|
924
|
-
|
|
1077
|
+
self.writeln(
|
|
1078
|
+
"let Boolean = Value::Function(Rc::new(|args: &[Value]| tish_boolean(args)));",
|
|
1079
|
+
);
|
|
1080
|
+
self.writeln(
|
|
1081
|
+
"let parseInt = Value::Function(Rc::new(|args: &[Value]| tish_parse_int(args)));",
|
|
1082
|
+
);
|
|
1083
|
+
self.writeln(
|
|
1084
|
+
"let parseFloat = Value::Function(Rc::new(|args: &[Value]| tish_parse_float(args)));",
|
|
1085
|
+
);
|
|
1086
|
+
self.writeln(
|
|
1087
|
+
"let decodeURI = Value::Function(Rc::new(|args: &[Value]| tish_decode_uri(args)));",
|
|
1088
|
+
);
|
|
1089
|
+
self.writeln(
|
|
1090
|
+
"let encodeURI = Value::Function(Rc::new(|args: &[Value]| tish_encode_uri(args)));",
|
|
1091
|
+
);
|
|
1092
|
+
self.writeln(
|
|
1093
|
+
"let isFinite = Value::Function(Rc::new(|args: &[Value]| tish_is_finite(args)));",
|
|
1094
|
+
);
|
|
925
1095
|
self.writeln("let isNaN = Value::Function(Rc::new(|args: &[Value]| tish_is_nan(args)));");
|
|
926
1096
|
self.writeln("let Infinity = Value::Number(f64::INFINITY);");
|
|
927
1097
|
self.writeln("let NaN = Value::Number(f64::NAN);");
|
|
928
1098
|
self.writeln("let Math = Value::Object(Rc::new(RefCell::new(ObjectMap::from([");
|
|
929
1099
|
self.indent += 1;
|
|
930
|
-
self.writeln(
|
|
1100
|
+
self.writeln(
|
|
1101
|
+
"(Arc::from(\"abs\"), Value::Function(Rc::new(|args: &[Value]| tish_math_abs(args)))),",
|
|
1102
|
+
);
|
|
931
1103
|
self.writeln("(Arc::from(\"sqrt\"), Value::Function(Rc::new(|args: &[Value]| tish_math_sqrt(args)))),");
|
|
932
|
-
self.writeln(
|
|
933
|
-
|
|
1104
|
+
self.writeln(
|
|
1105
|
+
"(Arc::from(\"min\"), Value::Function(Rc::new(|args: &[Value]| tish_math_min(args)))),",
|
|
1106
|
+
);
|
|
1107
|
+
self.writeln(
|
|
1108
|
+
"(Arc::from(\"max\"), Value::Function(Rc::new(|args: &[Value]| tish_math_max(args)))),",
|
|
1109
|
+
);
|
|
934
1110
|
self.writeln("(Arc::from(\"floor\"), Value::Function(Rc::new(|args: &[Value]| tish_math_floor(args)))),");
|
|
935
1111
|
self.writeln("(Arc::from(\"ceil\"), Value::Function(Rc::new(|args: &[Value]| tish_math_ceil(args)))),");
|
|
936
1112
|
self.writeln("(Arc::from(\"round\"), Value::Function(Rc::new(|args: &[Value]| tish_math_round(args)))),");
|
|
937
1113
|
self.writeln("(Arc::from(\"random\"), Value::Function(Rc::new(|args: &[Value]| tish_math_random(args)))),");
|
|
938
|
-
self.writeln(
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
self.writeln(
|
|
942
|
-
|
|
943
|
-
|
|
1114
|
+
self.writeln(
|
|
1115
|
+
"(Arc::from(\"pow\"), Value::Function(Rc::new(|args: &[Value]| tish_math_pow(args)))),",
|
|
1116
|
+
);
|
|
1117
|
+
self.writeln(
|
|
1118
|
+
"(Arc::from(\"sin\"), Value::Function(Rc::new(|args: &[Value]| tish_math_sin(args)))),",
|
|
1119
|
+
);
|
|
1120
|
+
self.writeln(
|
|
1121
|
+
"(Arc::from(\"cos\"), Value::Function(Rc::new(|args: &[Value]| tish_math_cos(args)))),",
|
|
1122
|
+
);
|
|
1123
|
+
self.writeln(
|
|
1124
|
+
"(Arc::from(\"tan\"), Value::Function(Rc::new(|args: &[Value]| tish_math_tan(args)))),",
|
|
1125
|
+
);
|
|
1126
|
+
self.writeln(
|
|
1127
|
+
"(Arc::from(\"log\"), Value::Function(Rc::new(|args: &[Value]| tish_math_log(args)))),",
|
|
1128
|
+
);
|
|
1129
|
+
self.writeln(
|
|
1130
|
+
"(Arc::from(\"exp\"), Value::Function(Rc::new(|args: &[Value]| tish_math_exp(args)))),",
|
|
1131
|
+
);
|
|
944
1132
|
self.writeln("(Arc::from(\"sign\"), Value::Function(Rc::new(|args: &[Value]| tish_math_sign(args)))),");
|
|
945
1133
|
self.writeln("(Arc::from(\"trunc\"), Value::Function(Rc::new(|args: &[Value]| tish_math_trunc(args)))),");
|
|
946
1134
|
self.writeln("(Arc::from(\"PI\"), Value::Number(std::f64::consts::PI)),");
|
|
@@ -968,7 +1156,9 @@ impl Codegen {
|
|
|
968
1156
|
|
|
969
1157
|
self.writeln("let Date = Value::Object(Rc::new(RefCell::new(ObjectMap::from([");
|
|
970
1158
|
self.indent += 1;
|
|
971
|
-
self.writeln(
|
|
1159
|
+
self.writeln(
|
|
1160
|
+
"(Arc::from(\"now\"), Value::Function(Rc::new(|args: &[Value]| tish_date_now(args)))),",
|
|
1161
|
+
);
|
|
972
1162
|
self.indent -= 1;
|
|
973
1163
|
self.writeln("]))));");
|
|
974
1164
|
|
|
@@ -993,14 +1183,18 @@ impl Codegen {
|
|
|
993
1183
|
self.writeln("p.insert(Arc::from(\"cwd\"), Value::Function(Rc::new(|args: &[Value]| tish_process_cwd(args))));");
|
|
994
1184
|
self.writeln("p.insert(Arc::from(\"exec\"), Value::Function(Rc::new(|args: &[Value]| tish_process_exec(args))));");
|
|
995
1185
|
self.writeln("let argv: Vec<Value> = std::env::args().map(|s| Value::String(s.into())).collect();");
|
|
996
|
-
self.writeln(
|
|
1186
|
+
self.writeln(
|
|
1187
|
+
"p.insert(Arc::from(\"argv\"), Value::Array(Rc::new(RefCell::new(argv))));",
|
|
1188
|
+
);
|
|
997
1189
|
self.writeln("let mut env_obj = ObjectMap::default();");
|
|
998
1190
|
self.writeln("for (key, value) in std::env::vars() {");
|
|
999
1191
|
self.indent += 1;
|
|
1000
1192
|
self.writeln("env_obj.insert(Arc::from(key.as_str()), Value::String(value.into()));");
|
|
1001
1193
|
self.indent -= 1;
|
|
1002
1194
|
self.writeln("}");
|
|
1003
|
-
self.writeln(
|
|
1195
|
+
self.writeln(
|
|
1196
|
+
"p.insert(Arc::from(\"env\"), Value::Object(Rc::new(RefCell::new(env_obj))));",
|
|
1197
|
+
);
|
|
1004
1198
|
self.writeln("p");
|
|
1005
1199
|
self.indent -= 1;
|
|
1006
1200
|
self.writeln("})));");
|
|
@@ -1032,16 +1226,28 @@ impl Codegen {
|
|
|
1032
1226
|
}
|
|
1033
1227
|
|
|
1034
1228
|
if self.has_feature("fs") {
|
|
1035
|
-
self.writeln(
|
|
1036
|
-
|
|
1229
|
+
self.writeln(
|
|
1230
|
+
"let readFile = Value::Function(Rc::new(|args: &[Value]| tish_read_file(args)));",
|
|
1231
|
+
);
|
|
1232
|
+
self.writeln(
|
|
1233
|
+
"let writeFile = Value::Function(Rc::new(|args: &[Value]| tish_write_file(args)));",
|
|
1234
|
+
);
|
|
1037
1235
|
self.writeln("let fileExists = Value::Function(Rc::new(|args: &[Value]| tish_file_exists(args)));");
|
|
1038
|
-
self.writeln(
|
|
1039
|
-
|
|
1040
|
-
|
|
1236
|
+
self.writeln(
|
|
1237
|
+
"let isDir = Value::Function(Rc::new(|args: &[Value]| tish_is_dir(args)));",
|
|
1238
|
+
);
|
|
1239
|
+
self.writeln(
|
|
1240
|
+
"let readDir = Value::Function(Rc::new(|args: &[Value]| tish_read_dir(args)));",
|
|
1241
|
+
);
|
|
1242
|
+
self.writeln(
|
|
1243
|
+
"let mkdir = Value::Function(Rc::new(|args: &[Value]| tish_mkdir(args)));",
|
|
1244
|
+
);
|
|
1041
1245
|
}
|
|
1042
1246
|
|
|
1043
1247
|
if self.has_feature("regex") {
|
|
1044
|
-
self.writeln(
|
|
1248
|
+
self.writeln(
|
|
1249
|
+
"let RegExp = Value::Function(Rc::new(|args: &[Value]| regexp_new(args)));",
|
|
1250
|
+
);
|
|
1045
1251
|
}
|
|
1046
1252
|
|
|
1047
1253
|
if self.program_has_jsx {
|
|
@@ -1049,7 +1255,9 @@ impl Codegen {
|
|
|
1049
1255
|
self.writeln("let Fragment = fragment_value();");
|
|
1050
1256
|
self.writeln("let h = Value::Function(Rc::new(|args: &[Value]| ui_h(args)));");
|
|
1051
1257
|
self.writeln("let text = Value::Function(Rc::new(|args: &[Value]| ui_text(args)));");
|
|
1052
|
-
self.writeln(
|
|
1258
|
+
self.writeln(
|
|
1259
|
+
"let useState = Value::Function(Rc::new(|args: &[Value]| native_use_state(args)));",
|
|
1260
|
+
);
|
|
1053
1261
|
self.writeln("let createRoot = Value::Function(Rc::new(|args: &[Value]| native_create_root(args)));");
|
|
1054
1262
|
}
|
|
1055
1263
|
|
|
@@ -1060,7 +1268,10 @@ impl Codegen {
|
|
|
1060
1268
|
*self.function_scope_stack.last_mut().unwrap() = top_level_funcs.clone();
|
|
1061
1269
|
for func_name in &top_level_funcs {
|
|
1062
1270
|
let escaped = Self::escape_ident(func_name);
|
|
1063
|
-
self.writeln(&format!(
|
|
1271
|
+
self.writeln(&format!(
|
|
1272
|
+
"let {}_cell: Rc<RefCell<Value>> = Rc::new(RefCell::new(Value::Null));",
|
|
1273
|
+
escaped
|
|
1274
|
+
));
|
|
1064
1275
|
}
|
|
1065
1276
|
|
|
1066
1277
|
// Initialize usage analyzer for move/clone optimization
|
|
@@ -1097,9 +1308,11 @@ impl Codegen {
|
|
|
1097
1308
|
self.indent += 1;
|
|
1098
1309
|
self.type_context.push_scope();
|
|
1099
1310
|
self.outer_vars_stack.push(Vec::new());
|
|
1100
|
-
self.rc_cell_storage_scopes
|
|
1311
|
+
self.rc_cell_storage_scopes
|
|
1312
|
+
.push(std::collections::HashSet::new());
|
|
1101
1313
|
// Prepass: vars that must be RefCell because nested closures capture and mutate them
|
|
1102
|
-
let vars_mutated_by_nested =
|
|
1314
|
+
let vars_mutated_by_nested =
|
|
1315
|
+
Self::collect_vars_mutated_by_nested_closures(statements);
|
|
1103
1316
|
for v in &vars_mutated_by_nested {
|
|
1104
1317
|
self.refcell_wrapped_vars.insert(v.clone());
|
|
1105
1318
|
}
|
|
@@ -1109,7 +1322,10 @@ impl Codegen {
|
|
|
1109
1322
|
// Create cells for all functions in this scope
|
|
1110
1323
|
for func_name in &func_names {
|
|
1111
1324
|
let escaped = Self::escape_ident(func_name);
|
|
1112
|
-
self.writeln(&format!(
|
|
1325
|
+
self.writeln(&format!(
|
|
1326
|
+
"let {}_cell: Rc<RefCell<Value>> = Rc::new(RefCell::new(Value::Null));",
|
|
1327
|
+
escaped
|
|
1328
|
+
));
|
|
1113
1329
|
}
|
|
1114
1330
|
for s in statements {
|
|
1115
1331
|
self.emit_statement(s)?;
|
|
@@ -1124,7 +1340,13 @@ impl Codegen {
|
|
|
1124
1340
|
self.indent -= 1;
|
|
1125
1341
|
self.writeln("}");
|
|
1126
1342
|
}
|
|
1127
|
-
Statement::VarDecl {
|
|
1343
|
+
Statement::VarDecl {
|
|
1344
|
+
name,
|
|
1345
|
+
mutable,
|
|
1346
|
+
type_ann,
|
|
1347
|
+
init,
|
|
1348
|
+
..
|
|
1349
|
+
} => {
|
|
1128
1350
|
// Determine the Rust type from annotation
|
|
1129
1351
|
let rust_type = type_ann
|
|
1130
1352
|
.as_ref()
|
|
@@ -1133,10 +1355,10 @@ impl Codegen {
|
|
|
1133
1355
|
|
|
1134
1356
|
// Track the variable type
|
|
1135
1357
|
self.type_context.define(name.as_ref(), rust_type.clone());
|
|
1136
|
-
|
|
1358
|
+
|
|
1137
1359
|
let mutability = if *mutable { "let mut" } else { "let" };
|
|
1138
1360
|
let escaped_name = Self::escape_ident(name.as_ref());
|
|
1139
|
-
|
|
1361
|
+
|
|
1140
1362
|
if rust_type.is_native() {
|
|
1141
1363
|
// Generate native typed variable
|
|
1142
1364
|
let expr_str = match init.as_ref() {
|
|
@@ -1164,8 +1386,7 @@ impl Codegen {
|
|
|
1164
1386
|
let s = self.emit_expr(e)?;
|
|
1165
1387
|
// Variable refs (Ident) in init must always clone: they may be used
|
|
1166
1388
|
// multiple times (e.g. in a loop body) and we cannot move.
|
|
1167
|
-
let needs = matches!(e, Expr::Ident { .. })
|
|
1168
|
-
|| self.should_clone(e);
|
|
1389
|
+
let needs = matches!(e, Expr::Ident { .. }) || self.should_clone(e);
|
|
1169
1390
|
(s, needs)
|
|
1170
1391
|
}
|
|
1171
1392
|
None => ("Value::Null".to_string(), false),
|
|
@@ -1177,23 +1398,39 @@ impl Codegen {
|
|
|
1177
1398
|
} else {
|
|
1178
1399
|
expr_str.to_string()
|
|
1179
1400
|
};
|
|
1180
|
-
self.writeln(&format!(
|
|
1401
|
+
self.writeln(&format!(
|
|
1402
|
+
"let {} = std::rc::Rc::new(RefCell::new({}));",
|
|
1403
|
+
escaped_name, init_val
|
|
1404
|
+
));
|
|
1181
1405
|
self.rc_cell_storage_define(name.as_ref());
|
|
1182
1406
|
} else if clone_needed {
|
|
1183
|
-
self.writeln(&format!(
|
|
1407
|
+
self.writeln(&format!(
|
|
1408
|
+
"{} {} = ({}).clone();",
|
|
1409
|
+
mutability, escaped_name, expr_str
|
|
1410
|
+
));
|
|
1184
1411
|
} else {
|
|
1185
1412
|
self.writeln(&format!("{} {} = {};", mutability, escaped_name, expr_str));
|
|
1186
1413
|
}
|
|
1187
1414
|
}
|
|
1188
|
-
|
|
1415
|
+
|
|
1189
1416
|
if let Some(scope) = self.outer_vars_stack.last_mut() {
|
|
1190
1417
|
scope.push(name.to_string());
|
|
1191
1418
|
}
|
|
1192
1419
|
}
|
|
1193
|
-
Statement::VarDeclDestructure {
|
|
1420
|
+
Statement::VarDeclDestructure {
|
|
1421
|
+
pattern,
|
|
1422
|
+
mutable,
|
|
1423
|
+
init,
|
|
1424
|
+
span,
|
|
1425
|
+
..
|
|
1426
|
+
} => {
|
|
1194
1427
|
let expr = self.emit_expr(init)?;
|
|
1195
1428
|
let mutability = if *mutable { "let mut" } else { "let" };
|
|
1196
|
-
let clone_suffix = if Self::needs_clone(init) {
|
|
1429
|
+
let clone_suffix = if Self::needs_clone(init) {
|
|
1430
|
+
".clone()"
|
|
1431
|
+
} else {
|
|
1432
|
+
""
|
|
1433
|
+
};
|
|
1197
1434
|
self.writeln(&format!("let _destruct_val = ({}){};", expr, clone_suffix));
|
|
1198
1435
|
self.emit_destruct_bindings(pattern, "_destruct_val", mutability, *span)?;
|
|
1199
1436
|
}
|
|
@@ -1232,7 +1469,12 @@ impl Codegen {
|
|
|
1232
1469
|
self.indent -= 1;
|
|
1233
1470
|
self.writeln("}");
|
|
1234
1471
|
}
|
|
1235
|
-
Statement::ForOf {
|
|
1472
|
+
Statement::ForOf {
|
|
1473
|
+
name,
|
|
1474
|
+
iterable,
|
|
1475
|
+
body,
|
|
1476
|
+
..
|
|
1477
|
+
} => {
|
|
1236
1478
|
let iter_expr = self.emit_expr(iterable)?;
|
|
1237
1479
|
self.writeln(&format!("{{ let _fof = ({}).clone();", iter_expr));
|
|
1238
1480
|
self.indent += 1;
|
|
@@ -1242,7 +1484,10 @@ impl Codegen {
|
|
|
1242
1484
|
self.indent += 1;
|
|
1243
1485
|
self.writeln("for _v in _arr.borrow().iter() {");
|
|
1244
1486
|
self.indent += 1;
|
|
1245
|
-
self.writeln(&format!(
|
|
1487
|
+
self.writeln(&format!(
|
|
1488
|
+
"let {} = _v.clone();",
|
|
1489
|
+
Self::escape_ident(name.as_ref())
|
|
1490
|
+
));
|
|
1246
1491
|
self.emit_statement(body)?;
|
|
1247
1492
|
self.indent -= 1;
|
|
1248
1493
|
self.writeln("}");
|
|
@@ -1320,12 +1565,10 @@ impl Codegen {
|
|
|
1320
1565
|
}
|
|
1321
1566
|
}
|
|
1322
1567
|
Statement::Continue { .. } => {
|
|
1323
|
-
let snippet = self
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
)
|
|
1328
|
-
});
|
|
1568
|
+
let snippet = self
|
|
1569
|
+
.loop_stack
|
|
1570
|
+
.last()
|
|
1571
|
+
.map(|(label, update)| (label.clone(), update.clone()));
|
|
1329
1572
|
if let Some((label, Some(update))) = snippet {
|
|
1330
1573
|
self.writeln(&update);
|
|
1331
1574
|
self.writeln(&format!("continue {};", label));
|
|
@@ -1341,7 +1584,12 @@ impl Codegen {
|
|
|
1341
1584
|
span: None,
|
|
1342
1585
|
});
|
|
1343
1586
|
}
|
|
1344
|
-
Statement::Switch {
|
|
1587
|
+
Statement::Switch {
|
|
1588
|
+
expr,
|
|
1589
|
+
cases,
|
|
1590
|
+
default_body,
|
|
1591
|
+
..
|
|
1592
|
+
} => {
|
|
1345
1593
|
let e = self.emit_expr(expr)?;
|
|
1346
1594
|
self.writeln(&format!("let _sv = {};", e));
|
|
1347
1595
|
self.writeln("match () {");
|
|
@@ -1407,7 +1655,7 @@ impl Codegen {
|
|
|
1407
1655
|
self.writeln("Ok(Value::Null)");
|
|
1408
1656
|
self.indent -= 1;
|
|
1409
1657
|
self.writeln("})();");
|
|
1410
|
-
|
|
1658
|
+
|
|
1411
1659
|
if let Some(catch_stmt) = catch_body {
|
|
1412
1660
|
if let Some(param) = catch_param {
|
|
1413
1661
|
self.writeln("if let Err(e) = _try_result {");
|
|
@@ -1417,7 +1665,10 @@ impl Codegen {
|
|
|
1417
1665
|
self.writeln("Ok(tish_err) => {");
|
|
1418
1666
|
self.indent += 1;
|
|
1419
1667
|
self.writeln("if let tishlang_runtime::TishError::Throw(v) = *tish_err {");
|
|
1420
|
-
self.writeln(&format!(
|
|
1668
|
+
self.writeln(&format!(
|
|
1669
|
+
"let {} = v.clone();",
|
|
1670
|
+
Self::escape_ident(param.as_ref())
|
|
1671
|
+
));
|
|
1421
1672
|
self.emit_statement(catch_stmt)?;
|
|
1422
1673
|
self.writeln("} else { return Err(Box::new(tish_err)); }");
|
|
1423
1674
|
self.indent -= 1;
|
|
@@ -1434,25 +1685,36 @@ impl Codegen {
|
|
|
1434
1685
|
}
|
|
1435
1686
|
self.writeln("}");
|
|
1436
1687
|
}
|
|
1437
|
-
|
|
1688
|
+
|
|
1438
1689
|
if let Some(finally_stmt) = finally_body {
|
|
1439
1690
|
self.emit_statement(finally_stmt)?;
|
|
1440
1691
|
}
|
|
1441
1692
|
}
|
|
1442
|
-
Statement::FunDecl {
|
|
1693
|
+
Statement::FunDecl {
|
|
1694
|
+
name,
|
|
1695
|
+
params,
|
|
1696
|
+
rest_param,
|
|
1697
|
+
body,
|
|
1698
|
+
span,
|
|
1699
|
+
..
|
|
1700
|
+
} => {
|
|
1443
1701
|
// Use Rc<RefCell<>> pattern to allow recursive function calls
|
|
1444
1702
|
// The function can reference itself through the cell
|
|
1445
1703
|
let name_raw = name.as_ref();
|
|
1446
1704
|
let name_str = Self::escape_ident(name_raw);
|
|
1447
1705
|
// Check if cell was already created by block prescan
|
|
1448
|
-
let cell_exists = self
|
|
1706
|
+
let cell_exists = self
|
|
1707
|
+
.function_scope_stack
|
|
1449
1708
|
.last()
|
|
1450
1709
|
.map(|scope| scope.contains(&name_raw.to_string()))
|
|
1451
1710
|
.unwrap_or(false);
|
|
1452
1711
|
if !cell_exists {
|
|
1453
|
-
self.writeln(&format!(
|
|
1712
|
+
self.writeln(&format!(
|
|
1713
|
+
"let {}_cell: Rc<RefCell<Value>> = Rc::new(RefCell::new(Value::Null));",
|
|
1714
|
+
name_str
|
|
1715
|
+
));
|
|
1454
1716
|
}
|
|
1455
|
-
|
|
1717
|
+
|
|
1456
1718
|
// Analyze body to find which identifiers are actually referenced
|
|
1457
1719
|
let mut referenced = HashSet::new();
|
|
1458
1720
|
Self::collect_stmt_idents(body, &mut referenced);
|
|
@@ -1461,9 +1723,10 @@ impl Codegen {
|
|
|
1461
1723
|
.flat_map(|p| p.bound_names())
|
|
1462
1724
|
.map(|n| n.to_string())
|
|
1463
1725
|
.collect();
|
|
1464
|
-
|
|
1726
|
+
|
|
1465
1727
|
// Collect all outer parameters that need to be captured (only those referenced)
|
|
1466
|
-
let outer_params: Vec<String> = self
|
|
1728
|
+
let outer_params: Vec<String> = self
|
|
1729
|
+
.outer_params_stack
|
|
1467
1730
|
.iter()
|
|
1468
1731
|
.flat_map(|p| p.iter().cloned())
|
|
1469
1732
|
.filter(|name| referenced.contains(name) && !param_names.contains(name))
|
|
@@ -1472,11 +1735,31 @@ impl Codegen {
|
|
|
1472
1735
|
// Exclude params and variables declared in this function's body (locals)
|
|
1473
1736
|
let mut local_var_names = HashSet::new();
|
|
1474
1737
|
Self::collect_local_var_names(body, &mut local_var_names);
|
|
1475
|
-
let outer_vars: Vec<String> = self
|
|
1738
|
+
let outer_vars: Vec<String> = self
|
|
1739
|
+
.outer_vars_stack
|
|
1476
1740
|
.iter()
|
|
1477
1741
|
.flat_map(|v| v.iter().cloned())
|
|
1478
|
-
.filter(|name|
|
|
1479
|
-
|
|
1742
|
+
.filter(|name| {
|
|
1743
|
+
referenced.contains(name)
|
|
1744
|
+
&& !param_names.contains(name)
|
|
1745
|
+
&& !local_var_names.contains(name)
|
|
1746
|
+
})
|
|
1747
|
+
.filter(|name| {
|
|
1748
|
+
![
|
|
1749
|
+
"Boolean",
|
|
1750
|
+
"console",
|
|
1751
|
+
"Math",
|
|
1752
|
+
"JSON",
|
|
1753
|
+
"Date",
|
|
1754
|
+
"process",
|
|
1755
|
+
"setTimeout",
|
|
1756
|
+
"clearTimeout",
|
|
1757
|
+
"Promise",
|
|
1758
|
+
"RegExp",
|
|
1759
|
+
"Polars",
|
|
1760
|
+
]
|
|
1761
|
+
.contains(&name.as_str())
|
|
1762
|
+
})
|
|
1480
1763
|
.collect();
|
|
1481
1764
|
|
|
1482
1765
|
// Outer vars that are assigned in the body need RefCell (capture cell, add to refcell_wrapped_vars).
|
|
@@ -1499,9 +1782,15 @@ impl Codegen {
|
|
|
1499
1782
|
for outer_var in &outer_vars {
|
|
1500
1783
|
let var_escaped = Self::escape_ident(outer_var);
|
|
1501
1784
|
if self.rc_cell_storage_contains(outer_var) {
|
|
1502
|
-
self.writeln(&format!(
|
|
1785
|
+
self.writeln(&format!(
|
|
1786
|
+
"let {}_cell = {}.clone();",
|
|
1787
|
+
var_escaped, var_escaped
|
|
1788
|
+
));
|
|
1503
1789
|
} else {
|
|
1504
|
-
self.writeln(&format!(
|
|
1790
|
+
self.writeln(&format!(
|
|
1791
|
+
"let {}_cell = std::rc::Rc::new(RefCell::new({}.clone()));",
|
|
1792
|
+
var_escaped, var_escaped
|
|
1793
|
+
));
|
|
1505
1794
|
}
|
|
1506
1795
|
}
|
|
1507
1796
|
|
|
@@ -1510,32 +1799,62 @@ impl Codegen {
|
|
|
1510
1799
|
// Clone RefCell for outer vars so closure can capture
|
|
1511
1800
|
for outer_var in &outer_vars {
|
|
1512
1801
|
let var_escaped = Self::escape_ident(outer_var);
|
|
1513
|
-
self.writeln(&format!(
|
|
1802
|
+
self.writeln(&format!(
|
|
1803
|
+
"let {}_cell = {}_cell.clone();",
|
|
1804
|
+
var_escaped, var_escaped
|
|
1805
|
+
));
|
|
1514
1806
|
}
|
|
1515
1807
|
// Clone the cell so the closure can reference the function recursively
|
|
1516
1808
|
let needs_self_ref = referenced.contains(name_raw);
|
|
1517
1809
|
if needs_self_ref {
|
|
1518
|
-
self.writeln(&format!(
|
|
1810
|
+
self.writeln(&format!(
|
|
1811
|
+
"let {}_ref = {}_cell.clone();",
|
|
1812
|
+
name_str, name_str
|
|
1813
|
+
));
|
|
1519
1814
|
}
|
|
1520
1815
|
// Clone sibling function cells for mutual recursion
|
|
1521
|
-
let sibling_fns: Vec<String> = self
|
|
1816
|
+
let sibling_fns: Vec<String> = self
|
|
1817
|
+
.function_scope_stack
|
|
1522
1818
|
.last()
|
|
1523
|
-
.map(|scope|
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1819
|
+
.map(|scope| {
|
|
1820
|
+
scope
|
|
1821
|
+
.iter()
|
|
1822
|
+
.filter(|s| s.as_str() != name_raw && referenced.contains(s.as_str()))
|
|
1823
|
+
.cloned()
|
|
1824
|
+
.collect()
|
|
1825
|
+
})
|
|
1527
1826
|
.unwrap_or_default();
|
|
1528
1827
|
for sibling in &sibling_fns {
|
|
1529
1828
|
let sibling_escaped = Self::escape_ident(sibling);
|
|
1530
|
-
self.writeln(&format!(
|
|
1829
|
+
self.writeln(&format!(
|
|
1830
|
+
"let {}_ref = {}_cell.clone();",
|
|
1831
|
+
sibling_escaped, sibling_escaped
|
|
1832
|
+
));
|
|
1531
1833
|
}
|
|
1532
1834
|
// Clone outer parameters so they can be captured by the move closure
|
|
1533
1835
|
for outer_param in &outer_params {
|
|
1534
1836
|
let param_escaped = Self::escape_ident(outer_param);
|
|
1535
|
-
self.writeln(&format!(
|
|
1837
|
+
self.writeln(&format!(
|
|
1838
|
+
"let {} = {}.clone();",
|
|
1839
|
+
param_escaped, param_escaped
|
|
1840
|
+
));
|
|
1536
1841
|
}
|
|
1537
1842
|
// Only clone builtins that are actually referenced (clone so outer scope can still use them, e.g. process for PORT before serve)
|
|
1538
|
-
for builtin in &[
|
|
1843
|
+
for builtin in &[
|
|
1844
|
+
"Boolean",
|
|
1845
|
+
"console",
|
|
1846
|
+
"Math",
|
|
1847
|
+
"JSON",
|
|
1848
|
+
"Date",
|
|
1849
|
+
"Uint8Array",
|
|
1850
|
+
"AudioContext",
|
|
1851
|
+
"process",
|
|
1852
|
+
"setTimeout",
|
|
1853
|
+
"clearTimeout",
|
|
1854
|
+
"Promise",
|
|
1855
|
+
"RegExp",
|
|
1856
|
+
"Polars",
|
|
1857
|
+
] {
|
|
1539
1858
|
if referenced.contains(*builtin) {
|
|
1540
1859
|
self.writeln(&format!("let {} = {}.clone();", builtin, builtin));
|
|
1541
1860
|
}
|
|
@@ -1545,21 +1864,33 @@ impl Codegen {
|
|
|
1545
1864
|
// Mutable outer vars: capture the RefCell so assignments use borrow_mut
|
|
1546
1865
|
for outer_var in &mutable_outer_vars {
|
|
1547
1866
|
let var_escaped = Self::escape_ident(outer_var);
|
|
1548
|
-
self.writeln(&format!(
|
|
1867
|
+
self.writeln(&format!(
|
|
1868
|
+
"let {} = {}_cell.clone();",
|
|
1869
|
+
var_escaped, var_escaped
|
|
1870
|
+
));
|
|
1549
1871
|
}
|
|
1550
1872
|
// Read-only outer vars: Value binding from borrow (avoids param-shadow issues)
|
|
1551
1873
|
for outer_var in &read_only_outer_vars {
|
|
1552
1874
|
let var_escaped = Self::escape_ident(outer_var);
|
|
1553
|
-
self.writeln(&format!(
|
|
1875
|
+
self.writeln(&format!(
|
|
1876
|
+
"let {} = (*{}_cell.borrow()).clone();",
|
|
1877
|
+
var_escaped, var_escaped
|
|
1878
|
+
));
|
|
1554
1879
|
}
|
|
1555
1880
|
// Make the function available by its name inside the closure (only if recursive)
|
|
1556
1881
|
if needs_self_ref {
|
|
1557
|
-
self.writeln(&format!(
|
|
1882
|
+
self.writeln(&format!(
|
|
1883
|
+
"let {} = (*{}_ref.borrow()).clone();",
|
|
1884
|
+
name_str, name_str
|
|
1885
|
+
));
|
|
1558
1886
|
}
|
|
1559
1887
|
// Make sibling functions available for mutual recursion
|
|
1560
1888
|
for sibling in &sibling_fns {
|
|
1561
1889
|
let sibling_escaped = Self::escape_ident(sibling);
|
|
1562
|
-
self.writeln(&format!(
|
|
1890
|
+
self.writeln(&format!(
|
|
1891
|
+
"let {} = (*{}_ref.borrow()).clone();",
|
|
1892
|
+
sibling_escaped, sibling_escaped
|
|
1893
|
+
));
|
|
1563
1894
|
}
|
|
1564
1895
|
// Extract just the parameter names (type annotations are parsed but not used in codegen yet)
|
|
1565
1896
|
let current_param_names: Vec<String> = params
|
|
@@ -1594,10 +1925,10 @@ impl Codegen {
|
|
|
1594
1925
|
params.len()
|
|
1595
1926
|
));
|
|
1596
1927
|
}
|
|
1597
|
-
|
|
1928
|
+
|
|
1598
1929
|
// Push current params to stack for nested functions
|
|
1599
1930
|
self.outer_params_stack.push(current_param_names);
|
|
1600
|
-
|
|
1931
|
+
|
|
1601
1932
|
// Function bodies are sync closures (even Tish async fn) - use block_on for await
|
|
1602
1933
|
self.async_context_stack.push(false);
|
|
1603
1934
|
|
|
@@ -1606,7 +1937,7 @@ impl Codegen {
|
|
|
1606
1937
|
for v in &mutable_outer_vars {
|
|
1607
1938
|
self.refcell_wrapped_vars.insert(v.clone());
|
|
1608
1939
|
}
|
|
1609
|
-
|
|
1940
|
+
|
|
1610
1941
|
// Pre-scan body for nested functions (handles function body as Block)
|
|
1611
1942
|
if let Statement::Block { statements, .. } = body.as_ref() {
|
|
1612
1943
|
let nested_func_names = self.prescan_function_decls(statements);
|
|
@@ -1614,7 +1945,10 @@ impl Codegen {
|
|
|
1614
1945
|
// Create cells for nested functions
|
|
1615
1946
|
for func_name in &nested_func_names {
|
|
1616
1947
|
let escaped = Self::escape_ident(func_name);
|
|
1617
|
-
self.writeln(&format!(
|
|
1948
|
+
self.writeln(&format!(
|
|
1949
|
+
"let {}_cell: Rc<RefCell<Value>> = Rc::new(RefCell::new(Value::Null));",
|
|
1950
|
+
escaped
|
|
1951
|
+
));
|
|
1618
1952
|
}
|
|
1619
1953
|
for s in statements {
|
|
1620
1954
|
self.emit_statement(s)?;
|
|
@@ -1625,22 +1959,25 @@ impl Codegen {
|
|
|
1625
1959
|
self.emit_statement(body)?;
|
|
1626
1960
|
self.function_scope_stack.pop();
|
|
1627
1961
|
}
|
|
1628
|
-
|
|
1962
|
+
|
|
1629
1963
|
self.async_context_stack.pop();
|
|
1630
1964
|
|
|
1631
1965
|
// Restore refcell_wrapped_vars (remove mutable outer vars we added)
|
|
1632
1966
|
self.refcell_wrapped_vars = saved_refcell;
|
|
1633
|
-
|
|
1967
|
+
|
|
1634
1968
|
// Pop params stack
|
|
1635
1969
|
self.outer_params_stack.pop();
|
|
1636
|
-
|
|
1970
|
+
|
|
1637
1971
|
self.writeln("Value::Null");
|
|
1638
1972
|
self.indent -= 1;
|
|
1639
1973
|
self.writeln("}))");
|
|
1640
1974
|
self.indent -= 1;
|
|
1641
1975
|
self.writeln("};");
|
|
1642
1976
|
// Update the cell with the actual function value
|
|
1643
|
-
self.writeln(&format!(
|
|
1977
|
+
self.writeln(&format!(
|
|
1978
|
+
"*{}_cell.borrow_mut() = {}.clone();",
|
|
1979
|
+
name_str, name_str
|
|
1980
|
+
));
|
|
1644
1981
|
}
|
|
1645
1982
|
}
|
|
1646
1983
|
Ok(())
|
|
@@ -1673,7 +2010,10 @@ impl Codegen {
|
|
|
1673
2010
|
}
|
|
1674
2011
|
}
|
|
1675
2012
|
}
|
|
1676
|
-
Ok(format!(
|
|
2013
|
+
Ok(format!(
|
|
2014
|
+
"{{ let mut _args: Vec<Value> = Vec::new(); {} _args }}",
|
|
2015
|
+
parts.join(" ")
|
|
2016
|
+
))
|
|
1677
2017
|
} else {
|
|
1678
2018
|
let mut emitted = Vec::new();
|
|
1679
2019
|
for arg in args {
|
|
@@ -1695,7 +2035,13 @@ impl Codegen {
|
|
|
1695
2035
|
}
|
|
1696
2036
|
}
|
|
1697
2037
|
|
|
1698
|
-
fn emit_destruct_bindings(
|
|
2038
|
+
fn emit_destruct_bindings(
|
|
2039
|
+
&mut self,
|
|
2040
|
+
pattern: &DestructPattern,
|
|
2041
|
+
value_expr: &str,
|
|
2042
|
+
mutability: &str,
|
|
2043
|
+
span: Span,
|
|
2044
|
+
) -> Result<(), CompileError> {
|
|
1699
2045
|
// Flat `let` bindings so names stay in scope for the rest of the function (e.g. JSX).
|
|
1700
2046
|
match pattern {
|
|
1701
2047
|
DestructPattern::Array(elements) => {
|
|
@@ -2852,7 +3198,7 @@ impl Codegen {
|
|
|
2852
3198
|
}
|
|
2853
3199
|
})
|
|
2854
3200
|
}
|
|
2855
|
-
|
|
3201
|
+
|
|
2856
3202
|
/// Collect all identifiers referenced in an arrow body
|
|
2857
3203
|
fn collect_referenced_idents(body: &ArrowBody) -> HashSet<String> {
|
|
2858
3204
|
let mut idents = HashSet::new();
|
|
@@ -2862,10 +3208,12 @@ impl Codegen {
|
|
|
2862
3208
|
}
|
|
2863
3209
|
idents
|
|
2864
3210
|
}
|
|
2865
|
-
|
|
3211
|
+
|
|
2866
3212
|
fn collect_expr_idents(expr: &Expr, idents: &mut HashSet<String>) {
|
|
2867
3213
|
match expr {
|
|
2868
|
-
Expr::Ident { name, .. } => {
|
|
3214
|
+
Expr::Ident { name, .. } => {
|
|
3215
|
+
idents.insert(name.to_string());
|
|
3216
|
+
}
|
|
2869
3217
|
Expr::Assign { name, value, .. } => {
|
|
2870
3218
|
idents.insert(name.to_string());
|
|
2871
3219
|
Self::collect_expr_idents(value, idents);
|
|
@@ -2879,7 +3227,9 @@ impl Codegen {
|
|
|
2879
3227
|
Self::collect_expr_idents(callee, idents);
|
|
2880
3228
|
for arg in args {
|
|
2881
3229
|
match arg {
|
|
2882
|
-
CallArg::Expr(e) | CallArg::Spread(e) =>
|
|
3230
|
+
CallArg::Expr(e) | CallArg::Spread(e) => {
|
|
3231
|
+
Self::collect_expr_idents(e, idents)
|
|
3232
|
+
}
|
|
2883
3233
|
}
|
|
2884
3234
|
}
|
|
2885
3235
|
}
|
|
@@ -2887,19 +3237,28 @@ impl Codegen {
|
|
|
2887
3237
|
Self::collect_expr_idents(callee, idents);
|
|
2888
3238
|
for arg in args {
|
|
2889
3239
|
match arg {
|
|
2890
|
-
CallArg::Expr(e) | CallArg::Spread(e) =>
|
|
3240
|
+
CallArg::Expr(e) | CallArg::Spread(e) => {
|
|
3241
|
+
Self::collect_expr_idents(e, idents)
|
|
3242
|
+
}
|
|
2891
3243
|
}
|
|
2892
3244
|
}
|
|
2893
3245
|
}
|
|
2894
3246
|
Expr::Member { object, prop, .. } => {
|
|
2895
3247
|
Self::collect_expr_idents(object, idents);
|
|
2896
|
-
if let MemberProp::Expr(e) = prop {
|
|
3248
|
+
if let MemberProp::Expr(e) = prop {
|
|
3249
|
+
Self::collect_expr_idents(e, idents);
|
|
3250
|
+
}
|
|
2897
3251
|
}
|
|
2898
3252
|
Expr::MemberAssign { object, value, .. } => {
|
|
2899
3253
|
Self::collect_expr_idents(object, idents);
|
|
2900
3254
|
Self::collect_expr_idents(value, idents);
|
|
2901
3255
|
}
|
|
2902
|
-
Expr::IndexAssign {
|
|
3256
|
+
Expr::IndexAssign {
|
|
3257
|
+
object,
|
|
3258
|
+
index,
|
|
3259
|
+
value,
|
|
3260
|
+
..
|
|
3261
|
+
} => {
|
|
2903
3262
|
Self::collect_expr_idents(object, idents);
|
|
2904
3263
|
Self::collect_expr_idents(index, idents);
|
|
2905
3264
|
Self::collect_expr_idents(value, idents);
|
|
@@ -2908,13 +3267,20 @@ impl Codegen {
|
|
|
2908
3267
|
Self::collect_expr_idents(object, idents);
|
|
2909
3268
|
Self::collect_expr_idents(index, idents);
|
|
2910
3269
|
}
|
|
2911
|
-
Expr::Conditional {
|
|
3270
|
+
Expr::Conditional {
|
|
3271
|
+
cond,
|
|
3272
|
+
then_branch,
|
|
3273
|
+
else_branch,
|
|
3274
|
+
..
|
|
3275
|
+
} => {
|
|
2912
3276
|
Self::collect_expr_idents(cond, idents);
|
|
2913
3277
|
Self::collect_expr_idents(then_branch, idents);
|
|
2914
3278
|
Self::collect_expr_idents(else_branch, idents);
|
|
2915
3279
|
}
|
|
2916
|
-
Expr::PostfixInc { name, .. }
|
|
2917
|
-
|
|
3280
|
+
Expr::PostfixInc { name, .. }
|
|
3281
|
+
| Expr::PostfixDec { name, .. }
|
|
3282
|
+
| Expr::PrefixInc { name, .. }
|
|
3283
|
+
| Expr::PrefixDec { name, .. } => {
|
|
2918
3284
|
idents.insert(name.to_string());
|
|
2919
3285
|
}
|
|
2920
3286
|
Expr::CompoundAssign { name, value, .. } => {
|
|
@@ -2928,23 +3294,25 @@ impl Codegen {
|
|
|
2928
3294
|
Expr::Array { elements, .. } => {
|
|
2929
3295
|
for el in elements {
|
|
2930
3296
|
match el {
|
|
2931
|
-
ArrayElement::Expr(e) | ArrayElement::Spread(e) =>
|
|
3297
|
+
ArrayElement::Expr(e) | ArrayElement::Spread(e) => {
|
|
3298
|
+
Self::collect_expr_idents(e, idents)
|
|
3299
|
+
}
|
|
2932
3300
|
}
|
|
2933
3301
|
}
|
|
2934
3302
|
}
|
|
2935
3303
|
Expr::Object { props, .. } => {
|
|
2936
3304
|
for prop in props {
|
|
2937
3305
|
match prop {
|
|
2938
|
-
ObjectProp::KeyValue(_, e) | ObjectProp::Spread(e) =>
|
|
3306
|
+
ObjectProp::KeyValue(_, e) | ObjectProp::Spread(e) => {
|
|
3307
|
+
Self::collect_expr_idents(e, idents)
|
|
3308
|
+
}
|
|
2939
3309
|
}
|
|
2940
3310
|
}
|
|
2941
3311
|
}
|
|
2942
|
-
Expr::ArrowFunction { body, .. } => {
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
}
|
|
2947
|
-
}
|
|
3312
|
+
Expr::ArrowFunction { body, .. } => match body {
|
|
3313
|
+
ArrowBody::Expr(e) => Self::collect_expr_idents(e, idents),
|
|
3314
|
+
ArrowBody::Block(s) => Self::collect_stmt_idents(s, idents),
|
|
3315
|
+
},
|
|
2948
3316
|
Expr::NullishCoalesce { left, right, .. } => {
|
|
2949
3317
|
Self::collect_expr_idents(left, idents);
|
|
2950
3318
|
Self::collect_expr_idents(right, idents);
|
|
@@ -2952,12 +3320,20 @@ impl Codegen {
|
|
|
2952
3320
|
Expr::TypeOf { operand, .. } => Self::collect_expr_idents(operand, idents),
|
|
2953
3321
|
Expr::Await { operand, .. } => Self::collect_expr_idents(operand, idents),
|
|
2954
3322
|
Expr::TemplateLiteral { exprs, .. } => {
|
|
2955
|
-
for e in exprs {
|
|
3323
|
+
for e in exprs {
|
|
3324
|
+
Self::collect_expr_idents(e, idents);
|
|
3325
|
+
}
|
|
2956
3326
|
}
|
|
2957
|
-
Expr::JsxElement {
|
|
3327
|
+
Expr::JsxElement {
|
|
3328
|
+
props, children, ..
|
|
3329
|
+
} => {
|
|
2958
3330
|
for p in props {
|
|
2959
3331
|
match p {
|
|
2960
|
-
tishlang_ast::JsxProp::Attr {
|
|
3332
|
+
tishlang_ast::JsxProp::Attr {
|
|
3333
|
+
value: tishlang_ast::JsxAttrValue::Expr(e),
|
|
3334
|
+
..
|
|
3335
|
+
}
|
|
3336
|
+
| tishlang_ast::JsxProp::Spread(e) => Self::collect_expr_idents(e, idents),
|
|
2961
3337
|
_ => {}
|
|
2962
3338
|
}
|
|
2963
3339
|
}
|
|
@@ -2978,7 +3354,7 @@ impl Codegen {
|
|
|
2978
3354
|
Expr::Literal { .. } => {}
|
|
2979
3355
|
}
|
|
2980
3356
|
}
|
|
2981
|
-
|
|
3357
|
+
|
|
2982
3358
|
/// Collect variable names that are assigned to in a statement/body (target of =, +=, ++, etc).
|
|
2983
3359
|
fn collect_assigned_idents_in_stmt(stmt: &Statement, names: &mut HashSet<String>) {
|
|
2984
3360
|
match stmt {
|
|
@@ -2989,14 +3365,25 @@ impl Codegen {
|
|
|
2989
3365
|
Self::collect_assigned_idents_in_stmt(s, names);
|
|
2990
3366
|
}
|
|
2991
3367
|
}
|
|
2992
|
-
Statement::If {
|
|
3368
|
+
Statement::If {
|
|
3369
|
+
cond,
|
|
3370
|
+
then_branch,
|
|
3371
|
+
else_branch,
|
|
3372
|
+
..
|
|
3373
|
+
} => {
|
|
2993
3374
|
Self::collect_assigned_idents_in_expr(cond, names);
|
|
2994
3375
|
Self::collect_assigned_idents_in_stmt(then_branch, names);
|
|
2995
3376
|
if let Some(eb) = else_branch {
|
|
2996
3377
|
Self::collect_assigned_idents_in_stmt(eb, names);
|
|
2997
3378
|
}
|
|
2998
3379
|
}
|
|
2999
|
-
Statement::For {
|
|
3380
|
+
Statement::For {
|
|
3381
|
+
init,
|
|
3382
|
+
cond,
|
|
3383
|
+
update,
|
|
3384
|
+
body,
|
|
3385
|
+
..
|
|
3386
|
+
} => {
|
|
3000
3387
|
if let Some(i) = init {
|
|
3001
3388
|
Self::collect_assigned_idents_in_stmt(i, names);
|
|
3002
3389
|
}
|
|
@@ -3016,7 +3403,12 @@ impl Codegen {
|
|
|
3016
3403
|
Self::collect_assigned_idents_in_expr(cond, names);
|
|
3017
3404
|
Self::collect_assigned_idents_in_stmt(body, names);
|
|
3018
3405
|
}
|
|
3019
|
-
Statement::Switch {
|
|
3406
|
+
Statement::Switch {
|
|
3407
|
+
expr,
|
|
3408
|
+
cases,
|
|
3409
|
+
default_body,
|
|
3410
|
+
..
|
|
3411
|
+
} => {
|
|
3020
3412
|
Self::collect_assigned_idents_in_expr(expr, names);
|
|
3021
3413
|
for (case_expr, stmts) in cases {
|
|
3022
3414
|
if let Some(e) = case_expr {
|
|
@@ -3032,7 +3424,12 @@ impl Codegen {
|
|
|
3032
3424
|
}
|
|
3033
3425
|
}
|
|
3034
3426
|
}
|
|
3035
|
-
Statement::Try {
|
|
3427
|
+
Statement::Try {
|
|
3428
|
+
body,
|
|
3429
|
+
catch_body,
|
|
3430
|
+
finally_body,
|
|
3431
|
+
..
|
|
3432
|
+
} => {
|
|
3036
3433
|
Self::collect_assigned_idents_in_stmt(body, names);
|
|
3037
3434
|
if let Some(c) = catch_body {
|
|
3038
3435
|
Self::collect_assigned_idents_in_stmt(c, names);
|
|
@@ -3048,7 +3445,10 @@ impl Codegen {
|
|
|
3048
3445
|
}
|
|
3049
3446
|
}
|
|
3050
3447
|
Statement::Throw { value, .. } => Self::collect_assigned_idents_in_expr(value, names),
|
|
3051
|
-
Statement::Break { .. }
|
|
3448
|
+
Statement::Break { .. }
|
|
3449
|
+
| Statement::Continue { .. }
|
|
3450
|
+
| Statement::Import { .. }
|
|
3451
|
+
| Statement::Export { .. } => {}
|
|
3052
3452
|
}
|
|
3053
3453
|
}
|
|
3054
3454
|
|
|
@@ -3066,15 +3466,22 @@ impl Codegen {
|
|
|
3066
3466
|
names.insert(name.to_string());
|
|
3067
3467
|
Self::collect_assigned_idents_in_expr(value, names);
|
|
3068
3468
|
}
|
|
3069
|
-
Expr::PostfixInc { name, .. }
|
|
3070
|
-
| Expr::
|
|
3469
|
+
Expr::PostfixInc { name, .. }
|
|
3470
|
+
| Expr::PostfixDec { name, .. }
|
|
3471
|
+
| Expr::PrefixInc { name, .. }
|
|
3472
|
+
| Expr::PrefixDec { name, .. } => {
|
|
3071
3473
|
names.insert(name.to_string());
|
|
3072
3474
|
}
|
|
3073
3475
|
Expr::MemberAssign { object, value, .. } => {
|
|
3074
3476
|
Self::collect_assigned_idents_in_expr(object, names);
|
|
3075
3477
|
Self::collect_assigned_idents_in_expr(value, names);
|
|
3076
3478
|
}
|
|
3077
|
-
Expr::IndexAssign {
|
|
3479
|
+
Expr::IndexAssign {
|
|
3480
|
+
object,
|
|
3481
|
+
index,
|
|
3482
|
+
value,
|
|
3483
|
+
..
|
|
3484
|
+
} => {
|
|
3078
3485
|
Self::collect_assigned_idents_in_expr(object, names);
|
|
3079
3486
|
Self::collect_assigned_idents_in_expr(index, names);
|
|
3080
3487
|
Self::collect_assigned_idents_in_expr(value, names);
|
|
@@ -3114,17 +3521,20 @@ impl Codegen {
|
|
|
3114
3521
|
Self::collect_assigned_idents_in_expr(object, names);
|
|
3115
3522
|
Self::collect_assigned_idents_in_expr(index, names);
|
|
3116
3523
|
}
|
|
3117
|
-
Expr::Conditional {
|
|
3524
|
+
Expr::Conditional {
|
|
3525
|
+
cond,
|
|
3526
|
+
then_branch,
|
|
3527
|
+
else_branch,
|
|
3528
|
+
..
|
|
3529
|
+
} => {
|
|
3118
3530
|
Self::collect_assigned_idents_in_expr(cond, names);
|
|
3119
3531
|
Self::collect_assigned_idents_in_expr(then_branch, names);
|
|
3120
3532
|
Self::collect_assigned_idents_in_expr(else_branch, names);
|
|
3121
3533
|
}
|
|
3122
|
-
Expr::ArrowFunction { body, .. } => {
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
}
|
|
3127
|
-
}
|
|
3534
|
+
Expr::ArrowFunction { body, .. } => match body {
|
|
3535
|
+
ArrowBody::Expr(e) => Self::collect_assigned_idents_in_expr(e, names),
|
|
3536
|
+
ArrowBody::Block(s) => Self::collect_assigned_idents_in_stmt(s, names),
|
|
3537
|
+
},
|
|
3128
3538
|
Expr::Array { elements, .. } => {
|
|
3129
3539
|
for el in elements {
|
|
3130
3540
|
match el {
|
|
@@ -3152,7 +3562,9 @@ impl Codegen {
|
|
|
3152
3562
|
Self::collect_assigned_idents_in_expr(e, names);
|
|
3153
3563
|
}
|
|
3154
3564
|
}
|
|
3155
|
-
Expr::JsxElement {
|
|
3565
|
+
Expr::JsxElement {
|
|
3566
|
+
props, children, ..
|
|
3567
|
+
} => {
|
|
3156
3568
|
for p in props {
|
|
3157
3569
|
match p {
|
|
3158
3570
|
tishlang_ast::JsxProp::Attr {
|
|
@@ -3196,10 +3608,7 @@ impl Codegen {
|
|
|
3196
3608
|
Statement::VarDeclDestructure { pattern, .. } => {
|
|
3197
3609
|
Self::collect_destruct_names(pattern, names);
|
|
3198
3610
|
}
|
|
3199
|
-
Statement::For {
|
|
3200
|
-
init: Some(i),
|
|
3201
|
-
..
|
|
3202
|
-
} => {
|
|
3611
|
+
Statement::For { init: Some(i), .. } => {
|
|
3203
3612
|
if let Statement::VarDecl { name, .. } = i.as_ref() {
|
|
3204
3613
|
names.insert(name.to_string());
|
|
3205
3614
|
}
|
|
@@ -3288,11 +3697,17 @@ impl Codegen {
|
|
|
3288
3697
|
}
|
|
3289
3698
|
match body {
|
|
3290
3699
|
ArrowBody::Expr(e) => Self::collect_mutated_captures_from_expr(e, block_vars, result),
|
|
3291
|
-
ArrowBody::Block(s) =>
|
|
3700
|
+
ArrowBody::Block(s) => {
|
|
3701
|
+
Self::collect_mutated_captures_from_statements(s, block_vars, result)
|
|
3702
|
+
}
|
|
3292
3703
|
}
|
|
3293
3704
|
}
|
|
3294
3705
|
|
|
3295
|
-
fn collect_mutated_captures_from_expr(
|
|
3706
|
+
fn collect_mutated_captures_from_expr(
|
|
3707
|
+
expr: &Expr,
|
|
3708
|
+
block_vars: &HashSet<String>,
|
|
3709
|
+
result: &mut HashSet<String>,
|
|
3710
|
+
) {
|
|
3296
3711
|
match expr {
|
|
3297
3712
|
Expr::ArrowFunction { params, body, .. } => {
|
|
3298
3713
|
Self::collect_mutated_captures_from_arrow(params, body, block_vars, result);
|
|
@@ -3323,13 +3738,17 @@ impl Codegen {
|
|
|
3323
3738
|
Self::collect_mutated_captures_from_expr(e, block_vars, result);
|
|
3324
3739
|
}
|
|
3325
3740
|
}
|
|
3326
|
-
Expr::Conditional {
|
|
3741
|
+
Expr::Conditional {
|
|
3742
|
+
cond,
|
|
3743
|
+
then_branch,
|
|
3744
|
+
else_branch,
|
|
3745
|
+
..
|
|
3746
|
+
} => {
|
|
3327
3747
|
Self::collect_mutated_captures_from_expr(cond, block_vars, result);
|
|
3328
3748
|
Self::collect_mutated_captures_from_expr(then_branch, block_vars, result);
|
|
3329
3749
|
Self::collect_mutated_captures_from_expr(else_branch, block_vars, result);
|
|
3330
3750
|
}
|
|
3331
|
-
Expr::Binary { left, right, .. }
|
|
3332
|
-
| Expr::NullishCoalesce { left, right, .. } => {
|
|
3751
|
+
Expr::Binary { left, right, .. } | Expr::NullishCoalesce { left, right, .. } => {
|
|
3333
3752
|
Self::collect_mutated_captures_from_expr(left, block_vars, result);
|
|
3334
3753
|
Self::collect_mutated_captures_from_expr(right, block_vars, result);
|
|
3335
3754
|
}
|
|
@@ -3372,14 +3791,25 @@ impl Codegen {
|
|
|
3372
3791
|
Self::collect_mutated_captures_from_statements(s, block_vars, result);
|
|
3373
3792
|
}
|
|
3374
3793
|
}
|
|
3375
|
-
Statement::If {
|
|
3794
|
+
Statement::If {
|
|
3795
|
+
cond,
|
|
3796
|
+
then_branch,
|
|
3797
|
+
else_branch,
|
|
3798
|
+
..
|
|
3799
|
+
} => {
|
|
3376
3800
|
Self::collect_mutated_captures_from_expr(cond, block_vars, result);
|
|
3377
3801
|
Self::collect_mutated_captures_from_statements(then_branch, block_vars, result);
|
|
3378
3802
|
if let Some(eb) = else_branch {
|
|
3379
3803
|
Self::collect_mutated_captures_from_statements(eb, block_vars, result);
|
|
3380
3804
|
}
|
|
3381
3805
|
}
|
|
3382
|
-
Statement::For {
|
|
3806
|
+
Statement::For {
|
|
3807
|
+
init,
|
|
3808
|
+
cond,
|
|
3809
|
+
update,
|
|
3810
|
+
body,
|
|
3811
|
+
..
|
|
3812
|
+
} => {
|
|
3383
3813
|
if let Some(i) = init {
|
|
3384
3814
|
Self::collect_mutated_captures_from_statements(i, block_vars, result);
|
|
3385
3815
|
}
|
|
@@ -3399,7 +3829,12 @@ impl Codegen {
|
|
|
3399
3829
|
Self::collect_mutated_captures_from_expr(cond, block_vars, result);
|
|
3400
3830
|
Self::collect_mutated_captures_from_statements(body, block_vars, result);
|
|
3401
3831
|
}
|
|
3402
|
-
Statement::Switch {
|
|
3832
|
+
Statement::Switch {
|
|
3833
|
+
expr,
|
|
3834
|
+
cases,
|
|
3835
|
+
default_body,
|
|
3836
|
+
..
|
|
3837
|
+
} => {
|
|
3403
3838
|
Self::collect_mutated_captures_from_expr(expr, block_vars, result);
|
|
3404
3839
|
for (ce, stmts) in cases {
|
|
3405
3840
|
if let Some(e) = ce {
|
|
@@ -3415,7 +3850,12 @@ impl Codegen {
|
|
|
3415
3850
|
}
|
|
3416
3851
|
}
|
|
3417
3852
|
}
|
|
3418
|
-
Statement::Try {
|
|
3853
|
+
Statement::Try {
|
|
3854
|
+
body,
|
|
3855
|
+
catch_body,
|
|
3856
|
+
finally_body,
|
|
3857
|
+
..
|
|
3858
|
+
} => {
|
|
3419
3859
|
Self::collect_mutated_captures_from_statements(body, block_vars, result);
|
|
3420
3860
|
if let Some(c) = catch_body {
|
|
3421
3861
|
Self::collect_mutated_captures_from_statements(c, block_vars, result);
|
|
@@ -3433,7 +3873,9 @@ impl Codegen {
|
|
|
3433
3873
|
Statement::Return { value: Some(e), .. } => {
|
|
3434
3874
|
Self::collect_mutated_captures_from_expr(e, block_vars, result);
|
|
3435
3875
|
}
|
|
3436
|
-
Statement::Throw { value, .. } =>
|
|
3876
|
+
Statement::Throw { value, .. } => {
|
|
3877
|
+
Self::collect_mutated_captures_from_expr(value, block_vars, result)
|
|
3878
|
+
}
|
|
3437
3879
|
_ => {}
|
|
3438
3880
|
}
|
|
3439
3881
|
}
|
|
@@ -3452,37 +3894,66 @@ impl Codegen {
|
|
|
3452
3894
|
/// Collect variable names declared in a statement (VarDecl, Destructure, For init).
|
|
3453
3895
|
fn collect_local_var_names(stmt: &Statement, names: &mut HashSet<String>) {
|
|
3454
3896
|
match stmt {
|
|
3455
|
-
Statement::VarDecl { name, .. } => {
|
|
3897
|
+
Statement::VarDecl { name, .. } => {
|
|
3898
|
+
names.insert(name.to_string());
|
|
3899
|
+
}
|
|
3456
3900
|
Statement::VarDeclDestructure { pattern, .. } => {
|
|
3457
3901
|
Self::collect_destruct_names(pattern, names);
|
|
3458
3902
|
}
|
|
3459
3903
|
Statement::Block { statements, .. } => {
|
|
3460
|
-
for s in statements {
|
|
3904
|
+
for s in statements {
|
|
3905
|
+
Self::collect_local_var_names(s, names);
|
|
3906
|
+
}
|
|
3461
3907
|
}
|
|
3462
|
-
Statement::If {
|
|
3908
|
+
Statement::If {
|
|
3909
|
+
then_branch,
|
|
3910
|
+
else_branch,
|
|
3911
|
+
..
|
|
3912
|
+
} => {
|
|
3463
3913
|
Self::collect_local_var_names(then_branch, names);
|
|
3464
|
-
if let Some(eb) = else_branch {
|
|
3914
|
+
if let Some(eb) = else_branch {
|
|
3915
|
+
Self::collect_local_var_names(eb, names);
|
|
3916
|
+
}
|
|
3465
3917
|
}
|
|
3466
3918
|
Statement::For { init, body, .. } => {
|
|
3467
|
-
if let Some(i) = init {
|
|
3919
|
+
if let Some(i) = init {
|
|
3920
|
+
Self::collect_local_var_names(i, names);
|
|
3921
|
+
}
|
|
3468
3922
|
Self::collect_local_var_names(body, names);
|
|
3469
3923
|
}
|
|
3470
3924
|
Statement::ForOf { body, .. } => Self::collect_local_var_names(body, names),
|
|
3471
3925
|
Statement::While { body, .. } | Statement::DoWhile { body, .. } => {
|
|
3472
3926
|
Self::collect_local_var_names(body, names);
|
|
3473
3927
|
}
|
|
3474
|
-
Statement::Switch {
|
|
3928
|
+
Statement::Switch {
|
|
3929
|
+
cases,
|
|
3930
|
+
default_body,
|
|
3931
|
+
..
|
|
3932
|
+
} => {
|
|
3475
3933
|
for (_, stmts) in cases {
|
|
3476
|
-
for s in stmts {
|
|
3934
|
+
for s in stmts {
|
|
3935
|
+
Self::collect_local_var_names(s, names);
|
|
3936
|
+
}
|
|
3477
3937
|
}
|
|
3478
3938
|
if let Some(stmts) = default_body {
|
|
3479
|
-
for s in stmts {
|
|
3939
|
+
for s in stmts {
|
|
3940
|
+
Self::collect_local_var_names(s, names);
|
|
3941
|
+
}
|
|
3480
3942
|
}
|
|
3481
3943
|
}
|
|
3482
|
-
Statement::Try {
|
|
3944
|
+
Statement::Try {
|
|
3945
|
+
body,
|
|
3946
|
+
catch_body,
|
|
3947
|
+
finally_body,
|
|
3948
|
+
..
|
|
3949
|
+
} => {
|
|
3483
3950
|
Self::collect_local_var_names(body, names);
|
|
3484
|
-
if let Some(c) = catch_body {
|
|
3485
|
-
|
|
3951
|
+
if let Some(c) = catch_body {
|
|
3952
|
+
Self::collect_local_var_names(c, names);
|
|
3953
|
+
}
|
|
3954
|
+
if let Some(f) = finally_body {
|
|
3955
|
+
Self::collect_local_var_names(f, names);
|
|
3956
|
+
}
|
|
3486
3957
|
}
|
|
3487
3958
|
Statement::FunDecl { body, .. } => Self::collect_local_var_names(body, names),
|
|
3488
3959
|
_ => {}
|
|
@@ -3493,16 +3964,24 @@ impl Codegen {
|
|
|
3493
3964
|
match pattern {
|
|
3494
3965
|
DestructPattern::Array(elements) => {
|
|
3495
3966
|
for el in elements {
|
|
3496
|
-
if let Some(DestructElement::Ident(n)) = el {
|
|
3497
|
-
|
|
3967
|
+
if let Some(DestructElement::Ident(n)) = el {
|
|
3968
|
+
names.insert(n.to_string());
|
|
3969
|
+
}
|
|
3970
|
+
if let Some(DestructElement::Pattern(p)) = el {
|
|
3971
|
+
Self::collect_destruct_names(p, names);
|
|
3972
|
+
}
|
|
3498
3973
|
}
|
|
3499
3974
|
}
|
|
3500
3975
|
DestructPattern::Object(props) => {
|
|
3501
3976
|
for prop in props {
|
|
3502
3977
|
match &prop.value {
|
|
3503
|
-
DestructElement::Ident(n) => {
|
|
3978
|
+
DestructElement::Ident(n) => {
|
|
3979
|
+
names.insert(n.to_string());
|
|
3980
|
+
}
|
|
3504
3981
|
DestructElement::Pattern(p) => Self::collect_destruct_names(p, names),
|
|
3505
|
-
DestructElement::Rest(n) => {
|
|
3982
|
+
DestructElement::Rest(n) => {
|
|
3983
|
+
names.insert(n.to_string());
|
|
3984
|
+
}
|
|
3506
3985
|
}
|
|
3507
3986
|
}
|
|
3508
3987
|
}
|
|
@@ -3513,25 +3992,48 @@ impl Codegen {
|
|
|
3513
3992
|
match stmt {
|
|
3514
3993
|
Statement::ExprStmt { expr, .. } => Self::collect_expr_idents(expr, idents),
|
|
3515
3994
|
Statement::VarDecl { init, .. } => {
|
|
3516
|
-
if let Some(e) = init {
|
|
3995
|
+
if let Some(e) = init {
|
|
3996
|
+
Self::collect_expr_idents(e, idents);
|
|
3997
|
+
}
|
|
3517
3998
|
}
|
|
3518
3999
|
Statement::VarDeclDestructure { init, .. } => Self::collect_expr_idents(init, idents),
|
|
3519
4000
|
Statement::Block { statements, .. } => {
|
|
3520
|
-
for s in statements {
|
|
4001
|
+
for s in statements {
|
|
4002
|
+
Self::collect_stmt_idents(s, idents);
|
|
4003
|
+
}
|
|
3521
4004
|
}
|
|
3522
|
-
Statement::If {
|
|
4005
|
+
Statement::If {
|
|
4006
|
+
cond,
|
|
4007
|
+
then_branch,
|
|
4008
|
+
else_branch,
|
|
4009
|
+
..
|
|
4010
|
+
} => {
|
|
3523
4011
|
Self::collect_expr_idents(cond, idents);
|
|
3524
4012
|
Self::collect_stmt_idents(then_branch, idents);
|
|
3525
|
-
if let Some(e) = else_branch {
|
|
4013
|
+
if let Some(e) = else_branch {
|
|
4014
|
+
Self::collect_stmt_idents(e, idents);
|
|
4015
|
+
}
|
|
3526
4016
|
}
|
|
3527
4017
|
Statement::While { cond, body, .. } | Statement::DoWhile { body, cond, .. } => {
|
|
3528
4018
|
Self::collect_expr_idents(cond, idents);
|
|
3529
4019
|
Self::collect_stmt_idents(body, idents);
|
|
3530
4020
|
}
|
|
3531
|
-
Statement::For {
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
4021
|
+
Statement::For {
|
|
4022
|
+
init,
|
|
4023
|
+
cond,
|
|
4024
|
+
update,
|
|
4025
|
+
body,
|
|
4026
|
+
..
|
|
4027
|
+
} => {
|
|
4028
|
+
if let Some(s) = init {
|
|
4029
|
+
Self::collect_stmt_idents(s, idents);
|
|
4030
|
+
}
|
|
4031
|
+
if let Some(e) = cond {
|
|
4032
|
+
Self::collect_expr_idents(e, idents);
|
|
4033
|
+
}
|
|
4034
|
+
if let Some(e) = update {
|
|
4035
|
+
Self::collect_expr_idents(e, idents);
|
|
4036
|
+
}
|
|
3535
4037
|
Self::collect_stmt_idents(body, idents);
|
|
3536
4038
|
}
|
|
3537
4039
|
Statement::ForOf { iterable, body, .. } => {
|
|
@@ -3539,26 +4041,51 @@ impl Codegen {
|
|
|
3539
4041
|
Self::collect_stmt_idents(body, idents);
|
|
3540
4042
|
}
|
|
3541
4043
|
Statement::Return { value, .. } => {
|
|
3542
|
-
if let Some(e) = value {
|
|
4044
|
+
if let Some(e) = value {
|
|
4045
|
+
Self::collect_expr_idents(e, idents);
|
|
4046
|
+
}
|
|
3543
4047
|
}
|
|
3544
4048
|
Statement::Throw { value, .. } => Self::collect_expr_idents(value, idents),
|
|
3545
|
-
Statement::Try {
|
|
4049
|
+
Statement::Try {
|
|
4050
|
+
body,
|
|
4051
|
+
catch_body,
|
|
4052
|
+
finally_body,
|
|
4053
|
+
..
|
|
4054
|
+
} => {
|
|
3546
4055
|
Self::collect_stmt_idents(body, idents);
|
|
3547
|
-
if let Some(c) = catch_body {
|
|
3548
|
-
|
|
4056
|
+
if let Some(c) = catch_body {
|
|
4057
|
+
Self::collect_stmt_idents(c, idents);
|
|
4058
|
+
}
|
|
4059
|
+
if let Some(f) = finally_body {
|
|
4060
|
+
Self::collect_stmt_idents(f, idents);
|
|
4061
|
+
}
|
|
3549
4062
|
}
|
|
3550
|
-
Statement::Switch {
|
|
4063
|
+
Statement::Switch {
|
|
4064
|
+
expr,
|
|
4065
|
+
cases,
|
|
4066
|
+
default_body,
|
|
4067
|
+
..
|
|
4068
|
+
} => {
|
|
3551
4069
|
Self::collect_expr_idents(expr, idents);
|
|
3552
4070
|
for (case_expr, stmts) in cases {
|
|
3553
|
-
if let Some(e) = case_expr {
|
|
3554
|
-
|
|
4071
|
+
if let Some(e) = case_expr {
|
|
4072
|
+
Self::collect_expr_idents(e, idents);
|
|
4073
|
+
}
|
|
4074
|
+
for s in stmts {
|
|
4075
|
+
Self::collect_stmt_idents(s, idents);
|
|
4076
|
+
}
|
|
3555
4077
|
}
|
|
3556
4078
|
if let Some(stmts) = default_body {
|
|
3557
|
-
for s in stmts {
|
|
4079
|
+
for s in stmts {
|
|
4080
|
+
Self::collect_stmt_idents(s, idents);
|
|
4081
|
+
}
|
|
3558
4082
|
}
|
|
3559
4083
|
}
|
|
3560
4084
|
Statement::FunDecl { body, .. } => Self::collect_stmt_idents(body, idents),
|
|
3561
|
-
Statement::Break { .. }
|
|
4085
|
+
Statement::Break { .. }
|
|
4086
|
+
| Statement::Continue { .. }
|
|
4087
|
+
| Statement::Import { .. }
|
|
4088
|
+
| Statement::Export { .. } => {}
|
|
3562
4089
|
}
|
|
3563
4090
|
}
|
|
3564
4091
|
|
|
@@ -3571,7 +4098,7 @@ impl Codegen {
|
|
|
3571
4098
|
// Build the arrow function as a Value::Function closure
|
|
3572
4099
|
let mut code = String::new();
|
|
3573
4100
|
code.push_str("{\n");
|
|
3574
|
-
|
|
4101
|
+
|
|
3575
4102
|
// Find which identifiers are actually referenced in the body
|
|
3576
4103
|
let referenced = Self::collect_referenced_idents(body);
|
|
3577
4104
|
// Exclude the arrow's own parameters - they're not outer captures
|
|
@@ -3589,17 +4116,23 @@ impl Codegen {
|
|
|
3589
4116
|
}
|
|
3590
4117
|
|
|
3591
4118
|
// Collect outer parameters that need to be captured
|
|
3592
|
-
let outer_params: Vec<String> = self
|
|
4119
|
+
let outer_params: Vec<String> = self
|
|
4120
|
+
.outer_params_stack
|
|
3593
4121
|
.iter()
|
|
3594
4122
|
.flat_map(|p| p.iter().cloned())
|
|
3595
4123
|
.filter(|name| referenced.contains(name) && !param_names.contains(name))
|
|
3596
4124
|
.collect();
|
|
3597
|
-
|
|
4125
|
+
|
|
3598
4126
|
// Collect outer variables (from outer scopes) that need to be captured
|
|
3599
|
-
let outer_vars: Vec<String> = self
|
|
4127
|
+
let outer_vars: Vec<String> = self
|
|
4128
|
+
.outer_vars_stack
|
|
3600
4129
|
.iter()
|
|
3601
4130
|
.flat_map(|v| v.iter().cloned())
|
|
3602
|
-
.filter(|name|
|
|
4131
|
+
.filter(|name| {
|
|
4132
|
+
referenced.contains(name)
|
|
4133
|
+
&& !param_names.contains(name)
|
|
4134
|
+
&& !local_var_names.contains(name)
|
|
4135
|
+
})
|
|
3603
4136
|
.collect();
|
|
3604
4137
|
|
|
3605
4138
|
// Outer vars that are assigned in the body need RefCell; read-only get Value binding
|
|
@@ -3608,8 +4141,16 @@ impl Codegen {
|
|
|
3608
4141
|
ArrowBody::Expr(e) => Self::collect_assigned_idents_in_expr(e, &mut assigned_in_body),
|
|
3609
4142
|
ArrowBody::Block(s) => Self::collect_assigned_idents_in_stmt(s, &mut assigned_in_body),
|
|
3610
4143
|
}
|
|
3611
|
-
let mutable_outer_vars: Vec<String> = outer_vars
|
|
3612
|
-
|
|
4144
|
+
let mutable_outer_vars: Vec<String> = outer_vars
|
|
4145
|
+
.iter()
|
|
4146
|
+
.filter(|v| assigned_in_body.contains(*v))
|
|
4147
|
+
.cloned()
|
|
4148
|
+
.collect();
|
|
4149
|
+
let read_only_outer_vars: Vec<String> = outer_vars
|
|
4150
|
+
.iter()
|
|
4151
|
+
.filter(|v| !assigned_in_body.contains(*v))
|
|
4152
|
+
.cloned()
|
|
4153
|
+
.collect();
|
|
3613
4154
|
|
|
3614
4155
|
// Wrap outer captures in Rc<RefCell<>> and use _ref suffix.
|
|
3615
4156
|
// Clone existing Rc only when VarDecl actually emitted `Rc<RefCell<...>>` (see rc_cell_storage_*).
|
|
@@ -3617,38 +4158,70 @@ impl Codegen {
|
|
|
3617
4158
|
let param_escaped = Self::escape_ident(outer_param);
|
|
3618
4159
|
let ref_name = format!("{}_ref", param_escaped);
|
|
3619
4160
|
if self.rc_cell_storage_contains(outer_param) {
|
|
3620
|
-
code.push_str(&format!(
|
|
4161
|
+
code.push_str(&format!(
|
|
4162
|
+
" let {} = {}.clone();\n",
|
|
4163
|
+
ref_name, param_escaped
|
|
4164
|
+
));
|
|
3621
4165
|
} else {
|
|
3622
|
-
code.push_str(&format!(
|
|
4166
|
+
code.push_str(&format!(
|
|
4167
|
+
" let {} = std::rc::Rc::new(RefCell::new({}.clone()));\n",
|
|
4168
|
+
ref_name, param_escaped
|
|
4169
|
+
));
|
|
3623
4170
|
}
|
|
3624
4171
|
}
|
|
3625
4172
|
for outer_var in &outer_vars {
|
|
3626
4173
|
let var_escaped = Self::escape_ident(outer_var);
|
|
3627
4174
|
let ref_name = format!("{}_ref", var_escaped);
|
|
3628
4175
|
if self.rc_cell_storage_contains(outer_var) {
|
|
3629
|
-
code.push_str(&format!(
|
|
4176
|
+
code.push_str(&format!(
|
|
4177
|
+
" let {} = {}.clone();\n",
|
|
4178
|
+
ref_name, var_escaped
|
|
4179
|
+
));
|
|
3630
4180
|
} else {
|
|
3631
|
-
code.push_str(&format!(
|
|
4181
|
+
code.push_str(&format!(
|
|
4182
|
+
" let {} = std::rc::Rc::new(RefCell::new({}.clone()));\n",
|
|
4183
|
+
ref_name, var_escaped
|
|
4184
|
+
));
|
|
3632
4185
|
}
|
|
3633
4186
|
}
|
|
3634
4187
|
// Only clone builtins that are actually referenced (clone so outer scope can still use, e.g. process for PORT)
|
|
3635
|
-
for builtin in &[
|
|
4188
|
+
for builtin in &[
|
|
4189
|
+
"console",
|
|
4190
|
+
"Math",
|
|
4191
|
+
"JSON",
|
|
4192
|
+
"Date",
|
|
4193
|
+
"Uint8Array",
|
|
4194
|
+
"AudioContext",
|
|
4195
|
+
"process",
|
|
4196
|
+
"setTimeout",
|
|
4197
|
+
"clearTimeout",
|
|
4198
|
+
"Promise",
|
|
4199
|
+
"RegExp",
|
|
4200
|
+
"Polars",
|
|
4201
|
+
] {
|
|
3636
4202
|
if referenced.contains(*builtin) {
|
|
3637
4203
|
code.push_str(&format!(" let {} = {}.clone();\n", builtin, builtin));
|
|
3638
4204
|
}
|
|
3639
4205
|
}
|
|
3640
4206
|
|
|
3641
4207
|
// Clone only function cells that are actually referenced in this arrow
|
|
3642
|
-
let referenced_funcs: Vec<String> = self
|
|
4208
|
+
let referenced_funcs: Vec<String> = self
|
|
4209
|
+
.function_scope_stack
|
|
3643
4210
|
.last()
|
|
3644
|
-
.map(|scope|
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
4211
|
+
.map(|scope| {
|
|
4212
|
+
scope
|
|
4213
|
+
.iter()
|
|
4214
|
+
.filter(|f| referenced.contains(f.as_str()) && !param_names.contains(*f))
|
|
4215
|
+
.cloned()
|
|
4216
|
+
.collect()
|
|
4217
|
+
})
|
|
3648
4218
|
.unwrap_or_default();
|
|
3649
4219
|
for func_name in &referenced_funcs {
|
|
3650
4220
|
let escaped = Self::escape_ident(func_name);
|
|
3651
|
-
code.push_str(&format!(
|
|
4221
|
+
code.push_str(&format!(
|
|
4222
|
+
" let {}_ref = {}_cell.clone();\n",
|
|
4223
|
+
escaped, escaped
|
|
4224
|
+
));
|
|
3652
4225
|
}
|
|
3653
4226
|
|
|
3654
4227
|
code.push_str(" Value::Function(Rc::new(move |args: &[Value]| {\n");
|
|
@@ -3656,23 +4229,35 @@ impl Codegen {
|
|
|
3656
4229
|
// Make captured outer params available as plain Values (from _ref RefCells)
|
|
3657
4230
|
for outer_param in &outer_params {
|
|
3658
4231
|
let param_escaped = Self::escape_ident(outer_param);
|
|
3659
|
-
code.push_str(&format!(
|
|
4232
|
+
code.push_str(&format!(
|
|
4233
|
+
" let {} = (*{}_ref.borrow()).clone();\n",
|
|
4234
|
+
param_escaped, param_escaped
|
|
4235
|
+
));
|
|
3660
4236
|
}
|
|
3661
4237
|
// Mutable outer vars: capture RefCell so assignments use borrow_mut
|
|
3662
4238
|
for outer_var in &mutable_outer_vars {
|
|
3663
4239
|
let var_escaped = Self::escape_ident(outer_var);
|
|
3664
|
-
code.push_str(&format!(
|
|
4240
|
+
code.push_str(&format!(
|
|
4241
|
+
" let {} = {}_ref.clone();\n",
|
|
4242
|
+
var_escaped, var_escaped
|
|
4243
|
+
));
|
|
3665
4244
|
}
|
|
3666
4245
|
// Read-only outer vars: Value binding from borrow
|
|
3667
4246
|
for outer_var in &read_only_outer_vars {
|
|
3668
4247
|
let var_escaped = Self::escape_ident(outer_var);
|
|
3669
|
-
code.push_str(&format!(
|
|
4248
|
+
code.push_str(&format!(
|
|
4249
|
+
" let {} = (*{}_ref.borrow()).clone();\n",
|
|
4250
|
+
var_escaped, var_escaped
|
|
4251
|
+
));
|
|
3670
4252
|
}
|
|
3671
4253
|
|
|
3672
4254
|
// Make captured functions available
|
|
3673
4255
|
for func_name in &referenced_funcs {
|
|
3674
4256
|
let escaped = Self::escape_ident(func_name);
|
|
3675
|
-
code.push_str(&format!(
|
|
4257
|
+
code.push_str(&format!(
|
|
4258
|
+
" let {} = (*{}_ref.borrow()).clone();\n",
|
|
4259
|
+
escaped, escaped
|
|
4260
|
+
));
|
|
3676
4261
|
}
|
|
3677
4262
|
|
|
3678
4263
|
// Extract parameters from args
|
|
@@ -3727,18 +4312,18 @@ impl Codegen {
|
|
|
3727
4312
|
tishlang_ast::ArrowBody::Block(block_stmt) => {
|
|
3728
4313
|
// For block bodies, emit the block statement
|
|
3729
4314
|
self.function_scope_stack.push(Vec::new());
|
|
3730
|
-
|
|
4315
|
+
|
|
3731
4316
|
// Save current output, emit to temp, then restore
|
|
3732
4317
|
let saved_output = std::mem::take(&mut self.output);
|
|
3733
4318
|
let saved_indent = self.indent;
|
|
3734
4319
|
self.indent = 2; // Base indent inside the closure
|
|
3735
|
-
|
|
4320
|
+
|
|
3736
4321
|
self.emit_statement(block_stmt)?;
|
|
3737
|
-
|
|
4322
|
+
|
|
3738
4323
|
let body_code = std::mem::replace(&mut self.output, saved_output);
|
|
3739
4324
|
self.indent = saved_indent;
|
|
3740
4325
|
self.function_scope_stack.pop();
|
|
3741
|
-
|
|
4326
|
+
|
|
3742
4327
|
code.push_str(&body_code);
|
|
3743
4328
|
code.push_str(" Value::Null\n");
|
|
3744
4329
|
}
|
|
@@ -3758,7 +4343,11 @@ impl Codegen {
|
|
|
3758
4343
|
/// Emit an expression as a native Rust type (not wrapped in Value).
|
|
3759
4344
|
/// Falls back to emit_expr + conversion if the expression cannot be directly
|
|
3760
4345
|
/// emitted as the target type.
|
|
3761
|
-
fn emit_native_expr(
|
|
4346
|
+
fn emit_native_expr(
|
|
4347
|
+
&mut self,
|
|
4348
|
+
expr: &Expr,
|
|
4349
|
+
target_type: &RustType,
|
|
4350
|
+
) -> Result<String, CompileError> {
|
|
3762
4351
|
// Try to emit literals directly as native types
|
|
3763
4352
|
if let Expr::Literal { value, .. } = expr {
|
|
3764
4353
|
match (target_type, value) {
|
|
@@ -3777,7 +4366,7 @@ impl Codegen {
|
|
|
3777
4366
|
_ => {}
|
|
3778
4367
|
}
|
|
3779
4368
|
}
|
|
3780
|
-
|
|
4369
|
+
|
|
3781
4370
|
// Try to emit array literals directly as Vec<T>
|
|
3782
4371
|
if let (RustType::Vec(inner_type), Expr::Array { elements, .. }) = (target_type, expr) {
|
|
3783
4372
|
let mut items = Vec::new();
|
|
@@ -3796,7 +4385,7 @@ impl Codegen {
|
|
|
3796
4385
|
}
|
|
3797
4386
|
return Ok(format!("vec![{}]", items.join(", ")));
|
|
3798
4387
|
}
|
|
3799
|
-
|
|
4388
|
+
|
|
3800
4389
|
// Check if the identifier is already of the target type
|
|
3801
4390
|
if let Expr::Ident { name, .. } = expr {
|
|
3802
4391
|
let var_type = self.type_context.get_type(name.as_ref());
|
|
@@ -3808,7 +4397,7 @@ impl Codegen {
|
|
|
3808
4397
|
return Ok(esc);
|
|
3809
4398
|
}
|
|
3810
4399
|
}
|
|
3811
|
-
|
|
4400
|
+
|
|
3812
4401
|
// Fall back to emit_expr + conversion
|
|
3813
4402
|
let value_expr = self.emit_expr(expr)?;
|
|
3814
4403
|
Ok(target_type.from_value_expr(&value_expr))
|
|
@@ -3829,7 +4418,9 @@ impl Codegen {
|
|
|
3829
4418
|
// ── literals ─────────────────────────────────────────────────────────
|
|
3830
4419
|
Expr::Literal { value, .. } => match value {
|
|
3831
4420
|
Literal::Number(n) => Ok((format!("{}_f64", n), RustType::F64)),
|
|
3832
|
-
Literal::String(s) =>
|
|
4421
|
+
Literal::String(s) => {
|
|
4422
|
+
Ok((format!("{:?}.to_string()", s.as_ref()), RustType::String))
|
|
4423
|
+
}
|
|
3833
4424
|
Literal::Bool(b) => Ok((format!("{}", b), RustType::Bool)),
|
|
3834
4425
|
Literal::Null => Ok(("Value::Null".to_string(), RustType::Value)),
|
|
3835
4426
|
},
|
|
@@ -3855,7 +4446,13 @@ impl Codegen {
|
|
|
3855
4446
|
}
|
|
3856
4447
|
|
|
3857
4448
|
// ── binary expressions ───────────────────────────────────────────────
|
|
3858
|
-
Expr::Binary {
|
|
4449
|
+
Expr::Binary {
|
|
4450
|
+
left,
|
|
4451
|
+
op,
|
|
4452
|
+
right,
|
|
4453
|
+
span,
|
|
4454
|
+
..
|
|
4455
|
+
} => {
|
|
3859
4456
|
let (l, lt) = self.emit_typed_expr(left)?;
|
|
3860
4457
|
let (r, rt) = self.emit_typed_expr(right)?;
|
|
3861
4458
|
|
|
@@ -3882,14 +4479,27 @@ impl Codegen {
|
|
|
3882
4479
|
}
|
|
3883
4480
|
|
|
3884
4481
|
// Fall back: convert both sides to Value and use the runtime.
|
|
3885
|
-
let lv = if lt.is_native() {
|
|
3886
|
-
|
|
4482
|
+
let lv = if lt.is_native() {
|
|
4483
|
+
lt.to_value_expr(&l)
|
|
4484
|
+
} else {
|
|
4485
|
+
l
|
|
4486
|
+
};
|
|
4487
|
+
let rv = if rt.is_native() {
|
|
4488
|
+
rt.to_value_expr(&r)
|
|
4489
|
+
} else {
|
|
4490
|
+
r
|
|
4491
|
+
};
|
|
3887
4492
|
let result = self.emit_binop(&lv, *op, &rv, *span)?;
|
|
3888
4493
|
Ok((result, RustType::Value))
|
|
3889
4494
|
}
|
|
3890
4495
|
|
|
3891
4496
|
// ── array indexing ───────────────────────────────────────────────────
|
|
3892
|
-
Expr::Index {
|
|
4497
|
+
Expr::Index {
|
|
4498
|
+
object,
|
|
4499
|
+
index,
|
|
4500
|
+
optional,
|
|
4501
|
+
..
|
|
4502
|
+
} => {
|
|
3893
4503
|
// Native fast path: `vec[i]` where vec is Vec<T> and i is numeric.
|
|
3894
4504
|
if !optional {
|
|
3895
4505
|
if let Expr::Ident { name, .. } = object.as_ref() {
|
|
@@ -3958,19 +4568,28 @@ impl Codegen {
|
|
|
3958
4568
|
}
|
|
3959
4569
|
}
|
|
3960
4570
|
|
|
3961
|
-
fn emit_binop(
|
|
3962
|
-
&self,
|
|
3963
|
-
l: &str,
|
|
3964
|
-
op: BinOp,
|
|
3965
|
-
r: &str,
|
|
3966
|
-
span: Span,
|
|
3967
|
-
) -> Result<String, CompileError> {
|
|
4571
|
+
fn emit_binop(&self, l: &str, op: BinOp, r: &str, span: Span) -> Result<String, CompileError> {
|
|
3968
4572
|
Ok(match op {
|
|
3969
|
-
BinOp::Add => format!(
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
BinOp::
|
|
4573
|
+
BinOp::Add => format!(
|
|
4574
|
+
"tishlang_runtime::ops::add(&{}, &{}).unwrap_or(Value::Null)",
|
|
4575
|
+
l, r
|
|
4576
|
+
),
|
|
4577
|
+
BinOp::Sub => format!(
|
|
4578
|
+
"tishlang_runtime::ops::sub(&{}, &{}).unwrap_or(Value::Null)",
|
|
4579
|
+
l, r
|
|
4580
|
+
),
|
|
4581
|
+
BinOp::Mul => format!(
|
|
4582
|
+
"tishlang_runtime::ops::mul(&{}, &{}).unwrap_or(Value::Null)",
|
|
4583
|
+
l, r
|
|
4584
|
+
),
|
|
4585
|
+
BinOp::Div => format!(
|
|
4586
|
+
"tishlang_runtime::ops::div(&{}, &{}).unwrap_or(Value::Null)",
|
|
4587
|
+
l, r
|
|
4588
|
+
),
|
|
4589
|
+
BinOp::Mod => format!(
|
|
4590
|
+
"tishlang_runtime::ops::modulo(&{}, &{}).unwrap_or(Value::Null)",
|
|
4591
|
+
l, r
|
|
4592
|
+
),
|
|
3974
4593
|
BinOp::Pow => format!(
|
|
3975
4594
|
"Value::Number({{ let Value::Number(a) = &({}) else {{ panic!() }}; \
|
|
3976
4595
|
let Value::Number(b) = &({}) else {{ panic!() }}; a.powf(*b) }})",
|
|
@@ -3991,7 +4610,10 @@ impl Codegen {
|
|
|
3991
4610
|
BinOp::Shr => Self::emit_bitwise_binop(l, r, ">>"),
|
|
3992
4611
|
BinOp::In => format!("tish_in_operator(&{}, &{})", l, r),
|
|
3993
4612
|
BinOp::Eq | BinOp::Ne => {
|
|
3994
|
-
return Err(CompileError::new(
|
|
4613
|
+
return Err(CompileError::new(
|
|
4614
|
+
"Loose equality not supported",
|
|
4615
|
+
Some(span),
|
|
4616
|
+
))
|
|
3995
4617
|
}
|
|
3996
4618
|
})
|
|
3997
4619
|
}
|