@tishlang/tish 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) 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 +3 -1
  10. package/crates/tish/tests/integration_test.rs +197 -47
  11. package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
  12. package/crates/tish/tests/shortcircuit.rs +19 -4
  13. package/crates/tish_ast/src/ast.rs +12 -14
  14. package/crates/tish_build_utils/src/lib.rs +31 -6
  15. package/crates/tish_builtins/src/array.rs +52 -21
  16. package/crates/tish_builtins/src/construct.rs +2 -8
  17. package/crates/tish_builtins/src/globals.rs +30 -15
  18. package/crates/tish_builtins/src/lib.rs +5 -5
  19. package/crates/tish_builtins/src/math.rs +5 -3
  20. package/crates/tish_builtins/src/string.rs +71 -19
  21. package/crates/tish_bytecode/src/chunk.rs +0 -1
  22. package/crates/tish_bytecode/src/compiler.rs +164 -60
  23. package/crates/tish_bytecode/src/opcode.rs +13 -4
  24. package/crates/tish_bytecode/src/peephole.rs +2 -2
  25. package/crates/tish_compile/src/codegen.rs +921 -299
  26. package/crates/tish_compile/src/infer.rs +69 -19
  27. package/crates/tish_compile/src/lib.rs +15 -5
  28. package/crates/tish_compile/src/resolve.rs +112 -69
  29. package/crates/tish_compile/src/types.rs +10 -14
  30. package/crates/tish_compile_js/src/codegen.rs +34 -13
  31. package/crates/tish_compile_js/src/tests_jsx.rs +30 -6
  32. package/crates/tish_compiler_wasm/src/lib.rs +16 -13
  33. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +39 -48
  34. package/crates/tish_core/src/json.rs +5 -3
  35. package/crates/tish_core/src/lib.rs +1 -1
  36. package/crates/tish_core/src/uri.rs +9 -6
  37. package/crates/tish_core/src/value.rs +92 -28
  38. package/crates/tish_cranelift/src/link.rs +6 -9
  39. package/crates/tish_cranelift/src/lower.rs +14 -8
  40. package/crates/tish_eval/src/eval.rs +389 -142
  41. package/crates/tish_eval/src/lib.rs +10 -6
  42. package/crates/tish_eval/src/natives.rs +95 -38
  43. package/crates/tish_eval/src/promise.rs +14 -8
  44. package/crates/tish_eval/src/timers.rs +28 -19
  45. package/crates/tish_eval/src/value.rs +10 -3
  46. package/crates/tish_fmt/src/lib.rs +29 -13
  47. package/crates/tish_lexer/src/lib.rs +217 -63
  48. package/crates/tish_lexer/src/token.rs +6 -6
  49. package/crates/tish_llvm/src/lib.rs +15 -8
  50. package/crates/tish_lsp/src/main.rs +41 -43
  51. package/crates/tish_native/src/build.rs +1 -6
  52. package/crates/tish_native/src/lib.rs +48 -19
  53. package/crates/tish_opt/src/lib.rs +67 -50
  54. package/crates/tish_parser/src/lib.rs +36 -11
  55. package/crates/tish_parser/src/parser.rs +172 -87
  56. package/crates/tish_runtime/src/http.rs +15 -6
  57. package/crates/tish_runtime/src/http_fetch.rs +24 -14
  58. package/crates/tish_runtime/src/lib.rs +224 -168
  59. package/crates/tish_runtime/src/promise.rs +1 -5
  60. package/crates/tish_runtime/src/ws.rs +45 -20
  61. package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
  62. package/crates/tish_ui/src/jsx.rs +41 -22
  63. package/crates/tish_ui/src/lib.rs +2 -2
  64. package/crates/tish_vm/src/vm.rs +309 -112
  65. package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +8 -3
  66. package/crates/tish_wasm/src/lib.rs +38 -28
  67. package/crates/tishlang_cargo_bindgen/Cargo.toml +25 -0
  68. package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
  69. package/crates/tishlang_cargo_bindgen/src/discover.rs +52 -0
  70. package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
  71. package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
  72. package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
  73. package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -0
  74. package/package.json +1 -1
  75. package/platform/darwin-arm64/tish +0 -0
  76. package/platform/darwin-x64/tish +0 -0
  77. package/platform/linux-arm64/tish +0 -0
  78. package/platform/linux-x64/tish +0 -0
  79. 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
 
@@ -416,16 +538,28 @@ pub fn compile_project_full(
416
538
  > {
417
539
  use crate::resolve;
418
540
  let root = project_root.unwrap_or_else(|| entry_path.parent().unwrap_or(Path::new(".")));
419
- let modules = resolve::resolve_project(entry_path, project_root)
420
- .map_err(|e| CompileError { message: e, span: None })?;
421
- resolve::detect_cycles(&modules)
422
- .map_err(|e| CompileError { message: e, span: None })?;
423
- let merged = resolve::merge_modules(modules)
424
- .map_err(|e| CompileError { message: e, span: None })?;
425
- let native_modules = resolve::resolve_native_modules(&merged, root)
426
- .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
+ })?;
427
558
  let native_build = resolve::compute_native_build_artifacts(&merged, root, &native_modules)
428
- .map_err(|e| CompileError { message: e, span: None })?;
559
+ .map_err(|e| CompileError {
560
+ message: e,
561
+ span: None,
562
+ })?;
429
563
  let mut all_features: Vec<String> = features.to_vec();
