@tishlang/tish 1.5.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.
Files changed (85) hide show
  1. package/Cargo.toml +1 -0
  2. package/bin/tish +0 -0
  3. package/crates/js_to_tish/src/error.rs +2 -8
  4. package/crates/js_to_tish/src/transform/expr.rs +101 -130
  5. package/crates/js_to_tish/src/transform/stmt.rs +25 -22
  6. package/crates/tish/Cargo.toml +1 -1
  7. package/crates/tish/src/cli_help.rs +76 -29
  8. package/crates/tish/src/main.rs +85 -54
  9. package/crates/tish/tests/cargo_example_compile.rs +67 -0
  10. package/crates/tish/tests/fixtures/cargo_example_project/Cargo.toml +3 -0
  11. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/Cargo.toml +11 -0
  12. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +12 -0
  13. package/crates/tish/tests/fixtures/cargo_example_project/package.json +10 -0
  14. package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +3 -0
  15. package/crates/tish/tests/integration_test.rs +197 -47
  16. package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
  17. package/crates/tish/tests/shortcircuit.rs +19 -4
  18. package/crates/tish_ast/src/ast.rs +12 -14
  19. package/crates/tish_build_utils/src/lib.rs +64 -6
  20. package/crates/tish_builtins/src/array.rs +52 -21
  21. package/crates/tish_builtins/src/construct.rs +2 -8
  22. package/crates/tish_builtins/src/globals.rs +30 -15
  23. package/crates/tish_builtins/src/lib.rs +5 -5
  24. package/crates/tish_builtins/src/math.rs +5 -3
  25. package/crates/tish_builtins/src/string.rs +71 -19
  26. package/crates/tish_bytecode/src/chunk.rs +0 -1
  27. package/crates/tish_bytecode/src/compiler.rs +164 -60
  28. package/crates/tish_bytecode/src/opcode.rs +13 -4
  29. package/crates/tish_bytecode/src/peephole.rs +2 -2
  30. package/crates/tish_compile/Cargo.toml +1 -0
  31. package/crates/tish_compile/src/codegen.rs +989 -318
  32. package/crates/tish_compile/src/infer.rs +69 -19
  33. package/crates/tish_compile/src/lib.rs +21 -8
  34. package/crates/tish_compile/src/resolve.rs +515 -94
  35. package/crates/tish_compile/src/types.rs +10 -14
  36. package/crates/tish_compile_js/src/codegen.rs +34 -13
  37. package/crates/tish_compile_js/src/tests_jsx.rs +30 -6
  38. package/crates/tish_compiler_wasm/src/lib.rs +16 -13
  39. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +40 -48
  40. package/crates/tish_core/src/json.rs +5 -3
  41. package/crates/tish_core/src/lib.rs +1 -1
  42. package/crates/tish_core/src/uri.rs +9 -6
  43. package/crates/tish_core/src/value.rs +92 -28
  44. package/crates/tish_cranelift/src/link.rs +6 -9
  45. package/crates/tish_cranelift/src/lower.rs +14 -8
  46. package/crates/tish_eval/src/eval.rs +398 -141
  47. package/crates/tish_eval/src/lib.rs +10 -6
  48. package/crates/tish_eval/src/natives.rs +95 -38
  49. package/crates/tish_eval/src/promise.rs +14 -8
  50. package/crates/tish_eval/src/timers.rs +28 -19
  51. package/crates/tish_eval/src/value.rs +10 -3
  52. package/crates/tish_fmt/src/lib.rs +29 -13
  53. package/crates/tish_lexer/src/lib.rs +217 -63
  54. package/crates/tish_lexer/src/token.rs +6 -6
  55. package/crates/tish_llvm/src/lib.rs +15 -8
  56. package/crates/tish_lsp/src/main.rs +41 -43
  57. package/crates/tish_native/src/build.rs +38 -15
  58. package/crates/tish_native/src/lib.rs +76 -32
  59. package/crates/tish_opt/src/lib.rs +67 -50
  60. package/crates/tish_parser/src/lib.rs +36 -11
  61. package/crates/tish_parser/src/parser.rs +172 -87
  62. package/crates/tish_runtime/src/http.rs +15 -6
  63. package/crates/tish_runtime/src/http_fetch.rs +24 -14
  64. package/crates/tish_runtime/src/lib.rs +224 -168
  65. package/crates/tish_runtime/src/promise.rs +1 -5
  66. package/crates/tish_runtime/src/ws.rs +45 -20
  67. package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
  68. package/crates/tish_ui/src/jsx.rs +41 -22
  69. package/crates/tish_ui/src/lib.rs +2 -2
  70. package/crates/tish_vm/src/vm.rs +320 -116
  71. package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +8 -3
  72. package/crates/tish_wasm/src/lib.rs +38 -28
  73. package/crates/tishlang_cargo_bindgen/Cargo.toml +25 -0
  74. package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
  75. package/crates/tishlang_cargo_bindgen/src/discover.rs +52 -0
  76. package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
  77. package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
  78. package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
  79. package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -0
  80. package/package.json +1 -1
  81. package/platform/darwin-arm64/tish +0 -0
  82. package/platform/darwin-x64/tish +0 -0
  83. package/platform/linux-arm64/tish +0 -0
  84. package/platform/linux-x64/tish +0 -0
  85. 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::{ArrayElement, ArrowBody, BinOp, CallArg, CompoundOp, DestructElement, DestructPattern, Expr, FunParam, Literal, LogicalAssignOp, MemberProp, ObjectProp, Program, Span, Statement, UnaryOp};
