@tishlang/tish 1.0.29 → 1.0.33
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/crates/js_to_tish/src/transform/expr.rs +15 -6
- package/crates/tish/Cargo.toml +1 -1
- package/crates/tish/src/main.rs +1 -1
- package/crates/tish/tests/integration_test.rs +4 -3
- package/crates/tish_ast/src/ast.rs +65 -2
- package/crates/tish_build_utils/src/lib.rs +10 -2
- package/crates/tish_builtins/src/construct.rs +177 -0
- package/crates/tish_builtins/src/globals.rs +3 -5
- package/crates/tish_builtins/src/helpers.rs +2 -3
- package/crates/tish_builtins/src/lib.rs +1 -0
- package/crates/tish_builtins/src/object.rs +3 -4
- package/crates/tish_bytecode/src/compiler.rs +85 -11
- package/crates/tish_bytecode/src/opcode.rs +7 -3
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/codegen.rs +233 -71
- package/crates/tish_compile/src/lib.rs +35 -0
- package/crates/tish_compile_js/Cargo.toml +1 -0
- package/crates/tish_compile_js/src/codegen.rs +38 -94
- package/crates/tish_compile_js/src/lib.rs +0 -1
- package/crates/tish_compile_js/src/tests_jsx.rs +68 -0
- package/crates/tish_core/Cargo.toml +4 -0
- package/crates/tish_core/src/console_style.rs +7 -1
- package/crates/tish_core/src/json.rs +1 -2
- package/crates/tish_core/src/macros.rs +2 -3
- package/crates/tish_core/src/value.rs +10 -5
- package/crates/tish_eval/Cargo.toml +2 -0
- package/crates/tish_eval/src/eval.rs +149 -72
- package/crates/tish_eval/src/http.rs +3 -4
- package/crates/tish_eval/src/regex.rs +3 -2
- package/crates/tish_eval/src/value.rs +11 -13
- package/crates/tish_eval/src/value_convert.rs +4 -8
- package/crates/tish_fmt/src/lib.rs +49 -10
- package/crates/tish_lexer/src/token.rs +2 -0
- package/crates/tish_lint/src/lib.rs +9 -0
- package/crates/tish_lsp/README.md +1 -1
- package/crates/tish_native/src/build.rs +16 -2
- package/crates/tish_opt/src/lib.rs +15 -0
- package/crates/tish_parser/src/lib.rs +101 -1
- package/crates/tish_parser/src/parser.rs +161 -50
- package/crates/tish_runtime/src/http.rs +4 -5
- package/crates/tish_runtime/src/http_fetch.rs +9 -10
- package/crates/tish_runtime/src/lib.rs +9 -2
- package/crates/tish_runtime/src/promise.rs +2 -3
- package/crates/tish_runtime/src/promise_io.rs +2 -3
- package/crates/tish_runtime/src/ws.rs +7 -7
- package/crates/tish_ui/Cargo.toml +17 -0
- package/crates/tish_ui/src/jsx.rs +390 -0
- package/crates/tish_ui/src/lib.rs +16 -0
- package/crates/tish_ui/src/runtime/hooks.rs +122 -0
- package/crates/tish_ui/src/runtime/mod.rs +173 -0
- package/crates/tish_vm/src/vm.rs +121 -27
- package/justfile +3 -3
- 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
- package/crates/tish_compile_js/src/js_intrinsics.rs +0 -82
|
@@ -80,13 +80,17 @@ pub enum Opcode {
|
|
|
80
80
|
ArrayFilterBinOp = 35,
|
|
81
81
|
/// Load built-in module export. Operands: u16 spec_const_idx, u16 export_name_const_idx. Pushes Value.
|
|
82
82
|
LoadNativeExport = 36,
|
|
83
|
+
/// `new callee(...args)` (operand: u16 arg count). Stack: callee, then args — same as Call.
|
|
84
|
+
Construct = 37,
|
|
85
|
+
/// `new callee(...spread)` — stack: args array, then callee (same order as CallSpread).
|
|
86
|
+
ConstructSpread = 38,
|
|
83
87
|
}
|
|
84
88
|
|
|
85
89
|
impl Opcode {
|
|
86
|
-
/// Decode byte to opcode. Safe for b in 0..=
|
|
90
|
+
/// Decode byte to opcode. Safe for b in 0..=38 (matches #[repr(u8)] discriminants).
|
|
87
91
|
#[inline]
|
|
88
92
|
pub fn from_u8(b: u8) -> Option<Opcode> {
|
|
89
|
-
if b <=
|
|
93
|
+
if b <= 38 {
|
|
90
94
|
Some(unsafe { std::mem::transmute(b) })
|
|
91
95
|
} else {
|
|
92
96
|
None
|
|
@@ -97,7 +101,7 @@ impl Opcode {
|
|
|
97
101
|
pub fn instruction_size(self, code: &[u8], ip: usize) -> Option<usize> {
|
|
98
102
|
let size = match self {
|
|
99
103
|
Opcode::Nop | Opcode::Pop | Opcode::Dup | Opcode::Return | Opcode::ExitTry
|
|
100
|
-
| Opcode::ArrayMapIdentity => 1,
|
|
104
|
+
| Opcode::ArrayMapIdentity | Opcode::CallSpread | Opcode::ConstructSpread => 1,
|
|
101
105
|
Opcode::ArraySortByProperty | Opcode::ArrayMapBinOp | Opcode::ArrayFilterBinOp
|
|
102
106
|
| Opcode::LoadNativeExport => 5,
|
|
103
107
|
_ => 3,
|
|
@@ -18,6 +18,7 @@ ws = []
|
|
|
18
18
|
tishlang_ast = { path = "../tish_ast", version = ">=0.1" }
|
|
19
19
|
tishlang_opt = { path = "../tish_opt", version = ">=0.1" }
|
|
20
20
|
tishlang_parser = { path = "../tish_parser", version = ">=0.1" }
|
|
21
|
+
tishlang_ui = { path = "../tish_ui", default-features = false, features = ["compiler"] }
|
|
21
22
|
serde_json = "1.0"
|
|
22
23
|
|
|
23
24
|
[dev-dependencies]
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
use std::borrow::Cow;
|
|
4
4
|
use std::collections::{HashMap, HashSet};
|
|
5
5
|
use std::path::Path;
|
|
6
|
-
use tishlang_ast::{ArrayElement, ArrowBody, BinOp, CallArg, CompoundOp, DestructElement, DestructPattern, Expr, Literal, LogicalAssignOp, MemberProp, ObjectProp, Program, Span, Statement, UnaryOp};
|
|
6
|
+
use tishlang_ast::{ArrayElement, ArrowBody, BinOp, CallArg, CompoundOp, DestructElement, DestructPattern, Expr, FunParam, Literal, LogicalAssignOp, MemberProp, ObjectProp, Program, Span, Statement, UnaryOp};
|
|
7
7
|
use crate::resolve::is_builtin_native_spec;
|
|
8
8
|
use crate::types::{RustType, TypeContext};
|
|
9
9
|
|
|
@@ -120,6 +120,14 @@ impl UsageAnalyzer {
|
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
|
+
Expr::New { callee, args, .. } => {
|
|
124
|
+
self.analyze_expr(callee);
|
|
125
|
+
for arg in args {
|
|
126
|
+
match arg {
|
|
127
|
+
CallArg::Expr(e) | CallArg::Spread(e) => self.analyze_expr(e),
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
123
131
|
Expr::Member { object, prop, .. } => {
|
|
124
132
|
self.analyze_expr(object);
|
|
125
133
|
if let MemberProp::Expr(e) = prop {
|
|
@@ -283,6 +291,11 @@ fn program_uses_async(program: &Program) -> bool {
|
|
|
283
291
|
CallArg::Expr(e) | CallArg::Spread(e) => expr_has_await(e),
|
|
284
292
|
})
|
|
285
293
|
}
|
|
294
|
+
Expr::New { callee, args, .. } => {
|
|
295
|
+
expr_has_await(callee) || args.iter().any(|a| match a {
|
|
296
|
+
CallArg::Expr(e) | CallArg::Spread(e) => expr_has_await(e),
|
|
297
|
+
})
|
|
298
|
+
}
|
|
286
299
|
Expr::Member { object, prop, .. } => {
|
|
287
300
|
expr_has_await(object)
|
|
288
301
|
|| if let MemberProp::Expr(e) = prop {
|
|
@@ -468,6 +481,8 @@ struct Codegen {
|
|
|
468
481
|
usage_analyzer: Option<UsageAnalyzer>,
|
|
469
482
|
/// Type context for tracking variable types (for static typing)
|
|
470
483
|
type_context: TypeContext,
|
|
484
|
+
/// Program uses JSX; emit `tishlang_ui` imports and `h` / `Fragment` globals.
|
|
485
|
+
program_has_jsx: bool,
|
|
471
486
|
}
|
|
472
487
|
|
|
473
488
|
impl Codegen {
|
|
@@ -493,6 +508,7 @@ impl Codegen {
|
|
|
493
508
|
refcell_wrapped_vars: std::collections::HashSet::new(),
|
|
494
509
|
usage_analyzer: None,
|
|
495
510
|
type_context: TypeContext::new(),
|
|
511
|
+
program_has_jsx: false,
|
|
496
512
|
}
|
|
497
513
|
}
|
|
498
514
|
|
|
@@ -503,7 +519,12 @@ impl Codegen {
|
|
|
503
519
|
return self.builtin_native_module_rust_init(spec, export_name);
|
|
504
520
|
}
|
|
505
521
|
self.native_module_map.get(spec).map(|(crate_name, export_fn)| {
|
|
506
|
-
|
|
522
|
+
// Native modules return a namespace object (like an ES module).
|
|
523
|
+
// Named imports extract the field from that namespace: `import { foo } from "pkg"` → `ns.foo`.
|
|
524
|
+
format!(
|
|
525
|
+
"{{ let _ns = {}::{}(); match _ns {{ Value::Object(ref _o) => _o.borrow().get({:?}).cloned().unwrap_or(Value::Null), _ => Value::Null }} }}",
|
|
526
|
+
crate_name, export_fn, export_name
|
|
527
|
+
)
|
|
507
528
|
})
|
|
508
529
|
}
|
|
509
530
|
|
|
@@ -536,7 +557,7 @@ impl Codegen {
|
|
|
536
557
|
"exec" => Some("Value::Function(Rc::new(|args: &[Value]| tish_process_exec(args)))"),
|
|
537
558
|
"argv" => Some("Value::Array(Rc::new(RefCell::new(std::env::args().map(|s| Value::String(s.into())).collect())))"),
|
|
538
559
|
"env" => Some("Value::Object(Rc::new(RefCell::new(std::env::vars().map(|(k,v)| (Arc::from(k.as_str()), Value::String(v.into()))).collect())))"),
|
|
539
|
-
"process" => Some("{ let mut m =
|
|
560
|
+
"process" => Some("{ let mut m = ObjectMap::default(); m.insert(Arc::from(\"exit\"), Value::Function(Rc::new(|args: &[Value]| tish_process_exit(args)))); m.insert(Arc::from(\"cwd\"), Value::Function(Rc::new(|args: &[Value]| tish_process_cwd(args)))); m.insert(Arc::from(\"exec\"), Value::Function(Rc::new(|args: &[Value]| tish_process_exec(args)))); m.insert(Arc::from(\"argv\"), Value::Array(Rc::new(RefCell::new(std::env::args().map(|s| Value::String(s.into())).collect())))); m.insert(Arc::from(\"env\"), Value::Object(Rc::new(RefCell::new(std::env::vars().map(|(k,v)| (Arc::from(k.as_str()), Value::String(v.into()))).collect::<ObjectMap>())))); Value::Object(Rc::new(RefCell::new(m))) }"),
|
|
540
561
|
_ => None,
|
|
541
562
|
},
|
|
542
563
|
"tish:ws" if self.has_feature("ws") => match export_name {
|
|
@@ -634,6 +655,7 @@ impl Codegen {
|
|
|
634
655
|
| Expr::Array { .. }
|
|
635
656
|
| Expr::Object { .. }
|
|
636
657
|
| Expr::Call { .. }
|
|
658
|
+
| Expr::New { .. }
|
|
637
659
|
| Expr::Await { .. }
|
|
638
660
|
| Expr::ArrowFunction { .. }
|
|
639
661
|
| Expr::Binary { .. }
|
|
@@ -727,12 +749,17 @@ impl Codegen {
|
|
|
727
749
|
use tishlang_ast::ArrowBody;
|
|
728
750
|
|
|
729
751
|
if let Expr::ArrowFunction { params, body, .. } = expr {
|
|
730
|
-
// Must have exactly 2 params
|
|
731
752
|
if params.len() != 2 {
|
|
732
753
|
return None;
|
|
733
754
|
}
|
|
734
|
-
let param_a = params[0]
|
|
735
|
-
|
|
755
|
+
let (param_a, param_b) = match (¶ms[0], ¶ms[1]) {
|
|
756
|
+
(FunParam::Simple(a), FunParam::Simple(b))
|
|
757
|
+
if a.default.is_none() && b.default.is_none() =>
|
|
758
|
+
{
|
|
759
|
+
(a.name.as_ref(), b.name.as_ref())
|
|
760
|
+
}
|
|
761
|
+
_ => return None,
|
|
762
|
+
};
|
|
736
763
|
|
|
737
764
|
// Body must be a single expression that's a subtraction
|
|
738
765
|
let body_expr = match body {
|
|
@@ -763,12 +790,15 @@ impl Codegen {
|
|
|
763
790
|
|
|
764
791
|
fn emit_program(&mut self, program: &Program) -> Result<(), CompileError> {
|
|
765
792
|
self.is_async = program_uses_async(program);
|
|
793
|
+
self.program_has_jsx = tishlang_ui::jsx::program_contains_jsx(program);
|
|
766
794
|
self.write("#![allow(unused, non_snake_case)]\n\n");
|
|
767
795
|
self.write("use std::cell::RefCell;\n");
|
|
768
|
-
self.write("use std::collections::HashMap;\n");
|
|
769
796
|
self.write("use std::rc::Rc;\n");
|
|
770
797
|
self.write("use std::sync::Arc;\n");
|
|
771
|
-
self.write("use tishlang_runtime::{console_debug as tish_console_debug, console_info as tish_console_info, console_log as tish_console_log, console_warn as tish_console_warn, console_error as tish_console_error, boolean as tish_boolean, decode_uri as tish_decode_uri, encode_uri as tish_encode_uri, in_operator as tish_in_operator, is_finite as tish_is_finite, is_nan as tish_is_nan, json_parse as tish_json_parse, json_stringify as tish_json_stringify, math_abs as tish_math_abs, math_ceil as tish_math_ceil, math_floor as tish_math_floor, math_max as tish_math_max, math_min as tish_math_min, math_round as tish_math_round, math_sqrt as tish_math_sqrt, parse_float as tish_parse_float, parse_int as tish_parse_int, math_random as tish_math_random, math_pow as tish_math_pow, math_sin as tish_math_sin, math_cos as tish_math_cos, math_tan as tish_math_tan, math_log as tish_math_log, math_exp as tish_math_exp, math_sign as tish_math_sign, math_trunc as tish_math_trunc, date_now as tish_date_now, array_is_array as tish_array_is_array, string_from_char_code as tish_string_from_char_code, object_assign as tish_object_assign, object_keys as tish_object_keys, object_values as tish_object_values, object_entries as tish_object_entries, object_from_entries as tish_object_from_entries, TishError, Value};\n");
|
|
798
|
+
self.write("use tishlang_runtime::{console_debug as tish_console_debug, console_info as tish_console_info, console_log as tish_console_log, console_warn as tish_console_warn, console_error as tish_console_error, boolean as tish_boolean, decode_uri as tish_decode_uri, encode_uri as tish_encode_uri, in_operator as tish_in_operator, is_finite as tish_is_finite, is_nan as tish_is_nan, json_parse as tish_json_parse, json_stringify as tish_json_stringify, math_abs as tish_math_abs, math_ceil as tish_math_ceil, math_floor as tish_math_floor, math_max as tish_math_max, math_min as tish_math_min, math_round as tish_math_round, math_sqrt as tish_math_sqrt, parse_float as tish_parse_float, parse_int as tish_parse_int, math_random as tish_math_random, math_pow as tish_math_pow, math_sin as tish_math_sin, math_cos as tish_math_cos, math_tan as tish_math_tan, math_log as tish_math_log, math_exp as tish_math_exp, math_sign as tish_math_sign, math_trunc as tish_math_trunc, date_now as tish_date_now, array_is_array as tish_array_is_array, string_from_char_code as tish_string_from_char_code, object_assign as tish_object_assign, object_keys as tish_object_keys, object_values as tish_object_values, object_entries as tish_object_entries, object_from_entries as tish_object_from_entries, tish_construct, tish_uint8_array_constructor, tish_audio_context_constructor, ObjectMap, TishError, Value};\n");
|
|
799
|
+
if self.program_has_jsx {
|
|
800
|
+
self.write("use tishlang_ui::{fragment_value, install_thread_local_host, native_create_root, native_use_state, ui_h, ui_text, HeadlessHost};\n");
|
|
801
|
+
}
|
|
772
802
|
if self.has_feature("process") {
|
|
773
803
|
self.write("use tishlang_runtime::{process_exit as tish_process_exit, process_cwd as tish_process_cwd, process_exec as tish_process_exec};\n");
|
|
774
804
|
}
|
|
@@ -818,7 +848,7 @@ impl Codegen {
|
|
|
818
848
|
self.indent += 1;
|
|
819
849
|
|
|
820
850
|
// Initialize builtins
|
|
821
|
-
self.writeln("let mut console = Value::Object(Rc::new(RefCell::new(
|
|
851
|
+
self.writeln("let mut console = Value::Object(Rc::new(RefCell::new(ObjectMap::from([");
|
|
822
852
|
self.indent += 1;
|
|
823
853
|
self.writeln("(Arc::from(\"debug\"), Value::Function(Rc::new(|args: &[Value]| { tish_console_debug(args); Value::Null }))),");
|
|
824
854
|
self.writeln("(Arc::from(\"info\"), Value::Function(Rc::new(|args: &[Value]| { tish_console_info(args); Value::Null }))),");
|
|
@@ -836,7 +866,7 @@ impl Codegen {
|
|
|
836
866
|
self.writeln("let isNaN = Value::Function(Rc::new(|args: &[Value]| tish_is_nan(args)));");
|
|
837
867
|
self.writeln("let Infinity = Value::Number(f64::INFINITY);");
|
|
838
868
|
self.writeln("let NaN = Value::Number(f64::NAN);");
|
|
839
|
-
self.writeln("let Math = Value::Object(Rc::new(RefCell::new(
|
|
869
|
+
self.writeln("let Math = Value::Object(Rc::new(RefCell::new(ObjectMap::from([");
|
|
840
870
|
self.indent += 1;
|
|
841
871
|
self.writeln("(Arc::from(\"abs\"), Value::Function(Rc::new(|args: &[Value]| tish_math_abs(args)))),");
|
|
842
872
|
self.writeln("(Arc::from(\"sqrt\"), Value::Function(Rc::new(|args: &[Value]| tish_math_sqrt(args)))),");
|
|
@@ -858,32 +888,32 @@ impl Codegen {
|
|
|
858
888
|
self.writeln("(Arc::from(\"E\"), Value::Number(std::f64::consts::E)),");
|
|
859
889
|
self.indent -= 1;
|
|
860
890
|
self.writeln("]))));");
|
|
861
|
-
self.writeln("let JSON = Value::Object(Rc::new(RefCell::new(
|
|
891
|
+
self.writeln("let JSON = Value::Object(Rc::new(RefCell::new(ObjectMap::from([");
|
|
862
892
|
self.indent += 1;
|
|
863
893
|
self.writeln("(Arc::from(\"parse\"), Value::Function(Rc::new(|args: &[Value]| tish_json_parse(args)))),");
|
|
864
894
|
self.writeln("(Arc::from(\"stringify\"), Value::Function(Rc::new(|args: &[Value]| tish_json_stringify(args)))),");
|
|
865
895
|
self.indent -= 1;
|
|
866
896
|
self.writeln("]))));");
|
|
867
897
|
|
|
868
|
-
self.writeln("let Array = Value::Object(Rc::new(RefCell::new(
|
|
898
|
+
self.writeln("let Array = Value::Object(Rc::new(RefCell::new(ObjectMap::from([");
|
|
869
899
|
self.indent += 1;
|
|
870
900
|
self.writeln("(Arc::from(\"isArray\"), Value::Function(Rc::new(|args: &[Value]| tish_array_is_array(args)))),");
|
|
871
901
|
self.indent -= 1;
|
|
872
902
|
self.writeln("]))));");
|
|
873
903
|
|
|
874
|
-
self.writeln("let String = Value::Object(Rc::new(RefCell::new(
|
|
904
|
+
self.writeln("let String = Value::Object(Rc::new(RefCell::new(ObjectMap::from([");
|
|
875
905
|
self.indent += 1;
|
|
876
906
|
self.writeln("(Arc::from(\"fromCharCode\"), Value::Function(Rc::new(|args: &[Value]| tish_string_from_char_code(args)))),");
|
|
877
907
|
self.indent -= 1;
|
|
878
908
|
self.writeln("]))));");
|
|
879
909
|
|
|
880
|
-
self.writeln("let Date = Value::Object(Rc::new(RefCell::new(
|
|
910
|
+
self.writeln("let Date = Value::Object(Rc::new(RefCell::new(ObjectMap::from([");
|
|
881
911
|
self.indent += 1;
|
|
882
912
|
self.writeln("(Arc::from(\"now\"), Value::Function(Rc::new(|args: &[Value]| tish_date_now(args)))),");
|
|
883
913
|
self.indent -= 1;
|
|
884
914
|
self.writeln("]))));");
|
|
885
915
|
|
|
886
|
-
self.writeln("let Object = Value::Object(Rc::new(RefCell::new(
|
|
916
|
+
self.writeln("let Object = Value::Object(Rc::new(RefCell::new(ObjectMap::from([");
|
|
887
917
|
self.indent += 1;
|
|
888
918
|
self.writeln("(Arc::from(\"assign\"), Value::Function(Rc::new(|args: &[Value]| tish_object_assign(args)))),");
|
|
889
919
|
self.writeln("(Arc::from(\"keys\"), Value::Function(Rc::new(|args: &[Value]| tish_object_keys(args)))),");
|
|
@@ -893,16 +923,19 @@ impl Codegen {
|
|
|
893
923
|
self.indent -= 1;
|
|
894
924
|
self.writeln("]))));");
|
|
895
925
|
|
|
926
|
+
self.writeln("let Uint8Array = tish_uint8_array_constructor();");
|
|
927
|
+
self.writeln("let AudioContext = tish_audio_context_constructor();");
|
|
928
|
+
|
|
896
929
|
if self.has_feature("process") {
|
|
897
930
|
self.writeln("let process = Value::Object(Rc::new(RefCell::new({");
|
|
898
931
|
self.indent += 1;
|
|
899
|
-
self.writeln("let mut p =
|
|
932
|
+
self.writeln("let mut p = ObjectMap::default();");
|
|
900
933
|
self.writeln("p.insert(Arc::from(\"exit\"), Value::Function(Rc::new(|args: &[Value]| tish_process_exit(args))));");
|
|
901
934
|
self.writeln("p.insert(Arc::from(\"cwd\"), Value::Function(Rc::new(|args: &[Value]| tish_process_cwd(args))));");
|
|
902
935
|
self.writeln("p.insert(Arc::from(\"exec\"), Value::Function(Rc::new(|args: &[Value]| tish_process_exec(args))));");
|
|
903
936
|
self.writeln("let argv: Vec<Value> = std::env::args().map(|s| Value::String(s.into())).collect();");
|
|
904
937
|
self.writeln("p.insert(Arc::from(\"argv\"), Value::Array(Rc::new(RefCell::new(argv))));");
|
|
905
|
-
self.writeln("let mut env_obj =
|
|
938
|
+
self.writeln("let mut env_obj = ObjectMap::default();");
|
|
906
939
|
self.writeln("for (key, value) in std::env::vars() {");
|
|
907
940
|
self.indent += 1;
|
|
908
941
|
self.writeln("env_obj.insert(Arc::from(key.as_str()), Value::String(value.into()));");
|
|
@@ -952,6 +985,15 @@ impl Codegen {
|
|
|
952
985
|
self.writeln("let RegExp = Value::Function(Rc::new(|args: &[Value]| regexp_new(args)));");
|
|
953
986
|
}
|
|
954
987
|
|
|
988
|
+
if self.program_has_jsx {
|
|
989
|
+
self.writeln("install_thread_local_host(Box::new(HeadlessHost::default()));");
|
|
990
|
+
self.writeln("let Fragment = fragment_value();");
|
|
991
|
+
self.writeln("let h = Value::Function(Rc::new(|args: &[Value]| ui_h(args)));");
|
|
992
|
+
self.writeln("let text = Value::Function(Rc::new(|args: &[Value]| ui_text(args)));");
|
|
993
|
+
self.writeln("let useState = Value::Function(Rc::new(|args: &[Value]| native_use_state(args)));");
|
|
994
|
+
self.writeln("let createRoot = Value::Function(Rc::new(|args: &[Value]| native_create_root(args)));");
|
|
995
|
+
}
|
|
996
|
+
|
|
955
997
|
// Polars, Egui etc. are emitted via VarDecl from import { X } from 'tish:...'
|
|
956
998
|
|
|
957
999
|
// Pre-scan for top-level function declarations and create cells (for mutual recursion)
|
|
@@ -1078,11 +1120,8 @@ impl Codegen {
|
|
|
1078
1120
|
let expr = self.emit_expr(init)?;
|
|
1079
1121
|
let mutability = if *mutable { "let mut" } else { "let" };
|
|
1080
1122
|
let clone_suffix = if Self::needs_clone(init) { ".clone()" } else { "" };
|
|
1081
|
-
self.writeln(&format!("
|
|
1082
|
-
self.indent += 1;
|
|
1123
|
+
self.writeln(&format!("let _destruct_val = ({}){};", expr, clone_suffix));
|
|
1083
1124
|
self.emit_destruct_bindings(pattern, "_destruct_val", mutability, *span)?;
|
|
1084
|
-
self.indent -= 1;
|
|
1085
|
-
self.writeln("}");
|
|
1086
1125
|
}
|
|
1087
1126
|
Statement::ExprStmt { expr, .. } => {
|
|
1088
1127
|
let e = self.emit_expr(expr)?;
|
|
@@ -1326,7 +1365,7 @@ impl Codegen {
|
|
|
1326
1365
|
self.emit_statement(finally_stmt)?;
|
|
1327
1366
|
}
|
|
1328
1367
|
}
|
|
1329
|
-
Statement::FunDecl { name, params, rest_param, body, .. } => {
|
|
1368
|
+
Statement::FunDecl { name, params, rest_param, body, span, .. } => {
|
|
1330
1369
|
// Use Rc<RefCell<>> pattern to allow recursive function calls
|
|
1331
1370
|
// The function can reference itself through the cell
|
|
1332
1371
|
let name_raw = name.as_ref();
|
|
@@ -1343,7 +1382,11 @@ impl Codegen {
|
|
|
1343
1382
|
// Analyze body to find which identifiers are actually referenced
|
|
1344
1383
|
let mut referenced = HashSet::new();
|
|
1345
1384
|
Self::collect_stmt_idents(body, &mut referenced);
|
|
1346
|
-
let param_names: HashSet<String> = params
|
|
1385
|
+
let param_names: HashSet<String> = params
|
|
1386
|
+
.iter()
|
|
1387
|
+
.flat_map(|p| p.bound_names())
|
|
1388
|
+
.map(|n| n.to_string())
|
|
1389
|
+
.collect();
|
|
1347
1390
|
|
|
1348
1391
|
// Collect all outer parameters that need to be captured (only those referenced)
|
|
1349
1392
|
let outer_params: Vec<String> = self.outer_params_stack
|
|
@@ -1418,7 +1461,7 @@ impl Codegen {
|
|
|
1418
1461
|
self.writeln(&format!("let {} = {}.clone();", param_escaped, param_escaped));
|
|
1419
1462
|
}
|
|
1420
1463
|
// Only clone builtins that are actually referenced (clone so outer scope can still use them, e.g. process for PORT before serve)
|
|
1421
|
-
for builtin in &["Boolean", "console", "Math", "JSON", "Date", "process", "setTimeout", "clearTimeout", "Promise", "RegExp", "Polars"] {
|
|
1464
|
+
for builtin in &["Boolean", "console", "Math", "JSON", "Date", "Uint8Array", "AudioContext", "process", "setTimeout", "clearTimeout", "Promise", "RegExp", "Polars"] {
|
|
1422
1465
|
if referenced.contains(*builtin) {
|
|
1423
1466
|
self.writeln(&format!("let {} = {}.clone();", builtin, builtin));
|
|
1424
1467
|
}
|
|
@@ -1445,13 +1488,30 @@ impl Codegen {
|
|
|
1445
1488
|
self.writeln(&format!("let {} = (*{}_ref.borrow()).clone();", sibling_escaped, sibling_escaped));
|
|
1446
1489
|
}
|
|
1447
1490
|
// Extract just the parameter names (type annotations are parsed but not used in codegen yet)
|
|
1448
|
-
let current_param_names: Vec<String> = params
|
|
1491
|
+
let current_param_names: Vec<String> = params
|
|
1492
|
+
.iter()
|
|
1493
|
+
.flat_map(|p| p.bound_names())
|
|
1494
|
+
.map(|n| n.to_string())
|
|
1495
|
+
.collect();
|
|
1496
|
+
let formal_span = *span;
|
|
1449
1497
|
for (i, p) in params.iter().enumerate() {
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1498
|
+
match p {
|
|
1499
|
+
FunParam::Simple(tp) => {
|
|
1500
|
+
self.writeln(&format!(
|
|
1501
|
+
"let mut {} = args.get({}).cloned().unwrap_or(Value::Null);",
|
|
1502
|
+
Self::escape_ident(tp.name.as_ref()),
|
|
1503
|
+
i
|
|
1504
|
+
));
|
|
1505
|
+
}
|
|
1506
|
+
FunParam::Destructure { pattern, .. } => {
|
|
1507
|
+
let tmp = format!("_formal_{}", i);
|
|
1508
|
+
self.writeln(&format!(
|
|
1509
|
+
"let {} = args.get({}).cloned().unwrap_or(Value::Null);",
|
|
1510
|
+
tmp, i
|
|
1511
|
+
));
|
|
1512
|
+
self.emit_destruct_bindings(pattern, &tmp, "let mut", formal_span)?;
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1455
1515
|
}
|
|
1456
1516
|
if let Some(rest) = rest_param {
|
|
1457
1517
|
self.writeln(&format!(
|
|
@@ -1562,58 +1622,71 @@ impl Codegen {
|
|
|
1562
1622
|
}
|
|
1563
1623
|
|
|
1564
1624
|
fn emit_destruct_bindings(&mut self, pattern: &DestructPattern, value_expr: &str, mutability: &str, span: Span) -> Result<(), CompileError> {
|
|
1625
|
+
// Flat `let` bindings so names stay in scope for the rest of the function (e.g. JSX).
|
|
1565
1626
|
match pattern {
|
|
1566
1627
|
DestructPattern::Array(elements) => {
|
|
1567
|
-
self.writeln(&format!("if let Value::Array(ref _arr) = {} {{", value_expr));
|
|
1568
|
-
self.indent += 1;
|
|
1569
|
-
self.writeln("let _arr_borrow = _arr.borrow();");
|
|
1570
1628
|
for (i, elem) in elements.iter().enumerate() {
|
|
1571
1629
|
if let Some(el) = elem {
|
|
1572
1630
|
match el {
|
|
1573
1631
|
DestructElement::Ident(name) => {
|
|
1574
|
-
self.writeln(&format!(
|
|
1575
|
-
|
|
1632
|
+
self.writeln(&format!(
|
|
1633
|
+
"{} {} = match &({}) {{ Value::Array(ref _a) => _a.borrow().get({}).cloned().unwrap_or(Value::Null), _ => Value::Null }};",
|
|
1634
|
+
mutability,
|
|
1635
|
+
Self::escape_ident(name.as_ref()),
|
|
1636
|
+
value_expr,
|
|
1637
|
+
i
|
|
1638
|
+
));
|
|
1576
1639
|
}
|
|
1577
1640
|
DestructElement::Pattern(nested) => {
|
|
1578
|
-
let nested_var = format!("
|
|
1579
|
-
self.writeln(&format!(
|
|
1580
|
-
|
|
1641
|
+
let nested_var = format!("_nested_arr_{}", i);
|
|
1642
|
+
self.writeln(&format!(
|
|
1643
|
+
"let {} = match &({}) {{ Value::Array(ref _a) => _a.borrow().get({}).cloned().unwrap_or(Value::Null), _ => Value::Null }};",
|
|
1644
|
+
nested_var, value_expr, i
|
|
1645
|
+
));
|
|
1581
1646
|
self.emit_destruct_bindings(nested, &nested_var, mutability, span)?;
|
|
1582
1647
|
}
|
|
1583
1648
|
DestructElement::Rest(name) => {
|
|
1584
|
-
self.writeln(&format!(
|
|
1585
|
-
|
|
1649
|
+
self.writeln(&format!(
|
|
1650
|
+
"{} {} = match &({}) {{ Value::Array(ref _a) => {{ let _b = _a.borrow(); Value::Array(Rc::new(RefCell::new(_b.iter().skip({}).cloned().collect()))) }}, _ => Value::Array(Rc::new(RefCell::new(Vec::new()))) }};",
|
|
1651
|
+
mutability,
|
|
1652
|
+
Self::escape_ident(name.as_ref()),
|
|
1653
|
+
value_expr,
|
|
1654
|
+
i
|
|
1655
|
+
));
|
|
1586
1656
|
}
|
|
1587
1657
|
}
|
|
1588
1658
|
}
|
|
1589
1659
|
}
|
|
1590
|
-
self.indent -= 1;
|
|
1591
|
-
self.writeln("}");
|
|
1592
1660
|
}
|
|
1593
1661
|
DestructPattern::Object(props) => {
|
|
1594
|
-
self.writeln(&format!("if let Value::Object(ref _obj) = {} {{", value_expr));
|
|
1595
|
-
self.indent += 1;
|
|
1596
|
-
self.writeln("let _obj_borrow = _obj.borrow();");
|
|
1597
1662
|
for prop in props {
|
|
1598
1663
|
let key = prop.key.as_ref();
|
|
1599
1664
|
match &prop.value {
|
|
1600
1665
|
DestructElement::Ident(name) => {
|
|
1601
|
-
self.writeln(&format!(
|
|
1602
|
-
|
|
1666
|
+
self.writeln(&format!(
|
|
1667
|
+
"{} {} = match &({}) {{ Value::Object(ref _o) => _o.borrow().get({:?}).cloned().unwrap_or(Value::Null), _ => Value::Null }};",
|
|
1668
|
+
mutability,
|
|
1669
|
+
Self::escape_ident(name.as_ref()),
|
|
1670
|
+
value_expr,
|
|
1671
|
+
key
|
|
1672
|
+
));
|
|
1603
1673
|
}
|
|
1604
1674
|
DestructElement::Pattern(nested) => {
|
|
1605
|
-
let nested_var = format!("
|
|
1606
|
-
self.writeln(&format!(
|
|
1607
|
-
|
|
1675
|
+
let nested_var = format!("_nested_obj_{}", key);
|
|
1676
|
+
self.writeln(&format!(
|
|
1677
|
+
"let {} = match &({}) {{ Value::Object(ref _o) => _o.borrow().get({:?}).cloned().unwrap_or(Value::Null), _ => Value::Null }};",
|
|
1678
|
+
nested_var, value_expr, key
|
|
1679
|
+
));
|
|
1608
1680
|
self.emit_destruct_bindings(nested, &nested_var, mutability, span)?;
|
|
1609
1681
|
}
|
|
1610
1682
|
DestructElement::Rest(_) => {
|
|
1611
|
-
return Err(CompileError::new(
|
|
1683
|
+
return Err(CompileError::new(
|
|
1684
|
+
"Rest in object destructuring not supported",
|
|
1685
|
+
Some(span),
|
|
1686
|
+
));
|
|
1612
1687
|
}
|
|
1613
1688
|
}
|
|
1614
1689
|
}
|
|
1615
|
-
self.indent -= 1;
|
|
1616
|
-
self.writeln("}");
|
|
1617
1690
|
}
|
|
1618
1691
|
}
|
|
1619
1692
|
Ok(())
|
|
@@ -2164,7 +2237,7 @@ impl Codegen {
|
|
|
2164
2237
|
}
|
|
2165
2238
|
}
|
|
2166
2239
|
}
|
|
2167
|
-
format!("{{ let mut _obj:
|
|
2240
|
+
format!("{{ let mut _obj: ObjectMap = ObjectMap::default(); {} Value::Object(Rc::new(RefCell::new(_obj))) }}", parts.join(" "))
|
|
2168
2241
|
} else {
|
|
2169
2242
|
let mut parts = Vec::new();
|
|
2170
2243
|
for prop in props {
|
|
@@ -2178,7 +2251,7 @@ impl Codegen {
|
|
|
2178
2251
|
}
|
|
2179
2252
|
}
|
|
2180
2253
|
format!(
|
|
2181
|
-
"Value::Object(Rc::new(RefCell::new(
|
|
2254
|
+
"Value::Object(Rc::new(RefCell::new(ObjectMap::from([{}]))))",
|
|
2182
2255
|
parts.join(", ")
|
|
2183
2256
|
)
|
|
2184
2257
|
}
|
|
@@ -2338,8 +2411,8 @@ impl Codegen {
|
|
|
2338
2411
|
val
|
|
2339
2412
|
)
|
|
2340
2413
|
}
|
|
2341
|
-
Expr::ArrowFunction { params, body, .. } => {
|
|
2342
|
-
self.emit_arrow_function(params, body)?
|
|
2414
|
+
Expr::ArrowFunction { params, body, span, .. } => {
|
|
2415
|
+
self.emit_arrow_function(params, body, *span)?
|
|
2343
2416
|
}
|
|
2344
2417
|
Expr::TemplateLiteral { quasis, exprs, .. } => {
|
|
2345
2418
|
// Build the template string
|
|
@@ -2356,7 +2429,33 @@ impl Codegen {
|
|
|
2356
2429
|
format!("Value::String([{}].concat().into())", parts.join(", "))
|
|
2357
2430
|
}
|
|
2358
2431
|
Expr::JsxElement { .. } | Expr::JsxFragment { .. } => {
|
|
2359
|
-
|
|
2432
|
+
tishlang_ui::jsx::emit_jsx_rust(expr, &mut |e| {
|
|
2433
|
+
self.emit_expr(e).map_err(|ce| ce.message)
|
|
2434
|
+
})
|
|
2435
|
+
.map_err(|m| CompileError::new(m, None))?
|
|
2436
|
+
}
|
|
2437
|
+
Expr::New { callee, args, .. } => {
|
|
2438
|
+
let callee_expr = self.emit_expr(callee)?;
|
|
2439
|
+
let has_spread = args.iter().any(|a| matches!(a, CallArg::Spread(_)));
|
|
2440
|
+
if has_spread {
|
|
2441
|
+
let args_code = self.emit_call_args(args)?;
|
|
2442
|
+
return Ok(format!(
|
|
2443
|
+
"{{ let _callee = ({}).clone(); let _spread_args = {}; tish_construct(&_callee, &_spread_args[..]) }}",
|
|
2444
|
+
callee_expr, args_code
|
|
2445
|
+
));
|
|
2446
|
+
}
|
|
2447
|
+
let arg_exprs: Result<Vec<_>, _> =
|
|
2448
|
+
args.iter().map(|a| self.emit_call_arg(a)).collect();
|
|
2449
|
+
let arg_exprs = arg_exprs?;
|
|
2450
|
+
let args_vec = arg_exprs
|
|
2451
|
+
.iter()
|
|
2452
|
+
.map(|a| format!("{}.clone()", a))
|
|
2453
|
+
.collect::<Vec<_>>()
|
|
2454
|
+
.join(", ");
|
|
2455
|
+
format!(
|
|
2456
|
+
"tish_construct(&({}).clone(), &[{}])",
|
|
2457
|
+
callee_expr, args_vec
|
|
2458
|
+
)
|
|
2360
2459
|
}
|
|
2361
2460
|
Expr::NativeModuleLoad { spec, export_name, .. } => {
|
|
2362
2461
|
self.native_module_rust_init(spec.as_ref(), export_name.as_ref())
|
|
@@ -2410,6 +2509,14 @@ impl Codegen {
|
|
|
2410
2509
|
}
|
|
2411
2510
|
}
|
|
2412
2511
|
}
|
|
2512
|
+
Expr::New { callee, args, .. } => {
|
|
2513
|
+
Self::collect_expr_idents(callee, idents);
|
|
2514
|
+
for arg in args {
|
|
2515
|
+
match arg {
|
|
2516
|
+
CallArg::Expr(e) | CallArg::Spread(e) => Self::collect_expr_idents(e, idents),
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2413
2520
|
Expr::Member { object, prop, .. } => {
|
|
2414
2521
|
Self::collect_expr_idents(object, idents);
|
|
2415
2522
|
if let MemberProp::Expr(e) = prop { Self::collect_expr_idents(e, idents); }
|
|
@@ -2613,6 +2720,16 @@ impl Codegen {
|
|
|
2613
2720
|
}
|
|
2614
2721
|
}
|
|
2615
2722
|
}
|
|
2723
|
+
Expr::New { callee, args, .. } => {
|
|
2724
|
+
Self::collect_assigned_idents_in_expr(callee, names);
|
|
2725
|
+
for arg in args {
|
|
2726
|
+
match arg {
|
|
2727
|
+
CallArg::Expr(e) | CallArg::Spread(e) => {
|
|
2728
|
+
Self::collect_assigned_idents_in_expr(e, names);
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2616
2733
|
Expr::Member { object, prop, .. } => {
|
|
2617
2734
|
Self::collect_assigned_idents_in_expr(object, names);
|
|
2618
2735
|
if let MemberProp::Expr(e) = prop {
|
|
@@ -2725,12 +2842,16 @@ impl Codegen {
|
|
|
2725
2842
|
/// Collect variable names that are both captured and mutated by a closure body.
|
|
2726
2843
|
/// block_vars: vars declared in the enclosing block (candidates for mutation).
|
|
2727
2844
|
fn collect_mutated_captures_from_closure(
|
|
2728
|
-
params: &[
|
|
2845
|
+
params: &[FunParam],
|
|
2729
2846
|
body: &Statement,
|
|
2730
2847
|
block_vars: &HashSet<String>,
|
|
2731
2848
|
result: &mut HashSet<String>,
|
|
2732
2849
|
) {
|
|
2733
|
-
let param_names: HashSet<String> = params
|
|
2850
|
+
let param_names: HashSet<String> = params
|
|
2851
|
+
.iter()
|
|
2852
|
+
.flat_map(|p| p.bound_names())
|
|
2853
|
+
.map(|n| n.to_string())
|
|
2854
|
+
.collect();
|
|
2734
2855
|
let mut local_var_names = HashSet::new();
|
|
2735
2856
|
Self::collect_local_var_names(body, &mut local_var_names);
|
|
2736
2857
|
let mut referenced = HashSet::new();
|
|
@@ -2754,12 +2875,16 @@ impl Codegen {
|
|
|
2754
2875
|
}
|
|
2755
2876
|
|
|
2756
2877
|
fn collect_mutated_captures_from_arrow(
|
|
2757
|
-
params: &[
|
|
2878
|
+
params: &[FunParam],
|
|
2758
2879
|
body: &ArrowBody,
|
|
2759
2880
|
block_vars: &HashSet<String>,
|
|
2760
2881
|
result: &mut HashSet<String>,
|
|
2761
2882
|
) {
|
|
2762
|
-
let param_names: HashSet<String> = params
|
|
2883
|
+
let param_names: HashSet<String> = params
|
|
2884
|
+
.iter()
|
|
2885
|
+
.flat_map(|p| p.bound_names())
|
|
2886
|
+
.map(|n| n.to_string())
|
|
2887
|
+
.collect();
|
|
2763
2888
|
let mut local_var_names = HashSet::new();
|
|
2764
2889
|
match body {
|
|
2765
2890
|
ArrowBody::Expr(_) => {}
|
|
@@ -2808,6 +2933,16 @@ impl Codegen {
|
|
|
2808
2933
|
}
|
|
2809
2934
|
}
|
|
2810
2935
|
}
|
|
2936
|
+
Expr::New { callee, args, .. } => {
|
|
2937
|
+
Self::collect_mutated_captures_from_expr(callee, block_vars, result);
|
|
2938
|
+
for arg in args {
|
|
2939
|
+
match arg {
|
|
2940
|
+
CallArg::Expr(e) | CallArg::Spread(e) => {
|
|
2941
|
+
Self::collect_mutated_captures_from_expr(e, block_vars, result);
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2811
2946
|
Expr::Member { object, prop, .. } => {
|
|
2812
2947
|
Self::collect_mutated_captures_from_expr(object, block_vars, result);
|
|
2813
2948
|
if let MemberProp::Expr(e) = prop {
|
|
@@ -3055,8 +3190,9 @@ impl Codegen {
|
|
|
3055
3190
|
|
|
3056
3191
|
fn emit_arrow_function(
|
|
3057
3192
|
&mut self,
|
|
3058
|
-
params: &[
|
|
3193
|
+
params: &[FunParam],
|
|
3059
3194
|
body: &tishlang_ast::ArrowBody,
|
|
3195
|
+
span: Span,
|
|
3060
3196
|
) -> Result<String, CompileError> {
|
|
3061
3197
|
// Build the arrow function as a Value::Function closure
|
|
3062
3198
|
let mut code = String::new();
|
|
@@ -3065,7 +3201,11 @@ impl Codegen {
|
|
|
3065
3201
|
// Find which identifiers are actually referenced in the body
|
|
3066
3202
|
let referenced = Self::collect_referenced_idents(body);
|
|
3067
3203
|
// Exclude the arrow's own parameters - they're not outer captures
|
|
3068
|
-
let param_names: HashSet<String> = params
|
|
3204
|
+
let param_names: HashSet<String> = params
|
|
3205
|
+
.iter()
|
|
3206
|
+
.flat_map(|p| p.bound_names())
|
|
3207
|
+
.map(|n| n.to_string())
|
|
3208
|
+
.collect();
|
|
3069
3209
|
|
|
3070
3210
|
// Exclude variables declared inside the arrow body (locals)
|
|
3071
3211
|
let mut local_var_names = HashSet::new();
|
|
@@ -3120,7 +3260,7 @@ impl Codegen {
|
|
|
3120
3260
|
}
|
|
3121
3261
|
}
|
|
3122
3262
|
// Only clone builtins that are actually referenced (clone so outer scope can still use, e.g. process for PORT)
|
|
3123
|
-
for builtin in &["console", "Math", "JSON", "Date", "process", "setTimeout", "clearTimeout", "Promise", "RegExp", "Polars"] {
|
|
3263
|
+
for builtin in &["console", "Math", "JSON", "Date", "Uint8Array", "AudioContext", "process", "setTimeout", "clearTimeout", "Promise", "RegExp", "Polars"] {
|
|
3124
3264
|
if referenced.contains(*builtin) {
|
|
3125
3265
|
code.push_str(&format!(" let {} = {}.clone();\n", builtin, builtin));
|
|
3126
3266
|
}
|
|
@@ -3164,13 +3304,35 @@ impl Codegen {
|
|
|
3164
3304
|
}
|
|
3165
3305
|
|
|
3166
3306
|
// Extract parameters from args
|
|
3167
|
-
let current_param_names: Vec<String> = params
|
|
3307
|
+
let current_param_names: Vec<String> = params
|
|
3308
|
+
.iter()
|
|
3309
|
+
.flat_map(|p| p.bound_names())
|
|
3310
|
+
.map(|n| n.to_string())
|
|
3311
|
+
.collect();
|
|
3168
3312
|
for (i, p) in params.iter().enumerate() {
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3313
|
+
match p {
|
|
3314
|
+
FunParam::Simple(tp) => {
|
|
3315
|
+
code.push_str(&format!(
|
|
3316
|
+
" let mut {} = args.get({}).cloned().unwrap_or(Value::Null);\n",
|
|
3317
|
+
Self::escape_ident(tp.name.as_ref()),
|
|
3318
|
+
i
|
|
3319
|
+
));
|
|
3320
|
+
}
|
|
3321
|
+
FunParam::Destructure { pattern, .. } => {
|
|
3322
|
+
let tmp = format!("_formal_{}", i);
|
|
3323
|
+
code.push_str(&format!(
|
|
3324
|
+
" let {} = args.get({}).cloned().unwrap_or(Value::Null);\n",
|
|
3325
|
+
tmp, i
|
|
3326
|
+
));
|
|
3327
|
+
let saved = std::mem::take(&mut self.output);
|
|
3328
|
+
let saved_indent = self.indent;
|
|
3329
|
+
self.indent = 8;
|
|
3330
|
+
self.emit_destruct_bindings(pattern, &tmp, "let mut", span)?;
|
|
3331
|
+
let frag = std::mem::replace(&mut self.output, saved);
|
|
3332
|
+
self.indent = saved_indent;
|
|
3333
|
+
code.push_str(&frag);
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3174
3336
|
}
|
|
3175
3337
|
|
|
3176
3338
|
// Push current params for potential nested arrows
|