430
564
  for f in resolve::extract_native_import_features(&merged) {
431
565
  if !all_features.contains(&f) {
@@ -463,7 +597,11 @@ pub fn compile_with_native_modules(
463
597
  native_init: &std::collections::HashMap<String, crate::resolve::NativeModuleInit>,
464
598
  optimize: bool,
465
599
  ) -> Result<String, CompileError> {
466
- let program = if optimize { tishlang_opt::optimize(program) } else { program.clone() };
600
+ let program = if optimize {
601
+ tishlang_opt::optimize(program)
602
+ } else {
603
+ program.clone()
604
+ };
467
605
  // Type-inference pass: fills in `type_ann` on unannotated VarDecl nodes where
468
606
  // the type is unambiguous (literals, arithmetic of typed vars, etc.).
469
607
  let program = crate::infer::infer_program(&program);
@@ -668,17 +806,23 @@ impl Codegen {
668
806
  /// Escape Rust reserved keywords by prefixing with r#
669
807
  fn escape_ident(name: &str) -> Cow<'_, str> {
670
808
  // Rust standard library macros that conflict with variable names
671
- const RUST_MACROS: &[&str] = &["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
+ ];
672
817
  if RUST_MACROS.contains(&name) {
673
818
  return Cow::Owned(format!("r#{}", name));
674
819
  }
675
820
  const RUST_KEYWORDS: &[&str] = &[
676
- "as", "async", "await", "break", "const", "continue", "crate", "dyn",
677
- "else", "enum", "extern", "false", "fn", "for", "if", "impl", "in",
678
- "let", "loop", "match", "mod", "move", "mut", "pub", "ref", "return",
679
- "self", "Self", "static", "struct", "super", "trait", "true", "type",
680
- "unsafe", "use", "where", "while", "abstract", "become", "box", "do",
681
- "final", "macro", "override", "priv", "try", "typeof", "unsized",
821
+ "as", "async", "await", "break", "const", "continue", "crate", "dyn", "else", "enum",
822
+ "extern", "false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod",
823
+ "move", "mut", "pub", "ref", "return", "self", "Self", "static", "struct", "super",
824
+ "trait", "true", "type", "unsafe", "use", "where", "while", "abstract", "become",
825
+ "box", "do", "final", "macro", "override", "priv", "try", "typeof", "unsized",
682
826
  "virtual", "yield",
683
827
  ];
684
828
  if RUST_KEYWORDS.contains(&name) {
@@ -717,20 +861,20 @@ impl Codegen {
717
861
  if !Self::needs_clone(expr) {
718
862
  return false;
719
863
  }
720
-
864
+
721
865
  // Check for last-use optimization on simple identifiers
722
866
  if let Expr::Ident { name, .. } = expr {
723
867
  // Don't optimize RefCell-wrapped vars (they're borrowed, not owned)
724
868
  if self.refcell_wrapped_vars.contains(name.as_ref()) {
725
869
  return true;
726
870
  }
727
-
871
+
728
872
  // Inside a loop, any variable used in an init (e.g. "let x = outerVar") must be cloned:
729
873
  // the loop body runs multiple times, so we cannot move on the first iteration.
730
874
  if !self.loop_stack.is_empty() {
731
875
  return true;
732
876
  }
733
-
877
+
734
878
  // Check if this is the last use
735
879
  if let Some(ref mut analyzer) = self.usage_analyzer {
736
880
  if analyzer.is_last_use(name.as_ref()) {
@@ -738,7 +882,7 @@ impl Codegen {
738
882
  }
739
883
  }
740
884
  }
741
-
885
+
742
886
  true
743
887
  }
744
888
 
@@ -806,7 +950,7 @@ impl Codegen {
806
950
  /// Returns Some(true) for ascending, Some(false) for descending, None if not detected
807
951
  fn detect_numeric_sort_comparator(expr: &Expr) -> Option<bool> {
808
952
  use tishlang_ast::ArrowBody;
809
-
953
+
810
954
  if let Expr::ArrowFunction { params, body, .. } = expr {
811
955
  if params.len() != 2 {
812
956
  return None;
@@ -819,7 +963,7 @@ impl Codegen {
819
963
  }
820
964
  _ => return None,
821
965
  };
822
-
966
+
823
967
  // Body must be a single expression that's a subtraction
824
968
  let body_expr = match body {
825
969
  ArrowBody::Expr(e) => e.as_ref(),
@@ -831,10 +975,24 @@ impl Codegen {
831
975
  }
832
976
  }
833
977
  };
834
-
835
- if let Expr::Binary { 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
+ {
836
986
  // Check for a - b (ascending) or b - a (descending)
837
- 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
+ {
838
996
  if left_name.as_ref() == param_a && right_name.as_ref() == param_b {
839
997
  return Some(true); // ascending
840
998
  }
@@ -916,31 +1074,61 @@ impl Codegen {
916
1074
  self.writeln("(Arc::from(\"error\"), Value::Function(Rc::new(|args: &[Value]| { tish_console_error(args); Value::Null }))),");
917
1075
  self.indent -= 1;
918
1076
  self.writeln("]))));");
919
- self.writeln("let Boolean = Value::Function(Rc::new(|args: &[Value]| tish_boolean(args)));");
920
- self.writeln("let parseInt = Value::Function(Rc::new(|args: &[Value]| tish_parse_int(args)));");
921
- self.writeln("let parseFloat = Value::Function(Rc::new(|args: &[Value]| tish_parse_float(args)));");
922
- self.writeln("let decodeURI = Value::Function(Rc::new(|args: &[Value]| tish_decode_uri(args)));");
923
- self.writeln("let encodeURI = Value::Function(Rc::new(|args: &[Value]| tish_encode_uri(args)));");
924
- 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
+ );
925
1095
  self.writeln("let isNaN = Value::Function(Rc::new(|args: &[Value]| tish_is_nan(args)));");
926
1096
  self.writeln("let Infinity = Value::Number(f64::INFINITY);");
927
1097
  self.writeln("let NaN = Value::Number(f64::NAN);");
928
1098
  self.writeln("let Math = Value::Object(Rc::new(RefCell::new(ObjectMap::from([");
929
1099
  self.indent += 1;
930
- self.writeln("(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
+ );
931
1103
  self.writeln("(Arc::from(\"sqrt\"), Value::Function(Rc::new(|args: &[Value]| tish_math_sqrt(args)))),");
932
- self.writeln("(Arc::from(\"min\"), Value::Function(Rc::new(|args: &[Value]| tish_math_min(args)))),");
933
- 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
+ );
934
1110
  self.writeln("(Arc::from(\"floor\"), Value::Function(Rc::new(|args: &[Value]| tish_math_floor(args)))),");
935
1111
  self.writeln("(Arc::from(\"ceil\"), Value::Function(Rc::new(|args: &[Value]| tish_math_ceil(args)))),");
936
1112
  self.writeln("(Arc::from(\"round\"), Value::Function(Rc::new(|args: &[Value]| tish_math_round(args)))),");
937
1113
  self.writeln("(Arc::from(\"random\"), Value::Function(Rc::new(|args: &[Value]| tish_math_random(args)))),");
938
- self.writeln("(Arc::from(\"pow\"), Value::Function(Rc::new(|args: &[Value]| tish_math_pow(args)))),");
939
- self.writeln("(Arc::from(\"sin\"), Value::Function(Rc::new(|args: &[Value]| tish_math_sin(args)))),");
940
- self.writeln("(Arc::from(\"cos\"), Value::Function(Rc::new(|args: &[Value]| tish_math_cos(args)))),");
941
- self.writeln("(Arc::from(\"tan\"), Value::Function(Rc::new(|args: &[Value]| tish_math_tan(args)))),");
942
- self.writeln("(Arc::from(\"log\"), Value::Function(Rc::new(|args: &[Value]| tish_math_log(args)))),");
943
- 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
+ );
944
1132
  self.writeln("(Arc::from(\"sign\"), Value::Function(Rc::new(|args: &[Value]| tish_math_sign(args)))),");
945
1133
  self.writeln("(Arc::from(\"trunc\"), Value::Function(Rc::new(|args: &[Value]| tish_math_trunc(args)))),");
946
1134
  self.writeln("(Arc::from(\"PI\"), Value::Number(std::f64::consts::PI)),");
@@ -968,7 +1156,9 @@ impl Codegen {
968
1156
 
969
1157
  self.writeln("let Date = Value::Object(Rc::new(RefCell::new(ObjectMap::from([");
970
1158
  self.indent += 1;
971
- self.writeln("(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
+ );
972
1162
  self.indent -= 1;
973
1163
  self.writeln("]))));");
974
1164
 
@@ -993,14 +1183,18 @@ impl Codegen {
993
1183
  self.writeln("p.insert(Arc::from(\"cwd\"), Value::Function(Rc::new(|args: &[Value]| tish_process_cwd(args))));");
994
1184
  self.writeln("p.insert(Arc::from(\"exec\"), Value::Function(Rc::new(|args: &[Value]| tish_process_exec(args))));");
995
1185
  self.writeln("let argv: Vec<Value> = std::env::args().map(|s| Value::String(s.into())).collect();");
996
- self.writeln("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
+ );
997
1189
  self.writeln("let mut env_obj = ObjectMap::default();");
998
1190
  self.writeln("for (key, value) in std::env::vars() {");
999
1191
  self.indent += 1;
1000
1192
  self.writeln("env_obj.insert(Arc::from(key.as_str()), Value::String(value.into()));");
1001
1193
  self.indent -= 1;
1002
1194
  self.writeln("}");
1003
- self.writeln("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
+ );
1004
1198
  self.writeln("p");
1005
1199
  self.indent -= 1;
1006
1200
  self.writeln("})));");
@@ -1032,16 +1226,28 @@ impl Codegen {
1032
1226
  }
1033
1227
 
1034
1228
  if self.has_feature("fs") {
1035
- self.writeln("let readFile = Value::Function(Rc::new(|args: &[Value]| tish_read_file(args)));");
1036
- 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
+ );
1037
1235
  self.writeln("let fileExists = Value::Function(Rc::new(|args: &[Value]| tish_file_exists(args)));");
1038
- self.writeln("let isDir = Value::Function(Rc::new(|args: &[Value]| tish_is_dir(args)));");
1039
- self.writeln("let readDir = Value::Function(Rc::new(|args: &[Value]| tish_read_dir(args)));");
1040
- 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
+ );
1041
1245
  }
1042
1246
 
1043
1247
  if self.has_feature("regex") {
1044
- 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
+ );
1045
1251
  }
1046
1252
 
1047
1253
  if self.program_has_jsx {
@@ -1049,7 +1255,9 @@ impl Codegen {
1049
1255
  self.writeln("let Fragment = fragment_value();");
1050
1256
  self.writeln("let h = Value::Function(Rc::new(|args: &[Value]| ui_h(args)));");
1051
1257
  self.writeln("let text = Value::Function(Rc::new(|args: &[Value]| ui_text(args)));");
1052
- self.writeln("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
+ );
1053
1261
  self.writeln("let createRoot = Value::Function(Rc::new(|args: &[Value]| native_create_root(args)));");
1054
1262
  }
1055
1263
 
@@ -1060,7 +1268,10 @@ impl Codegen {
1060
1268
  *self.function_scope_stack.last_mut().unwrap() = top_level_funcs.clone();
1061
1269
  for func_name in &top_level_funcs {
1062
1270
  let escaped = Self::escape_ident(func_name);
1063
- self.writeln(&format!("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
+ ));
1064
1275
  }
1065
1276
 
1066
1277
  // Initialize usage analyzer for move/clone optimization
@@ -1097,9 +1308,11 @@ impl Codegen {
1097
1308
  self.indent += 1;
1098
1309
  self.type_context.push_scope();
1099
1310
  self.outer_vars_stack.push(Vec::new());
1100
- self.rc_cell_storage_scopes.push(std::collections::HashSet::new());
1311
+ self.rc_cell_storage_scopes
1312
+ .push(std::collections::HashSet::new());
1101
1313
  // Prepass: vars that must be RefCell because nested closures capture and mutate them
1102
- let vars_mutated_by_nested = Self::collect_vars_mutated_by_nested_closures(statements);
1314
+ let vars_mutated_by_nested =
1315
+ Self::collect_vars_mutated_by_nested_closures(statements);
1103
1316
  for v in &vars_mutated_by_nested {
1104
1317
  self.refcell_wrapped_vars.insert(v.clone());
1105
1318
  }
@@ -1109,7 +1322,10 @@ impl Codegen {
1109
1322
  // Create cells for all functions in this scope
1110
1323
  for func_name in &func_names {
1111
1324
  let escaped = Self::escape_ident(func_name);
1112
- self.writeln(&format!("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
+ ));
1113
1329
  }
1114
1330
  for s in statements {
1115
1331
  self.emit_statement(s)?;
@@ -1124,7 +1340,13 @@ impl Codegen {
1124
1340
  self.indent -= 1;
1125
1341
  self.writeln("}");
1126
1342
  }
1127
- Statement::VarDecl { name, mutable, type_ann, init, .. } => {
1343
+ Statement::VarDecl {
1344
+ name,
1345
+ mutable,
1346
+ type_ann,
1347
+ init,
1348
+ ..
1349
+ } => {
1128
1350
  // Determine the Rust type from annotation
1129
1351
  let rust_type = type_ann
1130
1352
  .as_ref()
@@ -1133,10 +1355,10 @@ impl Codegen {
1133
1355
 
1134
1356
  // Track the variable type
1135
1357
  self.type_context.define(name.as_ref(), rust_type.clone());
1136
-
1358
+
1137
1359
  let mutability = if *mutable { "let mut" } else { "let" };
1138
1360
  let escaped_name = Self::escape_ident(name.as_ref());
1139
-
1361
+
1140
1362
  if rust_type.is_native() {
1141
1363
  // Generate native typed variable
1142
1364
  let expr_str = match init.as_ref() {
@@ -1164,8 +1386,7 @@ impl Codegen {
1164
1386
  let s = self.emit_expr(e)?;
1165
1387
  // Variable refs (Ident) in init must always clone: they may be used
1166
1388
  // multiple times (e.g. in a loop body) and we cannot move.
1167
- let needs = matches!(e, Expr::Ident { .. })
1168
- || self.should_clone(e);
1389
+ let needs = matches!(e, Expr::Ident { .. }) || self.should_clone(e);
1169
1390
  (s, needs)
1170
1391
  }
1171
1392
  None => ("Value::Null".to_string(), false),
@@ -1177,23 +1398,39 @@ impl Codegen {
1177
1398
  } else {
1178
1399
  expr_str.to_string()
1179
1400
  };
1180
- self.writeln(&format!("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
+ ));
1181
1405
  self.rc_cell_storage_define(name.as_ref());
1182
1406
  } else if clone_needed {
1183
- self.writeln(&format!("{} {} = ({}).clone();", mutability, escaped_name, expr_str));
1407
+ self.writeln(&format!(
1408
+ "{} {} = ({}).clone();",
1409
+ mutability, escaped_name, expr_str
1410
+ ));
1184
1411
  } else {
1185
1412
  self.writeln(&format!("{} {} = {};", mutability, escaped_name, expr_str));
1186
1413
  }
1187
1414
  }
1188
-
1415
+
1189
1416
  if let Some(scope) = self.outer_vars_stack.last_mut() {
1190
1417
  scope.push(name.to_string());
1191
1418
  }
1192
1419
  }
1193
- Statement::VarDeclDestructure { pattern, mutable, init, span, .. } => {
1420
+ Statement::VarDeclDestructure {
1421
+ pattern,
1422
+ mutable,
1423
+ init,
1424
+ span,
1425
+ ..
1426
+ } => {
1194
1427
  let expr = self.emit_expr(init)?;
1195
1428
  let mutability = if *mutable { "let mut" } else { "let" };
1196
- let clone_suffix = if Self::needs_clone(init) { ".clone()" } else { "" };
1429
+ let clone_suffix = if Self::needs_clone(init) {
1430
+ ".clone()"
1431
+ } else {
1432
+ ""
1433
+ };
1197
1434
  self.writeln(&format!("let _destruct_val = ({}){};", expr, clone_suffix));
1198
1435
  self.emit_destruct_bindings(pattern, "_destruct_val", mutability, *span)?;
1199
1436
  }
@@ -1232,7 +1469,12 @@ impl Codegen {
1232
1469
  self.indent -= 1;
1233
1470
  self.writeln("}");
1234
1471
  }
1235
- Statement::ForOf { name, iterable, body, .. } => {
1472
+ Statement::ForOf {
1473
+ name,
1474
+ iterable,
1475
+ body,
1476
+ ..
1477
+ } => {
1236
1478
  let iter_expr = self.emit_expr(iterable)?;
1237
1479
  self.writeln(&format!("{{ let _fof = ({}).clone();", iter_expr));
1238
1480
  self.indent += 1;
@@ -1242,7 +1484,10 @@ impl Codegen {
1242
1484
  self.indent += 1;
1243
1485
  self.writeln("for _v in _arr.borrow().iter() {");
1244
1486
  self.indent += 1;
1245
- self.writeln(&format!("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
+ ));
1246
1491
  self.emit_statement(body)?;
1247
1492
  self.indent -= 1;
1248
1493
  self.writeln("}");
@@ -1320,12 +1565,10 @@ impl Codegen {
1320
1565
  }
1321
1566
  }
1322
1567
  Statement::Continue { .. } => {
1323
- let snippet = self.loop_stack.last().map(|(label, update)| {
1324
- (
1325
- label.clone(),
1326
- update.clone(),
1327
- )
1328
- });
1568
+ let snippet = self
1569
+ .loop_stack
1570
+ .last()
1571
+ .map(|(label, update)| (label.clone(), update.clone()));
1329
1572
  if let Some((label, Some(update))) = snippet {
1330
1573
  self.writeln(&update);
1331
1574
  self.writeln(&format!("continue {};", label));
@@ -1341,7 +1584,12 @@ impl Codegen {
1341
1584
  span: None,
1342
1585
  });
1343
1586
  }
1344
- Statement::Switch { expr, cases, default_body, .. } => {
1587
+ Statement::Switch {
1588
+ expr,
1589
+ cases,
1590
+ default_body,
1591
+ ..
1592
+ } => {
1345
1593
  let e = self.emit_expr(expr)?;
1346
1594
  self.writeln(&format!("let _sv = {};", e));
1347
1595
  self.writeln("match () {");
@@ -1407,7 +1655,7 @@ impl Codegen {
1407
1655
  self.writeln("Ok(Value::Null)");
1408
1656
  self.indent -= 1;
1409
1657
  self.writeln("})();");
1410
-
1658
+
1411
1659
  if let Some(catch_stmt) = catch_body {
1412
1660
  if let Some(param) = catch_param {
1413
1661
  self.writeln("if let Err(e) = _try_result {");
@@ -1417,7 +1665,10 @@ impl Codegen {
1417
1665
  self.writeln("Ok(tish_err) => {");
1418
1666
  self.indent += 1;
1419
1667
  self.writeln("if let tishlang_runtime::TishError::Throw(v) = *tish_err {");
1420
- self.writeln(&format!("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
+ ));
1421
1672
  self.emit_statement(catch_stmt)?;
1422
1673
  self.writeln("} else { return Err(Box::new(tish_err)); }");
1423
1674
  self.indent -= 1;
@@ -1434,25 +1685,36 @@ impl Codegen {
1434
1685
  }
1435
1686
  self.writeln("}");
1436
1687
  }
1437
-
1688
+
1438
1689
  if let Some(finally_stmt) = finally_body {
1439
1690
  self.emit_statement(finally_stmt)?;
1440
1691
  }
1441
1692
  }
1442
- Statement::FunDecl { name, params, rest_param, body, span, .. } => {
1693
+ Statement::FunDecl {
1694
+ name,
1695
+ params,
1696
+ rest_param,
1697
+ body,
1698
+ span,
1699
+ ..
1700
+ } => {
1443
1701
  // Use Rc<RefCell<>> pattern to allow recursive function calls
1444
1702
  // The function can reference itself through the cell
1445
1703
  let name_raw = name.as_ref();
1446
1704
  let name_str = Self::escape_ident(name_raw);
1447
1705
  // Check if cell was already created by block prescan
1448
- let cell_exists = self.function_scope_stack
1706
+ let cell_exists = self
1707
+ .function_scope_stack
1449
1708
  .last()
1450
1709
  .map(|scope| scope.contains(&name_raw.to_string()))
1451
1710
  .unwrap_or(false);
1452
1711
  if !cell_exists {
1453
- self.writeln(&format!("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
+ ));
1454
1716
  }
1455
-
1717
+
1456
1718
  // Analyze body to find which identifiers are actually referenced
1457
1719
  let mut referenced = HashSet::new();
1458
1720
  Self::collect_stmt_idents(body, &mut referenced);
@@ -1461,9 +1723,10 @@ impl Codegen {
1461
1723
  .flat_map(|p| p.bound_names())
1462
1724
  .map(|n| n.to_string())
1463
1725
  .collect();
1464
-
1726
+
1465
1727
  // Collect all outer parameters that need to be captured (only those referenced)
1466
- let outer_params: Vec<String> = self.outer_params_stack
1728
+ let outer_params: Vec<String> = self
1729
+ .outer_params_stack
1467
1730
  .iter()
1468
1731
  .flat_map(|p| p.iter().cloned())
1469
1732
  .filter(|name| referenced.contains(name) && !param_names.contains(name))
@@ -1472,11 +1735,31 @@ impl Codegen {
1472
1735
  // Exclude params and variables declared in this function's body (locals)
1473
1736
  let mut local_var_names = HashSet::new();
1474
1737
  Self::collect_local_var_names(body, &mut local_var_names);
1475
- let outer_vars: Vec<String> = self.outer_vars_stack
1738
+ let outer_vars: Vec<String> = self
1739
+ .outer_vars_stack
1476
1740
  .iter()
1477
1741
  .flat_map(|v| v.iter().cloned())
1478
- .filter(|name| referenced.contains(name) && !param_names.contains(name) && !local_var_names.contains(name))
1479
- .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
+ })
1480
1763
  .collect();
1481
1764
 
1482
1765
  // Outer vars that are assigned in the body need RefCell (capture cell, add to refcell_wrapped_vars).
@@ -1499,9 +1782,15 @@ impl Codegen {
1499
1782
  for outer_var in &outer_vars {
1500
1783
  let var_escaped = Self::escape_ident(outer_var);
1501
1784
  if self.rc_cell_storage_contains(outer_var) {
1502
- self.writeln(&format!("let {}_cell = {}.clone();", var_escaped, var_escaped));
1785
+ self.writeln(&format!(
1786
+ "let {}_cell = {}.clone();",
1787
+ var_escaped, var_escaped
1788
+ ));
1503
1789
  } else {
1504
- 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
+ ));
1505
1794
  }
1506
1795
  }
1507
1796
 
@@ -1510,32 +1799,62 @@ impl Codegen {
1510
1799
  // Clone RefCell for outer vars so closure can capture
1511
1800
  for outer_var in &outer_vars {
1512
1801
  let var_escaped = Self::escape_ident(outer_var);
1513
- self.writeln(&format!("let {}_cell = {}_cell.clone();", var_escaped, var_escaped));
1802
+ self.writeln(&format!(
1803
+ "let {}_cell = {}_cell.clone();",
1804
+ var_escaped, var_escaped
1805
+ ));
1514
1806
  }
1515
1807
  // Clone the cell so the closure can reference the function recursively
1516
1808
  let needs_self_ref = referenced.contains(name_raw);
1517
1809
  if needs_self_ref {
1518
- self.writeln(&format!("let {}_ref = {}_cell.clone();", name_str, name_str));
1810
+ self.writeln(&format!(
1811
+ "let {}_ref = {}_cell.clone();",
1812
+ name_str, name_str
1813
+ ));
1519
1814
  }
1520
1815
  // Clone sibling function cells for mutual recursion
1521
- let sibling_fns: Vec<String> = self.function_scope_stack
1816
+ let sibling_fns: Vec<String> = self
1817
+ .function_scope_stack
1522
1818
  .last()
1523
- .map(|scope| scope.iter()
1524
- .filter(|s| s.as_str() != name_raw && referenced.contains(s.as_str()))
1525
- .cloned()
1526
- .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
+ })
1527
1826
  .unwrap_or_default();
1528
1827
  for sibling in &sibling_fns {
1529
1828
  let sibling_escaped = Self::escape_ident(sibling);
1530
- self.writeln(&format!("let {}_ref = {}_cell.clone();", sibling_escaped, sibling_escaped));
1829
+ self.writeln(&format!(
1830
+ "let {}_ref = {}_cell.clone();",
1831
+ sibling_escaped, sibling_escaped
1832
+ ));
1531
1833
  }
1532
1834
  // Clone outer parameters so they can be captured by the move closure
1533
1835
  for outer_param in &outer_params {
1534
1836
  let param_escaped = Self::escape_ident(outer_param);
1535
- self.writeln(&format!("let {} = {}.clone();", param_escaped, param_escaped));
1837
+ self.writeln(&format!(
1838
+ "let {} = {}.clone();",
1839
+ param_escaped, param_escaped
1840
+ ));
1536
1841
  }
1537
1842
  // Only clone builtins that are actually referenced (clone so outer scope can still use them, e.g. process for PORT before serve)
1538
- for builtin in &["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
+ ] {
1539
1858
  if referenced.contains(*builtin) {
1540
1859
  self.writeln(&format!("let {} = {}.clone();", builtin, builtin));
1541
1860
  }
@@ -1545,21 +1864,33 @@ impl Codegen {
1545
1864
  // Mutable outer vars: capture the RefCell so assignments use borrow_mut
1546
1865
  for outer_var in &mutable_outer_vars {
1547
1866
  let var_escaped = Self::escape_ident(outer_var);
1548
- self.writeln(&format!("let {} = {}_cell.clone();", var_escaped, var_escaped));
1867
+ self.writeln(&format!(
1868
+ "let {} = {}_cell.clone();",
1869
+ var_escaped, var_escaped
1870
+ ));
1549
1871
  }
1550
1872
  // Read-only outer vars: Value binding from borrow (avoids param-shadow issues)
1551
1873
  for outer_var in &read_only_outer_vars {
1552
1874
  let var_escaped = Self::escape_ident(outer_var);
1553
- self.writeln(&format!("let {} = (*{}_cell.borrow()).clone();", var_escaped, var_escaped));
1875
+ self.writeln(&format!(
1876
+ "let {} = (*{}_cell.borrow()).clone();",
1877
+ var_escaped, var_escaped
1878
+ ));
1554
1879
  }
1555
1880
  // Make the function available by its name inside the closure (only if recursive)
1556
1881
  if needs_self_ref {
1557
- self.writeln(&format!("let {} = (*{}_ref.borrow()).clone();", name_str, name_str));
1882
+ self.writeln(&format!(
1883
+ "let {} = (*{}_ref.borrow()).clone();",
1884
+ name_str, name_str
1885
+ ));
1558
1886
  }
1559
1887
  // Make sibling functions available for mutual recursion
1560
1888
  for sibling in &sibling_fns {
1561
1889
  let sibling_escaped = Self::escape_ident(sibling);
1562
- self.writeln(&format!("let {} = (*{}_ref.borrow()).clone();", sibling_escaped, sibling_escaped));
1890
+ self.writeln(&format!(
1891
+ "let {} = (*{}_ref.borrow()).clone();",
1892
+ sibling_escaped, sibling_escaped
1893
+ ));
1563
1894
  }
1564
1895
  // Extract just the parameter names (type annotations are parsed but not used in codegen yet)
1565
1896
  let current_param_names: Vec<String> = params
@@ -1594,10 +1925,10 @@ impl Codegen {
1594
1925
  params.len()
1595
1926
  ));
1596
1927
  }
1597
-
1928
+
1598
1929
  // Push current params to stack for nested functions
1599
1930
  self.outer_params_stack.push(current_param_names);
1600
-
1931
+
1601
1932
  // Function bodies are sync closures (even Tish async fn) - use block_on for await
1602
1933
  self.async_context_stack.push(false);
1603
1934
 
@@ -1606,7 +1937,7 @@ impl Codegen {
1606
1937
  for v in &mutable_outer_vars {
1607
1938
  self.refcell_wrapped_vars.insert(v.clone());
1608
1939
  }
1609
-
1940
+
1610
1941
  // Pre-scan body for nested functions (handles function body as Block)
1611
1942
  if let Statement::Block { statements, .. } = body.as_ref() {
1612
1943
  let nested_func_names = self.prescan_function_decls(statements);
@@ -1614,7 +1945,10 @@ impl Codegen {
1614
1945
  // Create cells for nested functions
1615
1946
  for func_name in &nested_func_names {
1616
1947
  let escaped = Self::escape_ident(func_name);
1617
- self.writeln(&format!("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
+ ));
1618
1952
  }
1619
1953
  for s in statements {
1620
1954
  self.emit_statement(s)?;
@@ -1625,22 +1959,25 @@ impl Codegen {
1625
1959
  self.emit_statement(body)?;
1626
1960
  self.function_scope_stack.pop();
1627
1961
  }
1628
-
1962
+
1629
1963
  self.async_context_stack.pop();
1630
1964
 
1631
1965
  // Restore refcell_wrapped_vars (remove mutable outer vars we added)
1632
1966
  self.refcell_wrapped_vars = saved_refcell;
1633
-
1967
+
1634
1968
  // Pop params stack
1635
1969
  self.outer_params_stack.pop();
1636
-
1970
+
1637
1971
  self.writeln("Value::Null");
1638
1972
  self.indent -= 1;
1639
1973
  self.writeln("}))");
1640
1974
  self.indent -= 1;
1641
1975
  self.writeln("};");
1642
1976
  // Update the cell with the actual function value
1643
- self.writeln(&format!("*{}_cell.borrow_mut() = {}.clone();", name_str, name_str));
1977
+ self.writeln(&format!(
1978
+ "*{}_cell.borrow_mut() = {}.clone();",
1979
+ name_str, name_str
1980
+ ));
1644
1981
  }
1645
1982
  }
1646
1983
  Ok(())
@@ -1673,7 +2010,10 @@ impl Codegen {
1673
2010
  }
1674
2011
  }
1675
2012
  }
1676
- Ok(format!("{{ 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
+ ))
1677
2017
  } else {
1678
2018
  let mut emitted = Vec::new();
1679
2019
  for arg in args {
@@ -1695,7 +2035,13 @@ impl Codegen {
1695
2035
  }
1696
2036
  }
1697
2037
 
1698
- fn emit_destruct_bindings(&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> {
1699
2045
  // Flat `let` bindings so names stay in scope for the rest of the function (e.g. JSX).
1700
2046
  match pattern {
1701
2047
  DestructPattern::Array(elements) => {
@@ -2852,7 +3198,7 @@ impl Codegen {
2852
3198
  }
2853
3199
  })
2854
3200
  }
2855
-
3201
+
2856
3202
  /// Collect all identifiers referenced in an arrow body
2857
3203
  fn collect_referenced_idents(body: &ArrowBody) -> HashSet<String> {
2858
3204
  let mut idents = HashSet::new();
@@ -2862,10 +3208,12 @@ impl Codegen {
2862
3208
  }
2863
3209
  idents
2864
3210
  }
2865
-
3211
+
2866
3212
  fn collect_expr_idents(expr: &Expr, idents: &mut HashSet<String>) {
2867
3213
  match expr {
2868
- Expr::Ident { name, .. } => { idents.insert(name.to_string()); }
3214
+ Expr::Ident { name, .. } => {
3215
+ idents.insert(name.to_string());
3216
+ }
2869
3217
  Expr::Assign { name, value, .. } => {
2870
3218
  idents.insert(name.to_string());
2871
3219
  Self::collect_expr_idents(value, idents);
@@ -2879,7 +3227,9 @@ impl Codegen {
2879
3227
  Self::collect_expr_idents(callee, idents);
2880
3228
  for arg in args {
2881
3229
  match arg {
2882
- CallArg::Expr(e) | CallArg::Spread(e) => Self::collect_expr_idents(e, idents),
3230
+ CallArg::Expr(e) | CallArg::Spread(e) => {
3231
+ Self::collect_expr_idents(e, idents)
3232
+ }
2883
3233
  }
2884
3234
  }
2885
3235
  }
@@ -2887,19 +3237,28 @@ impl Codegen {
2887
3237
  Self::collect_expr_idents(callee, idents);
2888
3238
  for arg in args {
2889
3239
  match arg {
2890
- CallArg::Expr(e) | CallArg::Spread(e) => Self::collect_expr_idents(e, idents),
3240
+ CallArg::Expr(e) | CallArg::Spread(e) => {
3241
+ Self::collect_expr_idents(e, idents)
3242
+ }
2891
3243
  }
2892
3244
  }
2893
3245
  }
2894
3246
  Expr::Member { object, prop, .. } => {
2895
3247
  Self::collect_expr_idents(object, idents);
2896
- if let MemberProp::Expr(e) = prop { Self::collect_expr_idents(e, idents); }
3248
+ if let MemberProp::Expr(e) = prop {
3249
+ Self::collect_expr_idents(e, idents);
3250
+ }
2897
3251
  }
2898
3252
  Expr::MemberAssign { object, value, .. } => {
2899
3253
  Self::collect_expr_idents(object, idents);
2900
3254
  Self::collect_expr_idents(value, idents);
2901
3255
  }
2902
- Expr::IndexAssign { object, index, value, .. } => {
3256
+ Expr::IndexAssign {
3257
+ object,
3258
+ index,
3259
+ value,
3260
+ ..
3261
+ } => {
2903
3262
  Self::collect_expr_idents(object, idents);
2904
3263
  Self::collect_expr_idents(index, idents);
2905
3264
  Self::collect_expr_idents(value, idents);
@@ -2908,13 +3267,20 @@ impl Codegen {
2908
3267
  Self::collect_expr_idents(object, idents);
2909
3268
  Self::collect_expr_idents(index, idents);
2910
3269
  }
2911
- Expr::Conditional { cond, then_branch, else_branch, .. } => {
3270
+ Expr::Conditional {
3271
+ cond,
3272
+ then_branch,
3273
+ else_branch,
3274
+ ..
3275
+ } => {
2912
3276
  Self::collect_expr_idents(cond, idents);
2913
3277
  Self::collect_expr_idents(then_branch, idents);
2914
3278
  Self::collect_expr_idents(else_branch, idents);
2915
3279
  }
2916
- Expr::PostfixInc { name, .. } | Expr::PostfixDec { name, .. } |
2917
- Expr::PrefixInc { name, .. } | Expr::PrefixDec { name, .. } => {
3280
+ Expr::PostfixInc { name, .. }
3281
+ | Expr::PostfixDec { name, .. }
3282
+ | Expr::PrefixInc { name, .. }
3283
+ | Expr::PrefixDec { name, .. } => {
2918
3284
  idents.insert(name.to_string());
2919
3285
  }
2920
3286
  Expr::CompoundAssign { name, value, .. } => {
@@ -2928,23 +3294,25 @@ impl Codegen {
2928
3294
  Expr::Array { elements, .. } => {
2929
3295
  for el in elements {
2930
3296
  match el {
2931
- ArrayElement::Expr(e) | ArrayElement::Spread(e) => Self::collect_expr_idents(e, idents),
3297
+ ArrayElement::Expr(e) | ArrayElement::Spread(e) => {
3298
+ Self::collect_expr_idents(e, idents)
3299
+ }
2932
3300
  }
2933
3301
  }
2934
3302
  }
2935
3303
  Expr::Object { props, .. } => {
2936
3304
  for prop in props {
2937
3305
  match prop {
2938
- ObjectProp::KeyValue(_, e) | ObjectProp::Spread(e) => Self::collect_expr_idents(e, idents),
3306
+ ObjectProp::KeyValue(_, e) | ObjectProp::Spread(e) => {
3307
+ Self::collect_expr_idents(e, idents)
3308
+ }
2939
3309
  }
2940
3310
  }
2941
3311
  }
2942
- Expr::ArrowFunction { body, .. } => {
2943
- match body {
2944
- ArrowBody::Expr(e) => Self::collect_expr_idents(e, idents),
2945
- ArrowBody::Block(s) => Self::collect_stmt_idents(s, idents),
2946
- }
2947
- }
3312
+ Expr::ArrowFunction { body, .. } => match body {
3313
+ ArrowBody::Expr(e) => Self::collect_expr_idents(e, idents),
3314
+ ArrowBody::Block(s) => Self::collect_stmt_idents(s, idents),
3315
+ },
2948
3316
  Expr::NullishCoalesce { left, right, .. } => {
2949
3317
  Self::collect_expr_idents(left, idents);
2950
3318
  Self::collect_expr_idents(right, idents);
@@ -2952,12 +3320,20 @@ impl Codegen {
2952
3320
  Expr::TypeOf { operand, .. } => Self::collect_expr_idents(operand, idents),
2953
3321
  Expr::Await { operand, .. } => Self::collect_expr_idents(operand, idents),
2954
3322
  Expr::TemplateLiteral { exprs, .. } => {
2955
- for e in exprs { Self::collect_expr_idents(e, idents); }
3323
+ for e in exprs {
3324
+ Self::collect_expr_idents(e, idents);
3325
+ }
2956
3326
  }
2957
- Expr::JsxElement { props, children, .. } => {
3327
+ Expr::JsxElement {
3328
+ props, children, ..
3329
+ } => {
2958
3330
  for p in props {
2959
3331
  match p {
2960
- 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),
2961
3337
  _ => {}
2962
3338
  }
2963
3339
  }
@@ -2978,7 +3354,7 @@ impl Codegen {
2978
3354
  Expr::Literal { .. } => {}
2979
3355
  }
2980
3356
  }
2981
-
3357
+
2982
3358
  /// Collect variable names that are assigned to in a statement/body (target of =, +=, ++, etc).
2983
3359
  fn collect_assigned_idents_in_stmt(stmt: &Statement, names: &mut HashSet<String>) {
2984
3360
  match stmt {
@@ -2989,14 +3365,25 @@ impl Codegen {
2989
3365
  Self::collect_assigned_idents_in_stmt(s, names);
2990
3366
  }
2991
3367
  }
2992
- Statement::If { cond, then_branch, else_branch, .. } => {
3368
+ Statement::If {
3369
+ cond,
3370
+ then_branch,
3371
+ else_branch,
3372
+ ..
3373
+ } => {
2993
3374
  Self::collect_assigned_idents_in_expr(cond, names);
2994
3375
  Self::collect_assigned_idents_in_stmt(then_branch, names);
2995
3376
  if let Some(eb) = else_branch {
2996
3377
  Self::collect_assigned_idents_in_stmt(eb, names);
2997
3378
  }
2998
3379
  }
2999
- Statement::For { init, cond, update, body, .. } => {
3380
+ Statement::For {
3381
+ init,
3382
+ cond,
3383
+ update,
3384
+ body,
3385
+ ..
3386
+ } => {
3000
3387
  if let Some(i) = init {
3001
3388
  Self::collect_assigned_idents_in_stmt(i, names);
3002
3389
  }
@@ -3016,7 +3403,12 @@ impl Codegen {
3016
3403
  Self::collect_assigned_idents_in_expr(cond, names);
3017
3404
  Self::collect_assigned_idents_in_stmt(body, names);
3018
3405
  }
3019
- Statement::Switch { expr, cases, default_body, .. } => {
3406
+ Statement::Switch {
3407
+ expr,
3408
+ cases,
3409
+ default_body,
3410
+ ..
3411
+ } => {
3020
3412
  Self::collect_assigned_idents_in_expr(expr, names);
3021
3413
  for (case_expr, stmts) in cases {
3022
3414
  if let Some(e) = case_expr {
@@ -3032,7 +3424,12 @@ impl Codegen {
3032
3424
  }
3033
3425
  }
3034
3426
  }
3035
- Statement::Try { body, catch_body, finally_body, .. } => {
3427
+ Statement::Try {
3428
+ body,
3429
+ catch_body,
3430
+ finally_body,
3431
+ ..
3432
+ } => {
3036
3433
  Self::collect_assigned_idents_in_stmt(body, names);
3037
3434
  if let Some(c) = catch_body {
3038
3435
  Self::collect_assigned_idents_in_stmt(c, names);
@@ -3048,7 +3445,10 @@ impl Codegen {
3048
3445
  }
3049
3446
  }
3050
3447
  Statement::Throw { value, .. } => Self::collect_assigned_idents_in_expr(value, names),
3051
- Statement::Break { .. } | Statement::Continue { .. } | Statement::Import { .. } | Statement::Export { .. } => {}
3448
+ Statement::Break { .. }
3449
+ | Statement::Continue { .. }
3450
+ | Statement::Import { .. }
3451
+ | Statement::Export { .. } => {}
3052
3452
  }
3053
3453
  }
3054
3454
 
@@ -3066,15 +3466,22 @@ impl Codegen {
3066
3466
  names.insert(name.to_string());
3067
3467
  Self::collect_assigned_idents_in_expr(value, names);
3068
3468
  }
3069
- Expr::PostfixInc { name, .. } | Expr::PostfixDec { name, .. }
3070
- | Expr::PrefixInc { name, .. } | Expr::PrefixDec { name, .. } => {
3469
+ Expr::PostfixInc { name, .. }
3470
+ | Expr::PostfixDec { name, .. }
3471
+ | Expr::PrefixInc { name, .. }
3472
+ | Expr::PrefixDec { name, .. } => {
3071
3473
  names.insert(name.to_string());
3072
3474
  }
3073
3475
  Expr::MemberAssign { object, value, .. } => {
3074
3476
  Self::collect_assigned_idents_in_expr(object, names);
3075
3477
  Self::collect_assigned_idents_in_expr(value, names);
3076
3478
  }
3077
- Expr::IndexAssign { object, index, value, .. } => {
3479
+ Expr::IndexAssign {
3480
+ object,
3481
+ index,
3482
+ value,
3483
+ ..
3484
+ } => {
3078
3485
  Self::collect_assigned_idents_in_expr(object, names);
3079
3486
  Self::collect_assigned_idents_in_expr(index, names);
3080
3487
  Self::collect_assigned_idents_in_expr(value, names);
@@ -3114,17 +3521,20 @@ impl Codegen {
3114
3521
  Self::collect_assigned_idents_in_expr(object, names);
3115
3522
  Self::collect_assigned_idents_in_expr(index, names);
3116
3523
  }
3117
- Expr::Conditional { cond, then_branch, else_branch, .. } => {
3524
+ Expr::Conditional {
3525
+ cond,
3526
+ then_branch,
3527
+ else_branch,
3528
+ ..
3529
+ } => {
3118
3530
  Self::collect_assigned_idents_in_expr(cond, names);
3119
3531
  Self::collect_assigned_idents_in_expr(then_branch, names);
3120
3532
  Self::collect_assigned_idents_in_expr(else_branch, names);
3121
3533
  }
3122
- Expr::ArrowFunction { body, .. } => {
3123
- match body {
3124
- ArrowBody::Expr(e) => Self::collect_assigned_idents_in_expr(e, names),
3125
- ArrowBody::Block(s) => Self::collect_assigned_idents_in_stmt(s, names),
3126
- }
3127
- }
3534
+ Expr::ArrowFunction { body, .. } => match body {
3535
+ ArrowBody::Expr(e) => Self::collect_assigned_idents_in_expr(e, names),
3536
+ ArrowBody::Block(s) => Self::collect_assigned_idents_in_stmt(s, names),
3537
+ },
3128
3538
  Expr::Array { elements, .. } => {
3129
3539
  for el in elements {
3130
3540
  match el {
@@ -3152,7 +3562,9 @@ impl Codegen {
3152
3562
  Self::collect_assigned_idents_in_expr(e, names);
3153
3563
  }
3154
3564
  }
3155
- Expr::JsxElement { props, children, .. } => {
3565
+ Expr::JsxElement {
3566
+ props, children, ..
3567
+ } => {
3156
3568
  for p in props {
3157
3569
  match p {
3158
3570
  tishlang_ast::JsxProp::Attr {
@@ -3196,10 +3608,7 @@ impl Codegen {
3196
3608
  Statement::VarDeclDestructure { pattern, .. } => {
3197
3609
  Self::collect_destruct_names(pattern, names);
3198
3610
  }
3199
- Statement::For {
3200
- init: Some(i),
3201
- ..
3202
- } => {
3611
+ Statement::For { init: Some(i), .. } => {
3203
3612
  if let Statement::VarDecl { name, .. } = i.as_ref() {
3204
3613
  names.insert(name.to_string());
3205
3614
  }
@@ -3288,11 +3697,17 @@ impl Codegen {
3288
3697
  }
3289
3698
  match body {
3290
3699
  ArrowBody::Expr(e) => Self::collect_mutated_captures_from_expr(e, block_vars, result),
3291
- ArrowBody::Block(s) => 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
+ }
3292
3703
  }
3293
3704
  }
3294
3705
 
3295
- 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
+ ) {
3296
3711
  match expr {
3297
3712
  Expr::ArrowFunction { params, body, .. } => {
3298
3713
  Self::collect_mutated_captures_from_arrow(params, body, block_vars, result);
@@ -3323,13 +3738,17 @@ impl Codegen {
3323
3738
  Self::collect_mutated_captures_from_expr(e, block_vars, result);
3324
3739
  }
3325
3740
  }
3326
- Expr::Conditional { cond, then_branch, else_branch, .. } => {
3741
+ Expr::Conditional {
3742
+ cond,
3743
+ then_branch,
3744
+ else_branch,
3745
+ ..
3746
+ } => {
3327
3747
  Self::collect_mutated_captures_from_expr(cond, block_vars, result);
3328
3748
  Self::collect_mutated_captures_from_expr(then_branch, block_vars, result);
3329
3749
  Self::collect_mutated_captures_from_expr(else_branch, block_vars, result);
3330
3750
  }
3331
- Expr::Binary { left, right, .. }
3332
- | Expr::NullishCoalesce { left, right, .. } => {
3751
+ Expr::Binary { left, right, .. } | Expr::NullishCoalesce { left, right, .. } => {
3333
3752
  Self::collect_mutated_captures_from_expr(left, block_vars, result);
3334
3753
  Self::collect_mutated_captures_from_expr(right, block_vars, result);
3335
3754
  }
@@ -3372,14 +3791,25 @@ impl Codegen {
3372
3791
  Self::collect_mutated_captures_from_statements(s, block_vars, result);
3373
3792
  }
3374
3793
  }
3375
- Statement::If { cond, then_branch, else_branch, .. } => {
3794
+ Statement::If {
3795
+ cond,
3796
+ then_branch,
3797
+ else_branch,
3798
+ ..
3799
+ } => {
3376
3800
  Self::collect_mutated_captures_from_expr(cond, block_vars, result);
3377
3801
  Self::collect_mutated_captures_from_statements(then_branch, block_vars, result);
3378
3802
  if let Some(eb) = else_branch {
3379
3803
  Self::collect_mutated_captures_from_statements(eb, block_vars, result);
3380
3804
  }
3381
3805
  }
3382
- Statement::For { init, cond, update, body, .. } => {
3806
+ Statement::For {
3807
+ init,
3808
+ cond,
3809
+ update,
3810
+ body,
3811
+ ..
3812
+ } => {
3383
3813
  if let Some(i) = init {
3384
3814
  Self::collect_mutated_captures_from_statements(i, block_vars, result);
3385
3815
  }
@@ -3399,7 +3829,12 @@ impl Codegen {
3399
3829
  Self::collect_mutated_captures_from_expr(cond, block_vars, result);
3400
3830
  Self::collect_mutated_captures_from_statements(body, block_vars, result);
3401
3831
  }
3402
- Statement::Switch { expr, cases, default_body, .. } => {
3832
+ Statement::Switch {
3833
+ expr,
3834
+ cases,
3835
+ default_body,
3836
+ ..
3837
+ } => {
3403
3838
  Self::collect_mutated_captures_from_expr(expr, block_vars, result);
3404
3839
  for (ce, stmts) in cases {
3405
3840
  if let Some(e) = ce {
@@ -3415,7 +3850,12 @@ impl Codegen {
3415
3850
  }
3416
3851
  }
3417
3852
  }
3418
- Statement::Try { body, catch_body, finally_body, .. } => {
3853
+ Statement::Try {
3854
+ body,
3855
+ catch_body,
3856
+ finally_body,
3857
+ ..
3858
+ } => {
3419
3859
  Self::collect_mutated_captures_from_statements(body, block_vars, result);
3420
3860
  if let Some(c) = catch_body {
3421
3861
  Self::collect_mutated_captures_from_statements(c, block_vars, result);
@@ -3433,7 +3873,9 @@ impl Codegen {
3433
3873
  Statement::Return { value: Some(e), .. } => {
3434
3874
  Self::collect_mutated_captures_from_expr(e, block_vars, result);
3435
3875
  }
3436
- Statement::Throw { value, .. } => 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
+ }
3437
3879
  _ => {}
3438
3880
  }
3439
3881
  }
@@ -3452,37 +3894,66 @@ impl Codegen {
3452
3894
  /// Collect variable names declared in a statement (VarDecl, Destructure, For init).
3453
3895
  fn collect_local_var_names(stmt: &Statement, names: &mut HashSet<String>) {
3454
3896
  match stmt {
3455
- Statement::VarDecl { name, .. } => { names.insert(name.to_string()); }
3897
+ Statement::VarDecl { name, .. } => {
3898
+ names.insert(name.to_string());
3899
+ }
3456
3900
  Statement::VarDeclDestructure { pattern, .. } => {
3457
3901
  Self::collect_destruct_names(pattern, names);
3458
3902
  }
3459
3903
  Statement::Block { statements, .. } => {
3460
- for s in statements { Self::collect_local_var_names(s, names); }
3904
+ for s in statements {
3905
+ Self::collect_local_var_names(s, names);
3906
+ }
3461
3907
  }
3462
- Statement::If { then_branch, else_branch, .. } => {
3908
+ Statement::If {
3909
+ then_branch,
3910
+ else_branch,
3911
+ ..
3912
+ } => {
3463
3913
  Self::collect_local_var_names(then_branch, names);
3464
- if let Some(eb) = else_branch { Self::collect_local_var_names(eb, names); }
3914
+ if let Some(eb) = else_branch {
3915
+ Self::collect_local_var_names(eb, names);
3916
+ }
3465
3917
  }
3466
3918
  Statement::For { init, body, .. } => {
3467
- if let Some(i) = init { Self::collect_local_var_names(i, names); }
3919
+ if let Some(i) = init {
3920
+ Self::collect_local_var_names(i, names);
3921
+ }
3468
3922
  Self::collect_local_var_names(body, names);
3469
3923
  }
3470
3924
  Statement::ForOf { body, .. } => Self::collect_local_var_names(body, names),
3471
3925
  Statement::While { body, .. } | Statement::DoWhile { body, .. } => {
3472
3926
  Self::collect_local_var_names(body, names);
3473
3927
  }
3474
- Statement::Switch { cases, default_body, .. } => {
3928
+ Statement::Switch {
3929
+ cases,
3930
+ default_body,
3931
+ ..
3932
+ } => {
3475
3933
  for (_, stmts) in cases {
3476
- 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
+ }
3477
3937
  }
3478
3938
  if let Some(stmts) = default_body {
3479
- 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
+ }
3480
3942
  }
3481
3943
  }
3482
- Statement::Try { body, catch_body, finally_body, .. } => {
3944
+ Statement::Try {
3945
+ body,
3946
+ catch_body,
3947
+ finally_body,
3948
+ ..
3949
+ } => {
3483
3950
  Self::collect_local_var_names(body, names);
3484
- if let Some(c) = catch_body { Self::collect_local_var_names(c, names); }
3485
- 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
+ }
3486
3957
  }
3487
3958
  Statement::FunDecl { body, .. } => Self::collect_local_var_names(body, names),
3488
3959
  _ => {}
@@ -3493,16 +3964,24 @@ impl Codegen {
3493
3964
  match pattern {
3494
3965
  DestructPattern::Array(elements) => {
3495
3966
  for el in elements {
3496
- if let Some(DestructElement::Ident(n)) = el { names.insert(n.to_string()); }
3497
- 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
+ }
3498
3973
  }
3499
3974
  }
3500
3975
  DestructPattern::Object(props) => {
3501
3976
  for prop in props {
3502
3977
  match &prop.value {
3503
- DestructElement::Ident(n) => { names.insert(n.to_string()); }
3978
+ DestructElement::Ident(n) => {
3979
+ names.insert(n.to_string());
3980
+ }
3504
3981
  DestructElement::Pattern(p) => Self::collect_destruct_names(p, names),
3505
- DestructElement::Rest(n) => { names.insert(n.to_string()); }
3982
+ DestructElement::Rest(n) => {
3983
+ names.insert(n.to_string());
3984
+ }
3506
3985
  }
3507
3986
  }
3508
3987
  }
@@ -3513,25 +3992,48 @@ impl Codegen {
3513
3992
  match stmt {
3514
3993
  Statement::ExprStmt { expr, .. } => Self::collect_expr_idents(expr, idents),
3515
3994
  Statement::VarDecl { init, .. } => {
3516
- if let Some(e) = init { Self::collect_expr_idents(e, idents); }
3995
+ if let Some(e) = init {
3996
+ Self::collect_expr_idents(e, idents);
3997
+ }
3517
3998
  }
3518
3999
  Statement::VarDeclDestructure { init, .. } => Self::collect_expr_idents(init, idents),
3519
4000
  Statement::Block { statements, .. } => {
3520
- for s in statements { Self::collect_stmt_idents(s, idents); }
4001
+ for s in statements {
4002
+ Self::collect_stmt_idents(s, idents);
4003
+ }
3521
4004
  }
3522
- Statement::If { cond, then_branch, else_branch, .. } => {
4005
+ Statement::If {
4006
+ cond,
4007
+ then_branch,
4008
+ else_branch,
4009
+ ..
4010
+ } => {
3523
4011
  Self::collect_expr_idents(cond, idents);
3524
4012
  Self::collect_stmt_idents(then_branch, idents);
3525
- if let Some(e) = else_branch { Self::collect_stmt_idents(e, idents); }
4013
+ if let Some(e) = else_branch {
4014
+ Self::collect_stmt_idents(e, idents);
4015
+ }
3526
4016
  }
3527
4017
  Statement::While { cond, body, .. } | Statement::DoWhile { body, cond, .. } => {
3528
4018
  Self::collect_expr_idents(cond, idents);
3529
4019
  Self::collect_stmt_idents(body, idents);
3530
4020
  }
3531
- Statement::For { init, cond, update, body, .. } => {
3532
- if let Some(s) = init { Self::collect_stmt_idents(s, idents); }
3533
- if let Some(e) = cond { Self::collect_expr_idents(e, idents); }
3534
- 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
+ }
3535
4037
  Self::collect_stmt_idents(body, idents);
3536
4038
  }
3537
4039
  Statement::ForOf { iterable, body, .. } => {
@@ -3539,26 +4041,51 @@ impl Codegen {
3539
4041
  Self::collect_stmt_idents(body, idents);
3540
4042
  }
3541
4043
  Statement::Return { value, .. } => {
3542
- if let Some(e) = value { Self::collect_expr_idents(e, idents); }
4044
+ if let Some(e) = value {
4045
+ Self::collect_expr_idents(e, idents);
4046
+ }
3543
4047
  }
3544
4048
  Statement::Throw { value, .. } => Self::collect_expr_idents(value, idents),
3545
- Statement::Try { body, catch_body, finally_body, .. } => {
4049
+ Statement::Try {
4050
+ body,
4051
+ catch_body,
4052
+ finally_body,
4053
+ ..
4054
+ } => {
3546
4055
  Self::collect_stmt_idents(body, idents);
3547
- if let Some(c) = catch_body { Self::collect_stmt_idents(c, idents); }
3548
- 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
+ }
3549
4062
  }
3550
- Statement::Switch { expr, cases, default_body, .. } => {
4063
+ Statement::Switch {
4064
+ expr,
4065
+ cases,
4066
+ default_body,
4067
+ ..
4068
+ } => {
3551
4069
  Self::collect_expr_idents(expr, idents);
3552
4070
  for (case_expr, stmts) in cases {
3553
- if let Some(e) = case_expr { Self::collect_expr_idents(e, idents); }
3554
- 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
+ }
3555
4077
  }
3556
4078
  if let Some(stmts) = default_body {
3557
- for s in stmts { Self::collect_stmt_idents(s, idents); }
4079
+ for s in stmts {
4080
+ Self::collect_stmt_idents(s, idents);
4081
+ }
3558
4082
  }
3559
4083
  }
3560
4084
  Statement::FunDecl { body, .. } => Self::collect_stmt_idents(body, idents),
3561
- Statement::Break { .. } | Statement::Continue { .. } | Statement::Import { .. } | Statement::Export { .. } => {}
4085
+ Statement::Break { .. }
4086
+ | Statement::Continue { .. }
4087
+ | Statement::Import { .. }
4088
+ | Statement::Export { .. } => {}
3562
4089
  }
3563
4090
  }
3564
4091
 
@@ -3571,7 +4098,7 @@ impl Codegen {
3571
4098
  // Build the arrow function as a Value::Function closure
3572
4099
  let mut code = String::new();
3573
4100
  code.push_str("{\n");
3574
-
4101
+
3575
4102
  // Find which identifiers are actually referenced in the body
3576
4103
  let referenced = Self::collect_referenced_idents(body);
3577
4104
  // Exclude the arrow's own parameters - they're not outer captures
@@ -3589,17 +4116,23 @@ impl Codegen {
3589
4116
  }
3590
4117
 
3591
4118
  // Collect outer parameters that need to be captured
3592
- let outer_params: Vec<String> = self.outer_params_stack
4119
+ let outer_params: Vec<String> = self
4120
+ .outer_params_stack
3593
4121
  .iter()
3594
4122
  .flat_map(|p| p.iter().cloned())
3595
4123
  .filter(|name| referenced.contains(name) && !param_names.contains(name))
3596
4124
  .collect();
3597
-
4125
+
3598
4126
  // Collect outer variables (from outer scopes) that need to be captured
3599
- let outer_vars: Vec<String> = self.outer_vars_stack
4127
+ let outer_vars: Vec<String> = self
4128
+ .outer_vars_stack
3600
4129
  .iter()
3601
4130
  .flat_map(|v| v.iter().cloned())
3602
- .filter(|name| 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
+ })
3603
4136
  .collect();
3604
4137
 
3605
4138
  // Outer vars that are assigned in the body need RefCell; read-only get Value binding
@@ -3608,8 +4141,16 @@ impl Codegen {
3608
4141
  ArrowBody::Expr(e) => Self::collect_assigned_idents_in_expr(e, &mut assigned_in_body),
3609
4142
  ArrowBody::Block(s) => Self::collect_assigned_idents_in_stmt(s, &mut assigned_in_body),
3610
4143
  }
3611
- let mutable_outer_vars: Vec<String> = outer_vars.iter().filter(|v| assigned_in_body.contains(*v)).cloned().collect();
3612
- 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();
3613
4154
 
3614
4155
  // Wrap outer captures in Rc<RefCell<>> and use _ref suffix.
3615
4156
  // Clone existing Rc only when VarDecl actually emitted `Rc<RefCell<...>>` (see rc_cell_storage_*).
@@ -3617,38 +4158,70 @@ impl Codegen {
3617
4158
  let param_escaped = Self::escape_ident(outer_param);
3618
4159
  let ref_name = format!("{}_ref", param_escaped);
3619
4160
  if self.rc_cell_storage_contains(outer_param) {
3620
- code.push_str(&format!(" let {} = {}.clone();\n", ref_name, param_escaped));
4161
+ code.push_str(&format!(
4162
+ " let {} = {}.clone();\n",
4163
+ ref_name, param_escaped
4164
+ ));
3621
4165
  } else {
3622
- 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
+ ));
3623
4170
  }
3624
4171
  }
3625
4172
  for outer_var in &outer_vars {
3626
4173
  let var_escaped = Self::escape_ident(outer_var);
3627
4174
  let ref_name = format!("{}_ref", var_escaped);
3628
4175
  if self.rc_cell_storage_contains(outer_var) {
3629
- code.push_str(&format!(" let {} = {}.clone();\n", ref_name, var_escaped));
4176
+ code.push_str(&format!(
4177
+ " let {} = {}.clone();\n",
4178
+ ref_name, var_escaped
4179
+ ));
3630
4180
  } else {
3631
- 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
+ ));
3632
4185
  }
3633
4186
  }
3634
4187
  // Only clone builtins that are actually referenced (clone so outer scope can still use, e.g. process for PORT)
3635
- for builtin in &["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
+ ] {
3636
4202
  if referenced.contains(*builtin) {
3637
4203
  code.push_str(&format!(" let {} = {}.clone();\n", builtin, builtin));
3638
4204
  }
3639
4205
  }
3640
4206
 
3641
4207
  // Clone only function cells that are actually referenced in this arrow
3642
- let referenced_funcs: Vec<String> = self.function_scope_stack
4208
+ let referenced_funcs: Vec<String> = self
4209
+ .function_scope_stack
3643
4210
  .last()
3644
- .map(|scope| scope.iter()
3645
- .filter(|f| referenced.contains(f.as_str()) && !param_names.contains(*f))
3646
- .cloned()
3647
- .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
+ })
3648
4218
  .unwrap_or_default();
3649
4219
  for func_name in &referenced_funcs {
3650
4220
  let escaped = Self::escape_ident(func_name);
3651
- code.push_str(&format!(" let {}_ref = {}_cell.clone();\n", escaped, escaped));
4221
+ code.push_str(&format!(
4222
+ " let {}_ref = {}_cell.clone();\n",
4223
+ escaped, escaped
4224
+ ));
3652
4225
  }
3653
4226
 
3654
4227
  code.push_str(" Value::Function(Rc::new(move |args: &[Value]| {\n");
@@ -3656,23 +4229,35 @@ impl Codegen {
3656
4229
  // Make captured outer params available as plain Values (from _ref RefCells)
3657
4230
  for outer_param in &outer_params {
3658
4231
  let param_escaped = Self::escape_ident(outer_param);
3659
- code.push_str(&format!(" 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
+ ));
3660
4236
  }
3661
4237
  // Mutable outer vars: capture RefCell so assignments use borrow_mut
3662
4238
  for outer_var in &mutable_outer_vars {
3663
4239
  let var_escaped = Self::escape_ident(outer_var);
3664
- code.push_str(&format!(" let {} = {}_ref.clone();\n", var_escaped, var_escaped));
4240
+ code.push_str(&format!(
4241
+ " let {} = {}_ref.clone();\n",
4242
+ var_escaped, var_escaped
4243
+ ));
3665
4244
  }
3666
4245
  // Read-only outer vars: Value binding from borrow
3667
4246
  for outer_var in &read_only_outer_vars {
3668
4247
  let var_escaped = Self::escape_ident(outer_var);
3669
- code.push_str(&format!(" 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
+ ));
3670
4252
  }
3671
4253
 
3672
4254
  // Make captured functions available
3673
4255
  for func_name in &referenced_funcs {
3674
4256
  let escaped = Self::escape_ident(func_name);
3675
- code.push_str(&format!(" let {} = (*{}_ref.borrow()).clone();\n", escaped, escaped));
4257
+ code.push_str(&format!(
4258
+ " let {} = (*{}_ref.borrow()).clone();\n",
4259
+ escaped, escaped
4260
+ ));
3676
4261
  }
3677
4262
 
3678
4263
  // Extract parameters from args
@@ -3727,18 +4312,18 @@ impl Codegen {
3727
4312
  tishlang_ast::ArrowBody::Block(block_stmt) => {
3728
4313
  // For block bodies, emit the block statement
3729
4314
  self.function_scope_stack.push(Vec::new());
3730
-
4315
+
3731
4316
  // Save current output, emit to temp, then restore
3732
4317
  let saved_output = std::mem::take(&mut self.output);
3733
4318
  let saved_indent = self.indent;
3734
4319
  self.indent = 2; // Base indent inside the closure
3735
-
4320
+
3736
4321
  self.emit_statement(block_stmt)?;
3737
-
4322
+
3738
4323
  let body_code = std::mem::replace(&mut self.output, saved_output);
3739
4324
  self.indent = saved_indent;
3740
4325
  self.function_scope_stack.pop();
3741
-
4326
+
3742
4327
  code.push_str(&body_code);
3743
4328
  code.push_str(" Value::Null\n");
3744
4329
  }
@@ -3758,7 +4343,11 @@ impl Codegen {
3758
4343
  /// Emit an expression as a native Rust type (not wrapped in Value).
3759
4344
  /// Falls back to emit_expr + conversion if the expression cannot be directly
3760
4345
  /// emitted as the target type.
3761
- fn emit_native_expr(&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> {
3762
4351
  // Try to emit literals directly as native types
3763
4352
  if let Expr::Literal { value, .. } = expr {
3764
4353
  match (target_type, value) {
@@ -3777,7 +4366,7 @@ impl Codegen {
3777
4366
  _ => {}
3778
4367
  }
3779
4368
  }
3780
-
4369
+
3781
4370
  // Try to emit array literals directly as Vec<T>
3782
4371
  if let (RustType::Vec(inner_type), Expr::Array { elements, .. }) = (target_type, expr) {
3783
4372
  let mut items = Vec::new();
@@ -3796,7 +4385,7 @@ impl Codegen {
3796
4385
  }
3797
4386
  return Ok(format!("vec![{}]", items.join(", ")));
3798
4387
  }
3799
-
4388
+
3800
4389
  // Check if the identifier is already of the target type
3801
4390
  if let Expr::Ident { name, .. } = expr {
3802
4391
  let var_type = self.type_context.get_type(name.as_ref());
@@ -3808,7 +4397,7 @@ impl Codegen {
3808
4397
  return Ok(esc);
3809
4398
  }
3810
4399
  }
3811
-
4400
+
3812
4401
  // Fall back to emit_expr + conversion
3813
4402
  let value_expr = self.emit_expr(expr)?;
3814
4403
  Ok(target_type.from_value_expr(&value_expr))
@@ -3829,7 +4418,9 @@ impl Codegen {
3829
4418
  // ── literals ─────────────────────────────────────────────────────────
3830
4419
  Expr::Literal { value, .. } => match value {
3831
4420
  Literal::Number(n) => Ok((format!("{}_f64", n), RustType::F64)),
3832
- Literal::String(s) => Ok((format!("{:?}.to_string()", s.as_ref()), RustType::String)),
4421
+ Literal::String(s) => {
4422
+ Ok((format!("{:?}.to_string()", s.as_ref()), RustType::String))
4423
+ }
3833
4424
  Literal::Bool(b) => Ok((format!("{}", b), RustType::Bool)),
3834
4425
  Literal::Null => Ok(("Value::Null".to_string(), RustType::Value)),
3835
4426
  },
@@ -3855,7 +4446,13 @@ impl Codegen {
3855
4446
  }
3856
4447
 
3857
4448
  // ── binary expressions ───────────────────────────────────────────────
3858
- Expr::Binary { left, op, right, span, .. } => {
4449
+ Expr::Binary {
4450
+ left,
4451
+ op,
4452
+ right,
4453
+ span,
4454
+ ..
4455
+ } => {
3859
4456
  let (l, lt) = self.emit_typed_expr(left)?;
3860
4457
  let (r, rt) = self.emit_typed_expr(right)?;
3861
4458
 
@@ -3882,14 +4479,27 @@ impl Codegen {
3882
4479
  }
3883
4480
 
3884
4481
  // Fall back: convert both sides to Value and use the runtime.
3885
- let lv = if lt.is_native() { lt.to_value_expr(&l) } else { l };
3886
- 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
+ };
3887
4492
  let result = self.emit_binop(&lv, *op, &rv, *span)?;
3888
4493
  Ok((result, RustType::Value))
3889
4494
  }
3890
4495
 
3891
4496
  // ── array indexing ───────────────────────────────────────────────────
3892
- Expr::Index { object, index, optional, .. } => {
4497
+ Expr::Index {
4498
+ object,
4499
+ index,
4500
+ optional,
4501
+ ..
4502
+ } => {
3893
4503
  // Native fast path: `vec[i]` where vec is Vec<T> and i is numeric.
3894
4504
  if !optional {
3895
4505
  if let Expr::Ident { name, .. } = object.as_ref() {
@@ -3958,19 +4568,28 @@ impl Codegen {
3958
4568
  }
3959
4569
  }
3960
4570
 
3961
- fn emit_binop(
3962
- &self,
3963
- l: &str,
3964
- op: BinOp,
3965
- r: &str,
3966
- span: Span,
3967
- ) -> Result<String, CompileError> {
4571
+ fn emit_binop(&self, l: &str, op: BinOp, r: &str, span: Span) -> Result<String, CompileError> {
3968
4572
  Ok(match op {
3969
- BinOp::Add => format!("tishlang_runtime::ops::add(&{}, &{}).unwrap_or(Value::Null)", l, r),
3970
- BinOp::Sub => format!("tishlang_runtime::ops::sub(&{}, &{}).unwrap_or(Value::Null)", l, r),
3971
- BinOp::Mul => format!("tishlang_runtime::ops::mul(&{}, &{}).unwrap_or(Value::Null)", l, r),
3972
- BinOp::Div => format!("tishlang_runtime::ops::div(&{}, &{}).unwrap_or(Value::Null)", l, r),
3973
- 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
+ ),
3974
4593
  BinOp::Pow => format!(
3975
4594
  "Value::Number({{ let Value::Number(a) = &({}) else {{ panic!() }}; \
3976
4595
  let Value::Number(b) = &({}) else {{ panic!() }}; a.powf(*b) }})",
@@ -3991,7 +4610,10 @@ impl Codegen {
3991
4610
  BinOp::Shr => Self::emit_bitwise_binop(l, r, ">>"),
3992
4611
  BinOp::In => format!("tish_in_operator(&{}, &{})", l, r),
3993
4612
  BinOp::Eq | BinOp::Ne => {
3994
- return Err(CompileError::new("Loose equality not supported", Some(span)))
4613
+ return Err(CompileError::new(
4614
+ "Loose equality not supported",
4615
+ Some(span),
4616
+ ))
3995
4617
  }
3996
4618
  })
3997
4619
  }