7
- use crate::resolve::is_builtin_native_spec;
8
- use crate::types::{RustType, TypeContext};
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 { cond, then_branch, else_branch, .. } => {
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 { init, cond, update, body, .. } => {
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 { expr, cases, default_body, .. } => {
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 { body, catch_body, finally_body, .. } => {
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
- match body {
158
- ArrowBody::Expr(e) => self.analyze_expr(e),
159
- ArrowBody::Block(s) => self.analyze_statement(s),
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 { cond, then_branch, else_branch, .. } => {
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, .. } | Expr::PostfixDec { name, .. } | Expr::PrefixInc { name, .. } | Expr::PrefixDec { 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 { object, index, value, .. } => {
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 { props, children, .. } => {
234
+ Expr::JsxElement {
235
+ props, children, ..
236
+ } => {
200
237
  for p in props {
201
238
  match p {
202
- tishlang_ast::JsxProp::Attr { value: tishlang_ast::JsxAttrValue::Expr(e), .. } | tishlang_ast::JsxProp::Spread(e) => self.analyze_expr(e),
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 { message: msg.into(), span }
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 { then_branch, else_branch, .. } => {
266
- stmt_has_async(then_branch) || else_branch.as_ref().is_some_and(|s| stmt_has_async(s.as_ref()))
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, .. } | Statement::For { body, .. } | Statement::ForOf { 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 { cases, default_body, .. } => {
271
- cases.iter().any(|(_, stmts)| stmts.iter().any(stmt_has_async))
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 { body, catch_body, finally_body, .. } => {
335
+ Statement::Try {
336
+ body,
337
+ catch_body,
338
+ finally_body,
339
+ ..
340
+ } => {
277
341
  stmt_has_async(body)
278
- || catch_body.as_ref().is_some_and(|s| stmt_has_async(s.as_ref()))
279
- || finally_body.as_ref().is_some_and(|s| stmt_has_async(s.as_ref()))
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) || args.iter().any(|a| match a {
291
- CallArg::Expr(e) | CallArg::Spread(e) => expr_has_await(e),
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) || args.iter().any(|a| match a {
296
- CallArg::Expr(e) | CallArg::Spread(e) => expr_has_await(e),
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 { cond, then_branch, else_branch, .. } => {
309
- expr_has_await(cond) || expr_has_await(then_branch) || expr_has_await(else_branch)
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, .. } | Expr::CompoundAssign { value, .. } | Expr::LogicalAssign { value, .. }
319
- | Expr::MemberAssign { value, .. } | Expr::IndexAssign { value, .. } => expr_has_await(value),
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 { props, children, .. } => {
403
+ Expr::JsxElement {
404
+ props, children, ..
405
+ } => {
326
406
  props.iter().any(|p| match p {
327
- tishlang_ast::JsxProp::Attr { value: tishlang_ast::JsxAttrValue::Expr(e), .. } | tishlang_ast::JsxProp::Spread(e) => expr_has_await(e),
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.iter().any(|c| matches!(c, tishlang_ast::JsxChild::Expr(e) if expr_has_await(e)))
330
- }
331
- Expr::JsxFragment { children, .. } => {
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 { cond, then_branch, else_branch, .. } => {
344
- expr_has_await(cond) || stmt_has_await(then_branch)
345
- || else_branch.as_ref().is_some_and(|s| stmt_has_await(s.as_ref()))
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 { init, cond, update, body, .. } => {
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, .. } => expr_has_await(iterable) || stmt_has_await(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 { expr, cases, default_body, .. } => {
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 { body, catch_body, finally_body, .. } => {
475
+ Statement::Try {
476
+ body,
477
+ catch_body,
478
+ finally_body,
479
+ ..
480
+ } => {
369
481
  stmt_has_await(body)
370
- || catch_body.as_ref().is_some_and(|s| stmt_has_await(s.as_ref()))
371
- || finally_body.as_ref().is_some_and(|s| stmt_has_await(s.as_ref()))
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.statements.iter().any(|s| stmt_has_async(s) || stmt_has_await(s))
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(program: &Program, project_root: Option<&Path>) -> Result<String, CompileError> {
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
 
@@ -393,37 +515,66 @@ pub fn compile_project(
393
515
  project_root: Option<&Path>,
394
516
  features: &[String],
395
517
  ) -> Result<String, CompileError> {
396
- let (rust, _, _) = compile_project_full(entry_path, project_root, features, true)?;
518
+ let (rust, _, _, _) = compile_project_full(entry_path, project_root, features, true)?;
397
519
  Ok(rust)
398
520
  }
399
521
 
400
- /// Compile a project and return Rust code, resolved native modules, and the **effective** feature list
401
- /// (CLI features plus any inferred from `tish:fs` / `tish:http` / … imports). Pass this list to
402
- /// `tishlang_runtime` when linking (e.g. `build_via_cargo`) so Cargo `features` match codegen.
522
+ /// Compile a project and return Rust code, resolved native modules, the **effective** feature list
523
+ /// (CLI features plus any inferred from `tish:fs` / `tish:http` / … imports), and native build
524
+ /// artifacts (Cargo dep lines, optional `generated_native.rs` source, init strategy per spec).
403
525
  pub fn compile_project_full(
404
526
  entry_path: &Path,
405
527
  project_root: Option<&Path>,
406
528
  features: &[String],
407
529
  optimize: bool,
408
- ) -> Result<(String, Vec<crate::resolve::ResolvedNativeModule>, Vec<String>), CompileError> {
530
+ ) -> Result<
531
+ (
532
+ String,
533
+ Vec<crate::resolve::ResolvedNativeModule>,
534
+ Vec<String>,
535
+ crate::resolve::NativeBuildArtifacts,
536
+ ),
537
+ CompileError,
538
+ > {
409
539
  use crate::resolve;
410
540
  let root = project_root.unwrap_or_else(|| entry_path.parent().unwrap_or(Path::new(".")));
411
- let modules = resolve::resolve_project(entry_path, project_root)
412
- .map_err(|e| CompileError { message: e, span: None })?;
413
- resolve::detect_cycles(&modules)
414
- .map_err(|e| CompileError { message: e, span: None })?;
415
- let merged = resolve::merge_modules(modules)
416
- .map_err(|e| CompileError { message: e, span: None })?;
417
- let native_modules = resolve::resolve_native_modules(&merged, root)
418
- .map_err(|e| CompileError { message: e, span: None })?;
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
+ })?;
558
+ let native_build = resolve::compute_native_build_artifacts(&merged, root, &native_modules)
559
+ .map_err(|e| CompileError {
560
+ message: e,
561
+ span: None,
562
+ })?;
419
563
  let mut all_features: Vec<String> = features.to_vec();
420
564
  for f in resolve::extract_native_import_features(&merged) {
421
565
  if !all_features.contains(&f) {
422
566
  all_features.push(f);
423
567
  }
424
568
  }
425
- let rust = compile_with_native_modules(&merged, project_root, &all_features, &native_modules, optimize)?;
426
- Ok((rust, native_modules, all_features))
569
+ let rust = compile_with_native_modules(
570
+ &merged,
571
+ project_root,
572
+ &all_features,
573
+ &native_modules,
574
+ &native_build.native_init,
575
+ optimize,
576
+ )?;
577
+ Ok((rust, native_modules, all_features, native_build))
427
578
  }
428
579
 
429
580
  /// Compile with explicit feature flags. When features are provided, codegen uses them
@@ -433,7 +584,8 @@ pub fn compile_with_features(
433
584
  project_root: Option<&Path>,
434
585
  features: &[String],
435
586
  ) -> Result<String, CompileError> {
436
- compile_with_native_modules(program, project_root, features, &[], true)
587
+ let empty = std::collections::HashMap::new();
588
+ compile_with_native_modules(program, project_root, features, &[], &empty, true)
437
589
  }
438
590
 
439
591
  /// Compile with resolved native modules. Native imports emit calls to the module crates directly.
@@ -442,16 +594,34 @@ pub fn compile_with_native_modules(
442
594
  project_root: Option<&Path>,
443
595
  features: &[String],
444
596
  native_modules: &[crate::resolve::ResolvedNativeModule],
597
+ native_init: &std::collections::HashMap<String, crate::resolve::NativeModuleInit>,
445
598
  optimize: bool,
446
599
  ) -> Result<String, CompileError> {
447
- let program = if optimize { tishlang_opt::optimize(program) } else { program.clone() };
600
+ let program = if optimize {
601
+ tishlang_opt::optimize(program)
602
+ } else {
603
+ program.clone()
604
+ };
448
605
  // Type-inference pass: fills in `type_ann` on unannotated VarDecl nodes where
449
606
  // the type is unambiguous (literals, arithmetic of typed vars, etc.).
450
607
  let program = crate::infer::infer_program(&program);
451
- let map: std::collections::HashMap<String, (String, String)> = native_modules
452
- .iter()
453
- .map(|m| (m.spec.clone(), (m.crate_name.clone(), m.export_fn.clone())))
454
- .collect();
608
+ let map: std::collections::HashMap<String, crate::resolve::NativeModuleInit> =
609
+ if native_init.is_empty() {
610
+ native_modules
611
+ .iter()
612
+ .map(|m| {
613
+ (
614
+ m.spec.clone(),
615
+ crate::resolve::NativeModuleInit::Legacy {
616
+ crate_name: m.crate_name.clone(),
617
+ export_fn: m.export_fn.clone(),
618
+ },
619
+ )
620
+ })
621
+ .collect()
622
+ } else {
623
+ native_init.clone()
624
+ };
455
625
  let mut g = Codegen::new_with_native_modules(project_root, features, map);
456
626
  g.emit_program(&program)?;
457
627
  Ok(g.output)
@@ -465,8 +635,8 @@ struct Codegen {
465
635
  project_root: Option<std::path::PathBuf>,
466
636
  /// Requested features (http, process, fs, regex, polars). When non-empty, used instead of #[cfg].
467
637
  features: std::collections::HashSet<String>,
468
- /// spec -> (crate_name, export_fn) for native modules resolved via package.json
469
- native_module_map: std::collections::HashMap<String, (String, String)>,
638
+ /// spec -> native init strategy (legacy adapter object vs generated `generated_native` wrapper)
639
+ native_module_init: std::collections::HashMap<String, crate::resolve::NativeModuleInit>,
470
640
  /// Stack: true = async Rust context (run body), false = sync closure (Tish fn body)
471
641
  async_context_stack: Vec<bool>,
472
642
  loop_stack: Vec<(String, Option<String>)>, // (break_label, continue_update) for innermost loop
@@ -497,7 +667,7 @@ impl Codegen {
497
667
  fn new_with_native_modules(
498
668
  project_root: Option<&Path>,
499
669
  features: &[String],
500
- native_module_map: std::collections::HashMap<String, (String, String)>,
670
+ native_module_init: std::collections::HashMap<String, crate::resolve::NativeModuleInit>,
501
671
  ) -> Self {
502
672
  let features: std::collections::HashSet<String> = features.iter().cloned().collect();
503
673
  Self {
@@ -507,7 +677,7 @@ impl Codegen {
507
677
  is_async: false,
508
678
  project_root: project_root.map(|p| p.to_path_buf()),
509
679
  features,
510
- native_module_map,
680
+ native_module_init,
511
681
  async_context_stack: Vec::new(),
512
682
  loop_stack: Vec::new(),
513
683
  function_scope_stack: vec![Vec::new()], // Start with global scope
@@ -540,12 +710,21 @@ impl Codegen {
540
710
  if is_builtin_native_spec(spec) {
541
711
  return self.builtin_native_module_rust_init(spec, export_name);
542
712
  }
543
- self.native_module_map.get(spec).map(|(crate_name, export_fn)| {
713
+ self.native_module_init.get(spec).map(|init| {
544
714
  // Native modules return a namespace object (like an ES module).
545
715
  // Named imports extract the field from that namespace: `import { foo } from "pkg"` → `ns.foo`.
716
+ let init_expr = match init {
717
+ crate::resolve::NativeModuleInit::Legacy {
718
+ crate_name,
719
+ export_fn,
720
+ } => format!("{}::{}()", crate_name, export_fn),
721
+ crate::resolve::NativeModuleInit::Generated { export_fn, .. } => {
722
+ format!("crate::generated_native::{}()", export_fn)
723
+ }
724
+ };
546
725
  format!(
547
- "{{ let _ns = {}::{}(); match _ns {{ Value::Object(ref _o) => _o.borrow().get({:?}).cloned().unwrap_or(Value::Null), _ => Value::Null }} }}",
548
- crate_name, export_fn, export_name
726
+ "{{ let _ns = {}; match _ns {{ Value::Object(ref _o) => _o.borrow().get({:?}).cloned().unwrap_or(Value::Null), _ => Value::Null }} }}",
727
+ init_expr, export_name
549
728
  )
550
729
  })
551
730
  }
@@ -627,17 +806,23 @@ impl Codegen {
627
806
  /// Escape Rust reserved keywords by prefixing with r#
628
807
  fn escape_ident(name: &str) -> Cow<'_, str> {
629
808
  // Rust standard library macros that conflict with variable names
630
- const RUST_MACROS: &[&str] = &["line", "column", "file", "module_path", "stringify", "concat"];
809
+ const RUST_MACROS: &[&str] = &[
810
+ "line",
811
+ "column",
812
+ "file",
813
+ "module_path",
814
+ "stringify",
815
+ "concat",
816
+ ];
631
817
  if RUST_MACROS.contains(&name) {
632
818
  return Cow::Owned(format!("r#{}", name));
633
819
  }
634
820
  const RUST_KEYWORDS: &[&str] = &[
635
- "as", "async", "await", "break", "const", "continue", "crate", "dyn",
636
- "else", "enum", "extern", "false", "fn", "for", "if", "impl", "in",
637
- "let", "loop", "match", "mod", "move", "mut", "pub", "ref", "return",
638
- "self", "Self", "static", "struct", "super", "trait", "true", "type",
639
- "unsafe", "use", "where", "while", "abstract", "become", "box", "do",
640
- "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",
641
826
  "virtual", "yield",
642
827
  ];
643
828
  if RUST_KEYWORDS.contains(&name) {
@@ -676,20 +861,20 @@ impl Codegen {
676
861
  if !Self::needs_clone(expr) {
677
862
  return false;
678
863
  }
679
-
864
+
680
865
  // Check for last-use optimization on simple identifiers
681
866
  if let Expr::Ident { name, .. } = expr {
682
867
  // Don't optimize RefCell-wrapped vars (they're borrowed, not owned)
683
868
  if self.refcell_wrapped_vars.contains(name.as_ref()) {
684
869
  return true;
685
870
  }
686
-
871
+
687
872
  // Inside a loop, any variable used in an init (e.g. "let x = outerVar") must be cloned:
688
873
  // the loop body runs multiple times, so we cannot move on the first iteration.
689
874
  if !self.loop_stack.is_empty() {
690
875
  return true;
691
876
  }
692
-
877
+
693
878
  // Check if this is the last use
694
879
  if let Some(ref mut analyzer) = self.usage_analyzer {
695
880
  if analyzer.is_last_use(name.as_ref()) {
@@ -697,7 +882,7 @@ impl Codegen {
697
882
  }
698
883
  }
699
884
  }
700
-
885
+
701
886
  true
702
887
  }
703
888
 
@@ -765,7 +950,7 @@ impl Codegen {
765
950
  /// Returns Some(true) for ascending, Some(false) for descending, None if not detected
766
951
  fn detect_numeric_sort_comparator(expr: &Expr) -> Option<bool> {
767
952
  use tishlang_ast::ArrowBody;
768
-
953
+
769
954
  if let Expr::ArrowFunction { params, body, .. } = expr {
770
955
  if params.len() != 2 {
771
956
  return None;
@@ -778,7 +963,7 @@ impl Codegen {
778
963
  }
779
964
  _ => return None,
780
965
  };
781
-
966
+
782
967
  // Body must be a single expression that's a subtraction
783
968
  let body_expr = match body {
784
969
  ArrowBody::Expr(e) => e.as_ref(),
@@ -790,10 +975,24 @@ impl Codegen {
790
975
  }
791
976
  }
792
977
  };
793
-
794
- if let Expr::Binary { left, op: BinOp::Sub, right, .. } = body_expr {
978
+
979
+ if let Expr::Binary {
980
+ left,
981
+ op: BinOp::Sub,
982
+ right,
983
+ ..
984
+ } = body_expr
985
+ {
795
986
  // Check for a - b (ascending) or b - a (descending)
796
- if let (Expr::Ident { name: left_name, .. }, Expr::Ident { name: right_name, .. }) = (left.as_ref(), right.as_ref()) {
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
+ {
797
996
  if left_name.as_ref() == param_a && right_name.as_ref() == param_b {
798
997
  return Some(true); // ascending
799
998
  }
@@ -875,31 +1074,61 @@ impl Codegen {
875
1074
  self.writeln("(Arc::from(\"error\"), Value::Function(Rc::new(|args: &[Value]| { tish_console_error(args); Value::Null }))),");
876
1075
  self.indent -= 1;
877
1076
  self.writeln("]))));");
878
- self.writeln("let Boolean = Value::Function(Rc::new(|args: &[Value]| tish_boolean(args)));");
879
- self.writeln("let parseInt = Value::Function(Rc::new(|args: &[Value]| tish_parse_int(args)));");
880
- self.writeln("let parseFloat = Value::Function(Rc::new(|args: &[Value]| tish_parse_float(args)));");
881
- self.writeln("let decodeURI = Value::Function(Rc::new(|args: &[Value]| tish_decode_uri(args)));");
882
- self.writeln("let encodeURI = Value::Function(Rc::new(|args: &[Value]| tish_encode_uri(args)));");
883
- self.writeln("let isFinite = Value::Function(Rc::new(|args: &[Value]| tish_is_finite(args)));");
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
+ );
884
1095
  self.writeln("let isNaN = Value::Function(Rc::new(|args: &[Value]| tish_is_nan(args)));");
885
1096
  self.writeln("let Infinity = Value::Number(f64::INFINITY);");
886
1097
  self.writeln("let NaN = Value::Number(f64::NAN);");
887
1098
  self.writeln("let Math = Value::Object(Rc::new(RefCell::new(ObjectMap::from([");
888
1099
  self.indent += 1;
889
- self.writeln("(Arc::from(\"abs\"), Value::Function(Rc::new(|args: &[Value]| tish_math_abs(args)))),");
1100
+ self.writeln(
1101
+ "(Arc::from(\"abs\"), Value::Function(Rc::new(|args: &[Value]| tish_math_abs(args)))),",
1102
+ );
890
1103
  self.writeln("(Arc::from(\"sqrt\"), Value::Function(Rc::new(|args: &[Value]| tish_math_sqrt(args)))),");
891
- self.writeln("(Arc::from(\"min\"), Value::Function(Rc::new(|args: &[Value]| tish_math_min(args)))),");
892
- self.writeln("(Arc::from(\"max\"), Value::Function(Rc::new(|args: &[Value]| tish_math_max(args)))),");
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
+ );
893
1110
  self.writeln("(Arc::from(\"floor\"), Value::Function(Rc::new(|args: &[Value]| tish_math_floor(args)))),");
894
1111
  self.writeln("(Arc::from(\"ceil\"), Value::Function(Rc::new(|args: &[Value]| tish_math_ceil(args)))),");
895
1112
  self.writeln("(Arc::from(\"round\"), Value::Function(Rc::new(|args: &[Value]| tish_math_round(args)))),");
896
1113
  self.writeln("(Arc::from(\"random\"), Value::Function(Rc::new(|args: &[Value]| tish_math_random(args)))),");
897
- self.writeln("(Arc::from(\"pow\"), Value::Function(Rc::new(|args: &[Value]| tish_math_pow(args)))),");
898
- self.writeln("(Arc::from(\"sin\"), Value::Function(Rc::new(|args: &[Value]| tish_math_sin(args)))),");
899
- self.writeln("(Arc::from(\"cos\"), Value::Function(Rc::new(|args: &[Value]| tish_math_cos(args)))),");
900
- self.writeln("(Arc::from(\"tan\"), Value::Function(Rc::new(|args: &[Value]| tish_math_tan(args)))),");
901
- self.writeln("(Arc::from(\"log\"), Value::Function(Rc::new(|args: &[Value]| tish_math_log(args)))),");
902
- self.writeln("(Arc::from(\"exp\"), Value::Function(Rc::new(|args: &[Value]| tish_math_exp(args)))),");
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
+ );
903
1132
  self.writeln("(Arc::from(\"sign\"), Value::Function(Rc::new(|args: &[Value]| tish_math_sign(args)))),");
904
1133
  self.writeln("(Arc::from(\"trunc\"), Value::Function(Rc::new(|args: &[Value]| tish_math_trunc(args)))),");
905
1134
  self.writeln("(Arc::from(\"PI\"), Value::Number(std::f64::consts::PI)),");
@@ -927,7 +1156,9 @@ impl Codegen {
927
1156
 
928
1157
  self.writeln("let Date = Value::Object(Rc::new(RefCell::new(ObjectMap::from([");
929
1158
  self.indent += 1;
930
- self.writeln("(Arc::from(\"now\"), Value::Function(Rc::new(|args: &[Value]| tish_date_now(args)))),");
1159
+ self.writeln(
1160
+ "(Arc::from(\"now\"), Value::Function(Rc::new(|args: &[Value]| tish_date_now(args)))),",
1161
+ );
931
1162
  self.indent -= 1;
932
1163
  self.writeln("]))));");
933
1164
 
@@ -952,14 +1183,18 @@ impl Codegen {
952
1183
  self.writeln("p.insert(Arc::from(\"cwd\"), Value::Function(Rc::new(|args: &[Value]| tish_process_cwd(args))));");
953
1184
  self.writeln("p.insert(Arc::from(\"exec\"), Value::Function(Rc::new(|args: &[Value]| tish_process_exec(args))));");
954
1185
  self.writeln("let argv: Vec<Value> = std::env::args().map(|s| Value::String(s.into())).collect();");
955
- self.writeln("p.insert(Arc::from(\"argv\"), Value::Array(Rc::new(RefCell::new(argv))));");
1186
+ self.writeln(
1187
+ "p.insert(Arc::from(\"argv\"), Value::Array(Rc::new(RefCell::new(argv))));",
1188
+ );
956
1189
  self.writeln("let mut env_obj = ObjectMap::default();");
957
1190
  self.writeln("for (key, value) in std::env::vars() {");
958
1191
  self.indent += 1;
959
1192
  self.writeln("env_obj.insert(Arc::from(key.as_str()), Value::String(value.into()));");
960
1193
  self.indent -= 1;
961
1194
  self.writeln("}");
962
- self.writeln("p.insert(Arc::from(\"env\"), Value::Object(Rc::new(RefCell::new(env_obj))));");
1195
+ self.writeln(
1196
+ "p.insert(Arc::from(\"env\"), Value::Object(Rc::new(RefCell::new(env_obj))));",
1197
+ );
963
1198
  self.writeln("p");
964
1199
  self.indent -= 1;
965
1200
  self.writeln("})));");
@@ -991,16 +1226,28 @@ impl Codegen {
991
1226
  }
992
1227
 
993
1228
  if self.has_feature("fs") {
994
- self.writeln("let readFile = Value::Function(Rc::new(|args: &[Value]| tish_read_file(args)));");
995
- self.writeln("let writeFile = Value::Function(Rc::new(|args: &[Value]| tish_write_file(args)));");
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
+ );
996
1235
  self.writeln("let fileExists = Value::Function(Rc::new(|args: &[Value]| tish_file_exists(args)));");
997
- self.writeln("let isDir = Value::Function(Rc::new(|args: &[Value]| tish_is_dir(args)));");
998
- self.writeln("let readDir = Value::Function(Rc::new(|args: &[Value]| tish_read_dir(args)));");
999
- self.writeln("let mkdir = Value::Function(Rc::new(|args: &[Value]| tish_mkdir(args)));");
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
+ );
1000
1245
  }
1001
1246
 
1002
1247
  if self.has_feature("regex") {
1003
- self.writeln("let RegExp = Value::Function(Rc::new(|args: &[Value]| regexp_new(args)));");
1248
+ self.writeln(
1249
+ "let RegExp = Value::Function(Rc::new(|args: &[Value]| regexp_new(args)));",
1250
+ );
1004
1251
  }
1005
1252
 
1006
1253
  if self.program_has_jsx {
@@ -1008,7 +1255,9 @@ impl Codegen {
1008
1255
  self.writeln("let Fragment = fragment_value();");
1009
1256
  self.writeln("let h = Value::Function(Rc::new(|args: &[Value]| ui_h(args)));");
1010
1257
  self.writeln("let text = Value::Function(Rc::new(|args: &[Value]| ui_text(args)));");
1011
- self.writeln("let useState = Value::Function(Rc::new(|args: &[Value]| native_use_state(args)));");
1258
+ self.writeln(
1259
+ "let useState = Value::Function(Rc::new(|args: &[Value]| native_use_state(args)));",
1260
+ );
1012
1261
  self.writeln("let createRoot = Value::Function(Rc::new(|args: &[Value]| native_create_root(args)));");
1013
1262
  }
1014
1263
 
@@ -1019,7 +1268,10 @@ impl Codegen {
1019
1268
  *self.function_scope_stack.last_mut().unwrap() = top_level_funcs.clone();
1020
1269
  for func_name in &top_level_funcs {
1021
1270
  let escaped = Self::escape_ident(func_name);
1022
- self.writeln(&format!("let {}_cell: Rc<RefCell<Value>> = Rc::new(RefCell::new(Value::Null));", escaped));
1271
+ self.writeln(&format!(
1272
+ "let {}_cell: Rc<RefCell<Value>> = Rc::new(RefCell::new(Value::Null));",
1273
+ escaped
1274
+ ));
1023
1275
  }
1024
1276
 
1025
1277
  // Initialize usage analyzer for move/clone optimization
@@ -1056,9 +1308,11 @@ impl Codegen {
1056
1308
  self.indent += 1;
1057
1309
  self.type_context.push_scope();
1058
1310
  self.outer_vars_stack.push(Vec::new());
1059
- self.rc_cell_storage_scopes.push(std::collections::HashSet::new());
1311
+ self.rc_cell_storage_scopes
1312
+ .push(std::collections::HashSet::new());
1060
1313
  // Prepass: vars that must be RefCell because nested closures capture and mutate them
1061
- let vars_mutated_by_nested = Self::collect_vars_mutated_by_nested_closures(statements);
1314
+ let vars_mutated_by_nested =
1315
+ Self::collect_vars_mutated_by_nested_closures(statements);
1062
1316
  for v in &vars_mutated_by_nested {
1063
1317
  self.refcell_wrapped_vars.insert(v.clone());
1064
1318
  }
@@ -1068,7 +1322,10 @@ impl Codegen {
1068
1322
  // Create cells for all functions in this scope
1069
1323
  for func_name in &func_names {
1070
1324
  let escaped = Self::escape_ident(func_name);
1071
- self.writeln(&format!("let {}_cell: Rc<RefCell<Value>> = Rc::new(RefCell::new(Value::Null));", escaped));
1325
+ self.writeln(&format!(
1326
+ "let {}_cell: Rc<RefCell<Value>> = Rc::new(RefCell::new(Value::Null));",
1327
+ escaped
1328
+ ));
1072
1329
  }
1073
1330
  for s in statements {
1074
1331
  self.emit_statement(s)?;
@@ -1083,7 +1340,13 @@ impl Codegen {
1083
1340
  self.indent -= 1;
1084
1341
  self.writeln("}");
1085
1342
  }
1086
- Statement::VarDecl { name, mutable, type_ann, init, .. } => {
1343
+ Statement::VarDecl {
1344
+ name,
1345
+ mutable,
1346
+ type_ann,
1347
+ init,
1348
+ ..
1349
+ } => {
1087
1350
  // Determine the Rust type from annotation
1088
1351
  let rust_type = type_ann
1089
1352
  .as_ref()
@@ -1092,10 +1355,10 @@ impl Codegen {
1092
1355
 
1093
1356
  // Track the variable type
1094
1357
  self.type_context.define(name.as_ref(), rust_type.clone());
1095
-
1358
+
1096
1359
  let mutability = if *mutable { "let mut" } else { "let" };
1097
1360
  let escaped_name = Self::escape_ident(name.as_ref());
1098
-
1361
+
1099
1362
  if rust_type.is_native() {
1100
1363
  // Generate native typed variable
1101
1364
  let expr_str = match init.as_ref() {
@@ -1123,8 +1386,7 @@ impl Codegen {
1123
1386
  let s = self.emit_expr(e)?;
1124
1387
  // Variable refs (Ident) in init must always clone: they may be used
1125
1388
  // multiple times (e.g. in a loop body) and we cannot move.
1126
- let needs = matches!(e, Expr::Ident { .. })
1127
- || self.should_clone(e);
1389
+ let needs = matches!(e, Expr::Ident { .. }) || self.should_clone(e);
1128
1390
  (s, needs)
1129
1391
  }
1130
1392
  None => ("Value::Null".to_string(), false),
@@ -1136,23 +1398,39 @@ impl Codegen {
1136
1398
  } else {
1137
1399
  expr_str.to_string()
1138
1400
  };
1139
- self.writeln(&format!("let {} = std::rc::Rc::new(RefCell::new({}));", escaped_name, init_val));
1401
+ self.writeln(&format!(
1402
+ "let {} = std::rc::Rc::new(RefCell::new({}));",
1403
+ escaped_name, init_val
1404
+ ));
1140
1405
  self.rc_cell_storage_define(name.as_ref());
1141
1406
  } else if clone_needed {
1142
- self.writeln(&format!("{} {} = ({}).clone();", mutability, escaped_name, expr_str));
1407
+ self.writeln(&format!(
1408
+ "{} {} = ({}).clone();",
1409
+ mutability, escaped_name, expr_str
1410
+ ));
1143
1411
  } else {
1144
1412
  self.writeln(&format!("{} {} = {};", mutability, escaped_name, expr_str));
1145
1413
  }
1146
1414
  }
1147
-
1415
+
1148
1416
  if let Some(scope) = self.outer_vars_stack.last_mut() {
1149
1417
  scope.push(name.to_string());
1150
1418
  }
1151
1419
  }
1152
- Statement::VarDeclDestructure { pattern, mutable, init, span, .. } => {
1420
+ Statement::VarDeclDestructure {
1421
+ pattern,
1422
+ mutable,
1423
+ init,
1424
+ span,
1425
+ ..
1426
+ } => {
1153
1427
  let expr = self.emit_expr(init)?;
1154
1428
  let mutability = if *mutable { "let mut" } else { "let" };
1155
- let clone_suffix = if Self::needs_clone(init) { ".clone()" } else { "" };
1429
+ let clone_suffix = if Self::needs_clone(init) {
1430
+ ".clone()"
1431
+ } else {
1432
+ ""
1433
+ };
1156
1434
  self.writeln(&format!("let _destruct_val = ({}){};", expr, clone_suffix));
1157
1435
  self.emit_destruct_bindings(pattern, "_destruct_val", mutability, *span)?;
1158
1436
  }
@@ -1191,7 +1469,12 @@ impl Codegen {
1191
1469
  self.indent -= 1;
1192
1470
  self.writeln("}");
1193
1471
  }
1194
- Statement::ForOf { name, iterable, body, .. } => {
1472
+ Statement::ForOf {
1473
+ name,
1474
+ iterable,
1475
+ body,
1476
+ ..
1477
+ } => {
1195
1478
  let iter_expr = self.emit_expr(iterable)?;
1196
1479
  self.writeln(&format!("{{ let _fof = ({}).clone();", iter_expr));
1197
1480
  self.indent += 1;
@@ -1201,7 +1484,10 @@ impl Codegen {
1201
1484
  self.indent += 1;
1202
1485
  self.writeln("for _v in _arr.borrow().iter() {");
1203
1486
  self.indent += 1;
1204
- self.writeln(&format!("let {} = _v.clone();", Self::escape_ident(name.as_ref())));
1487
+ self.writeln(&format!(
1488
+ "let {} = _v.clone();",
1489
+ Self::escape_ident(name.as_ref())
1490
+ ));
1205
1491
  self.emit_statement(body)?;
1206
1492
  self.indent -= 1;
1207
1493
  self.writeln("}");
@@ -1279,12 +1565,10 @@ impl Codegen {
1279
1565
  }
1280
1566
  }
1281
1567
  Statement::Continue { .. } => {
1282
- let snippet = self.loop_stack.last().map(|(label, update)| {
1283
- (
1284
- label.clone(),
1285
- update.clone(),
1286
- )
1287
- });
1568
+ let snippet = self
1569
+ .loop_stack
1570
+ .last()
1571
+ .map(|(label, update)| (label.clone(), update.clone()));
1288
1572
  if let Some((label, Some(update))) = snippet {
1289
1573
  self.writeln(&update);
1290
1574
  self.writeln(&format!("continue {};", label));
@@ -1300,7 +1584,12 @@ impl Codegen {
1300
1584
  span: None,
1301
1585
  });
1302
1586
  }
1303
- Statement::Switch { expr, cases, default_body, .. } => {
1587
+ Statement::Switch {
1588
+ expr,
1589
+ cases,
1590
+ default_body,
1591
+ ..
1592
+ } => {
1304
1593
  let e = self.emit_expr(expr)?;
1305
1594
  self.writeln(&format!("let _sv = {};", e));
1306
1595
  self.writeln("match () {");
@@ -1366,7 +1655,7 @@ impl Codegen {
1366
1655
  self.writeln("Ok(Value::Null)");
1367
1656
  self.indent -= 1;
1368
1657
  self.writeln("})();");
1369
-
1658
+
1370
1659
  if let Some(catch_stmt) = catch_body {
1371
1660
  if let Some(param) = catch_param {
1372
1661
  self.writeln("if let Err(e) = _try_result {");
@@ -1376,7 +1665,10 @@ impl Codegen {
1376
1665
  self.writeln("Ok(tish_err) => {");
1377
1666
  self.indent += 1;
1378
1667
  self.writeln("if let tishlang_runtime::TishError::Throw(v) = *tish_err {");
1379
- self.writeln(&format!("let {} = v.clone();", Self::escape_ident(param.as_ref())));
1668
+ self.writeln(&format!(
1669
+ "let {} = v.clone();",
1670
+ Self::escape_ident(param.as_ref())
1671
+ ));
1380
1672
  self.emit_statement(catch_stmt)?;
1381
1673
  self.writeln("} else { return Err(Box::new(tish_err)); }");
1382
1674
  self.indent -= 1;
@@ -1393,25 +1685,36 @@ impl Codegen {
1393
1685
  }
1394
1686
  self.writeln("}");
1395
1687
  }
1396
-
1688
+
1397
1689
  if let Some(finally_stmt) = finally_body {
1398
1690
  self.emit_statement(finally_stmt)?;
1399
1691
  }
1400
1692
  }
1401
- Statement::FunDecl { name, params, rest_param, body, span, .. } => {
1693
+ Statement::FunDecl {
1694
+ name,
1695
+ params,
1696
+ rest_param,
1697
+ body,
1698
+ span,
1699
+ ..
1700
+ } => {
1402
1701
  // Use Rc<RefCell<>> pattern to allow recursive function calls
1403
1702
  // The function can reference itself through the cell
1404
1703
  let name_raw = name.as_ref();
1405
1704
  let name_str = Self::escape_ident(name_raw);
1406
1705
  // Check if cell was already created by block prescan
1407
- let cell_exists = self.function_scope_stack
1706
+ let cell_exists = self
1707
+ .function_scope_stack
1408
1708
  .last()
1409
1709
  .map(|scope| scope.contains(&name_raw.to_string()))
1410
1710
  .unwrap_or(false);
1411
1711
  if !cell_exists {
1412
- self.writeln(&format!("let {}_cell: Rc<RefCell<Value>> = Rc::new(RefCell::new(Value::Null));", name_str));
1712
+ self.writeln(&format!(
1713
+ "let {}_cell: Rc<RefCell<Value>> = Rc::new(RefCell::new(Value::Null));",
1714
+ name_str
1715
+ ));
1413
1716
  }
1414
-
1717
+
1415
1718
  // Analyze body to find which identifiers are actually referenced
1416
1719
  let mut referenced = HashSet::new();
1417
1720
  Self::collect_stmt_idents(body, &mut referenced);
@@ -1420,9 +1723,10 @@ impl Codegen {
1420
1723
  .flat_map(|p| p.bound_names())
1421
1724
  .map(|n| n.to_string())
1422
1725
  .collect();
1423
-
1726
+
1424
1727
  // Collect all outer parameters that need to be captured (only those referenced)
1425
- let outer_params: Vec<String> = self.outer_params_stack
1728
+ let outer_params: Vec<String> = self
1729
+ .outer_params_stack
1426
1730
  .iter()
1427
1731
  .flat_map(|p| p.iter().cloned())
1428
1732
  .filter(|name| referenced.contains(name) && !param_names.contains(name))
@@ -1431,11 +1735,31 @@ impl Codegen {
1431
1735
  // Exclude params and variables declared in this function's body (locals)
1432
1736
  let mut local_var_names = HashSet::new();
1433
1737
  Self::collect_local_var_names(body, &mut local_var_names);
1434
- let outer_vars: Vec<String> = self.outer_vars_stack
1738
+ let outer_vars: Vec<String> = self
1739
+ .outer_vars_stack
1435
1740
  .iter()
1436
1741
  .flat_map(|v| v.iter().cloned())
1437
- .filter(|name| referenced.contains(name) && !param_names.contains(name) && !local_var_names.contains(name))
1438
- .filter(|name| !["Boolean", "console", "Math", "JSON", "Date", "process", "setTimeout", "clearTimeout", "Promise", "RegExp", "Polars"].contains(&name.as_str()))
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
+ })
1439
1763
  .collect();
1440
1764
 
1441
1765
  // Outer vars that are assigned in the body need RefCell (capture cell, add to refcell_wrapped_vars).
@@ -1458,9 +1782,15 @@ impl Codegen {
1458
1782
  for outer_var in &outer_vars {
1459
1783
  let var_escaped = Self::escape_ident(outer_var);
1460
1784
  if self.rc_cell_storage_contains(outer_var) {
1461
- self.writeln(&format!("let {}_cell = {}.clone();", var_escaped, var_escaped));
1785
+ self.writeln(&format!(
1786
+ "let {}_cell = {}.clone();",
1787
+ var_escaped, var_escaped
1788
+ ));
1462
1789
  } else {
1463
- self.writeln(&format!("let {}_cell = std::rc::Rc::new(RefCell::new({}.clone()));", var_escaped, var_escaped));
1790
+ self.writeln(&format!(
1791
+ "let {}_cell = std::rc::Rc::new(RefCell::new({}.clone()));",
1792
+ var_escaped, var_escaped
1793
+ ));
1464
1794
  }
1465
1795
  }
1466
1796
 
@@ -1469,32 +1799,62 @@ impl Codegen {
1469
1799
  // Clone RefCell for outer vars so closure can capture
1470
1800
  for outer_var in &outer_vars {
1471
1801
  let var_escaped = Self::escape_ident(outer_var);
1472
- self.writeln(&format!("let {}_cell = {}_cell.clone();", var_escaped, var_escaped));
1802
+ self.writeln(&format!(
1803
+ "let {}_cell = {}_cell.clone();",
1804
+ var_escaped, var_escaped
1805
+ ));
1473
1806
  }
1474
1807
  // Clone the cell so the closure can reference the function recursively
1475
1808
  let needs_self_ref = referenced.contains(name_raw);
1476
1809
  if needs_self_ref {
1477
- self.writeln(&format!("let {}_ref = {}_cell.clone();", name_str, name_str));
1810
+ self.writeln(&format!(
1811
+ "let {}_ref = {}_cell.clone();",
1812
+ name_str, name_str
1813
+ ));
1478
1814
  }
1479
1815
  // Clone sibling function cells for mutual recursion
1480
- let sibling_fns: Vec<String> = self.function_scope_stack
1816
+ let sibling_fns: Vec<String> = self
1817
+ .function_scope_stack
1481
1818
  .last()
1482
- .map(|scope| scope.iter()
1483
- .filter(|s| s.as_str() != name_raw && referenced.contains(s.as_str()))
1484
- .cloned()
1485
- .collect())
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
+ })
1486
1826
  .unwrap_or_default();
1487
1827
  for sibling in &sibling_fns {
1488
1828
  let sibling_escaped = Self::escape_ident(sibling);
1489
- self.writeln(&format!("let {}_ref = {}_cell.clone();", sibling_escaped, sibling_escaped));
1829
+ self.writeln(&format!(
1830
+ "let {}_ref = {}_cell.clone();",
1831
+ sibling_escaped, sibling_escaped
1832
+ ));
1490
1833
  }
1491
1834
  // Clone outer parameters so they can be captured by the move closure
1492
1835
  for outer_param in &outer_params {
1493
1836
  let param_escaped = Self::escape_ident(outer_param);
1494
- self.writeln(&format!("let {} = {}.clone();", param_escaped, param_escaped));
1837
+ self.writeln(&format!(
1838
+ "let {} = {}.clone();",
1839
+ param_escaped, param_escaped
1840
+ ));
1495
1841
  }
1496
1842
  // Only clone builtins that are actually referenced (clone so outer scope can still use them, e.g. process for PORT before serve)
1497
- for builtin in &["Boolean", "console", "Math", "JSON", "Date", "Uint8Array", "AudioContext", "process", "setTimeout", "clearTimeout", "Promise", "RegExp", "Polars"] {
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
+ ] {
1498
1858
  if referenced.contains(*builtin) {
1499
1859
  self.writeln(&format!("let {} = {}.clone();", builtin, builtin));
1500
1860
  }
@@ -1504,21 +1864,33 @@ impl Codegen {
1504
1864
  // Mutable outer vars: capture the RefCell so assignments use borrow_mut
1505
1865
  for outer_var in &mutable_outer_vars {
1506
1866
  let var_escaped = Self::escape_ident(outer_var);
1507
- self.writeln(&format!("let {} = {}_cell.clone();", var_escaped, var_escaped));
1867
+ self.writeln(&format!(
1868
+ "let {} = {}_cell.clone();",
1869
+ var_escaped, var_escaped
1870
+ ));
1508
1871
  }
1509
1872
  // Read-only outer vars: Value binding from borrow (avoids param-shadow issues)
1510
1873
  for outer_var in &read_only_outer_vars {
1511
1874
  let var_escaped = Self::escape_ident(outer_var);
1512
- self.writeln(&format!("let {} = (*{}_cell.borrow()).clone();", var_escaped, var_escaped));
1875
+ self.writeln(&format!(
1876
+ "let {} = (*{}_cell.borrow()).clone();",
1877
+ var_escaped, var_escaped
1878
+ ));
1513
1879
  }
1514
1880
  // Make the function available by its name inside the closure (only if recursive)
1515
1881
  if needs_self_ref {
1516
- self.writeln(&format!("let {} = (*{}_ref.borrow()).clone();", name_str, name_str));
1882
+ self.writeln(&format!(
1883
+ "let {} = (*{}_ref.borrow()).clone();",
1884
+ name_str, name_str
1885
+ ));
1517
1886
  }
1518
1887
  // Make sibling functions available for mutual recursion
1519
1888
  for sibling in &sibling_fns {
1520
1889
  let sibling_escaped = Self::escape_ident(sibling);
1521
- self.writeln(&format!("let {} = (*{}_ref.borrow()).clone();", sibling_escaped, sibling_escaped));
1890
+ self.writeln(&format!(
1891
+ "let {} = (*{}_ref.borrow()).clone();",
1892
+ sibling_escaped, sibling_escaped
1893
+ ));
1522
1894
  }
1523
1895
  // Extract just the parameter names (type annotations are parsed but not used in codegen yet)
1524
1896
  let current_param_names: Vec<String> = params
@@ -1553,10 +1925,10 @@ impl Codegen {
1553
1925
  params.len()
1554
1926
  ));
1555
1927
  }
1556
-
1928
+
1557
1929
  // Push current params to stack for nested functions
1558
1930
  self.outer_params_stack.push(current_param_names);
1559
-
1931
+
1560
1932
  // Function bodies are sync closures (even Tish async fn) - use block_on for await
1561
1933
  self.async_context_stack.push(false);
1562
1934
 
@@ -1565,7 +1937,7 @@ impl Codegen {
1565
1937
  for v in &mutable_outer_vars {
1566
1938
  self.refcell_wrapped_vars.insert(v.clone());
1567
1939
  }
1568
-
1940
+
1569
1941
  // Pre-scan body for nested functions (handles function body as Block)
1570
1942
  if let Statement::Block { statements, .. } = body.as_ref() {
1571
1943
  let nested_func_names = self.prescan_function_decls(statements);
@@ -1573,7 +1945,10 @@ impl Codegen {
1573
1945
  // Create cells for nested functions
1574
1946
  for func_name in &nested_func_names {
1575
1947
  let escaped = Self::escape_ident(func_name);
1576
- self.writeln(&format!("let {}_cell: Rc<RefCell<Value>> = Rc::new(RefCell::new(Value::Null));", escaped));
1948
+ self.writeln(&format!(
1949
+ "let {}_cell: Rc<RefCell<Value>> = Rc::new(RefCell::new(Value::Null));",
1950
+ escaped
1951
+ ));
1577
1952
  }
1578
1953
  for s in statements {
1579
1954
  self.emit_statement(s)?;
@@ -1584,22 +1959,25 @@ impl Codegen {
1584
1959
  self.emit_statement(body)?;
1585
1960
  self.function_scope_stack.pop();
1586
1961
  }
1587
-
1962
+
1588
1963
  self.async_context_stack.pop();
1589
1964
 
1590
1965
  // Restore refcell_wrapped_vars (remove mutable outer vars we added)
1591
1966
  self.refcell_wrapped_vars = saved_refcell;
1592
-
1967
+
1593
1968
  // Pop params stack
1594
1969
  self.outer_params_stack.pop();
1595
-
1970
+
1596
1971
  self.writeln("Value::Null");
1597
1972
  self.indent -= 1;
1598
1973
  self.writeln("}))");
1599
1974
  self.indent -= 1;
1600
1975
  self.writeln("};");
1601
1976
  // Update the cell with the actual function value
1602
- self.writeln(&format!("*{}_cell.borrow_mut() = {}.clone();", name_str, name_str));
1977
+ self.writeln(&format!(
1978
+ "*{}_cell.borrow_mut() = {}.clone();",
1979
+ name_str, name_str
1980
+ ));
1603
1981
  }
1604
1982
  }
1605
1983
  Ok(())
@@ -1632,7 +2010,10 @@ impl Codegen {
1632
2010
  }
1633
2011
  }
1634
2012
  }
1635
- Ok(format!("{{ let mut _args: Vec<Value> = Vec::new(); {} _args }}", parts.join(" ")))
2013
+ Ok(format!(
2014
+ "{{ let mut _args: Vec<Value> = Vec::new(); {} _args }}",
2015
+ parts.join(" ")
2016
+ ))
1636
2017
  } else {
1637
2018
  let mut emitted = Vec::new();
1638
2019
  for arg in args {
@@ -1654,7 +2035,13 @@ impl Codegen {
1654
2035
  }
1655
2036
  }
1656
2037
 
1657
- fn emit_destruct_bindings(&mut self, pattern: &DestructPattern, value_expr: &str, mutability: &str, span: Span) -> Result<(), CompileError> {
2038
+ fn emit_destruct_bindings(
2039
+ &mut self,
2040
+ pattern: &DestructPattern,
2041
+ value_expr: &str,
2042
+ mutability: &str,
2043
+ span: Span,
2044
+ ) -> Result<(), CompileError> {
1658
2045
  // Flat `let` bindings so names stay in scope for the rest of the function (e.g. JSX).
1659
2046
  match pattern {
1660
2047
  DestructPattern::Array(elements) => {
@@ -1779,7 +2166,15 @@ impl Codegen {
1779
2166
  }
1780
2167
  Expr::Call { callee, args, .. } => {
1781
2168
  // Compile-time embed: Polars.read_csv("<literal path>") when file exists
1782
- if let Some((crate_name, _)) = self.native_module_map.get("tish:polars") {
2169
+ if let Some(init) = self.native_module_init.get("tish:polars") {
2170
+ let crate_name = match init {
2171
+ crate::resolve::NativeModuleInit::Legacy { crate_name, .. } => {
2172
+ crate_name.as_str()
2173
+ }
2174
+ crate::resolve::NativeModuleInit::Generated { shim_crate, .. } => {
2175
+ shim_crate.as_str()
2176
+ }
2177
+ };
1783
2178
  if let (Some(root), Some(CallArg::Expr(first_arg))) =
1784
2179
  (self.project_root.as_ref(), args.first())
1785
2180
  {
@@ -2803,7 +3198,7 @@ impl Codegen {
2803
3198
  }
2804
3199
  })
2805
3200
  }
2806
-
3201
+
2807
3202
  /// Collect all identifiers referenced in an arrow body
2808
3203
  fn collect_referenced_idents(body: &ArrowBody) -> HashSet<String> {
2809
3204
  let mut idents = HashSet::new();
@@ -2813,10 +3208,12 @@ impl Codegen {
2813
3208
  }
2814
3209
  idents
2815
3210
  }
2816
-
3211
+
2817
3212
  fn collect_expr_idents(expr: &Expr, idents: &mut HashSet<String>) {
2818
3213
  match expr {
2819
- Expr::Ident { name, .. } => { idents.insert(name.to_string()); }
3214
+ Expr::Ident { name, .. } => {
3215
+ idents.insert(name.to_string());
3216
+ }
2820
3217
  Expr::Assign { name, value, .. } => {
2821
3218
  idents.insert(name.to_string());
2822
3219
  Self::collect_expr_idents(value, idents);
@@ -2830,7 +3227,9 @@ impl Codegen {
2830
3227
  Self::collect_expr_idents(callee, idents);
2831
3228
  for arg in args {
2832
3229
  match arg {
2833
- CallArg::Expr(e) | CallArg::Spread(e) => Self::collect_expr_idents(e, idents),
3230
+ CallArg::Expr(e) | CallArg::Spread(e) => {
3231
+ Self::collect_expr_idents(e, idents)
3232
+ }
2834
3233
  }
2835
3234
  }
2836
3235
  }
@@ -2838,19 +3237,28 @@ impl Codegen {
2838
3237
  Self::collect_expr_idents(callee, idents);
2839
3238
  for arg in args {
2840
3239
  match arg {
2841
- CallArg::Expr(e) | CallArg::Spread(e) => Self::collect_expr_idents(e, idents),
3240
+ CallArg::Expr(e) | CallArg::Spread(e) => {
3241
+ Self::collect_expr_idents(e, idents)
3242
+ }
2842
3243
  }
2843
3244
  }
2844
3245
  }
2845
3246
  Expr::Member { object, prop, .. } => {
2846
3247
  Self::collect_expr_idents(object, idents);
2847
- if let MemberProp::Expr(e) = prop { Self::collect_expr_idents(e, idents); }
3248
+ if let MemberProp::Expr(e) = prop {
3249
+ Self::collect_expr_idents(e, idents);
3250
+ }
2848
3251
  }
2849
3252
  Expr::MemberAssign { object, value, .. } => {
2850
3253
  Self::collect_expr_idents(object, idents);
2851
3254
  Self::collect_expr_idents(value, idents);
2852
3255
  }
2853
- Expr::IndexAssign { object, index, value, .. } => {
3256
+ Expr::IndexAssign {
3257
+ object,
3258
+ index,
3259
+ value,
3260
+ ..
3261
+ } => {
2854
3262
  Self::collect_expr_idents(object, idents);
2855
3263
  Self::collect_expr_idents(index, idents);
2856
3264
  Self::collect_expr_idents(value, idents);
@@ -2859,13 +3267,20 @@ impl Codegen {
2859
3267
  Self::collect_expr_idents(object, idents);
2860
3268
  Self::collect_expr_idents(index, idents);
2861
3269
  }
2862
- Expr::Conditional { cond, then_branch, else_branch, .. } => {
3270
+ Expr::Conditional {
3271
+ cond,
3272
+ then_branch,
3273
+ else_branch,
3274
+ ..
3275
+ } => {
2863
3276
  Self::collect_expr_idents(cond, idents);
2864
3277
  Self::collect_expr_idents(then_branch, idents);
2865
3278
  Self::collect_expr_idents(else_branch, idents);
2866
3279
  }
2867
- Expr::PostfixInc { name, .. } | Expr::PostfixDec { name, .. } |
2868
- Expr::PrefixInc { name, .. } | Expr::PrefixDec { name, .. } => {
3280
+ Expr::PostfixInc { name, .. }
3281
+ | Expr::PostfixDec { name, .. }
3282
+ | Expr::PrefixInc { name, .. }
3283
+ | Expr::PrefixDec { name, .. } => {
2869
3284
  idents.insert(name.to_string());
2870
3285
  }
2871
3286
  Expr::CompoundAssign { name, value, .. } => {
@@ -2879,23 +3294,25 @@ impl Codegen {
2879
3294
  Expr::Array { elements, .. } => {
2880
3295
  for el in elements {
2881
3296
  match el {
2882
- ArrayElement::Expr(e) | ArrayElement::Spread(e) => Self::collect_expr_idents(e, idents),
3297
+ ArrayElement::Expr(e) | ArrayElement::Spread(e) => {
3298
+ Self::collect_expr_idents(e, idents)
3299
+ }
2883
3300
  }
2884
3301
  }
2885
3302
  }
2886
3303
  Expr::Object { props, .. } => {
2887
3304
  for prop in props {
2888
3305
  match prop {
2889
- ObjectProp::KeyValue(_, e) | ObjectProp::Spread(e) => Self::collect_expr_idents(e, idents),
3306
+ ObjectProp::KeyValue(_, e) | ObjectProp::Spread(e) => {
3307
+ Self::collect_expr_idents(e, idents)
3308
+ }
2890
3309
  }
2891
3310
  }
2892
3311
  }
2893
- Expr::ArrowFunction { body, .. } => {
2894
- match body {
2895
- ArrowBody::Expr(e) => Self::collect_expr_idents(e, idents),
2896
- ArrowBody::Block(s) => Self::collect_stmt_idents(s, idents),
2897
- }
2898
- }
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
+ },
2899
3316
  Expr::NullishCoalesce { left, right, .. } => {
2900
3317
  Self::collect_expr_idents(left, idents);
2901
3318
  Self::collect_expr_idents(right, idents);
@@ -2903,12 +3320,20 @@ impl Codegen {
2903
3320
  Expr::TypeOf { operand, .. } => Self::collect_expr_idents(operand, idents),
2904
3321
  Expr::Await { operand, .. } => Self::collect_expr_idents(operand, idents),
2905
3322
  Expr::TemplateLiteral { exprs, .. } => {
2906
- for e in exprs { Self::collect_expr_idents(e, idents); }
3323
+ for e in exprs {
3324
+ Self::collect_expr_idents(e, idents);
3325
+ }
2907
3326
  }
2908
- Expr::JsxElement { props, children, .. } => {
3327
+ Expr::JsxElement {
3328
+ props, children, ..
3329
+ } => {
2909
3330
  for p in props {
2910
3331
  match p {
2911
- tishlang_ast::JsxProp::Attr { value: tishlang_ast::JsxAttrValue::Expr(e), .. } | tishlang_ast::JsxProp::Spread(e) => Self::collect_expr_idents(e, idents),
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),
2912
3337
  _ => {}
2913
3338
  }
2914
3339
  }
@@ -2929,7 +3354,7 @@ impl Codegen {
2929
3354
  Expr::Literal { .. } => {}
2930
3355
  }
2931
3356
  }
2932
-
3357
+
2933
3358
  /// Collect variable names that are assigned to in a statement/body (target of =, +=, ++, etc).
2934
3359
  fn collect_assigned_idents_in_stmt(stmt: &Statement, names: &mut HashSet<String>) {
2935
3360
  match stmt {
@@ -2940,14 +3365,25 @@ impl Codegen {
2940
3365
  Self::collect_assigned_idents_in_stmt(s, names);
2941
3366
  }
2942
3367
  }
2943
- Statement::If { cond, then_branch, else_branch, .. } => {
3368
+ Statement::If {
3369
+ cond,
3370
+ then_branch,
3371
+ else_branch,
3372
+ ..
3373
+ } => {
2944
3374
  Self::collect_assigned_idents_in_expr(cond, names);
2945
3375
  Self::collect_assigned_idents_in_stmt(then_branch, names);
2946
3376
  if let Some(eb) = else_branch {
2947
3377
  Self::collect_assigned_idents_in_stmt(eb, names);
2948
3378
  }
2949
3379
  }
2950
- Statement::For { init, cond, update, body, .. } => {
3380
+ Statement::For {
3381
+ init,
3382
+ cond,
3383
+ update,
3384
+ body,
3385
+ ..
3386
+ } => {
2951
3387
  if let Some(i) = init {
2952
3388
  Self::collect_assigned_idents_in_stmt(i, names);
2953
3389
  }
@@ -2967,7 +3403,12 @@ impl Codegen {
2967
3403
  Self::collect_assigned_idents_in_expr(cond, names);
2968
3404
  Self::collect_assigned_idents_in_stmt(body, names);
2969
3405
  }
2970
- Statement::Switch { expr, cases, default_body, .. } => {
3406
+ Statement::Switch {
3407
+ expr,
3408
+ cases,
3409
+ default_body,
3410
+ ..
3411
+ } => {
2971
3412
  Self::collect_assigned_idents_in_expr(expr, names);
2972
3413
  for (case_expr, stmts) in cases {
2973
3414
  if let Some(e) = case_expr {
@@ -2983,7 +3424,12 @@ impl Codegen {
2983
3424
  }
2984
3425
  }
2985
3426
  }
2986
- Statement::Try { body, catch_body, finally_body, .. } => {
3427
+ Statement::Try {
3428
+ body,
3429
+ catch_body,
3430
+ finally_body,
3431
+ ..
3432
+ } => {
2987
3433
  Self::collect_assigned_idents_in_stmt(body, names);
2988
3434
  if let Some(c) = catch_body {
2989
3435
  Self::collect_assigned_idents_in_stmt(c, names);
@@ -2999,7 +3445,10 @@ impl Codegen {
2999
3445
  }
3000
3446
  }
3001
3447
  Statement::Throw { value, .. } => Self::collect_assigned_idents_in_expr(value, names),
3002
- Statement::Break { .. } | Statement::Continue { .. } | Statement::Import { .. } | Statement::Export { .. } => {}
3448
+ Statement::Break { .. }
3449
+ | Statement::Continue { .. }
3450
+ | Statement::Import { .. }
3451
+ | Statement::Export { .. } => {}
3003
3452
  }
3004
3453
  }
3005
3454
 
@@ -3017,15 +3466,22 @@ impl Codegen {
3017
3466
  names.insert(name.to_string());
3018
3467
  Self::collect_assigned_idents_in_expr(value, names);
3019
3468
  }
3020
- Expr::PostfixInc { name, .. } | Expr::PostfixDec { name, .. }
3021
- | Expr::PrefixInc { name, .. } | Expr::PrefixDec { name, .. } => {
3469
+ Expr::PostfixInc { name, .. }
3470
+ | Expr::PostfixDec { name, .. }
3471
+ | Expr::PrefixInc { name, .. }
3472
+ | Expr::PrefixDec { name, .. } => {
3022
3473
  names.insert(name.to_string());
3023
3474
  }
3024
3475
  Expr::MemberAssign { object, value, .. } => {
3025
3476
  Self::collect_assigned_idents_in_expr(object, names);
3026
3477
  Self::collect_assigned_idents_in_expr(value, names);
3027
3478
  }
3028
- Expr::IndexAssign { object, index, value, .. } => {
3479
+ Expr::IndexAssign {
3480
+ object,
3481
+ index,
3482
+ value,
3483
+ ..
3484
+ } => {
3029
3485
  Self::collect_assigned_idents_in_expr(object, names);
3030
3486
  Self::collect_assigned_idents_in_expr(index, names);
3031
3487
  Self::collect_assigned_idents_in_expr(value, names);
@@ -3065,17 +3521,20 @@ impl Codegen {
3065
3521
  Self::collect_assigned_idents_in_expr(object, names);
3066
3522
  Self::collect_assigned_idents_in_expr(index, names);
3067
3523
  }
3068
- Expr::Conditional { cond, then_branch, else_branch, .. } => {
3524
+ Expr::Conditional {
3525
+ cond,
3526
+ then_branch,
3527
+ else_branch,
3528
+ ..
3529
+ } => {
3069
3530
  Self::collect_assigned_idents_in_expr(cond, names);
3070
3531
  Self::collect_assigned_idents_in_expr(then_branch, names);
3071
3532
  Self::collect_assigned_idents_in_expr(else_branch, names);
3072
3533
  }
3073
- Expr::ArrowFunction { body, .. } => {
3074
- match body {
3075
- ArrowBody::Expr(e) => Self::collect_assigned_idents_in_expr(e, names),
3076
- ArrowBody::Block(s) => Self::collect_assigned_idents_in_stmt(s, names),
3077
- }
3078
- }
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
+ },
3079
3538
  Expr::Array { elements, .. } => {
3080
3539
  for el in elements {
3081
3540
  match el {
@@ -3103,7 +3562,9 @@ impl Codegen {
3103
3562
  Self::collect_assigned_idents_in_expr(e, names);
3104
3563
  }
3105
3564
  }
3106
- Expr::JsxElement { props, children, .. } => {
3565
+ Expr::JsxElement {
3566
+ props, children, ..
3567
+ } => {
3107
3568
  for p in props {
3108
3569
  match p {
3109
3570
  tishlang_ast::JsxProp::Attr {
@@ -3147,10 +3608,7 @@ impl Codegen {
3147
3608
  Statement::VarDeclDestructure { pattern, .. } => {
3148
3609
  Self::collect_destruct_names(pattern, names);
3149
3610
  }
3150
- Statement::For {
3151
- init: Some(i),
3152
- ..
3153
- } => {
3611
+ Statement::For { init: Some(i), .. } => {
3154
3612
  if let Statement::VarDecl { name, .. } = i.as_ref() {
3155
3613
  names.insert(name.to_string());
3156
3614
  }
@@ -3239,11 +3697,17 @@ impl Codegen {
3239
3697
  }
3240
3698
  match body {
3241
3699
  ArrowBody::Expr(e) => Self::collect_mutated_captures_from_expr(e, block_vars, result),
3242
- ArrowBody::Block(s) => Self::collect_mutated_captures_from_statements(s, block_vars, result),
3700
+ ArrowBody::Block(s) => {
3701
+ Self::collect_mutated_captures_from_statements(s, block_vars, result)
3702
+ }
3243
3703
  }
3244
3704
  }
3245
3705
 
3246
- fn collect_mutated_captures_from_expr(expr: &Expr, block_vars: &HashSet<String>, result: &mut HashSet<String>) {
3706
+ fn collect_mutated_captures_from_expr(
3707
+ expr: &Expr,
3708
+ block_vars: &HashSet<String>,
3709
+ result: &mut HashSet<String>,
3710
+ ) {
3247
3711
  match expr {
3248
3712
  Expr::ArrowFunction { params, body, .. } => {
3249
3713
  Self::collect_mutated_captures_from_arrow(params, body, block_vars, result);
@@ -3274,13 +3738,17 @@ impl Codegen {
3274
3738
  Self::collect_mutated_captures_from_expr(e, block_vars, result);
3275
3739
  }
3276
3740
  }
3277
- Expr::Conditional { cond, then_branch, else_branch, .. } => {
3741
+ Expr::Conditional {
3742
+ cond,
3743
+ then_branch,
3744
+ else_branch,
3745
+ ..
3746
+ } => {
3278
3747
  Self::collect_mutated_captures_from_expr(cond, block_vars, result);
3279
3748
  Self::collect_mutated_captures_from_expr(then_branch, block_vars, result);
3280
3749
  Self::collect_mutated_captures_from_expr(else_branch, block_vars, result);
3281
3750
  }
3282
- Expr::Binary { left, right, .. }
3283
- | Expr::NullishCoalesce { left, right, .. } => {
3751
+ Expr::Binary { left, right, .. } | Expr::NullishCoalesce { left, right, .. } => {
3284
3752
  Self::collect_mutated_captures_from_expr(left, block_vars, result);
3285
3753
  Self::collect_mutated_captures_from_expr(right, block_vars, result);
3286
3754
  }
@@ -3323,14 +3791,25 @@ impl Codegen {
3323
3791
  Self::collect_mutated_captures_from_statements(s, block_vars, result);
3324
3792
  }
3325
3793
  }
3326
- Statement::If { cond, then_branch, else_branch, .. } => {
3794
+ Statement::If {
3795
+ cond,
3796
+ then_branch,
3797
+ else_branch,
3798
+ ..
3799
+ } => {
3327
3800
  Self::collect_mutated_captures_from_expr(cond, block_vars, result);
3328
3801
  Self::collect_mutated_captures_from_statements(then_branch, block_vars, result);
3329
3802
  if let Some(eb) = else_branch {
3330
3803
  Self::collect_mutated_captures_from_statements(eb, block_vars, result);
3331
3804
  }
3332
3805
  }
3333
- Statement::For { init, cond, update, body, .. } => {
3806
+ Statement::For {
3807
+ init,
3808
+ cond,
3809
+ update,
3810
+ body,
3811
+ ..
3812
+ } => {
3334
3813
  if let Some(i) = init {
3335
3814
  Self::collect_mutated_captures_from_statements(i, block_vars, result);
3336
3815
  }
@@ -3350,7 +3829,12 @@ impl Codegen {
3350
3829
  Self::collect_mutated_captures_from_expr(cond, block_vars, result);
3351
3830
  Self::collect_mutated_captures_from_statements(body, block_vars, result);
3352
3831
  }
3353
- Statement::Switch { expr, cases, default_body, .. } => {
3832
+ Statement::Switch {
3833
+ expr,
3834
+ cases,
3835
+ default_body,
3836
+ ..
3837
+ } => {
3354
3838
  Self::collect_mutated_captures_from_expr(expr, block_vars, result);
3355
3839
  for (ce, stmts) in cases {
3356
3840
  if let Some(e) = ce {
@@ -3366,7 +3850,12 @@ impl Codegen {
3366
3850
  }
3367
3851
  }
3368
3852
  }
3369
- Statement::Try { body, catch_body, finally_body, .. } => {
3853
+ Statement::Try {
3854
+ body,
3855
+ catch_body,
3856
+ finally_body,
3857
+ ..
3858
+ } => {
3370
3859
  Self::collect_mutated_captures_from_statements(body, block_vars, result);
3371
3860
  if let Some(c) = catch_body {
3372
3861
  Self::collect_mutated_captures_from_statements(c, block_vars, result);
@@ -3384,7 +3873,9 @@ impl Codegen {
3384
3873
  Statement::Return { value: Some(e), .. } => {
3385
3874
  Self::collect_mutated_captures_from_expr(e, block_vars, result);
3386
3875
  }
3387
- Statement::Throw { value, .. } => Self::collect_mutated_captures_from_expr(value, block_vars, result),
3876
+ Statement::Throw { value, .. } => {
3877
+ Self::collect_mutated_captures_from_expr(value, block_vars, result)
3878
+ }
3388
3879
  _ => {}
3389
3880
  }
3390
3881
  }
@@ -3403,37 +3894,66 @@ impl Codegen {
3403
3894
  /// Collect variable names declared in a statement (VarDecl, Destructure, For init).
3404
3895
  fn collect_local_var_names(stmt: &Statement, names: &mut HashSet<String>) {
3405
3896
  match stmt {
3406
- Statement::VarDecl { name, .. } => { names.insert(name.to_string()); }
3897
+ Statement::VarDecl { name, .. } => {
3898
+ names.insert(name.to_string());
3899
+ }
3407
3900
  Statement::VarDeclDestructure { pattern, .. } => {
3408
3901
  Self::collect_destruct_names(pattern, names);
3409
3902
  }
3410
3903
  Statement::Block { statements, .. } => {
3411
- for s in statements { Self::collect_local_var_names(s, names); }
3904
+ for s in statements {
3905
+ Self::collect_local_var_names(s, names);
3906
+ }
3412
3907
  }
3413
- Statement::If { then_branch, else_branch, .. } => {
3908
+ Statement::If {
3909
+ then_branch,
3910
+ else_branch,
3911
+ ..
3912
+ } => {
3414
3913
  Self::collect_local_var_names(then_branch, names);
3415
- if let Some(eb) = else_branch { Self::collect_local_var_names(eb, names); }
3914
+ if let Some(eb) = else_branch {
3915
+ Self::collect_local_var_names(eb, names);
3916
+ }
3416
3917
  }
3417
3918
  Statement::For { init, body, .. } => {
3418
- if let Some(i) = init { Self::collect_local_var_names(i, names); }
3919
+ if let Some(i) = init {
3920
+ Self::collect_local_var_names(i, names);
3921
+ }
3419
3922
  Self::collect_local_var_names(body, names);
3420
3923
  }
3421
3924
  Statement::ForOf { body, .. } => Self::collect_local_var_names(body, names),
3422
3925
  Statement::While { body, .. } | Statement::DoWhile { body, .. } => {
3423
3926
  Self::collect_local_var_names(body, names);
3424
3927
  }
3425
- Statement::Switch { cases, default_body, .. } => {
3928
+ Statement::Switch {
3929
+ cases,
3930
+ default_body,
3931
+ ..
3932
+ } => {
3426
3933
  for (_, stmts) in cases {
3427
- for s in stmts { Self::collect_local_var_names(s, names); }
3934
+ for s in stmts {
3935
+ Self::collect_local_var_names(s, names);
3936
+ }
3428
3937
  }
3429
3938
  if let Some(stmts) = default_body {
3430
- for s in stmts { Self::collect_local_var_names(s, names); }
3939
+ for s in stmts {
3940
+ Self::collect_local_var_names(s, names);
3941
+ }
3431
3942
  }
3432
3943
  }
3433
- Statement::Try { body, catch_body, finally_body, .. } => {
3944
+ Statement::Try {
3945
+ body,
3946
+ catch_body,
3947
+ finally_body,
3948
+ ..
3949
+ } => {
3434
3950
  Self::collect_local_var_names(body, names);
3435
- if let Some(c) = catch_body { Self::collect_local_var_names(c, names); }
3436
- if let Some(f) = finally_body { Self::collect_local_var_names(f, names); }
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
+ }
3437
3957
  }
3438
3958
  Statement::FunDecl { body, .. } => Self::collect_local_var_names(body, names),
3439
3959
  _ => {}
@@ -3444,16 +3964,24 @@ impl Codegen {
3444
3964
  match pattern {
3445
3965
  DestructPattern::Array(elements) => {
3446
3966
  for el in elements {
3447
- if let Some(DestructElement::Ident(n)) = el { names.insert(n.to_string()); }
3448
- if let Some(DestructElement::Pattern(p)) = el { Self::collect_destruct_names(p, names); }
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
+ }
3449
3973
  }
3450
3974
  }
3451
3975
  DestructPattern::Object(props) => {
3452
3976
  for prop in props {
3453
3977
  match &prop.value {
3454
- DestructElement::Ident(n) => { names.insert(n.to_string()); }
3978
+ DestructElement::Ident(n) => {
3979
+ names.insert(n.to_string());
3980
+ }
3455
3981
  DestructElement::Pattern(p) => Self::collect_destruct_names(p, names),
3456
- DestructElement::Rest(n) => { names.insert(n.to_string()); }
3982
+ DestructElement::Rest(n) => {
3983
+ names.insert(n.to_string());
3984
+ }
3457
3985
  }
3458
3986
  }
3459
3987
  }
@@ -3464,25 +3992,48 @@ impl Codegen {
3464
3992
  match stmt {
3465
3993
  Statement::ExprStmt { expr, .. } => Self::collect_expr_idents(expr, idents),
3466
3994
  Statement::VarDecl { init, .. } => {
3467
- if let Some(e) = init { Self::collect_expr_idents(e, idents); }
3995
+ if let Some(e) = init {
3996
+ Self::collect_expr_idents(e, idents);
3997
+ }
3468
3998
  }
3469
3999
  Statement::VarDeclDestructure { init, .. } => Self::collect_expr_idents(init, idents),
3470
4000
  Statement::Block { statements, .. } => {
3471
- for s in statements { Self::collect_stmt_idents(s, idents); }
4001
+ for s in statements {
4002
+ Self::collect_stmt_idents(s, idents);
4003
+ }
3472
4004
  }
3473
- Statement::If { cond, then_branch, else_branch, .. } => {
4005
+ Statement::If {
4006
+ cond,
4007
+ then_branch,
4008
+ else_branch,
4009
+ ..
4010
+ } => {
3474
4011
  Self::collect_expr_idents(cond, idents);
3475
4012
  Self::collect_stmt_idents(then_branch, idents);
3476
- if let Some(e) = else_branch { Self::collect_stmt_idents(e, idents); }
4013
+ if let Some(e) = else_branch {
4014
+ Self::collect_stmt_idents(e, idents);
4015
+ }
3477
4016
  }
3478
4017
  Statement::While { cond, body, .. } | Statement::DoWhile { body, cond, .. } => {
3479
4018
  Self::collect_expr_idents(cond, idents);
3480
4019
  Self::collect_stmt_idents(body, idents);
3481
4020
  }
3482
- Statement::For { init, cond, update, body, .. } => {
3483
- if let Some(s) = init { Self::collect_stmt_idents(s, idents); }
3484
- if let Some(e) = cond { Self::collect_expr_idents(e, idents); }
3485
- if let Some(e) = update { Self::collect_expr_idents(e, idents); }
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
+ }
3486
4037
  Self::collect_stmt_idents(body, idents);
3487
4038
  }
3488
4039
  Statement::ForOf { iterable, body, .. } => {
@@ -3490,26 +4041,51 @@ impl Codegen {
3490
4041
  Self::collect_stmt_idents(body, idents);
3491
4042
  }
3492
4043
  Statement::Return { value, .. } => {
3493
- if let Some(e) = value { Self::collect_expr_idents(e, idents); }
4044
+ if let Some(e) = value {
4045
+ Self::collect_expr_idents(e, idents);
4046
+ }
3494
4047
  }
3495
4048
  Statement::Throw { value, .. } => Self::collect_expr_idents(value, idents),
3496
- Statement::Try { body, catch_body, finally_body, .. } => {
4049
+ Statement::Try {
4050
+ body,
4051
+ catch_body,
4052
+ finally_body,
4053
+ ..
4054
+ } => {
3497
4055
  Self::collect_stmt_idents(body, idents);
3498
- if let Some(c) = catch_body { Self::collect_stmt_idents(c, idents); }
3499
- if let Some(f) = finally_body { Self::collect_stmt_idents(f, idents); }
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
+ }
3500
4062
  }
3501
- Statement::Switch { expr, cases, default_body, .. } => {
4063
+ Statement::Switch {
4064
+ expr,
4065
+ cases,
4066
+ default_body,
4067
+ ..
4068
+ } => {
3502
4069
  Self::collect_expr_idents(expr, idents);
3503
4070
  for (case_expr, stmts) in cases {
3504
- if let Some(e) = case_expr { Self::collect_expr_idents(e, idents); }
3505
- for s in stmts { Self::collect_stmt_idents(s, idents); }
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
+ }
3506
4077
  }
3507
4078
  if let Some(stmts) = default_body {
3508
- for s in stmts { Self::collect_stmt_idents(s, idents); }
4079
+ for s in stmts {
4080
+ Self::collect_stmt_idents(s, idents);
4081
+ }
3509
4082
  }
3510
4083
  }
3511
4084
  Statement::FunDecl { body, .. } => Self::collect_stmt_idents(body, idents),
3512
- Statement::Break { .. } | Statement::Continue { .. } | Statement::Import { .. } | Statement::Export { .. } => {}
4085
+ Statement::Break { .. }
4086
+ | Statement::Continue { .. }
4087
+ | Statement::Import { .. }
4088
+ | Statement::Export { .. } => {}
3513
4089
  }
3514
4090
  }
3515
4091
 
@@ -3522,7 +4098,7 @@ impl Codegen {
3522
4098
  // Build the arrow function as a Value::Function closure
3523
4099
  let mut code = String::new();
3524
4100
  code.push_str("{\n");
3525
-
4101
+
3526
4102
  // Find which identifiers are actually referenced in the body
3527
4103
  let referenced = Self::collect_referenced_idents(body);
3528
4104
  // Exclude the arrow's own parameters - they're not outer captures
@@ -3540,17 +4116,23 @@ impl Codegen {
3540
4116
  }
3541
4117
 
3542
4118
  // Collect outer parameters that need to be captured
3543
- let outer_params: Vec<String> = self.outer_params_stack
4119
+ let outer_params: Vec<String> = self
4120
+ .outer_params_stack
3544
4121
  .iter()
3545
4122
  .flat_map(|p| p.iter().cloned())
3546
4123
  .filter(|name| referenced.contains(name) && !param_names.contains(name))
3547
4124
  .collect();
3548
-
4125
+
3549
4126
  // Collect outer variables (from outer scopes) that need to be captured
3550
- let outer_vars: Vec<String> = self.outer_vars_stack
4127
+ let outer_vars: Vec<String> = self
4128
+ .outer_vars_stack
3551
4129
  .iter()
3552
4130
  .flat_map(|v| v.iter().cloned())
3553
- .filter(|name| referenced.contains(name) && !param_names.contains(name) && !local_var_names.contains(name))
4131
+ .filter(|name| {
4132
+ referenced.contains(name)
4133
+ && !param_names.contains(name)
4134
+ && !local_var_names.contains(name)
4135
+ })
3554
4136
  .collect();
3555
4137
 
3556
4138
  // Outer vars that are assigned in the body need RefCell; read-only get Value binding
@@ -3559,8 +4141,16 @@ impl Codegen {
3559
4141
  ArrowBody::Expr(e) => Self::collect_assigned_idents_in_expr(e, &mut assigned_in_body),
3560
4142
  ArrowBody::Block(s) => Self::collect_assigned_idents_in_stmt(s, &mut assigned_in_body),
3561
4143
  }
3562
- let mutable_outer_vars: Vec<String> = outer_vars.iter().filter(|v| assigned_in_body.contains(*v)).cloned().collect();
3563
- let read_only_outer_vars: Vec<String> = outer_vars.iter().filter(|v| !assigned_in_body.contains(*v)).cloned().collect();
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();
3564
4154
 
3565
4155
  // Wrap outer captures in Rc<RefCell<>> and use _ref suffix.
3566
4156
  // Clone existing Rc only when VarDecl actually emitted `Rc<RefCell<...>>` (see rc_cell_storage_*).
@@ -3568,38 +4158,70 @@ impl Codegen {
3568
4158
  let param_escaped = Self::escape_ident(outer_param);
3569
4159
  let ref_name = format!("{}_ref", param_escaped);
3570
4160
  if self.rc_cell_storage_contains(outer_param) {
3571
- code.push_str(&format!(" let {} = {}.clone();\n", ref_name, param_escaped));
4161
+ code.push_str(&format!(
4162
+ " let {} = {}.clone();\n",
4163
+ ref_name, param_escaped
4164
+ ));
3572
4165
  } else {
3573
- code.push_str(&format!(" let {} = std::rc::Rc::new(RefCell::new({}.clone()));\n", ref_name, param_escaped));
4166
+ code.push_str(&format!(
4167
+ " let {} = std::rc::Rc::new(RefCell::new({}.clone()));\n",
4168
+ ref_name, param_escaped
4169
+ ));
3574
4170
  }
3575
4171
  }
3576
4172
  for outer_var in &outer_vars {
3577
4173
  let var_escaped = Self::escape_ident(outer_var);
3578
4174
  let ref_name = format!("{}_ref", var_escaped);
3579
4175
  if self.rc_cell_storage_contains(outer_var) {
3580
- code.push_str(&format!(" let {} = {}.clone();\n", ref_name, var_escaped));
4176
+ code.push_str(&format!(
4177
+ " let {} = {}.clone();\n",
4178
+ ref_name, var_escaped
4179
+ ));
3581
4180
  } else {
3582
- code.push_str(&format!(" let {} = std::rc::Rc::new(RefCell::new({}.clone()));\n", ref_name, var_escaped));
4181
+ code.push_str(&format!(
4182
+ " let {} = std::rc::Rc::new(RefCell::new({}.clone()));\n",
4183
+ ref_name, var_escaped
4184
+ ));
3583
4185
  }
3584
4186
  }
3585
4187
  // Only clone builtins that are actually referenced (clone so outer scope can still use, e.g. process for PORT)
3586
- for builtin in &["console", "Math", "JSON", "Date", "Uint8Array", "AudioContext", "process", "setTimeout", "clearTimeout", "Promise", "RegExp", "Polars"] {
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
+ ] {
3587
4202
  if referenced.contains(*builtin) {
3588
4203
  code.push_str(&format!(" let {} = {}.clone();\n", builtin, builtin));
3589
4204
  }
3590
4205
  }
3591
4206
 
3592
4207
  // Clone only function cells that are actually referenced in this arrow
3593
- let referenced_funcs: Vec<String> = self.function_scope_stack
4208
+ let referenced_funcs: Vec<String> = self
4209
+ .function_scope_stack
3594
4210
  .last()
3595
- .map(|scope| scope.iter()
3596
- .filter(|f| referenced.contains(f.as_str()) && !param_names.contains(*f))
3597
- .cloned()
3598
- .collect())
4211
+ .map(|scope| {
4212
+ scope
4213
+ .iter()
4214
+ .filter(|f| referenced.contains(f.as_str()) && !param_names.contains(*f))
4215
+ .cloned()
4216
+ .collect()
4217
+ })
3599
4218
  .unwrap_or_default();
3600
4219
  for func_name in &referenced_funcs {
3601
4220
  let escaped = Self::escape_ident(func_name);
3602
- code.push_str(&format!(" let {}_ref = {}_cell.clone();\n", escaped, escaped));
4221
+ code.push_str(&format!(
4222
+ " let {}_ref = {}_cell.clone();\n",
4223
+ escaped, escaped
4224
+ ));
3603
4225
  }
3604
4226
 
3605
4227
  code.push_str(" Value::Function(Rc::new(move |args: &[Value]| {\n");
@@ -3607,23 +4229,35 @@ impl Codegen {
3607
4229
  // Make captured outer params available as plain Values (from _ref RefCells)
3608
4230
  for outer_param in &outer_params {
3609
4231
  let param_escaped = Self::escape_ident(outer_param);
3610
- code.push_str(&format!(" let {} = (*{}_ref.borrow()).clone();\n", param_escaped, param_escaped));
4232
+ code.push_str(&format!(
4233
+ " let {} = (*{}_ref.borrow()).clone();\n",
4234
+ param_escaped, param_escaped
4235
+ ));
3611
4236
  }
3612
4237
  // Mutable outer vars: capture RefCell so assignments use borrow_mut
3613
4238
  for outer_var in &mutable_outer_vars {
3614
4239
  let var_escaped = Self::escape_ident(outer_var);
3615
- code.push_str(&format!(" let {} = {}_ref.clone();\n", var_escaped, var_escaped));
4240
+ code.push_str(&format!(
4241
+ " let {} = {}_ref.clone();\n",
4242
+ var_escaped, var_escaped
4243
+ ));
3616
4244
  }
3617
4245
  // Read-only outer vars: Value binding from borrow
3618
4246
  for outer_var in &read_only_outer_vars {
3619
4247
  let var_escaped = Self::escape_ident(outer_var);
3620
- code.push_str(&format!(" let {} = (*{}_ref.borrow()).clone();\n", var_escaped, var_escaped));
4248
+ code.push_str(&format!(
4249
+ " let {} = (*{}_ref.borrow()).clone();\n",
4250
+ var_escaped, var_escaped
4251
+ ));
3621
4252
  }
3622
4253
 
3623
4254
  // Make captured functions available
3624
4255
  for func_name in &referenced_funcs {
3625
4256
  let escaped = Self::escape_ident(func_name);
3626
- code.push_str(&format!(" let {} = (*{}_ref.borrow()).clone();\n", escaped, escaped));
4257
+ code.push_str(&format!(
4258
+ " let {} = (*{}_ref.borrow()).clone();\n",
4259
+ escaped, escaped
4260
+ ));
3627
4261
  }
3628
4262
 
3629
4263
  // Extract parameters from args
@@ -3678,18 +4312,18 @@ impl Codegen {
3678
4312
  tishlang_ast::ArrowBody::Block(block_stmt) => {
3679
4313
  // For block bodies, emit the block statement
3680
4314
  self.function_scope_stack.push(Vec::new());
3681
-
4315
+
3682
4316
  // Save current output, emit to temp, then restore
3683
4317
  let saved_output = std::mem::take(&mut self.output);
3684
4318
  let saved_indent = self.indent;
3685
4319
  self.indent = 2; // Base indent inside the closure
3686
-
4320
+
3687
4321
  self.emit_statement(block_stmt)?;
3688
-
4322
+
3689
4323
  let body_code = std::mem::replace(&mut self.output, saved_output);
3690
4324
  self.indent = saved_indent;
3691
4325
  self.function_scope_stack.pop();
3692
-
4326
+
3693
4327
  code.push_str(&body_code);
3694
4328
  code.push_str(" Value::Null\n");
3695
4329
  }
@@ -3709,7 +4343,11 @@ impl Codegen {
3709
4343
  /// Emit an expression as a native Rust type (not wrapped in Value).
3710
4344
  /// Falls back to emit_expr + conversion if the expression cannot be directly
3711
4345
  /// emitted as the target type.
3712
- fn emit_native_expr(&mut self, expr: &Expr, target_type: &RustType) -> Result<String, CompileError> {
4346
+ fn emit_native_expr(
4347
+ &mut self,
4348
+ expr: &Expr,
4349
+ target_type: &RustType,
4350
+ ) -> Result<String, CompileError> {
3713
4351
  // Try to emit literals directly as native types
3714
4352
  if let Expr::Literal { value, .. } = expr {
3715
4353
  match (target_type, value) {
@@ -3728,7 +4366,7 @@ impl Codegen {
3728
4366
  _ => {}
3729
4367
  }
3730
4368
  }
3731
-
4369
+
3732
4370
  // Try to emit array literals directly as Vec<T>
3733
4371
  if let (RustType::Vec(inner_type), Expr::Array { elements, .. }) = (target_type, expr) {
3734
4372
  let mut items = Vec::new();
@@ -3747,7 +4385,7 @@ impl Codegen {
3747
4385
  }
3748
4386
  return Ok(format!("vec![{}]", items.join(", ")));
3749
4387
  }
3750
-
4388
+
3751
4389
  // Check if the identifier is already of the target type
3752
4390
  if let Expr::Ident { name, .. } = expr {
3753
4391
  let var_type = self.type_context.get_type(name.as_ref());
@@ -3759,7 +4397,7 @@ impl Codegen {
3759
4397
  return Ok(esc);
3760
4398
  }
3761
4399
  }
3762
-
4400
+
3763
4401
  // Fall back to emit_expr + conversion
3764
4402
  let value_expr = self.emit_expr(expr)?;
3765
4403
  Ok(target_type.from_value_expr(&value_expr))
@@ -3780,7 +4418,9 @@ impl Codegen {
3780
4418
  // ── literals ─────────────────────────────────────────────────────────
3781
4419
  Expr::Literal { value, .. } => match value {
3782
4420
  Literal::Number(n) => Ok((format!("{}_f64", n), RustType::F64)),
3783
- Literal::String(s) => Ok((format!("{:?}.to_string()", s.as_ref()), RustType::String)),
4421
+ Literal::String(s) => {
4422
+ Ok((format!("{:?}.to_string()", s.as_ref()), RustType::String))
4423
+ }
3784
4424
  Literal::Bool(b) => Ok((format!("{}", b), RustType::Bool)),
3785
4425
  Literal::Null => Ok(("Value::Null".to_string(), RustType::Value)),
3786
4426
  },
@@ -3806,7 +4446,13 @@ impl Codegen {
3806
4446
  }
3807
4447
 
3808
4448
  // ── binary expressions ───────────────────────────────────────────────
3809
- Expr::Binary { left, op, right, span, .. } => {
4449
+ Expr::Binary {
4450
+ left,
4451
+ op,
4452
+ right,
4453
+ span,
4454
+ ..
4455
+ } => {
3810
4456
  let (l, lt) = self.emit_typed_expr(left)?;
3811
4457
  let (r, rt) = self.emit_typed_expr(right)?;
3812
4458
 
@@ -3833,14 +4479,27 @@ impl Codegen {
3833
4479
  }
3834
4480
 
3835
4481
  // Fall back: convert both sides to Value and use the runtime.
3836
- let lv = if lt.is_native() { lt.to_value_expr(&l) } else { l };
3837
- let rv = if rt.is_native() { rt.to_value_expr(&r) } else { r };
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
+ };
3838
4492
  let result = self.emit_binop(&lv, *op, &rv, *span)?;
3839
4493
  Ok((result, RustType::Value))
3840
4494
  }
3841
4495
 
3842
4496
  // ── array indexing ───────────────────────────────────────────────────
3843
- Expr::Index { object, index, optional, .. } => {
4497
+ Expr::Index {
4498
+ object,
4499
+ index,
4500
+ optional,
4501
+ ..
4502
+ } => {
3844
4503
  // Native fast path: `vec[i]` where vec is Vec<T> and i is numeric.
3845
4504
  if !optional {
3846
4505
  if let Expr::Ident { name, .. } = object.as_ref() {
@@ -3909,19 +4568,28 @@ impl Codegen {
3909
4568
  }
3910
4569
  }
3911
4570
 
3912
- fn emit_binop(
3913
- &self,
3914
- l: &str,
3915
- op: BinOp,
3916
- r: &str,
3917
- span: Span,
3918
- ) -> Result<String, CompileError> {
4571
+ fn emit_binop(&self, l: &str, op: BinOp, r: &str, span: Span) -> Result<String, CompileError> {
3919
4572
  Ok(match op {
3920
- BinOp::Add => format!("tishlang_runtime::ops::add(&{}, &{}).unwrap_or(Value::Null)", l, r),
3921
- BinOp::Sub => format!("tishlang_runtime::ops::sub(&{}, &{}).unwrap_or(Value::Null)", l, r),
3922
- BinOp::Mul => format!("tishlang_runtime::ops::mul(&{}, &{}).unwrap_or(Value::Null)", l, r),
3923
- BinOp::Div => format!("tishlang_runtime::ops::div(&{}, &{}).unwrap_or(Value::Null)", l, r),
3924
- BinOp::Mod => format!("tishlang_runtime::ops::modulo(&{}, &{}).unwrap_or(Value::Null)", l, r),
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
+ ),
3925
4593
  BinOp::Pow => format!(
3926
4594
  "Value::Number({{ let Value::Number(a) = &({}) else {{ panic!() }}; \
3927
4595
  let Value::Number(b) = &({}) else {{ panic!() }}; a.powf(*b) }})",
@@ -3942,7 +4610,10 @@ impl Codegen {
3942
4610
  BinOp::Shr => Self::emit_bitwise_binop(l, r, ">>"),
3943
4611
  BinOp::In => format!("tish_in_operator(&{}, &{})", l, r),
3944
4612
  BinOp::Eq | BinOp::Ne => {
3945
- return Err(CompileError::new("Loose equality not supported", Some(span)))
4613
+ return Err(CompileError::new(
4614
+ "Loose equality not supported",
4615
+ Some(span),
4616
+ ))
3946
4617
  }
3947
4618
  })
3948
4619
  }