@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.
- package/Cargo.toml +1 -0
- package/bin/tish +0 -0
- package/crates/js_to_tish/src/error.rs +2 -8
- package/crates/js_to_tish/src/transform/expr.rs +101 -130
- package/crates/js_to_tish/src/transform/stmt.rs +25 -22
- package/crates/tish/Cargo.toml +1 -1
- package/crates/tish/src/cli_help.rs +76 -29
- package/crates/tish/src/main.rs +85 -54
- package/crates/tish/tests/cargo_example_compile.rs +67 -0
- package/crates/tish/tests/fixtures/cargo_example_project/Cargo.toml +3 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/Cargo.toml +11 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +12 -0
- package/crates/tish/tests/fixtures/cargo_example_project/package.json +10 -0
- package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +3 -0
- package/crates/tish/tests/integration_test.rs +197 -47
- package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
- package/crates/tish/tests/shortcircuit.rs +19 -4
- package/crates/tish_ast/src/ast.rs +12 -14
- package/crates/tish_build_utils/src/lib.rs +64 -6
- package/crates/tish_builtins/src/array.rs +52 -21
- package/crates/tish_builtins/src/construct.rs +2 -8
- package/crates/tish_builtins/src/globals.rs +30 -15
- package/crates/tish_builtins/src/lib.rs +5 -5
- package/crates/tish_builtins/src/math.rs +5 -3
- package/crates/tish_builtins/src/string.rs +71 -19
- package/crates/tish_bytecode/src/chunk.rs +0 -1
- package/crates/tish_bytecode/src/compiler.rs +164 -60
- package/crates/tish_bytecode/src/opcode.rs +13 -4
- package/crates/tish_bytecode/src/peephole.rs +2 -2
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/codegen.rs +989 -318
- package/crates/tish_compile/src/infer.rs +69 -19
- package/crates/tish_compile/src/lib.rs +21 -8
- package/crates/tish_compile/src/resolve.rs +515 -94
- package/crates/tish_compile/src/types.rs +10 -14
- package/crates/tish_compile_js/src/codegen.rs +34 -13
- package/crates/tish_compile_js/src/tests_jsx.rs +30 -6
- package/crates/tish_compiler_wasm/src/lib.rs +16 -13
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +40 -48
- package/crates/tish_core/src/json.rs +5 -3
- package/crates/tish_core/src/lib.rs +1 -1
- package/crates/tish_core/src/uri.rs +9 -6
- package/crates/tish_core/src/value.rs +92 -28
- package/crates/tish_cranelift/src/link.rs +6 -9
- package/crates/tish_cranelift/src/lower.rs +14 -8
- package/crates/tish_eval/src/eval.rs +398 -141
- package/crates/tish_eval/src/lib.rs +10 -6
- package/crates/tish_eval/src/natives.rs +95 -38
- package/crates/tish_eval/src/promise.rs +14 -8
- package/crates/tish_eval/src/timers.rs +28 -19
- package/crates/tish_eval/src/value.rs +10 -3
- package/crates/tish_fmt/src/lib.rs +29 -13
- package/crates/tish_lexer/src/lib.rs +217 -63
- package/crates/tish_lexer/src/token.rs +6 -6
- package/crates/tish_llvm/src/lib.rs +15 -8
- package/crates/tish_lsp/src/main.rs +41 -43
- package/crates/tish_native/src/build.rs +38 -15
- package/crates/tish_native/src/lib.rs +76 -32
- package/crates/tish_opt/src/lib.rs +67 -50
- package/crates/tish_parser/src/lib.rs +36 -11
- package/crates/tish_parser/src/parser.rs +172 -87
- package/crates/tish_runtime/src/http.rs +15 -6
- package/crates/tish_runtime/src/http_fetch.rs +24 -14
- package/crates/tish_runtime/src/lib.rs +224 -168
- package/crates/tish_runtime/src/promise.rs +1 -5
- package/crates/tish_runtime/src/ws.rs +45 -20
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
- package/crates/tish_ui/src/jsx.rs +41 -22
- package/crates/tish_ui/src/lib.rs +2 -2
- package/crates/tish_vm/src/vm.rs +320 -116
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +8 -3
- package/crates/tish_wasm/src/lib.rs +38 -28
- package/crates/tishlang_cargo_bindgen/Cargo.toml +25 -0
- package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +52 -0
- package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
- package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -0
- package/package.json +1 -1
- package/platform/darwin-arm64/tish +0 -0
- package/platform/darwin-x64/tish +0 -0
- package/platform/linux-arm64/tish +0 -0
- package/platform/linux-x64/tish +0 -0
- package/platform/win32-x64/tish.exe +0 -0
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
//! Code generation: AST -> Rust source.
|
|
2
2
|
|
|
3
|
+
use crate::resolve::is_builtin_native_spec;
|
|
4
|
+
use crate::types::{RustType, TypeContext};
|
|
3
5
|
use std::borrow::Cow;
|
|
4
6
|
use std::collections::{HashMap, HashSet};
|
|
5
7
|
use std::path::Path;
|
|
6
|
-
use tishlang_ast::{
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
use tishlang_ast::{
|
|
9
|
+
ArrayElement, ArrowBody, BinOp, CallArg, CompoundOp, DestructElement, DestructPattern, Expr,
|
|
10
|
+
FunParam, Literal, LogicalAssignOp, MemberProp, ObjectProp, Program, Span, Statement, UnaryOp,
|
|
11
|
+
};
|
|
9
12
|
|
|
10
13
|
/// Tracks variable usage for move/clone optimization.
|
|
11
14
|
/// A variable can be moved instead of cloned if it's at its last use.
|
|
@@ -41,7 +44,12 @@ impl UsageAnalyzer {
|
|
|
41
44
|
self.analyze_expr(e);
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
|
-
Statement::If {
|
|
47
|
+
Statement::If {
|
|
48
|
+
cond,
|
|
49
|
+
then_branch,
|
|
50
|
+
else_branch,
|
|
51
|
+
..
|
|
52
|
+
} => {
|
|
45
53
|
self.analyze_expr(cond);
|
|
46
54
|
self.analyze_statement(then_branch);
|
|
47
55
|
if let Some(e) = else_branch {
|
|
@@ -49,7 +57,13 @@ impl UsageAnalyzer {
|
|
|
49
57
|
}
|
|
50
58
|
}
|
|
51
59
|
Statement::Block { statements, .. } => self.analyze_statements(statements),
|
|
52
|
-
Statement::For {
|
|
60
|
+
Statement::For {
|
|
61
|
+
init,
|
|
62
|
+
cond,
|
|
63
|
+
update,
|
|
64
|
+
body,
|
|
65
|
+
..
|
|
66
|
+
} => {
|
|
53
67
|
if let Some(i) = init {
|
|
54
68
|
self.analyze_statement(i);
|
|
55
69
|
}
|
|
@@ -69,7 +83,12 @@ impl UsageAnalyzer {
|
|
|
69
83
|
self.analyze_expr(cond);
|
|
70
84
|
self.analyze_statement(body);
|
|
71
85
|
}
|
|
72
|
-
Statement::Switch {
|
|
86
|
+
Statement::Switch {
|
|
87
|
+
expr,
|
|
88
|
+
cases,
|
|
89
|
+
default_body,
|
|
90
|
+
..
|
|
91
|
+
} => {
|
|
73
92
|
self.analyze_expr(expr);
|
|
74
93
|
for (case_expr, stmts) in cases {
|
|
75
94
|
if let Some(e) = case_expr {
|
|
@@ -82,7 +101,12 @@ impl UsageAnalyzer {
|
|
|
82
101
|
}
|
|
83
102
|
}
|
|
84
103
|
Statement::Throw { value, .. } => self.analyze_expr(value),
|
|
85
|
-
Statement::Try {
|
|
104
|
+
Statement::Try {
|
|
105
|
+
body,
|
|
106
|
+
catch_body,
|
|
107
|
+
finally_body,
|
|
108
|
+
..
|
|
109
|
+
} => {
|
|
86
110
|
self.analyze_statement(body);
|
|
87
111
|
if let Some(c) = catch_body {
|
|
88
112
|
self.analyze_statement(c);
|
|
@@ -153,14 +177,17 @@ impl UsageAnalyzer {
|
|
|
153
177
|
}
|
|
154
178
|
}
|
|
155
179
|
}
|
|
156
|
-
Expr::ArrowFunction { body, .. } => {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
}
|
|
180
|
+
Expr::ArrowFunction { body, .. } => match body {
|
|
181
|
+
ArrowBody::Expr(e) => self.analyze_expr(e),
|
|
182
|
+
ArrowBody::Block(s) => self.analyze_statement(s),
|
|
183
|
+
},
|
|
162
184
|
Expr::Assign { value, .. } => self.analyze_expr(value),
|
|
163
|
-
Expr::Conditional {
|
|
185
|
+
Expr::Conditional {
|
|
186
|
+
cond,
|
|
187
|
+
then_branch,
|
|
188
|
+
else_branch,
|
|
189
|
+
..
|
|
190
|
+
} => {
|
|
164
191
|
self.analyze_expr(cond);
|
|
165
192
|
self.analyze_expr(then_branch);
|
|
166
193
|
self.analyze_expr(else_branch);
|
|
@@ -183,23 +210,37 @@ impl UsageAnalyzer {
|
|
|
183
210
|
*self.use_counts.entry(name.to_string()).or_insert(0) += 1;
|
|
184
211
|
self.analyze_expr(value);
|
|
185
212
|
}
|
|
186
|
-
Expr::PostfixInc { name, .. }
|
|
213
|
+
Expr::PostfixInc { name, .. }
|
|
214
|
+
| Expr::PostfixDec { name, .. }
|
|
215
|
+
| Expr::PrefixInc { name, .. }
|
|
216
|
+
| Expr::PrefixDec { name, .. } => {
|
|
187
217
|
*self.use_counts.entry(name.to_string()).or_insert(0) += 1;
|
|
188
218
|
}
|
|
189
219
|
Expr::MemberAssign { object, value, .. } => {
|
|
190
220
|
self.analyze_expr(object);
|
|
191
221
|
self.analyze_expr(value);
|
|
192
222
|
}
|
|
193
|
-
Expr::IndexAssign {
|
|
223
|
+
Expr::IndexAssign {
|
|
224
|
+
object,
|
|
225
|
+
index,
|
|
226
|
+
value,
|
|
227
|
+
..
|
|
228
|
+
} => {
|
|
194
229
|
self.analyze_expr(object);
|
|
195
230
|
self.analyze_expr(index);
|
|
196
231
|
self.analyze_expr(value);
|
|
197
232
|
}
|
|
198
233
|
Expr::Await { operand, .. } => self.analyze_expr(operand),
|
|
199
|
-
Expr::JsxElement {
|
|
234
|
+
Expr::JsxElement {
|
|
235
|
+
props, children, ..
|
|
236
|
+
} => {
|
|
200
237
|
for p in props {
|
|
201
238
|
match p {
|
|
202
|
-
tishlang_ast::JsxProp::Attr {
|
|
239
|
+
tishlang_ast::JsxProp::Attr {
|
|
240
|
+
value: tishlang_ast::JsxAttrValue::Expr(e),
|
|
241
|
+
..
|
|
242
|
+
}
|
|
243
|
+
| tishlang_ast::JsxProp::Spread(e) => self.analyze_expr(e),
|
|
203
244
|
_ => {}
|
|
204
245
|
}
|
|
205
246
|
}
|
|
@@ -240,7 +281,10 @@ pub struct CompileError {
|
|
|
240
281
|
|
|
241
282
|
impl CompileError {
|
|
242
283
|
fn new(msg: impl Into<String>, span: Option<Span>) -> Self {
|
|
243
|
-
Self {
|
|
284
|
+
Self {
|
|
285
|
+
message: msg.into(),
|
|
286
|
+
span,
|
|
287
|
+
}
|
|
244
288
|
}
|
|
245
289
|
}
|
|
246
290
|
|
|
@@ -262,21 +306,45 @@ fn program_uses_async(program: &Program) -> bool {
|
|
|
262
306
|
match s {
|
|
263
307
|
Statement::FunDecl { async_, .. } if *async_ => true,
|
|
264
308
|
Statement::Block { statements, .. } => statements.iter().any(stmt_has_async),
|
|
265
|
-
Statement::If {
|
|
266
|
-
|
|
309
|
+
Statement::If {
|
|
310
|
+
then_branch,
|
|
311
|
+
else_branch,
|
|
312
|
+
..
|
|
313
|
+
} => {
|
|
314
|
+
stmt_has_async(then_branch)
|
|
315
|
+
|| else_branch
|
|
316
|
+
.as_ref()
|
|
317
|
+
.is_some_and(|s| stmt_has_async(s.as_ref()))
|
|
267
318
|
}
|
|
268
|
-
Statement::While { body, .. }
|
|
319
|
+
Statement::While { body, .. }
|
|
320
|
+
| Statement::For { body, .. }
|
|
321
|
+
| Statement::ForOf { body, .. }
|
|
269
322
|
| Statement::DoWhile { body, .. } => stmt_has_async(body),
|
|
270
|
-
Statement::Switch {
|
|
271
|
-
cases
|
|
323
|
+
Statement::Switch {
|
|
324
|
+
cases,
|
|
325
|
+
default_body,
|
|
326
|
+
..
|
|
327
|
+
} => {
|
|
328
|
+
cases
|
|
329
|
+
.iter()
|
|
330
|
+
.any(|(_, stmts)| stmts.iter().any(stmt_has_async))
|
|
272
331
|
|| default_body
|
|
273
332
|
.as_ref()
|
|
274
333
|
.is_some_and(|stmts| stmts.iter().any(stmt_has_async))
|
|
275
334
|
}
|
|
276
|
-
Statement::Try {
|
|
335
|
+
Statement::Try {
|
|
336
|
+
body,
|
|
337
|
+
catch_body,
|
|
338
|
+
finally_body,
|
|
339
|
+
..
|
|
340
|
+
} => {
|
|
277
341
|
stmt_has_async(body)
|
|
278
|
-
|| catch_body
|
|
279
|
-
|
|
342
|
+
|| catch_body
|
|
343
|
+
.as_ref()
|
|
344
|
+
.is_some_and(|s| stmt_has_async(s.as_ref()))
|
|
345
|
+
|| finally_body
|
|
346
|
+
.as_ref()
|
|
347
|
+
.is_some_and(|s| stmt_has_async(s.as_ref()))
|
|
280
348
|
}
|
|
281
349
|
_ => false,
|
|
282
350
|
}
|
|
@@ -287,14 +355,16 @@ fn program_uses_async(program: &Program) -> bool {
|
|
|
287
355
|
Expr::Binary { left, right, .. } => expr_has_await(left) || expr_has_await(right),
|
|
288
356
|
Expr::Unary { operand, .. } | Expr::TypeOf { operand, .. } => expr_has_await(operand),
|
|
289
357
|
Expr::Call { callee, args, .. } => {
|
|
290
|
-
expr_has_await(callee)
|
|
291
|
-
|
|
292
|
-
|
|
358
|
+
expr_has_await(callee)
|
|
359
|
+
|| args.iter().any(|a| match a {
|
|
360
|
+
CallArg::Expr(e) | CallArg::Spread(e) => expr_has_await(e),
|
|
361
|
+
})
|
|
293
362
|
}
|
|
294
363
|
Expr::New { callee, args, .. } => {
|
|
295
|
-
expr_has_await(callee)
|
|
296
|
-
|
|
297
|
-
|
|
364
|
+
expr_has_await(callee)
|
|
365
|
+
|| args.iter().any(|a| match a {
|
|
366
|
+
CallArg::Expr(e) | CallArg::Spread(e) => expr_has_await(e),
|
|
367
|
+
})
|
|
298
368
|
}
|
|
299
369
|
Expr::Member { object, prop, .. } => {
|
|
300
370
|
expr_has_await(object)
|
|
@@ -305,32 +375,48 @@ fn program_uses_async(program: &Program) -> bool {
|
|
|
305
375
|
}
|
|
306
376
|
}
|
|
307
377
|
Expr::Index { object, index, .. } => expr_has_await(object) || expr_has_await(index),
|
|
308
|
-
Expr::Conditional {
|
|
309
|
-
|
|
378
|
+
Expr::Conditional {
|
|
379
|
+
cond,
|
|
380
|
+
then_branch,
|
|
381
|
+
else_branch,
|
|
382
|
+
..
|
|
383
|
+
} => expr_has_await(cond) || expr_has_await(then_branch) || expr_has_await(else_branch),
|
|
384
|
+
Expr::NullishCoalesce { left, right, .. } => {
|
|
385
|
+
expr_has_await(left) || expr_has_await(right)
|
|
310
386
|
}
|
|
311
|
-
Expr::NullishCoalesce { left, right, .. } => expr_has_await(left) || expr_has_await(right),
|
|
312
387
|
Expr::Array { elements, .. } => elements.iter().any(|el| match el {
|
|
313
388
|
ArrayElement::Expr(e) | ArrayElement::Spread(e) => expr_has_await(e),
|
|
314
389
|
}),
|
|
315
390
|
Expr::Object { props, .. } => props.iter().any(|p| match p {
|
|
316
391
|
ObjectProp::KeyValue(_, e) | ObjectProp::Spread(e) => expr_has_await(e),
|
|
317
392
|
}),
|
|
318
|
-
Expr::Assign { value, .. }
|
|
319
|
-
| Expr::
|
|
393
|
+
Expr::Assign { value, .. }
|
|
394
|
+
| Expr::CompoundAssign { value, .. }
|
|
395
|
+
| Expr::LogicalAssign { value, .. }
|
|
396
|
+
| Expr::MemberAssign { value, .. }
|
|
397
|
+
| Expr::IndexAssign { value, .. } => expr_has_await(value),
|
|
320
398
|
Expr::ArrowFunction { body, .. } => match body {
|
|
321
399
|
ArrowBody::Expr(e) => expr_has_await(e),
|
|
322
400
|
ArrowBody::Block(s) => stmt_has_async(s),
|
|
323
401
|
},
|
|
324
402
|
Expr::TemplateLiteral { exprs, .. } => exprs.iter().any(expr_has_await),
|
|
325
|
-
Expr::JsxElement {
|
|
403
|
+
Expr::JsxElement {
|
|
404
|
+
props, children, ..
|
|
405
|
+
} => {
|
|
326
406
|
props.iter().any(|p| match p {
|
|
327
|
-
tishlang_ast::JsxProp::Attr {
|
|
407
|
+
tishlang_ast::JsxProp::Attr {
|
|
408
|
+
value: tishlang_ast::JsxAttrValue::Expr(e),
|
|
409
|
+
..
|
|
410
|
+
}
|
|
411
|
+
| tishlang_ast::JsxProp::Spread(e) => expr_has_await(e),
|
|
328
412
|
_ => false,
|
|
329
|
-
}) || children
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
children.iter().any(|c| matches!(c, tishlang_ast::JsxChild::Expr(e) if expr_has_await(e)))
|
|
413
|
+
}) || children
|
|
414
|
+
.iter()
|
|
415
|
+
.any(|c| matches!(c, tishlang_ast::JsxChild::Expr(e) if expr_has_await(e)))
|
|
333
416
|
}
|
|
417
|
+
Expr::JsxFragment { children, .. } => children
|
|
418
|
+
.iter()
|
|
419
|
+
.any(|c| matches!(c, tishlang_ast::JsxChild::Expr(e) if expr_has_await(e))),
|
|
334
420
|
_ => false,
|
|
335
421
|
}
|
|
336
422
|
}
|
|
@@ -340,21 +426,42 @@ fn program_uses_async(program: &Program) -> bool {
|
|
|
340
426
|
Statement::VarDecl { init, .. } => init.as_ref().is_some_and(expr_has_await),
|
|
341
427
|
Statement::VarDeclDestructure { init, .. } => expr_has_await(init),
|
|
342
428
|
Statement::ExprStmt { expr, .. } => expr_has_await(expr),
|
|
343
|
-
Statement::If {
|
|
344
|
-
|
|
345
|
-
|
|
429
|
+
Statement::If {
|
|
430
|
+
cond,
|
|
431
|
+
then_branch,
|
|
432
|
+
else_branch,
|
|
433
|
+
..
|
|
434
|
+
} => {
|
|
435
|
+
expr_has_await(cond)
|
|
436
|
+
|| stmt_has_await(then_branch)
|
|
437
|
+
|| else_branch
|
|
438
|
+
.as_ref()
|
|
439
|
+
.is_some_and(|s| stmt_has_await(s.as_ref()))
|
|
346
440
|
}
|
|
347
441
|
Statement::While { cond, body, .. } => expr_has_await(cond) || stmt_has_await(body),
|
|
348
|
-
Statement::For {
|
|
442
|
+
Statement::For {
|
|
443
|
+
init,
|
|
444
|
+
cond,
|
|
445
|
+
update,
|
|
446
|
+
body,
|
|
447
|
+
..
|
|
448
|
+
} => {
|
|
349
449
|
init.as_ref().is_some_and(|s| stmt_has_await(s.as_ref()))
|
|
350
450
|
|| cond.as_ref().is_some_and(expr_has_await)
|
|
351
451
|
|| update.as_ref().is_some_and(expr_has_await)
|
|
352
452
|
|| stmt_has_await(body)
|
|
353
453
|
}
|
|
354
|
-
Statement::ForOf { iterable, body, .. } =>
|
|
454
|
+
Statement::ForOf { iterable, body, .. } => {
|
|
455
|
+
expr_has_await(iterable) || stmt_has_await(body)
|
|
456
|
+
}
|
|
355
457
|
Statement::Return { value, .. } => value.as_ref().is_some_and(expr_has_await),
|
|
356
458
|
Statement::FunDecl { body, .. } => stmt_has_await(body),
|
|
357
|
-
Statement::Switch {
|
|
459
|
+
Statement::Switch {
|
|
460
|
+
expr,
|
|
461
|
+
cases,
|
|
462
|
+
default_body,
|
|
463
|
+
..
|
|
464
|
+
} => {
|
|
358
465
|
expr_has_await(expr)
|
|
359
466
|
|| cases.iter().any(|(c, stmts)| {
|
|
360
467
|
c.as_ref().is_some_and(expr_has_await) || stmts.iter().any(stmt_has_await)
|
|
@@ -365,23 +472,38 @@ fn program_uses_async(program: &Program) -> bool {
|
|
|
365
472
|
}
|
|
366
473
|
Statement::DoWhile { body, cond, .. } => stmt_has_await(body) || expr_has_await(cond),
|
|
367
474
|
Statement::Throw { value, .. } => expr_has_await(value),
|
|
368
|
-
Statement::Try {
|
|
475
|
+
Statement::Try {
|
|
476
|
+
body,
|
|
477
|
+
catch_body,
|
|
478
|
+
finally_body,
|
|
479
|
+
..
|
|
480
|
+
} => {
|
|
369
481
|
stmt_has_await(body)
|
|
370
|
-
|| catch_body
|
|
371
|
-
|
|
482
|
+
|| catch_body
|
|
483
|
+
.as_ref()
|
|
484
|
+
.is_some_and(|s| stmt_has_await(s.as_ref()))
|
|
485
|
+
|| finally_body
|
|
486
|
+
.as_ref()
|
|
487
|
+
.is_some_and(|s| stmt_has_await(s.as_ref()))
|
|
372
488
|
}
|
|
373
489
|
Statement::Import { .. } | Statement::Export { .. } => false,
|
|
374
490
|
_ => false,
|
|
375
491
|
}
|
|
376
492
|
}
|
|
377
|
-
program
|
|
493
|
+
program
|
|
494
|
+
.statements
|
|
495
|
+
.iter()
|
|
496
|
+
.any(|s| stmt_has_async(s) || stmt_has_await(s))
|
|
378
497
|
}
|
|
379
498
|
|
|
380
499
|
pub fn compile(program: &Program) -> Result<String, CompileError> {
|
|
381
500
|
compile_with_project_root(program, None)
|
|
382
501
|
}
|
|
383
502
|
|
|
384
|
-
pub fn compile_with_project_root(
|
|
503
|
+
pub fn compile_with_project_root(
|
|
504
|
+
program: &Program,
|
|
505
|
+
project_root: Option<&Path>,
|
|
506
|
+
) -> Result<String, CompileError> {
|
|
385
507
|
compile_with_features(program, project_root, &[])
|
|
386
508
|
}
|
|
387
509
|
|
|
@@ -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,
|
|
401
|
-
/// (CLI features plus any inferred from `tish:fs` / `tish:http` / … imports)
|
|
402
|
-
///
|
|
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<
|
|
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
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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(
|
|
426
|
-
|
|
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
|
-
|
|
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 {
|
|
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,
|
|
452
|
-
.
|
|
453
|
-
|
|
454
|
-
|
|
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 -> (
|
|
469
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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 = {}
|
|
548
|
-
|
|
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] = &[
|
|
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
|
-
"
|
|
637
|
-
"
|
|
638
|
-
"
|
|
639
|
-
"
|
|
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 {
|
|
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 (
|
|
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(
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
self.writeln(
|
|
882
|
-
|
|
883
|
-
|
|
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(
|
|
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(
|
|
892
|
-
|
|
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(
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
self.writeln(
|
|
901
|
-
|
|
902
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
995
|
-
|
|
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(
|
|
998
|
-
|
|
999
|
-
|
|
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(
|
|
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(
|
|
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!(
|
|
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
|
|
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 =
|
|
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!(
|
|
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 {
|
|
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!(
|
|
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!(
|
|
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 {
|
|
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) {
|
|
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 {
|
|
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!(
|
|
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
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
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 {
|
|
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!(
|
|
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 {
|
|
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
|
|
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!(
|
|
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
|
|
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
|
|
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|
|
|
1438
|
-
|
|
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!(
|
|
1785
|
+
self.writeln(&format!(
|
|
1786
|
+
"let {}_cell = {}.clone();",
|
|
1787
|
+
var_escaped, var_escaped
|
|
1788
|
+
));
|
|
1462
1789
|
} else {
|
|
1463
|
-
self.writeln(&format!(
|
|
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!(
|
|
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!(
|
|
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
|
|
1816
|
+
let sibling_fns: Vec<String> = self
|
|
1817
|
+
.function_scope_stack
|
|
1481
1818
|
.last()
|
|
1482
|
-
.map(|scope|
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
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!(
|
|
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!(
|
|
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 &[
|
|
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!(
|
|
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!(
|
|
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!(
|
|
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!(
|
|
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!(
|
|
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!(
|
|
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!(
|
|
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(
|
|
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(
|
|
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, .. } => {
|
|
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) =>
|
|
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) =>
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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, .. }
|
|
2868
|
-
|
|
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) =>
|
|
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) =>
|
|
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
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
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 {
|
|
3323
|
+
for e in exprs {
|
|
3324
|
+
Self::collect_expr_idents(e, idents);
|
|
3325
|
+
}
|
|
2907
3326
|
}
|
|
2908
|
-
Expr::JsxElement {
|
|
3327
|
+
Expr::JsxElement {
|
|
3328
|
+
props, children, ..
|
|
3329
|
+
} => {
|
|
2909
3330
|
for p in props {
|
|
2910
3331
|
match p {
|
|
2911
|
-
tishlang_ast::JsxProp::Attr {
|
|
3332
|
+
tishlang_ast::JsxProp::Attr {
|
|
3333
|
+
value: tishlang_ast::JsxAttrValue::Expr(e),
|
|
3334
|
+
..
|
|
3335
|
+
}
|
|
3336
|
+
| tishlang_ast::JsxProp::Spread(e) => Self::collect_expr_idents(e, idents),
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 { .. }
|
|
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, .. }
|
|
3021
|
-
| Expr::
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
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 {
|
|
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) =>
|
|
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(
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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, .. } =>
|
|
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, .. } => {
|
|
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 {
|
|
3904
|
+
for s in statements {
|
|
3905
|
+
Self::collect_local_var_names(s, names);
|
|
3906
|
+
}
|
|
3412
3907
|
}
|
|
3413
|
-
Statement::If {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
3928
|
+
Statement::Switch {
|
|
3929
|
+
cases,
|
|
3930
|
+
default_body,
|
|
3931
|
+
..
|
|
3932
|
+
} => {
|
|
3426
3933
|
for (_, stmts) in cases {
|
|
3427
|
-
for s in stmts {
|
|
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 {
|
|
3939
|
+
for s in stmts {
|
|
3940
|
+
Self::collect_local_var_names(s, names);
|
|
3941
|
+
}
|
|
3431
3942
|
}
|
|
3432
3943
|
}
|
|
3433
|
-
Statement::Try {
|
|
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 {
|
|
3436
|
-
|
|
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 {
|
|
3448
|
-
|
|
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) => {
|
|
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) => {
|
|
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 {
|
|
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 {
|
|
4001
|
+
for s in statements {
|
|
4002
|
+
Self::collect_stmt_idents(s, idents);
|
|
4003
|
+
}
|
|
3472
4004
|
}
|
|
3473
|
-
Statement::If {
|
|
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 {
|
|
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 {
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
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 {
|
|
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 {
|
|
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 {
|
|
3499
|
-
|
|
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 {
|
|
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 {
|
|
3505
|
-
|
|
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 {
|
|
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 { .. }
|
|
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
|
|
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
|
|
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|
|
|
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
|
|
3563
|
-
|
|
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!(
|
|
4161
|
+
code.push_str(&format!(
|
|
4162
|
+
" let {} = {}.clone();\n",
|
|
4163
|
+
ref_name, param_escaped
|
|
4164
|
+
));
|
|
3572
4165
|
} else {
|
|
3573
|
-
code.push_str(&format!(
|
|
4166
|
+
code.push_str(&format!(
|
|
4167
|
+
" let {} = std::rc::Rc::new(RefCell::new({}.clone()));\n",
|
|
4168
|
+
ref_name, param_escaped
|
|
4169
|
+
));
|
|
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!(
|
|
4176
|
+
code.push_str(&format!(
|
|
4177
|
+
" let {} = {}.clone();\n",
|
|
4178
|
+
ref_name, var_escaped
|
|
4179
|
+
));
|
|
3581
4180
|
} else {
|
|
3582
|
-
code.push_str(&format!(
|
|
4181
|
+
code.push_str(&format!(
|
|
4182
|
+
" let {} = std::rc::Rc::new(RefCell::new({}.clone()));\n",
|
|
4183
|
+
ref_name, var_escaped
|
|
4184
|
+
));
|
|
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 &[
|
|
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
|
|
4208
|
+
let referenced_funcs: Vec<String> = self
|
|
4209
|
+
.function_scope_stack
|
|
3594
4210
|
.last()
|
|
3595
|
-
.map(|scope|
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
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!(
|
|
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!(
|
|
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!(
|
|
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!(
|
|
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!(
|
|
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(
|
|
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) =>
|
|
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 {
|
|
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() {
|
|
3837
|
-
|
|
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 {
|
|
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!(
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
BinOp::
|
|
4573
|
+
BinOp::Add => format!(
|
|
4574
|
+
"tishlang_runtime::ops::add(&{}, &{}).unwrap_or(Value::Null)",
|
|
4575
|
+
l, r
|
|
4576
|
+
),
|
|
4577
|
+
BinOp::Sub => format!(
|
|
4578
|
+
"tishlang_runtime::ops::sub(&{}, &{}).unwrap_or(Value::Null)",
|
|
4579
|
+
l, r
|
|
4580
|
+
),
|
|
4581
|
+
BinOp::Mul => format!(
|
|
4582
|
+
"tishlang_runtime::ops::mul(&{}, &{}).unwrap_or(Value::Null)",
|
|
4583
|
+
l, r
|
|
4584
|
+
),
|
|
4585
|
+
BinOp::Div => format!(
|
|
4586
|
+
"tishlang_runtime::ops::div(&{}, &{}).unwrap_or(Value::Null)",
|
|
4587
|
+
l, r
|
|
4588
|
+
),
|
|
4589
|
+
BinOp::Mod => format!(
|
|
4590
|
+
"tishlang_runtime::ops::modulo(&{}, &{}).unwrap_or(Value::Null)",
|
|
4591
|
+
l, r
|
|
4592
|
+
),
|
|
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(
|
|
4613
|
+
return Err(CompileError::new(
|
|
4614
|
+
"Loose equality not supported",
|
|
4615
|
+
Some(span),
|
|
4616
|
+
))
|
|
3946
4617
|
}
|
|
3947
4618
|
})
|
|
3948
4619
|
}
|