@tishlang/tish-format 1.0.12 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/Cargo.toml +49 -0
  2. package/LICENSE +13 -0
  3. package/README.md +138 -0
  4. package/bin/tish-format +0 -0
  5. package/crates/js_to_tish/Cargo.toml +11 -0
  6. package/crates/js_to_tish/README.md +18 -0
  7. package/crates/js_to_tish/src/error.rs +55 -0
  8. package/crates/js_to_tish/src/lib.rs +11 -0
  9. package/crates/js_to_tish/src/span_util.rs +35 -0
  10. package/crates/js_to_tish/src/transform/expr.rs +610 -0
  11. package/crates/js_to_tish/src/transform/stmt.rs +503 -0
  12. package/crates/js_to_tish/src/transform.rs +60 -0
  13. package/crates/tish/Cargo.toml +54 -0
  14. package/crates/tish/src/cargo_native_registry.rs +32 -0
  15. package/crates/tish/src/cli_help.rs +565 -0
  16. package/crates/tish/src/main.rs +781 -0
  17. package/crates/tish/src/repl_completion.rs +200 -0
  18. package/crates/tish/tests/cargo_example_compile.rs +67 -0
  19. package/crates/tish/tests/fixtures/cargo_example_project/Cargo.toml +3 -0
  20. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/Cargo.toml +11 -0
  21. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +12 -0
  22. package/crates/tish/tests/fixtures/cargo_example_project/package.json +10 -0
  23. package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +3 -0
  24. package/crates/tish/tests/integration_test.rs +1095 -0
  25. package/crates/tish/tests/run_optimize_stdout_parity.rs +50 -0
  26. package/crates/tish/tests/shortcircuit.rs +65 -0
  27. package/crates/tish_ast/Cargo.toml +9 -0
  28. package/crates/tish_ast/src/ast.rs +620 -0
  29. package/crates/tish_ast/src/lib.rs +5 -0
  30. package/crates/tish_build_utils/Cargo.toml +11 -0
  31. package/crates/tish_build_utils/src/lib.rs +577 -0
  32. package/crates/tish_builtins/Cargo.toml +20 -0
  33. package/crates/tish_builtins/src/array.rs +441 -0
  34. package/crates/tish_builtins/src/construct.rs +159 -0
  35. package/crates/tish_builtins/src/globals.rs +213 -0
  36. package/crates/tish_builtins/src/helpers.rs +35 -0
  37. package/crates/tish_builtins/src/lib.rs +16 -0
  38. package/crates/tish_builtins/src/math.rs +89 -0
  39. package/crates/tish_builtins/src/object.rs +36 -0
  40. package/crates/tish_builtins/src/string.rs +647 -0
  41. package/crates/tish_builtins/src/symbol.rs +83 -0
  42. package/crates/tish_bytecode/Cargo.toml +17 -0
  43. package/crates/tish_bytecode/src/chunk.rs +96 -0
  44. package/crates/tish_bytecode/src/compiler.rs +1760 -0
  45. package/crates/tish_bytecode/src/encoding.rs +100 -0
  46. package/crates/tish_bytecode/src/lib.rs +19 -0
  47. package/crates/tish_bytecode/src/opcode.rs +142 -0
  48. package/crates/tish_bytecode/src/peephole.rs +189 -0
  49. package/crates/tish_bytecode/src/serialize.rs +163 -0
  50. package/crates/tish_bytecode/tests/break_continue_bytecode.rs +44 -0
  51. package/crates/tish_bytecode/tests/constant_folding.rs +84 -0
  52. package/crates/tish_bytecode/tests/sort_optimization.rs +31 -0
  53. package/crates/tish_compile/Cargo.toml +26 -0
  54. package/crates/tish_compile/src/codegen.rs +5332 -0
  55. package/crates/tish_compile/src/infer.rs +292 -0
  56. package/crates/tish_compile/src/lib.rs +164 -0
  57. package/crates/tish_compile/src/resolve.rs +1388 -0
  58. package/crates/tish_compile/src/types.rs +501 -0
  59. package/crates/tish_compile_js/Cargo.toml +18 -0
  60. package/crates/tish_compile_js/examples/jsx_vdom_smoke.tish +8 -0
  61. package/crates/tish_compile_js/src/codegen.rs +871 -0
  62. package/crates/tish_compile_js/src/error.rs +20 -0
  63. package/crates/tish_compile_js/src/lib.rs +26 -0
  64. package/crates/tish_compile_js/src/tests_jsx.rs +350 -0
  65. package/crates/tish_compiler_wasm/Cargo.toml +21 -0
  66. package/crates/tish_compiler_wasm/src/lib.rs +57 -0
  67. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +473 -0
  68. package/crates/tish_core/Cargo.toml +26 -0
  69. package/crates/tish_core/src/console_style.rs +160 -0
  70. package/crates/tish_core/src/json.rs +387 -0
  71. package/crates/tish_core/src/lib.rs +17 -0
  72. package/crates/tish_core/src/macros.rs +36 -0
  73. package/crates/tish_core/src/uri.rs +118 -0
  74. package/crates/tish_core/src/value.rs +696 -0
  75. package/crates/tish_core/src/vmref.rs +178 -0
  76. package/crates/tish_cranelift/Cargo.toml +19 -0
  77. package/crates/tish_cranelift/src/lib.rs +43 -0
  78. package/crates/tish_cranelift/src/link.rs +117 -0
  79. package/crates/tish_cranelift/src/lower.rs +85 -0
  80. package/crates/tish_cranelift_runtime/Cargo.toml +25 -0
  81. package/crates/tish_cranelift_runtime/src/lib.rs +45 -0
  82. package/crates/tish_eval/Cargo.toml +45 -0
  83. package/crates/tish_eval/src/eval.rs +3717 -0
  84. package/crates/tish_eval/src/http.rs +188 -0
  85. package/crates/tish_eval/src/lib.rs +99 -0
  86. package/crates/tish_eval/src/natives.rs +399 -0
  87. package/crates/tish_eval/src/promise.rs +179 -0
  88. package/crates/tish_eval/src/regex.rs +299 -0
  89. package/crates/tish_eval/src/timers.rs +120 -0
  90. package/crates/tish_eval/src/value.rs +318 -0
  91. package/crates/tish_eval/src/value_convert.rs +111 -0
  92. package/crates/tish_fmt/Cargo.toml +16 -0
  93. package/crates/tish_fmt/src/bin/tish-fmt.rs +41 -0
  94. package/crates/tish_fmt/src/lib.rs +2101 -0
  95. package/crates/tish_jsx_web/Cargo.toml +9 -0
  96. package/crates/tish_jsx_web/README.md +5 -0
  97. package/crates/tish_jsx_web/src/lib.rs +2 -0
  98. package/crates/tish_lexer/Cargo.toml +9 -0
  99. package/crates/tish_lexer/src/lib.rs +716 -0
  100. package/crates/tish_lexer/src/token.rs +163 -0
  101. package/crates/tish_lint/Cargo.toml +18 -0
  102. package/crates/tish_lint/src/bin/tish-lint.rs +195 -0
  103. package/crates/tish_lint/src/lib.rs +289 -0
  104. package/crates/tish_llvm/Cargo.toml +13 -0
  105. package/crates/tish_llvm/src/lib.rs +115 -0
  106. package/crates/tish_lsp/Cargo.toml +25 -0
  107. package/crates/tish_lsp/README.md +26 -0
  108. package/crates/tish_lsp/src/builtin_goto.rs +362 -0
  109. package/crates/tish_lsp/src/import_goto.rs +562 -0
  110. package/crates/tish_lsp/src/main.rs +1046 -0
  111. package/crates/tish_native/Cargo.toml +16 -0
  112. package/crates/tish_native/src/build.rs +427 -0
  113. package/crates/tish_native/src/config.rs +48 -0
  114. package/crates/tish_native/src/lib.rs +416 -0
  115. package/crates/tish_opt/Cargo.toml +13 -0
  116. package/crates/tish_opt/src/lib.rs +943 -0
  117. package/crates/tish_parser/Cargo.toml +11 -0
  118. package/crates/tish_parser/src/lib.rs +332 -0
  119. package/crates/tish_parser/src/parser.rs +2304 -0
  120. package/crates/tish_pg/Cargo.toml +34 -0
  121. package/crates/tish_pg/README.md +38 -0
  122. package/crates/tish_pg/src/error.rs +52 -0
  123. package/crates/tish_pg/src/lib.rs +955 -0
  124. package/crates/tish_resolve/Cargo.toml +13 -0
  125. package/crates/tish_resolve/src/lib.rs +3561 -0
  126. package/crates/tish_resolve/src/pos.rs +141 -0
  127. package/crates/tish_runtime/Cargo.toml +96 -0
  128. package/crates/tish_runtime/src/http.rs +1298 -0
  129. package/crates/tish_runtime/src/http_fetch.rs +471 -0
  130. package/crates/tish_runtime/src/http_hyper.rs +418 -0
  131. package/crates/tish_runtime/src/http_prefork.rs +189 -0
  132. package/crates/tish_runtime/src/lib.rs +1192 -0
  133. package/crates/tish_runtime/src/native_promise.rs +15 -0
  134. package/crates/tish_runtime/src/promise.rs +248 -0
  135. package/crates/tish_runtime/src/promise_io.rs +38 -0
  136. package/crates/tish_runtime/src/timers.rs +166 -0
  137. package/crates/tish_runtime/src/ws.rs +761 -0
  138. package/crates/tish_runtime/tests/fetch_readable_stream.rs +102 -0
  139. package/crates/tish_ui/Cargo.toml +17 -0
  140. package/crates/tish_ui/src/jsx.rs +682 -0
  141. package/crates/tish_ui/src/lib.rs +20 -0
  142. package/crates/tish_ui/src/runtime/hooks.rs +569 -0
  143. package/crates/tish_ui/src/runtime/mod.rs +180 -0
  144. package/crates/tish_vm/Cargo.toml +47 -0
  145. package/crates/tish_vm/src/lib.rs +39 -0
  146. package/crates/tish_vm/src/vm.rs +2192 -0
  147. package/crates/tish_vm/tests/fixtures/or_string_cmd.tish +2 -0
  148. package/crates/tish_vm/tests/lexical_scope_declare.rs +34 -0
  149. package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +150 -0
  150. package/crates/tish_wasm/Cargo.toml +15 -0
  151. package/crates/tish_wasm/src/lib.rs +424 -0
  152. package/crates/tish_wasm_runtime/Cargo.toml +37 -0
  153. package/crates/tish_wasm_runtime/src/gpu.rs +413 -0
  154. package/crates/tish_wasm_runtime/src/lib.rs +42 -0
  155. package/crates/tishlang_cargo_bindgen/Cargo.toml +26 -0
  156. package/crates/tishlang_cargo_bindgen/src/classify.rs +263 -0
  157. package/crates/tishlang_cargo_bindgen/src/discover.rs +125 -0
  158. package/crates/tishlang_cargo_bindgen/src/infer.rs +382 -0
  159. package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
  160. package/crates/tishlang_cargo_bindgen/src/main.rs +167 -0
  161. package/crates/tishlang_cargo_bindgen/src/metadata.rs +117 -0
  162. package/justfile +268 -0
  163. package/package.json +1 -1
  164. package/platform/darwin-arm64/tish-fmt +0 -0
@@ -0,0 +1,3717 @@
1
+ //! Tree-walk evaluator for Tish.
2
+
3
+ #![allow(clippy::type_complexity, clippy::cloned_ref_to_slice_refs)]
4
+
5
+ use std::cell::RefCell;
6
+ use std::collections::HashMap;
7
+ use std::path::{Path, PathBuf};
8
+ use std::rc::Rc;
9
+ use std::sync::Arc;
10
+
11
+ use tishlang_ast::{
12
+ BinOp, CompoundOp, ExportDeclaration, Expr, FunParam, ImportSpecifier, Literal,
13
+ LogicalAssignOp, MemberProp, Span, Statement, UnaryOp,
14
+ };
15
+
16
+ #[cfg(any(feature = "fs", feature = "process"))]
17
+ use crate::natives;
18
+ use ahash::AHashMap;
19
+
20
+ use crate::value::{
21
+ eval_object_get, eval_object_has, eval_object_set, EvalObjectData, PropMap, Value,
22
+ };
23
+
24
+ struct Scope {
25
+ vars: PropMap,
26
+ consts: std::collections::HashSet<Arc<str>>,
27
+ parent: Option<Rc<std::cell::RefCell<Scope>>>,
28
+ }
29
+
30
+ impl Scope {
31
+ fn new() -> Rc<std::cell::RefCell<Self>> {
32
+ Rc::new(std::cell::RefCell::new(Self {
33
+ vars: PropMap::default(),
34
+ consts: std::collections::HashSet::new(),
35
+ parent: None,
36
+ }))
37
+ }
38
+
39
+ fn child(parent: Rc<std::cell::RefCell<Scope>>) -> Rc<std::cell::RefCell<Self>> {
40
+ Rc::new(std::cell::RefCell::new(Self {
41
+ vars: PropMap::default(),
42
+ consts: std::collections::HashSet::new(),
43
+ parent: Some(parent),
44
+ }))
45
+ }
46
+
47
+ fn get(&self, name: &str) -> Option<Value> {
48
+ if let Some(v) = self.vars.get(name) {
49
+ return Some(v.clone());
50
+ }
51
+ if let Some(ref parent) = self.parent {
52
+ return parent.borrow().get(name);
53
+ }
54
+ None
55
+ }
56
+
57
+ fn set(&mut self, name: Arc<str>, value: Value, mutable: bool) {
58
+ if !mutable {
59
+ self.consts.insert(Arc::clone(&name));
60
+ }
61
+ self.vars.insert(name, value);
62
+ }
63
+
64
+ fn assign(&mut self, name: &str, value: Value) -> Result<bool, String> {
65
+ if let Some(existing) = self.vars.get_mut(name) {
66
+ if self.consts.contains(name) {
67
+ return Err(format!("Cannot assign to const variable: {}", name));
68
+ }
69
+ *existing = value;
70
+ return Ok(true);
71
+ }
72
+ if let Some(ref parent) = self.parent {
73
+ return parent.borrow_mut().assign(name, value);
74
+ }
75
+ Ok(false)
76
+ }
77
+ }
78
+
79
+ pub struct Evaluator {
80
+ scope: Rc<std::cell::RefCell<Scope>>,
81
+ /// Cache of evaluated modules: canonical path -> exports object
82
+ module_cache: Rc<RefCell<HashMap<PathBuf, Value>>>,
83
+ /// Directory of the file currently being evaluated (for resolving relative imports)
84
+ current_dir: RefCell<Option<PathBuf>>,
85
+ /// Extra `tish:*` builtins from `TishNativeModule::virtual_builtin_modules` (shared across nested evaluators).
86
+ virtual_builtins: Rc<RefCell<HashMap<Arc<str>, Value>>>,
87
+ }
88
+
89
+ impl Evaluator {
90
+ #[allow(clippy::new_without_default)]
91
+ pub fn new() -> Self {
92
+ use crate::natives;
93
+
94
+ let scope = Scope::new();
95
+ {
96
+ let mut s = scope.borrow_mut();
97
+ let mut console = PropMap::with_capacity(5);
98
+ console.insert("debug".into(), Value::Native(natives::console_debug));
99
+ console.insert("info".into(), Value::Native(natives::console_info));
100
+ console.insert("log".into(), Value::Native(natives::console_log));
101
+ console.insert("warn".into(), Value::Native(natives::console_warn));
102
+ console.insert("error".into(), Value::Native(natives::console_error));
103
+ s.set(
104
+ "console".into(),
105
+ Value::object(console),
106
+ true,
107
+ );
108
+ s.set("parseInt".into(), Value::Native(natives::parse_int), true);
109
+ s.set(
110
+ "parseFloat".into(),
111
+ Value::Native(natives::parse_float),
112
+ true,
113
+ );
114
+ s.set("decodeURI".into(), Value::Native(natives::decode_uri), true);
115
+ s.set("encodeURI".into(), Value::Native(natives::encode_uri), true);
116
+ s.set(
117
+ "htmlEscape".into(),
118
+ Value::Native(natives::html_escape),
119
+ true,
120
+ );
121
+ s.set(
122
+ "Boolean".into(),
123
+ Value::Native(natives::boolean_native),
124
+ true,
125
+ );
126
+ s.set("isFinite".into(), Value::Native(natives::is_finite), true);
127
+ s.set("isNaN".into(), Value::Native(natives::is_nan), true);
128
+ s.set("Infinity".into(), Value::Number(f64::INFINITY), true);
129
+ s.set("NaN".into(), Value::Number(f64::NAN), true);
130
+ let mut math = PropMap::with_capacity(18);
131
+ math.insert("abs".into(), Value::Native(natives::math_abs));
132
+ math.insert("sqrt".into(), Value::Native(natives::math_sqrt));
133
+ math.insert("min".into(), Value::Native(natives::math_min));
134
+ math.insert("max".into(), Value::Native(natives::math_max));
135
+ math.insert("floor".into(), Value::Native(natives::math_floor));
136
+ math.insert("ceil".into(), Value::Native(natives::math_ceil));
137
+ math.insert("round".into(), Value::Native(natives::math_round));
138
+ math.insert("random".into(), Value::Native(natives::math_random));
139
+ math.insert("pow".into(), Value::Native(natives::math_pow));
140
+ math.insert("sin".into(), Value::Native(natives::math_sin));
141
+ math.insert("cos".into(), Value::Native(natives::math_cos));
142
+ math.insert("tan".into(), Value::Native(natives::math_tan));
143
+ math.insert("log".into(), Value::Native(natives::math_log));
144
+ math.insert("exp".into(), Value::Native(natives::math_exp));
145
+ math.insert("sign".into(), Value::Native(natives::math_sign));
146
+ math.insert("trunc".into(), Value::Native(natives::math_trunc));
147
+ math.insert("PI".into(), Value::Number(std::f64::consts::PI));
148
+ math.insert("E".into(), Value::Number(std::f64::consts::E));
149
+ s.set(
150
+ "Math".into(),
151
+ Value::object(math),
152
+ true,
153
+ );
154
+
155
+ let mut json = PropMap::with_capacity(2);
156
+ json.insert("parse".into(), Value::Native(Self::json_parse_native));
157
+ json.insert(
158
+ "stringify".into(),
159
+ Value::Native(Self::json_stringify_native),
160
+ );
161
+ s.set(
162
+ "JSON".into(),
163
+ Value::object(json),
164
+ true,
165
+ );
166
+
167
+ let mut object = PropMap::with_capacity(5);
168
+ object.insert("keys".into(), Value::Native(Self::object_keys));
169
+ object.insert("values".into(), Value::Native(Self::object_values));
170
+ object.insert("entries".into(), Value::Native(Self::object_entries));
171
+ object.insert("assign".into(), Value::Native(Self::object_assign));
172
+ object.insert(
173
+ "fromEntries".into(),
174
+ Value::Native(Self::object_from_entries),
175
+ );
176
+ s.set(
177
+ "Object".into(),
178
+ Value::object(object),
179
+ true,
180
+ );
181
+
182
+ let mut array_obj = PropMap::with_capacity(1);
183
+ array_obj.insert("isArray".into(), Value::Native(natives::array_is_array));
184
+ s.set(
185
+ "Array".into(),
186
+ Value::object(array_obj),
187
+ true,
188
+ );
189
+
190
+ let mut string_obj = PropMap::with_capacity(1);
191
+ string_obj.insert(
192
+ "fromCharCode".into(),
193
+ Value::Native(natives::string_from_char_code),
194
+ );
195
+ s.set(
196
+ "String".into(),
197
+ Value::object(string_obj),
198
+ true,
199
+ );
200
+
201
+ let mut date = PropMap::with_capacity(1);
202
+ date.insert("now".into(), Value::Native(natives::date_now));
203
+ s.set(
204
+ "Date".into(),
205
+ Value::object(date),
206
+ true,
207
+ );
208
+
209
+ s.set(
210
+ "Symbol".into(),
211
+ crate::value_convert::core_to_eval(tishlang_builtins::symbol::symbol_object()),
212
+ true,
213
+ );
214
+ s.set(
215
+ "Uint8Array".into(),
216
+ crate::value_convert::core_to_eval(
217
+ tishlang_builtins::construct::uint8_array_constructor_value(),
218
+ ),
219
+ true,
220
+ );
221
+ s.set(
222
+ "AudioContext".into(),
223
+ crate::value_convert::core_to_eval(
224
+ tishlang_builtins::construct::audio_context_constructor_value(),
225
+ ),
226
+ true,
227
+ );
228
+
229
+ #[cfg(feature = "regex")]
230
+ {
231
+ s.set(
232
+ "RegExp".into(),
233
+ Value::Native(Self::regexp_constructor_native),
234
+ true,
235
+ );
236
+ }
237
+
238
+ // fs, process: prefer `import { x } from 'tish:fs'` etc.
239
+ #[cfg(feature = "timers")]
240
+ {
241
+ s.set(
242
+ "setTimeout".into(),
243
+ Value::TimerBuiltin(Arc::from("setTimeout")),
244
+ true,
245
+ );
246
+ s.set(
247
+ "setInterval".into(),
248
+ Value::TimerBuiltin(Arc::from("setInterval")),
249
+ true,
250
+ );
251
+ s.set(
252
+ "clearTimeout".into(),
253
+ Value::Native(Self::clear_timeout_native),
254
+ true,
255
+ );
256
+ s.set(
257
+ "clearInterval".into(),
258
+ Value::Native(Self::clear_interval_native),
259
+ true,
260
+ );
261
+ }
262
+ #[cfg(feature = "http")]
263
+ {
264
+ s.set("fetch".into(), Value::Native(Self::fetch_native), true);
265
+ s.set(
266
+ "fetchAll".into(),
267
+ Value::Native(Self::fetch_all_native),
268
+ true,
269
+ );
270
+ s.set("Promise".into(), Value::PromiseConstructor, true);
271
+ s.set("serve".into(), Value::Serve, true);
272
+ }
273
+ }
274
+ Self {
275
+ scope,
276
+ module_cache: Rc::new(RefCell::new(HashMap::new())),
277
+ current_dir: RefCell::new(None),
278
+ virtual_builtins: Rc::new(RefCell::new(HashMap::new())),
279
+ }
280
+ }
281
+
282
+ /// Create an evaluator with extra native modules (e.g. Polars) registered.
283
+ pub fn with_modules(modules: &[&dyn crate::TishNativeModule]) -> Self {
284
+ let eval = Self::new();
285
+ {
286
+ let mut s = eval.scope.borrow_mut();
287
+ for module in modules {
288
+ for (name, value) in module.register() {
289
+ s.set(name, value, true);
290
+ }
291
+ }
292
+ }
293
+ {
294
+ let mut vb = eval.virtual_builtins.borrow_mut();
295
+ for module in modules {
296
+ for (spec, value) in module.virtual_builtin_modules() {
297
+ vb.insert(Arc::from(spec), value);
298
+ }
299
+ }
300
+ }
301
+ eval
302
+ }
303
+
304
+ pub fn set_current_dir(&self, dir: Option<&Path>) {
305
+ *self.current_dir.borrow_mut() = dir.map(PathBuf::from);
306
+ }
307
+
308
+ pub fn eval_program(&mut self, program: &tishlang_ast::Program) -> Result<Value, String> {
309
+ let mut last = Value::Null;
310
+ for stmt in &program.statements {
311
+ last = self.eval_statement(stmt).map_err(|e| e.to_string())?;
312
+ }
313
+ Ok(last)
314
+ }
315
+
316
+ fn eval_statement(&mut self, stmt: &Statement) -> Result<Value, EvalError> {
317
+ match stmt {
318
+ Statement::Block { statements, .. } => {
319
+ let scope = Scope::child(Rc::clone(&self.scope));
320
+ let prev = std::mem::replace(&mut self.scope, scope);
321
+ let mut last = Value::Null;
322
+ for s in statements {
323
+ last = self.eval_statement(s)?;
324
+ }
325
+ self.scope = prev;
326
+ Ok(last)
327
+ }
328
+ Statement::VarDecl {
329
+ name,
330
+ mutable,
331
+ init,
332
+ ..
333
+ } => {
334
+ let value = init
335
+ .as_ref()
336
+ .map(|e| self.eval_expr(e))
337
+ .transpose()?
338
+ .unwrap_or(Value::Null);
339
+ self.scope
340
+ .borrow_mut()
341
+ .set(Arc::clone(name), value, *mutable);
342
+ Ok(Value::Null)
343
+ }
344
+ Statement::VarDeclDestructure {
345
+ pattern,
346
+ mutable,
347
+ init,
348
+ ..
349
+ } => {
350
+ let value = self.eval_expr(init)?;
351
+ self.bind_destruct_pattern(pattern, &value, *mutable)?;
352
+ Ok(Value::Null)
353
+ }
354
+ Statement::ExprStmt { expr, .. } => self.eval_expr(expr),
355
+ Statement::If {
356
+ cond,
357
+ then_branch,
358
+ else_branch,
359
+ ..
360
+ } => {
361
+ let c = self.eval_expr(cond)?;
362
+ if c.is_truthy() {
363
+ self.eval_statement(then_branch)
364
+ } else if let Some(eb) = else_branch {
365
+ self.eval_statement(eb)
366
+ } else {
367
+ Ok(Value::Null)
368
+ }
369
+ }
370
+ Statement::While { cond, body, .. } => {
371
+ loop {
372
+ if !self.eval_expr(cond)?.is_truthy() {
373
+ break;
374
+ }
375
+ match self.eval_statement(body) {
376
+ Ok(_) => {}
377
+ Err(EvalError::Break) => break,
378
+ Err(EvalError::Continue) => continue,
379
+ Err(e) => return Err(e),
380
+ }
381
+ }
382
+ Ok(Value::Null)
383
+ }
384
+ Statement::ForOf {
385
+ name,
386
+ iterable,
387
+ body,
388
+ ..
389
+ } => {
390
+ let iter_val = self.eval_expr(iterable)?;
391
+ let elements = match &iter_val {
392
+ crate::value::Value::Array(arr) => {
393
+ arr.borrow().iter().cloned().collect::<Vec<_>>()
394
+ }
395
+ crate::value::Value::String(s) => s
396
+ .chars()
397
+ .map(|c| crate::value::Value::String(Arc::from(c.to_string())))
398
+ .collect::<Vec<_>>(),
399
+ _ => {
400
+ return Err(EvalError::Error(format!(
401
+ "for-of requires iterable (array or string), got {}",
402
+ iter_val
403
+ )));
404
+ }
405
+ };
406
+ for elem in elements {
407
+ self.scope.borrow_mut().set(Arc::clone(name), elem, true);
408
+ match self.eval_statement(body) {
409
+ Ok(_) => {}
410
+ Err(EvalError::Break) => break,
411
+ Err(EvalError::Continue) => continue,
412
+ Err(e) => return Err(e),
413
+ }
414
+ }
415
+ Ok(Value::Null)
416
+ }
417
+ Statement::For {
418
+ init,
419
+ cond,
420
+ update,
421
+ body,
422
+ ..
423
+ } => {
424
+ if let Some(i) = init {
425
+ self.eval_statement(i)?;
426
+ }
427
+ loop {
428
+ let cond_ok = cond
429
+ .as_ref()
430
+ .map(|c| self.eval_expr(c).map(|v| v.is_truthy()))
431
+ .transpose()?
432
+ .unwrap_or(true);
433
+ if !cond_ok {
434
+ break;
435
+ }
436
+ match self.eval_statement(body) {
437
+ Ok(_) => {}
438
+ Err(EvalError::Break) => break,
439
+ Err(EvalError::Continue) => {
440
+ if let Some(u) = update {
441
+ self.eval_expr(u)?;
442
+ }
443
+ continue;
444
+ }
445
+ Err(e) => return Err(e),
446
+ }
447
+ if let Some(u) = update {
448
+ self.eval_expr(u)?;
449
+ }
450
+ }
451
+ Ok(Value::Null)
452
+ }
453
+ Statement::Return { value, .. } => {
454
+ let v = value
455
+ .as_ref()
456
+ .map(|e| self.eval_expr(e))
457
+ .transpose()?
458
+ .unwrap_or(Value::Null);
459
+ Err(EvalError::Return(v))
460
+ }
461
+ Statement::Break { .. } => Err(EvalError::Break),
462
+ Statement::Continue { .. } => Err(EvalError::Continue),
463
+ Statement::FunDecl {
464
+ name,
465
+ params,
466
+ rest_param,
467
+ body,
468
+ ..
469
+ } => {
470
+ let formals: Arc<[FunParam]> = Arc::from(params.clone());
471
+ let rest_param_name = rest_param.as_ref().map(|p| Arc::clone(&p.name));
472
+ let body = Arc::new(body.as_ref().clone());
473
+ let func = Value::Function {
474
+ formals,
475
+ rest_param: rest_param_name,
476
+ body,
477
+ };
478
+ self.scope.borrow_mut().set(Arc::clone(name), func, true);
479
+ Ok(Value::Null)
480
+ }
481
+ Statement::Switch {
482
+ expr,
483
+ cases,
484
+ default_body,
485
+ ..
486
+ } => {
487
+ let v = self.eval_expr(expr)?;
488
+ let mut matched = false;
489
+ for (case_expr, body) in cases {
490
+ if let Some(ce) = case_expr {
491
+ let cv = self.eval_expr(ce)?;
492
+ if v.strict_eq(&cv) {
493
+ matched = true;
494
+ let scope = Scope::child(Rc::clone(&self.scope));
495
+ let prev = std::mem::replace(&mut self.scope, scope);
496
+ for s in body {
497
+ match self.eval_statement(s) {
498
+ Ok(_) => {}
499
+ Err(EvalError::Break) => {
500
+ self.scope = prev;
501
+ return Ok(Value::Null);
502
+ }
503
+ Err(e) => {
504
+ self.scope = prev;
505
+ return Err(e);
506
+ }
507
+ }
508
+ }
509
+ self.scope = prev;
510
+ break;
511
+ }
512
+ }
513
+ }
514
+ if !matched {
515
+ if let Some(body) = default_body {
516
+ let scope = Scope::child(Rc::clone(&self.scope));
517
+ let prev = std::mem::replace(&mut self.scope, scope);
518
+ for s in body {
519
+ match self.eval_statement(s) {
520
+ Ok(_) => {}
521
+ Err(EvalError::Break) => break,
522
+ Err(e) => {
523
+ self.scope = prev;
524
+ return Err(e);
525
+ }
526
+ }
527
+ }
528
+ self.scope = prev;
529
+ }
530
+ }
531
+ Ok(Value::Null)
532
+ }
533
+ Statement::DoWhile { body, cond, .. } => {
534
+ loop {
535
+ match self.eval_statement(body) {
536
+ Ok(_) => {}
537
+ Err(EvalError::Break) => break,
538
+ Err(EvalError::Continue) => {
539
+ if !self.eval_expr(cond)?.is_truthy() {
540
+ break;
541
+ }
542
+ continue;
543
+ }
544
+ Err(e) => return Err(e),
545
+ }
546
+ if !self.eval_expr(cond)?.is_truthy() {
547
+ break;
548
+ }
549
+ }
550
+ Ok(Value::Null)
551
+ }
552
+ Statement::Throw { value, .. } => {
553
+ let v = self.eval_expr(value)?;
554
+ Err(EvalError::Throw(v))
555
+ }
556
+ Statement::Try {
557
+ body,
558
+ catch_param,
559
+ catch_body,
560
+ finally_body,
561
+ ..
562
+ } => {
563
+ let try_result = self.eval_statement(body);
564
+
565
+ let result = match try_result {
566
+ Ok(v) => Ok(v),
567
+ Err(EvalError::Throw(thrown)) => {
568
+ if let Some(catch_stmt) = catch_body {
569
+ if let Some(param) = catch_param {
570
+ let scope = Scope::child(Rc::clone(&self.scope));
571
+ let prev = std::mem::replace(&mut self.scope, Rc::clone(&scope));
572
+ scope.borrow_mut().set(Arc::clone(param), thrown, true);
573
+ let res = self.eval_statement(catch_stmt);
574
+ self.scope = prev;
575
+ res
576
+ } else {
577
+ self.eval_statement(catch_stmt)
578
+ }
579
+ } else {
580
+ Err(EvalError::Throw(thrown))
581
+ }
582
+ }
583
+ Err(e) => Err(e),
584
+ };
585
+
586
+ if let Some(finally_stmt) = finally_body {
587
+ let _ = self.eval_statement(finally_stmt);
588
+ }
589
+
590
+ result
591
+ }
592
+ Statement::Import {
593
+ specifiers, from, ..
594
+ } => {
595
+ let exports_val = self.load_module(from)?;
596
+ let exports = match &exports_val {
597
+ Value::Object(m) => m.borrow().clone(),
598
+ _ => {
599
+ return Err(EvalError::Error(
600
+ "Module exports must be object".to_string(),
601
+ ))
602
+ }
603
+ };
604
+ let mut scope = self.scope.borrow_mut();
605
+ for spec in specifiers {
606
+ match spec {
607
+ ImportSpecifier::Named { name, alias, .. } => {
608
+ let v = exports.strings.get(name.as_ref()).ok_or_else(|| {
609
+ EvalError::Error(format!("Module does not export '{}'", name))
610
+ })?;
611
+ let bind = alias.as_deref().unwrap_or(name.as_ref());
612
+ scope.set(Arc::from(bind), v.clone(), false);
613
+ }
614
+ ImportSpecifier::Namespace { name, .. } => {
615
+ scope.set(Arc::clone(name), exports_val.clone(), false);
616
+ }
617
+ ImportSpecifier::Default { name, .. } => {
618
+ let v = exports.strings.get("default").ok_or_else(|| {
619
+ EvalError::Error("Module does not have default export".to_string())
620
+ })?;
621
+ scope.set(Arc::clone(name), v.clone(), false);
622
+ }
623
+ }
624
+ }
625
+ Ok(Value::Null)
626
+ }
627
+ Statement::Export { declaration, .. } => {
628
+ match declaration.as_ref() {
629
+ ExportDeclaration::Named(s) => {
630
+ let _ = self.eval_statement(s);
631
+ }
632
+ ExportDeclaration::Default(e) => {
633
+ let v = self.eval_expr(e)?;
634
+ self.scope.borrow_mut().set(Arc::from("default"), v, false);
635
+ }
636
+ }
637
+ Ok(Value::Null)
638
+ }
639
+ Statement::TypeAlias { .. }
640
+ | Statement::DeclareVar { .. }
641
+ | Statement::DeclareFun { .. } => Ok(Value::Null),
642
+ }
643
+ }
644
+
645
+ /// Load and evaluate a module, returning its exports object. Uses cache.
646
+ fn load_module(&mut self, from: &str) -> Result<Value, EvalError> {
647
+ if from.starts_with("cargo:") {
648
+ return Err(EvalError::Error(
649
+ "cargo:… imports are only supported by `tish build` with the Rust native backend."
650
+ .into(),
651
+ ));
652
+ }
653
+ if from.starts_with("tish:") {
654
+ return self.load_builtin_module(from);
655
+ }
656
+ // Scoped native modules (e.g. `@tishlang/waterui`) registered via `TishNativeModule::virtual_builtin_modules`.
657
+ if self.virtual_builtins.borrow().get(from).is_some() {
658
+ return self.load_builtin_module(from);
659
+ }
660
+ let dir = self.current_dir.borrow().clone().ok_or_else(|| {
661
+ EvalError::Error(
662
+ "Cannot resolve imports: no current file directory (use run_file)".to_string(),
663
+ )
664
+ })?;
665
+ let path = Self::resolve_import_path(from, &dir)?;
666
+ let path = path
667
+ .canonicalize()
668
+ .map_err(|e| EvalError::Error(format!("Cannot resolve import '{}': {}", from, e)))?;
669
+ {
670
+ let cache = self.module_cache.borrow();
671
+ if let Some(m) = cache.get(&path) {
672
+ return Ok(m.clone());
673
+ }
674
+ }
675
+ let source = std::fs::read_to_string(&path)
676
+ .map_err(|e| EvalError::Error(format!("Cannot read {}: {}", path.display(), e)))?;
677
+ let program = tishlang_parser::parse(&source)
678
+ .map_err(|e| EvalError::Error(format!("Parse error in {}: {}", path.display(), e)))?;
679
+ let module_scope = Scope::child(Rc::clone(&self.scope));
680
+ let prev_scope = std::mem::replace(&mut self.scope, Rc::clone(&module_scope));
681
+ let parent_dir = self.current_dir.borrow().clone();
682
+ let module_dir = path.parent().map(PathBuf::from);
683
+ *self.current_dir.borrow_mut() = module_dir;
684
+ let mut export_names: Vec<String> = Vec::new();
685
+ for stmt in &program.statements {
686
+ if let Statement::Export { declaration, .. } = stmt {
687
+ match declaration.as_ref() {
688
+ ExportDeclaration::Named(s) => {
689
+ let _ = self.eval_statement(s);
690
+ if let Statement::VarDecl { name, .. } | Statement::FunDecl { name, .. } =
691
+ s.as_ref()
692
+ {
693
+ export_names.push(name.to_string());
694
+ }
695
+ }
696
+ ExportDeclaration::Default(e) => {
697
+ let v = self.eval_expr(e)?;
698
+ self.scope.borrow_mut().set(Arc::from("default"), v, false);
699
+ export_names.push("default".to_string());
700
+ }
701
+ }
702
+ } else {
703
+ let _ = self.eval_statement(stmt);
704
+ }
705
+ }
706
+ let mut exports: PropMap = PropMap::default();
707
+ for name in export_names {
708
+ if let Some(v) = module_scope.borrow().get(&name) {
709
+ exports.insert(Arc::from(name.as_str()), v);
710
+ }
711
+ }
712
+ *self.current_dir.borrow_mut() = parent_dir;
713
+ self.scope = prev_scope;
714
+ let exports_val = Value::object(exports);
715
+ self.module_cache
716
+ .borrow_mut()
717
+ .insert(path, exports_val.clone());
718
+ Ok(exports_val)
719
+ }
720
+
721
+ fn resolve_import_path(from: &str, dir: &Path) -> Result<PathBuf, EvalError> {
722
+ if !from.starts_with("./") && !from.starts_with("../") {
723
+ return Err(EvalError::Error(format!(
724
+ "Only relative imports supported (./ or ../), got: {}",
725
+ from
726
+ )));
727
+ }
728
+ let base = dir.join(from);
729
+ let path = if base.extension().is_none() {
730
+ let with_ext = base.with_extension("tish");
731
+ if with_ext.exists() {
732
+ with_ext
733
+ } else {
734
+ base
735
+ }
736
+ } else {
737
+ base
738
+ };
739
+ Ok(path)
740
+ }
741
+
742
+ /// Load built-in module (tish:fs, tish:http, tish:process, …) or a virtual module from native crates.
743
+ fn load_builtin_module(&self, spec: &str) -> Result<Value, EvalError> {
744
+ if spec.starts_with("cargo:") {
745
+ return Err(EvalError::Error(
746
+ "cargo:… imports are only supported when compiling with `tish build` and the Rust native backend. They link Cargo crates via package.json tish.rustDependencies and a generated native wrapper — not the interpreter or VM.".into(),
747
+ ));
748
+ }
749
+ if let Some(v) = self.virtual_builtins.borrow().get(spec) {
750
+ return Ok(v.clone());
751
+ }
752
+ match spec {
753
+ "tish:fs" => {
754
+ #[cfg(feature = "fs")]
755
+ {
756
+ let mut exports: PropMap = PropMap::default();
757
+ exports.insert("readFile".into(), Value::Native(natives::read_file));
758
+ exports.insert("writeFile".into(), Value::Native(natives::write_file));
759
+ exports.insert("fileExists".into(), Value::Native(natives::file_exists));
760
+ exports.insert("isDir".into(), Value::Native(natives::is_dir));
761
+ exports.insert("readDir".into(), Value::Native(natives::read_dir));
762
+ exports.insert("mkdir".into(), Value::Native(natives::mkdir));
763
+ return Ok(Value::object(exports));
764
+ }
765
+ #[cfg(not(feature = "fs"))]
766
+ {
767
+ return Err(EvalError::Error(
768
+ "tish:fs requires the fs feature. Rebuild with: cargo build -p tishlang --features fs".into(),
769
+ ));
770
+ }
771
+ }
772
+ "tish:http" => {
773
+ #[cfg(feature = "http")]
774
+ {
775
+ let mut exports: PropMap = PropMap::default();
776
+ exports.insert("fetch".into(), Value::Native(Self::fetch_native));
777
+ exports.insert("fetchAll".into(), Value::Native(Self::fetch_all_native));
778
+ exports.insert("serve".into(), Value::Serve);
779
+ exports.insert("Promise".into(), Value::PromiseConstructor);
780
+ return Ok(Value::object(exports));
781
+ }
782
+ #[cfg(not(feature = "http"))]
783
+ {
784
+ return Err(EvalError::Error(
785
+ "tish:http requires the http feature. Rebuild with: cargo build -p tishlang --features http".into(),
786
+ ));
787
+ }
788
+ }
789
+ "tish:timers" => {
790
+ #[cfg(feature = "timers")]
791
+ {
792
+ let mut exports: PropMap = PropMap::default();
793
+ exports.insert(
794
+ "setTimeout".into(),
795
+ Value::TimerBuiltin(Arc::from("setTimeout")),
796
+ );
797
+ exports.insert(
798
+ "setInterval".into(),
799
+ Value::TimerBuiltin(Arc::from("setInterval")),
800
+ );
801
+ exports.insert(
802
+ "clearTimeout".into(),
803
+ Value::Native(Self::clear_timeout_native),
804
+ );
805
+ exports.insert(
806
+ "clearInterval".into(),
807
+ Value::Native(Self::clear_interval_native),
808
+ );
809
+ return Ok(Value::object(exports));
810
+ }
811
+ #[cfg(not(feature = "timers"))]
812
+ {
813
+ return Err(EvalError::Error(
814
+ "tish:timers requires the timers feature. Rebuild with: cargo build -p tishlang --features timers".into(),
815
+ ));
816
+ }
817
+ }
818
+ "tish:ws" => {
819
+ #[cfg(feature = "ws")]
820
+ {
821
+ let mut exports: PropMap = PropMap::default();
822
+ exports.insert(
823
+ "WebSocket".into(),
824
+ Value::Native(Self::ws_web_socket_native),
825
+ );
826
+ exports.insert("Server".into(), Value::Native(Self::ws_server_native));
827
+ exports.insert("wsSend".into(), Value::Native(Self::ws_send_native));
828
+ exports.insert(
829
+ "wsBroadcast".into(),
830
+ Value::Native(Self::ws_broadcast_native),
831
+ );
832
+ return Ok(Value::object(exports));
833
+ }
834
+ #[cfg(not(feature = "ws"))]
835
+ {
836
+ return Err(EvalError::Error(
837
+ "tish:ws requires the ws feature. Rebuild with: cargo build -p tishlang --features ws".into(),
838
+ ));
839
+ }
840
+ }
841
+ "tish:process" => {
842
+ #[cfg(feature = "process")]
843
+ {
844
+ let mut exports: PropMap = PropMap::default();
845
+ exports.insert("exit".into(), Value::Native(natives::process_exit));
846
+ exports.insert("cwd".into(), Value::Native(natives::process_cwd));
847
+ exports.insert("exec".into(), Value::Native(natives::process_exec));
848
+ let argv: Vec<Value> =
849
+ std::env::args().map(|s| Value::String(s.into())).collect();
850
+ exports.insert(
851
+ "argv".into(),
852
+ Value::Array(Rc::new(RefCell::new(argv.clone()))),
853
+ );
854
+ let env_obj: PropMap = std::env::vars()
855
+ .map(|(key, value)| (Arc::from(key.as_str()), Value::String(value.into())))
856
+ .collect();
857
+ exports.insert(
858
+ "env".into(),
859
+ Value::object(env_obj.clone()),
860
+ );
861
+ let mut process_obj = PropMap::default();
862
+ process_obj.insert("exit".into(), Value::Native(natives::process_exit));
863
+ process_obj.insert("cwd".into(), Value::Native(natives::process_cwd));
864
+ process_obj.insert("exec".into(), Value::Native(natives::process_exec));
865
+ process_obj.insert("argv".into(), Value::Array(Rc::new(RefCell::new(argv))));
866
+ process_obj.insert("env".into(), Value::object(env_obj));
867
+ exports.insert(
868
+ "process".into(),
869
+ Value::object(process_obj),
870
+ );
871
+ return Ok(Value::object(exports));
872
+ }
873
+ #[cfg(not(feature = "process"))]
874
+ {
875
+ return Err(EvalError::Error(
876
+ "tish:process requires the process feature. Rebuild with: cargo build -p tishlang --features process".into(),
877
+ ));
878
+ }
879
+ }
880
+ _ => {
881
+ return Err(EvalError::Error(format!(
882
+ "Unknown built-in module: {}. Supported: tish:fs, tish:http, tish:timers, tish:process, tish:ws (plus any registered by native modules)",
883
+ spec
884
+ )));
885
+ }
886
+ }
887
+ }
888
+
889
+ fn load_builtin_export(&self, spec: &str, export_name: &str) -> Result<Value, EvalError> {
890
+ let module = self.load_builtin_module(spec)?;
891
+ let exports = match &module {
892
+ Value::Object(m) => m.borrow().clone(),
893
+ _ => return Err(EvalError::Error("Built-in module must be object".into())),
894
+ };
895
+ exports
896
+ .strings
897
+ .get(export_name)
898
+ .cloned()
899
+ .ok_or_else(|| {
900
+ EvalError::Error(format!("Module {} does not export '{}'", spec, export_name))
901
+ })
902
+ }
903
+
904
+ fn eval_expr(&self, expr: &Expr) -> Result<Value, EvalError> {
905
+ match expr {
906
+ Expr::Literal { value, .. } => Ok(match value {
907
+ Literal::Number(n) => Value::Number(*n),
908
+ Literal::String(s) => Value::String(Arc::clone(s)),
909
+ Literal::Bool(b) => Value::Bool(*b),
910
+ Literal::Null => Value::Null,
911
+ }),
912
+ Expr::Ident { name, .. } => self
913
+ .scope
914
+ .borrow()
915
+ .get(name.as_ref())
916
+ .ok_or_else(|| EvalError::Error(format!("Undefined variable: {}", name))),
917
+ Expr::Binary {
918
+ left,
919
+ op,
920
+ right,
921
+ ..
922
+ } => {
923
+ let l = self.eval_expr(left)?;
924
+ let r = self.eval_expr(right)?;
925
+ self.eval_binop(&l, *op, &r).map_err(EvalError::Error)
926
+ }
927
+ Expr::Unary { op, operand, .. } => {
928
+ let o = self.eval_expr(operand)?;
929
+ self.eval_unary(*op, &o).map_err(EvalError::Error)
930
+ }
931
+ Expr::Call { callee, args, .. } => {
932
+ // Check for built-in method calls on arrays/strings
933
+ if let Expr::Member {
934
+ object,
935
+ prop: MemberProp::Name { name: method_name, .. },
936
+ ..
937
+ } = callee.as_ref()
938
+ {
939
+ let obj = self.eval_expr(object)?;
940
+ let arg_vals = self.eval_call_args(args)?;
941
+
942
+ // Array methods
943
+ if let Value::Array(arr) = &obj {
944
+ match method_name.as_ref() {
945
+ "push" => {
946
+ let mut arr_mut = arr.borrow_mut();
947
+ for v in &arg_vals {
948
+ arr_mut.push(v.clone());
949
+ }
950
+ return Ok(Value::Number(arr_mut.len() as f64));
951
+ }
952
+ "pop" => {
953
+ return Ok(arr.borrow_mut().pop().unwrap_or(Value::Null));
954
+ }
955
+ "shift" => {
956
+ let mut arr_mut = arr.borrow_mut();
957
+ if arr_mut.is_empty() {
958
+ return Ok(Value::Null);
959
+ }
960
+ return Ok(arr_mut.remove(0));
961
+ }
962
+ "unshift" => {
963
+ let mut arr_mut = arr.borrow_mut();
964
+ for (i, v) in arg_vals.iter().enumerate() {
965
+ arr_mut.insert(i, v.clone());
966
+ }
967
+ return Ok(Value::Number(arr_mut.len() as f64));
968
+ }
969
+ "indexOf" => {
970
+ let search = arg_vals.first().cloned().unwrap_or(Value::Null);
971
+ let arr_borrow = arr.borrow();
972
+ for (i, v) in arr_borrow.iter().enumerate() {
973
+ if v.strict_eq(&search) {
974
+ return Ok(Value::Number(i as f64));
975
+ }
976
+ }
977
+ return Ok(Value::Number(-1.0));
978
+ }
979
+ "includes" => {
980
+ let search = arg_vals.first().cloned().unwrap_or(Value::Null);
981
+ let arr_borrow = arr.borrow();
982
+ let len = arr_borrow.len() as i64;
983
+ let start = match arg_vals.get(1) {
984
+ Some(Value::Number(n)) if *n >= 0.0 => (*n as i64).min(len).max(0) as usize,
985
+ Some(Value::Number(n)) if *n < 0.0 => ((len + *n as i64).max(0)) as usize,
986
+ _ => 0,
987
+ };
988
+ for v in arr_borrow.iter().skip(start) {
989
+ if v.strict_eq(&search) {
990
+ return Ok(Value::Bool(true));
991
+ }
992
+ }
993
+ return Ok(Value::Bool(false));
994
+ }
995
+ "join" => {
996
+ let sep = match arg_vals.first() {
997
+ Some(Value::String(s)) => s.to_string(),
998
+ _ => ",".to_string(),
999
+ };
1000
+ let arr_borrow = arr.borrow();
1001
+ let parts: Vec<String> = arr_borrow.iter().map(|v| v.to_string()).collect();
1002
+ return Ok(Value::String(parts.join(&sep).into()));
1003
+ }
1004
+ "reverse" => {
1005
+ arr.borrow_mut().reverse();
1006
+ return Ok(obj.clone());
1007
+ }
1008
+ "shuffle" => {
1009
+ let mut v = arr.borrow().clone();
1010
+ use rand::seq::SliceRandom;
1011
+ v.shuffle(&mut rand::rng());
1012
+ return Ok(Value::Array(Rc::new(RefCell::new(v))));
1013
+ }
1014
+ "sort" => {
1015
+ let comparator = arg_vals.into_iter().next();
1016
+ let mut arr_mut = arr.borrow_mut();
1017
+
1018
+ if let Some(cmp_fn) = comparator {
1019
+ // Check for fast path: (a, b) => a - b numeric ascending
1020
+ let is_numeric_asc = Self::is_numeric_sort_comparator(&cmp_fn, false);
1021
+ let is_numeric_desc = !is_numeric_asc && Self::is_numeric_sort_comparator(&cmp_fn, true);
1022
+
1023
+ if is_numeric_asc {
1024
+ // Fast path: numeric ascending sort
1025
+ arr_mut.sort_by(|a, b| {
1026
+ let na = match a { Value::Number(n) => *n, _ => f64::NAN };
1027
+ let nb = match b { Value::Number(n) => *n, _ => f64::NAN };
1028
+ na.partial_cmp(&nb).unwrap_or(std::cmp::Ordering::Equal)
1029
+ });
1030
+ } else if is_numeric_desc {
1031
+ // Fast path: numeric descending sort
1032
+ arr_mut.sort_by(|a, b| {
1033
+ let na = match a { Value::Number(n) => *n, _ => f64::NAN };
1034
+ let nb = match b { Value::Number(n) => *n, _ => f64::NAN };
1035
+ nb.partial_cmp(&na).unwrap_or(std::cmp::Ordering::Equal)
1036
+ });
1037
+ } else {
1038
+ // General case: use comparator function with optimized scope reuse
1039
+ let len = arr_mut.len();
1040
+ let mut indices: Vec<usize> = (0..len).collect();
1041
+ let arr_values: Vec<Value> = std::mem::take(&mut *arr_mut);
1042
+
1043
+ if let Some((scope, params, body)) = self.create_callback_scope(&cmp_fn) {
1044
+ indices.sort_by(|&i, &j| {
1045
+ let result = self.call_with_scope(&scope, &params, &body, &[arr_values[i].clone(), arr_values[j].clone()]);
1046
+ match result {
1047
+ Ok(Value::Number(n)) if n < 0.0 => std::cmp::Ordering::Less,
1048
+ Ok(Value::Number(n)) if n > 0.0 => std::cmp::Ordering::Greater,
1049
+ _ => std::cmp::Ordering::Equal,
1050
+ }
1051
+ });
1052
+ } else {
1053
+ indices.sort_by(|&i, &j| {
1054
+ let result = self.call_func(&cmp_fn, &[arr_values[i].clone(), arr_values[j].clone()]);
1055
+ match result {
1056
+ Ok(Value::Number(n)) if n < 0.0 => std::cmp::Ordering::Less,
1057
+ Ok(Value::Number(n)) if n > 0.0 => std::cmp::Ordering::Greater,
1058
+ _ => std::cmp::Ordering::Equal,
1059
+ }
1060
+ });
1061
+ }
1062
+
1063
+ *arr_mut = indices.into_iter().map(|i| arr_values[i].clone()).collect();
1064
+ }
1065
+ } else {
1066
+ // Default string sort - precompute strings once
1067
+ let mut pairs: Vec<(String, usize)> = arr_mut
1068
+ .iter()
1069
+ .enumerate()
1070
+ .map(|(i, v)| (v.to_string(), i))
1071
+ .collect();
1072
+ pairs.sort_by(|a, b| a.0.cmp(&b.0));
1073
+ let arr_values: Vec<Value> = std::mem::take(&mut *arr_mut);
1074
+ *arr_mut = pairs.into_iter().map(|(_, i)| arr_values[i].clone()).collect();
1075
+ }
1076
+ drop(arr_mut);
1077
+ return Ok(obj.clone());
1078
+ }
1079
+ "splice" => {
1080
+ let mut arr_mut = arr.borrow_mut();
1081
+ let len = arr_mut.len() as i64;
1082
+
1083
+ let start = match arg_vals.first() {
1084
+ Some(Value::Number(n)) => {
1085
+ let n = *n as i64;
1086
+ if n < 0 { (len + n).max(0) as usize } else { n.min(len) as usize }
1087
+ }
1088
+ _ => 0,
1089
+ };
1090
+
1091
+ let delete_count = match arg_vals.get(1) {
1092
+ Some(Value::Number(n)) => (*n as i64).max(0) as usize,
1093
+ _ => (len as usize).saturating_sub(start),
1094
+ };
1095
+
1096
+ let actual_delete = delete_count.min(arr_mut.len().saturating_sub(start));
1097
+ let removed: Vec<Value> = arr_mut.drain(start..start + actual_delete).collect();
1098
+
1099
+ if arg_vals.len() > 2 {
1100
+ let items_to_insert: Vec<Value> = arg_vals[2..].to_vec();
1101
+ for (i, item) in items_to_insert.into_iter().enumerate() {
1102
+ arr_mut.insert(start + i, item);
1103
+ }
1104
+ }
1105
+
1106
+ return Ok(Value::Array(Rc::new(RefCell::new(removed))));
1107
+ }
1108
+ "slice" => {
1109
+ let arr_borrow = arr.borrow();
1110
+ let len = arr_borrow.len() as i64;
1111
+ let start = match arg_vals.first() {
1112
+ Some(Value::Number(n)) => {
1113
+ let n = *n as i64;
1114
+ if n < 0 { (len + n).max(0) as usize } else { n.min(len) as usize }
1115
+ }
1116
+ _ => 0,
1117
+ };
1118
+ let end = match arg_vals.get(1) {
1119
+ Some(Value::Number(n)) => {
1120
+ let n = *n as i64;
1121
+ if n < 0 { (len + n).max(0) as usize } else { n.min(len) as usize }
1122
+ }
1123
+ _ => len as usize,
1124
+ };
1125
+ let sliced: Vec<Value> = if start < end {
1126
+ arr_borrow[start..end].to_vec()
1127
+ } else {
1128
+ vec![]
1129
+ };
1130
+ return Ok(Value::Array(Rc::new(RefCell::new(sliced))));
1131
+ }
1132
+ "concat" => {
1133
+ let mut result = arr.borrow().clone();
1134
+ for v in &arg_vals {
1135
+ if let Value::Array(other) = v {
1136
+ result.extend(other.borrow().iter().cloned());
1137
+ } else {
1138
+ result.push(v.clone());
1139
+ }
1140
+ }
1141
+ return Ok(Value::Array(Rc::new(RefCell::new(result))));
1142
+ }
1143
+ "map" => {
1144
+ let callback = arg_vals.first().cloned().unwrap_or(Value::Null);
1145
+ let arr_borrow = arr.borrow();
1146
+ let mut result = Vec::with_capacity(arr_borrow.len());
1147
+ // Try fastest path: simple single-expression callbacks
1148
+ let first_result = self.eval_simple_callback(&callback, &[arr_borrow.first().cloned().unwrap_or(Value::Null)]);
1149
+ if first_result.is_some() {
1150
+ // Simple callback path - inline evaluation
1151
+ for v in arr_borrow.iter() {
1152
+ if let Some(r) = self.eval_simple_callback(&callback, &[v.clone()]) {
1153
+ result.push(r?);
1154
+ } else {
1155
+ // Shouldn't happen, but fall back
1156
+ result.push(self.call_func(&callback, &[v.clone()])?);
1157
+ }
1158
+ }
1159
+ } else if let Some((scope, params, body)) = self.create_callback_scope(&callback) {
1160
+ // Reusable scope path
1161
+ for (i, v) in arr_borrow.iter().enumerate() {
1162
+ let mapped = self.call_with_scope(&scope, &params, &body, &[v.clone(), Value::Number(i as f64)])?;
1163
+ result.push(mapped);
1164
+ }
1165
+ } else {
1166
+ // Full call_func path
1167
+ for (i, v) in arr_borrow.iter().enumerate() {
1168
+ let mapped = self.call_func(&callback, &[v.clone(), Value::Number(i as f64)])?;
1169
+ result.push(mapped);
1170
+ }
1171
+ }
1172
+ return Ok(Value::Array(Rc::new(RefCell::new(result))));
1173
+ }
1174
+ "filter" => {
1175
+ let callback = arg_vals.first().cloned().unwrap_or(Value::Null);
1176
+ let arr_borrow = arr.borrow();
1177
+ let mut result = Vec::new();
1178
+ // Try simple callback fast path
1179
+ let use_simple = arr_borrow.first().map(|v| {
1180
+ self.eval_simple_callback(&callback, &[v.clone()]).is_some()
1181
+ }).unwrap_or(false);
1182
+ if use_simple {
1183
+ for v in arr_borrow.iter() {
1184
+ if let Some(keep) = self.eval_simple_callback(&callback, &[v.clone()]) {
1185
+ if keep?.is_truthy() {
1186
+ result.push(v.clone());
1187
+ }
1188
+ }
1189
+ }
1190
+ } else if let Some((scope, params, body)) = self.create_callback_scope(&callback) {
1191
+ for (i, v) in arr_borrow.iter().enumerate() {
1192
+ let keep = self.call_with_scope(&scope, &params, &body, &[v.clone(), Value::Number(i as f64)])?;
1193
+ if keep.is_truthy() {
1194
+ result.push(v.clone());
1195
+ }
1196
+ }
1197
+ } else {
1198
+ for (i, v) in arr_borrow.iter().enumerate() {
1199
+ let keep = self.call_func(&callback, &[v.clone(), Value::Number(i as f64)])?;
1200
+ if keep.is_truthy() {
1201
+ result.push(v.clone());
1202
+ }
1203
+ }
1204
+ }
1205
+ return Ok(Value::Array(Rc::new(RefCell::new(result))));
1206
+ }
1207
+ "reduce" => {
1208
+ let callback = arg_vals.first().cloned().unwrap_or(Value::Null);
1209
+ let arr_borrow = arr.borrow();
1210
+ let (mut acc, start_idx) = if arg_vals.len() > 1 {
1211
+ (arg_vals[1].clone(), 0)
1212
+ } else if !arr_borrow.is_empty() {
1213
+ (arr_borrow[0].clone(), 1)
1214
+ } else {
1215
+ return Err(EvalError::Error("Reduce of empty array with no initial value".to_string()));
1216
+ };
1217
+ if let Some((scope, params, body)) = self.create_callback_scope(&callback) {
1218
+ for (i, v) in arr_borrow.iter().enumerate().skip(start_idx) {
1219
+ acc = self.call_with_scope(&scope, &params, &body, &[acc, v.clone(), Value::Number(i as f64)])?;
1220
+ }
1221
+ } else {
1222
+ for (i, v) in arr_borrow.iter().enumerate().skip(start_idx) {
1223
+ acc = self.call_func(&callback, &[acc, v.clone(), Value::Number(i as f64)])?;
1224
+ }
1225
+ }
1226
+ return Ok(acc);
1227
+ }
1228
+ "find" => {
1229
+ let callback = arg_vals.first().cloned().unwrap_or(Value::Null);
1230
+ let arr_borrow = arr.borrow();
1231
+ // Try simple callback fast path
1232
+ let use_simple = arr_borrow.first().map(|v| {
1233
+ self.eval_simple_callback(&callback, &[v.clone()]).is_some()
1234
+ }).unwrap_or(false);
1235
+ if use_simple {
1236
+ for v in arr_borrow.iter() {
1237
+ if let Some(found) = self.eval_simple_callback(&callback, &[v.clone()]) {
1238
+ if found?.is_truthy() {
1239
+ return Ok(v.clone());
1240
+ }
1241
+ }
1242
+ }
1243
+ } else if let Some((scope, params, body)) = self.create_callback_scope(&callback) {
1244
+ for (i, v) in arr_borrow.iter().enumerate() {
1245
+ let found = self.call_with_scope(&scope, &params, &body, &[v.clone(), Value::Number(i as f64)])?;
1246
+ if found.is_truthy() {
1247
+ return Ok(v.clone());
1248
+ }
1249
+ }
1250
+ } else {
1251
+ for (i, v) in arr_borrow.iter().enumerate() {
1252
+ let found = self.call_func(&callback, &[v.clone(), Value::Number(i as f64)])?;
1253
+ if found.is_truthy() {
1254
+ return Ok(v.clone());
1255
+ }
1256
+ }
1257
+ }
1258
+ return Ok(Value::Null);
1259
+ }
1260
+ "findIndex" => {
1261
+ let callback = arg_vals.first().cloned().unwrap_or(Value::Null);
1262
+ let arr_borrow = arr.borrow();
1263
+ if let Some((scope, params, body)) = self.create_callback_scope(&callback) {
1264
+ for (i, v) in arr_borrow.iter().enumerate() {
1265
+ let found = self.call_with_scope(&scope, &params, &body, &[v.clone(), Value::Number(i as f64)])?;
1266
+ if found.is_truthy() {
1267
+ return Ok(Value::Number(i as f64));
1268
+ }
1269
+ }
1270
+ } else {
1271
+ for (i, v) in arr_borrow.iter().enumerate() {
1272
+ let found = self.call_func(&callback, &[v.clone(), Value::Number(i as f64)])?;
1273
+ if found.is_truthy() {
1274
+ return Ok(Value::Number(i as f64));
1275
+ }
1276
+ }
1277
+ }
1278
+ return Ok(Value::Number(-1.0));
1279
+ }
1280
+ "forEach" => {
1281
+ let callback = arg_vals.first().cloned().unwrap_or(Value::Null);
1282
+ let arr_borrow = arr.borrow();
1283
+ if let Some((scope, params, body)) = self.create_callback_scope(&callback) {
1284
+ for (i, v) in arr_borrow.iter().enumerate() {
1285
+ self.call_with_scope(&scope, &params, &body, &[v.clone(), Value::Number(i as f64)])?;
1286
+ }
1287
+ } else {
1288
+ for (i, v) in arr_borrow.iter().enumerate() {
1289
+ self.call_func(&callback, &[v.clone(), Value::Number(i as f64)])?;
1290
+ }
1291
+ }
1292
+ return Ok(Value::Null);
1293
+ }
1294
+ "some" => {
1295
+ let callback = arg_vals.first().cloned().unwrap_or(Value::Null);
1296
+ let arr_borrow = arr.borrow();
1297
+ // Try simple callback fast path
1298
+ let use_simple = arr_borrow.first().map(|v| {
1299
+ self.eval_simple_callback(&callback, &[v.clone()]).is_some()
1300
+ }).unwrap_or(false);
1301
+ if use_simple {
1302
+ for v in arr_borrow.iter() {
1303
+ if let Some(result) = self.eval_simple_callback(&callback, &[v.clone()]) {
1304
+ if result?.is_truthy() {
1305
+ return Ok(Value::Bool(true));
1306
+ }
1307
+ }
1308
+ }
1309
+ } else if let Some((scope, params, body)) = self.create_callback_scope(&callback) {
1310
+ for (i, v) in arr_borrow.iter().enumerate() {
1311
+ let result = self.call_with_scope(&scope, &params, &body, &[v.clone(), Value::Number(i as f64)])?;
1312
+ if result.is_truthy() {
1313
+ return Ok(Value::Bool(true));
1314
+ }
1315
+ }
1316
+ } else {
1317
+ for (i, v) in arr_borrow.iter().enumerate() {
1318
+ let result = self.call_func(&callback, &[v.clone(), Value::Number(i as f64)])?;
1319
+ if result.is_truthy() {
1320
+ return Ok(Value::Bool(true));
1321
+ }
1322
+ }
1323
+ }
1324
+ return Ok(Value::Bool(false));
1325
+ }
1326
+ "every" => {
1327
+ let callback = arg_vals.first().cloned().unwrap_or(Value::Null);
1328
+ let arr_borrow = arr.borrow();
1329
+ // Try simple callback fast path
1330
+ let use_simple = arr_borrow.first().map(|v| {
1331
+ self.eval_simple_callback(&callback, &[v.clone()]).is_some()
1332
+ }).unwrap_or(false);
1333
+ if use_simple {
1334
+ for v in arr_borrow.iter() {
1335
+ if let Some(result) = self.eval_simple_callback(&callback, &[v.clone()]) {
1336
+ if !result?.is_truthy() {
1337
+ return Ok(Value::Bool(false));
1338
+ }
1339
+ }
1340
+ }
1341
+ } else if let Some((scope, params, body)) = self.create_callback_scope(&callback) {
1342
+ for (i, v) in arr_borrow.iter().enumerate() {
1343
+ let result = self.call_with_scope(&scope, &params, &body, &[v.clone(), Value::Number(i as f64)])?;
1344
+ if !result.is_truthy() {
1345
+ return Ok(Value::Bool(false));
1346
+ }
1347
+ }
1348
+ } else {
1349
+ for (i, v) in arr_borrow.iter().enumerate() {
1350
+ let result = self.call_func(&callback, &[v.clone(), Value::Number(i as f64)])?;
1351
+ if !result.is_truthy() {
1352
+ return Ok(Value::Bool(false));
1353
+ }
1354
+ }
1355
+ }
1356
+ return Ok(Value::Bool(true));
1357
+ }
1358
+ "flat" => {
1359
+ let depth = match arg_vals.first() {
1360
+ Some(Value::Number(n)) => *n as usize,
1361
+ _ => 1,
1362
+ };
1363
+ fn flatten(arr: &[Value], depth: usize) -> Vec<Value> {
1364
+ let mut result = Vec::new();
1365
+ for v in arr {
1366
+ if depth > 0 {
1367
+ if let Value::Array(inner) = v {
1368
+ result.extend(flatten(&inner.borrow(), depth - 1));
1369
+ continue;
1370
+ }
1371
+ }
1372
+ result.push(v.clone());
1373
+ }
1374
+ result
1375
+ }
1376
+ let flattened = flatten(&arr.borrow(), depth);
1377
+ return Ok(Value::Array(Rc::new(RefCell::new(flattened))));
1378
+ }
1379
+ _ => {}
1380
+ }
1381
+ }
1382
+
1383
+ // String methods
1384
+ if let Value::String(s) = &obj {
1385
+ match method_name.as_ref() {
1386
+ "indexOf" => {
1387
+ let search = match arg_vals.first() {
1388
+ Some(Value::String(ss)) => ss.as_ref(),
1389
+ _ => return Ok(Value::Number(-1.0)),
1390
+ };
1391
+ let from_char = match arg_vals.get(1) {
1392
+ Some(Value::Number(n)) if *n >= 0.0 => {
1393
+ (*n as usize).min(s.chars().count())
1394
+ }
1395
+ _ => 0,
1396
+ };
1397
+ let byte_start: usize = s.chars().take(from_char).map(|c| c.len_utf8()).sum();
1398
+ let found = s[byte_start..].find(search).map(|byte_pos| {
1399
+ let char_idx = from_char
1400
+ + s[byte_start..][..byte_pos].chars().count();
1401
+ char_idx as f64
1402
+ });
1403
+ return Ok(Value::Number(found.unwrap_or(-1.0)));
1404
+ }
1405
+ "lastIndexOf" => {
1406
+ return Ok(Self::string_last_index_of_eval(&arg_vals, s));
1407
+ }
1408
+ "includes" => {
1409
+ let search = match arg_vals.first() {
1410
+ Some(Value::String(ss)) => ss.as_ref(),
1411
+ _ => return Ok(Value::Bool(false)),
1412
+ };
1413
+ let from_char = match arg_vals.get(1) {
1414
+ Some(Value::Number(n)) if *n >= 0.0 => (*n as usize).min(s.chars().count()),
1415
+ Some(Value::Number(n)) if *n < 0.0 => {
1416
+ let len = s.chars().count() as i64;
1417
+ ((len + *n as i64).max(0)) as usize
1418
+ }
1419
+ _ => 0,
1420
+ };
1421
+ let byte_start: usize = s.chars().take(from_char).map(|c| c.len_utf8()).sum();
1422
+ return Ok(Value::Bool(s[byte_start..].contains(search)));
1423
+ }
1424
+ "slice" => {
1425
+ let chars: Vec<char> = s.chars().collect();
1426
+ let len = chars.len() as i64;
1427
+ let start = match arg_vals.first() {
1428
+ Some(Value::Number(n)) => {
1429
+ let n = *n as i64;
1430
+ if n < 0 { (len + n).max(0) as usize } else { n.min(len) as usize }
1431
+ }
1432
+ _ => 0,
1433
+ };
1434
+ let end = match arg_vals.get(1) {
1435
+ Some(Value::Number(n)) => {
1436
+ let n = *n as i64;
1437
+ if n < 0 { (len + n).max(0) as usize } else { n.min(len) as usize }
1438
+ }
1439
+ _ => len as usize,
1440
+ };
1441
+ let sliced: String = if start < end {
1442
+ chars[start..end].iter().collect()
1443
+ } else {
1444
+ String::new()
1445
+ };
1446
+ return Ok(Value::String(sliced.into()));
1447
+ }
1448
+ "substring" => {
1449
+ let chars: Vec<char> = s.chars().collect();
1450
+ let len = chars.len();
1451
+ let start = match arg_vals.first() {
1452
+ Some(Value::Number(n)) => (*n as usize).min(len),
1453
+ _ => 0,
1454
+ };
1455
+ let end = match arg_vals.get(1) {
1456
+ Some(Value::Number(n)) => (*n as usize).min(len),
1457
+ _ => len,
1458
+ };
1459
+ let (s, e) = (start.min(end), start.max(end));
1460
+ return Ok(Value::String(chars[s..e].iter().collect::<String>().into()));
1461
+ }
1462
+ "split" => {
1463
+ #[cfg(feature = "regex")]
1464
+ if let Some(sep) = arg_vals.first() {
1465
+ let limit = arg_vals.get(1).and_then(|v| match v {
1466
+ Value::Number(n) => Some(*n as usize),
1467
+ _ => None,
1468
+ });
1469
+ return Ok(crate::regex::string_split(s, sep, limit));
1470
+ }
1471
+ #[cfg(not(feature = "regex"))]
1472
+ {
1473
+ let sep = match arg_vals.first() {
1474
+ Some(Value::String(ss)) => ss.as_ref(),
1475
+ _ => return Ok(Value::Array(Rc::new(RefCell::new(vec![obj.clone()])))),
1476
+ };
1477
+ let parts: Vec<Value> = s.split(sep)
1478
+ .map(|p| Value::String(p.into()))
1479
+ .collect();
1480
+ return Ok(Value::Array(Rc::new(RefCell::new(parts))));
1481
+ }
1482
+ #[cfg(feature = "regex")]
1483
+ return Ok(Value::Array(Rc::new(RefCell::new(vec![obj.clone()]))));
1484
+ }
1485
+ "trim" => {
1486
+ return Ok(Value::String(s.trim().into()));
1487
+ }
1488
+ "toUpperCase" => {
1489
+ return Ok(Value::String(s.to_uppercase().into()));
1490
+ }
1491
+ "toLowerCase" => {
1492
+ return Ok(Value::String(s.to_lowercase().into()));
1493
+ }
1494
+ "startsWith" => {
1495
+ let search = match arg_vals.first() {
1496
+ Some(Value::String(ss)) => ss.as_ref(),
1497
+ _ => return Ok(Value::Bool(false)),
1498
+ };
1499
+ return Ok(Value::Bool(s.starts_with(search)));
1500
+ }
1501
+ "endsWith" => {
1502
+ let search = match arg_vals.first() {
1503
+ Some(Value::String(ss)) => ss.as_ref(),
1504
+ _ => return Ok(Value::Bool(false)),
1505
+ };
1506
+ return Ok(Value::Bool(s.ends_with(search)));
1507
+ }
1508
+ "replace" => {
1509
+ #[cfg(feature = "regex")]
1510
+ if let (Some(search), Some(replace)) = (arg_vals.first(), arg_vals.get(1)) {
1511
+ let is_fn = matches!(replace, Value::Function { .. } | Value::Native(_));
1512
+ if matches!(search, Value::RegExp(_)) && is_fn {
1513
+ let re = match search {
1514
+ Value::RegExp(r) => r.clone(),
1515
+ _ => unreachable!(),
1516
+ };
1517
+ let re_guard = re.borrow();
1518
+ let replace_fn = replace.clone();
1519
+ let input_str = s.as_ref();
1520
+ let mut invoke = |args: &[Value]| {
1521
+ self.call_func(&replace_fn, args)
1522
+ .map(|v| v.to_string())
1523
+ .map_err(|e: EvalError| e.to_string())
1524
+ };
1525
+ match crate::regex::string_replace_regex_with_fn(
1526
+ input_str,
1527
+ &re_guard,
1528
+ &mut invoke,
1529
+ ) {
1530
+ Ok(v) => return Ok(v),
1531
+ Err(_) => return Ok(Value::String(Arc::clone(s))),
1532
+ }
1533
+ }
1534
+ return Ok(crate::regex::string_replace(s.as_ref(), search, replace));
1535
+ }
1536
+ #[cfg(not(feature = "regex"))]
1537
+ {
1538
+ let search = match arg_vals.first() {
1539
+ Some(Value::String(ss)) => ss.to_string(),
1540
+ _ => return Ok(obj.clone()),
1541
+ };
1542
+ let replacement = match arg_vals.get(1) {
1543
+ Some(Value::String(ss)) => ss.to_string(),
1544
+ _ => String::new(),
1545
+ };
1546
+ return Ok(Value::String(s.replacen(&search, &replacement, 1).into()));
1547
+ }
1548
+ #[cfg(feature = "regex")]
1549
+ return Ok(obj.clone());
1550
+ }
1551
+ "replaceAll" => {
1552
+ let search = match arg_vals.first() {
1553
+ Some(Value::String(ss)) => ss.to_string(),
1554
+ _ => return Ok(obj.clone()),
1555
+ };
1556
+ let replacement = match arg_vals.get(1) {
1557
+ Some(Value::String(ss)) => ss.to_string(),
1558
+ _ => String::new(),
1559
+ };
1560
+ return Ok(Value::String(s.replace(&search, &replacement).into()));
1561
+ }
1562
+ "charAt" => {
1563
+ let idx = match arg_vals.first() {
1564
+ Some(Value::Number(n)) => *n as usize,
1565
+ _ => 0,
1566
+ };
1567
+ let chars: Vec<char> = s.chars().collect();
1568
+ return Ok(chars.get(idx)
1569
+ .map(|c| Value::String(c.to_string().into()))
1570
+ .unwrap_or(Value::String("".into())));
1571
+ }
1572
+ "charCodeAt" => {
1573
+ let idx = match arg_vals.first() {
1574
+ Some(Value::Number(n)) => *n as usize,
1575
+ _ => 0,
1576
+ };
1577
+ let chars: Vec<char> = s.chars().collect();
1578
+ return Ok(chars.get(idx)
1579
+ .map(|c| Value::Number(*c as u32 as f64))
1580
+ .unwrap_or(Value::Number(f64::NAN)));
1581
+ }
1582
+ "repeat" => {
1583
+ let count = match arg_vals.first() {
1584
+ Some(Value::Number(n)) if *n >= 0.0 => *n as usize,
1585
+ _ => 0,
1586
+ };
1587
+ return Ok(Value::String(s.repeat(count).into()));
1588
+ }
1589
+ "padStart" => {
1590
+ let target_len = match arg_vals.first() {
1591
+ Some(Value::Number(n)) => *n as usize,
1592
+ _ => return Ok(obj.clone()),
1593
+ };
1594
+ let pad = match arg_vals.get(1) {
1595
+ Some(Value::String(p)) => p.to_string(),
1596
+ _ => " ".to_string(),
1597
+ };
1598
+ let char_count = s.chars().count();
1599
+ if char_count >= target_len || pad.is_empty() {
1600
+ return Ok(obj.clone());
1601
+ }
1602
+ let needed = target_len - char_count;
1603
+ let padding: String = pad.chars().cycle().take(needed).collect();
1604
+ return Ok(Value::String(format!("{}{}", padding, s).into()));
1605
+ }
1606
+ "padEnd" => {
1607
+ let target_len = match arg_vals.first() {
1608
+ Some(Value::Number(n)) => *n as usize,
1609
+ _ => return Ok(obj.clone()),
1610
+ };
1611
+ let pad = match arg_vals.get(1) {
1612
+ Some(Value::String(p)) => p.to_string(),
1613
+ _ => " ".to_string(),
1614
+ };
1615
+ let char_count = s.chars().count();
1616
+ if char_count >= target_len || pad.is_empty() {
1617
+ return Ok(obj.clone());
1618
+ }
1619
+ let needed = target_len - char_count;
1620
+ let padding: String = pad.chars().cycle().take(needed).collect();
1621
+ return Ok(Value::String(format!("{}{}", s, padding).into()));
1622
+ }
1623
+ #[cfg(feature = "regex")]
1624
+ "match" => {
1625
+ if let Some(regexp) = arg_vals.first() {
1626
+ return Ok(crate::regex::string_match(s, regexp));
1627
+ }
1628
+ return Ok(Value::Null);
1629
+ }
1630
+ #[cfg(feature = "regex")]
1631
+ "search" => {
1632
+ if let Some(regexp) = arg_vals.first() {
1633
+ return Ok(crate::regex::string_search(s, regexp));
1634
+ }
1635
+ return Ok(Value::Number(-1.0));
1636
+ }
1637
+ _ => {}
1638
+ }
1639
+ }
1640
+
1641
+ // Number methods
1642
+ if let Value::Number(n) = &obj {
1643
+ if method_name.as_ref() == "toFixed" {
1644
+ let digits = arg_vals
1645
+ .first()
1646
+ .and_then(|v| match v {
1647
+ Value::Number(d) => Some(*d as i32),
1648
+ _ => None,
1649
+ })
1650
+ .unwrap_or(0)
1651
+ .clamp(0, 20); // ECMA-262: 0–20
1652
+ let formatted = format!("{:.*}", digits as usize, n);
1653
+ return Ok(Value::String(formatted.into()));
1654
+ }
1655
+ }
1656
+
1657
+ // RegExp methods
1658
+ #[cfg(feature = "regex")]
1659
+ if let Value::RegExp(re) = &obj {
1660
+ match method_name.as_ref() {
1661
+ "test" => {
1662
+ let input = arg_vals.first()
1663
+ .map(|v| v.to_string())
1664
+ .unwrap_or_default();
1665
+ let result = re.borrow_mut().test(&input);
1666
+ return Ok(Value::Bool(result));
1667
+ }
1668
+ "exec" => {
1669
+ let input = arg_vals.first()
1670
+ .map(|v| v.to_string())
1671
+ .unwrap_or_default();
1672
+ let result = crate::regex::regexp_exec(&mut re.borrow_mut(), &input);
1673
+ return Ok(result);
1674
+ }
1675
+ _ => {}
1676
+ }
1677
+ }
1678
+
1679
+ // Fall through to normal function call. `get_prop` only implements `length` on
1680
+ // strings, so method calls would otherwise become `call_func(Null)` → Not a function.
1681
+ if let Value::String(s) = &obj {
1682
+ if method_name.as_ref() == "lastIndexOf" {
1683
+ return Ok(Self::string_last_index_of_eval(&arg_vals, s));
1684
+ }
1685
+ }
1686
+ let f = self.get_prop(&obj, method_name).map_err(EvalError::Error)?;
1687
+ return self.call_func(&f, &arg_vals);
1688
+ }
1689
+
1690
+ let f = self.eval_expr(callee)?;
1691
+ let arg_vals = self.eval_call_args(args)?;
1692
+ self.call_func(&f, &arg_vals)
1693
+ }
1694
+ Expr::Member {
1695
+ object,
1696
+ prop,
1697
+ optional,
1698
+ ..
1699
+ } => {
1700
+ let obj = self.eval_expr(object)?;
1701
+ if *optional && matches!(obj, Value::Null) {
1702
+ return Ok(Value::Null);
1703
+ }
1704
+ let key = match prop {
1705
+ MemberProp::Name { name, .. } => Arc::clone(name),
1706
+ MemberProp::Expr(e) => {
1707
+ let v = self.eval_expr(e)?;
1708
+ match v {
1709
+ Value::String(s) => s,
1710
+ _ => return Err(EvalError::Error("Property key must be string".to_string())),
1711
+ }
1712
+ }
1713
+ };
1714
+ match self.get_prop(&obj, &key) {
1715
+ Ok(v) => Ok(v),
1716
+ Err(_) if *optional => Ok(Value::Null),
1717
+ Err(e) => Err(EvalError::Error(e)),
1718
+ }
1719
+ }
1720
+ Expr::Index {
1721
+ object,
1722
+ index,
1723
+ optional,
1724
+ ..
1725
+ } => {
1726
+ let obj = self.eval_expr(object)?;
1727
+ if *optional && matches!(obj, Value::Null) {
1728
+ return Ok(Value::Null);
1729
+ }
1730
+ let idx = self.eval_expr(index)?;
1731
+ self.get_index(&obj, &idx).map_err(EvalError::Error)
1732
+ }
1733
+ Expr::Conditional {
1734
+ cond,
1735
+ then_branch,
1736
+ else_branch,
1737
+ ..
1738
+ } => {
1739
+ if self.eval_expr(cond)?.is_truthy() {
1740
+ self.eval_expr(then_branch)
1741
+ } else {
1742
+ self.eval_expr(else_branch)
1743
+ }
1744
+ }
1745
+ Expr::NullishCoalesce { left, right, .. } => {
1746
+ let l = self.eval_expr(left)?;
1747
+ if matches!(l, Value::Null) {
1748
+ self.eval_expr(right)
1749
+ } else {
1750
+ Ok(l)
1751
+ }
1752
+ }
1753
+ Expr::Array { elements, .. } => {
1754
+ let mut vals = Vec::with_capacity(elements.len());
1755
+ for elem in elements {
1756
+ match elem {
1757
+ tishlang_ast::ArrayElement::Expr(e) => {
1758
+ vals.push(self.eval_expr(e)?);
1759
+ }
1760
+ tishlang_ast::ArrayElement::Spread(e) => {
1761
+ let spread_val = self.eval_expr(e)?;
1762
+ if let Value::Array(arr) = spread_val {
1763
+ vals.extend(arr.borrow().iter().cloned());
1764
+ }
1765
+ }
1766
+ }
1767
+ }
1768
+ Ok(Value::Array(Rc::new(RefCell::new(vals))))
1769
+ }
1770
+ Expr::Object { props, .. } => {
1771
+ let mut data = EvalObjectData::default();
1772
+ for prop in props {
1773
+ match prop {
1774
+ tishlang_ast::ObjectProp::KeyValue(k, v) => {
1775
+ data
1776
+ .strings
1777
+ .insert(Arc::clone(k), self.eval_expr(v)?);
1778
+ }
1779
+ tishlang_ast::ObjectProp::Spread(e) => {
1780
+ let spread_val = self.eval_expr(e)?;
1781
+ if let Value::Object(obj) = spread_val {
1782
+ let b = obj.borrow();
1783
+ for (k, v) in b.strings.iter() {
1784
+ data.strings.insert(Arc::clone(k), v.clone());
1785
+ }
1786
+ if let Some(ref sm) = b.symbols {
1787
+ if data.symbols.is_none() {
1788
+ data.symbols = Some(AHashMap::default());
1789
+ }
1790
+ let dm = data.symbols.as_mut().unwrap();
1791
+ for (id, v) in sm.iter() {
1792
+ dm.insert(*id, v.clone());
1793
+ }
1794
+ }
1795
+ }
1796
+ }
1797
+ }
1798
+ }
1799
+ Ok(Value::Object(Rc::new(RefCell::new(data))))
1800
+ }
1801
+ Expr::Assign { name, value, .. } => {
1802
+ let v = self.eval_expr(value)?;
1803
+ match self.scope.borrow_mut().assign(name.as_ref(), v.clone()) {
1804
+ Ok(true) => Ok(v),
1805
+ Ok(false) => Err(EvalError::Error(format!("Undefined variable: {}", name))),
1806
+ Err(e) => Err(EvalError::Error(e)),
1807
+ }
1808
+ }
1809
+ Expr::Await { operand, .. } => self.eval_await(operand),
1810
+ Expr::New { callee, args, .. } => {
1811
+ let c = self.eval_expr(callee)?;
1812
+ let arg_vals = self.eval_call_args(args)?;
1813
+ self.construct_value(&c, &arg_vals)
1814
+ }
1815
+ Expr::JsxElement { .. } | Expr::JsxFragment { .. } => Err(EvalError::Error(
1816
+ "JSX is not supported in the interpreter. Use 'tish build --target js' to compile to JavaScript.".to_string(),
1817
+ )),
1818
+ Expr::NativeModuleLoad { spec, export_name, .. } => {
1819
+ self.load_builtin_export(spec.as_ref(), export_name.as_ref())
1820
+ }
1821
+ Expr::TypeOf { operand, .. } => {
1822
+ let v = self.eval_expr(operand)?;
1823
+ Ok(Value::String(match &v {
1824
+ Value::Number(_) => "number".into(),
1825
+ Value::String(_) => "string".into(),
1826
+ Value::Bool(_) => "boolean".into(),
1827
+ Value::Null => "null".into(),
1828
+ Value::Array(_) => "object".into(),
1829
+ Value::Object(_) => "object".into(),
1830
+ Value::Symbol(_) => "symbol".into(),
1831
+ Value::Function { .. } | Value::Native(_) => "function".into(),
1832
+ Value::CoreFn(_) => "function".into(),
1833
+ #[cfg(feature = "http")]
1834
+ Value::CorePromise(_) => "object".into(),
1835
+ #[cfg(feature = "http")]
1836
+ Value::Serve
1837
+ | Value::PromiseResolver(_)
1838
+ | Value::PromiseConstructor
1839
+ | Value::BoundPromiseMethod(_, _) => "function".into(),
1840
+ #[cfg(feature = "timers")]
1841
+ Value::TimerBuiltin(_) => "function".into(),
1842
+ #[cfg(feature = "http")]
1843
+ Value::Promise(_) => "object".into(),
1844
+ #[cfg(feature = "regex")]
1845
+ Value::RegExp(_) => "object".into(),
1846
+ Value::Opaque(_) => "object".into(),
1847
+ Value::OpaqueMethod(_, _) => "function".into(),
1848
+ }))
1849
+ }
1850
+ Expr::PostfixInc { name, .. } => {
1851
+ let v = self.scope.borrow().get(name.as_ref())
1852
+ .ok_or_else(|| EvalError::Error(format!("Undefined variable: {}", name)))?;
1853
+ let n = match &v {
1854
+ Value::Number(x) => *x,
1855
+ _ => return Err(EvalError::Error(format!("Cannot apply ++ to {:?}", v))),
1856
+ };
1857
+ match self.scope.borrow_mut().assign(name.as_ref(), Value::Number(n + 1.0)) {
1858
+ Ok(true) => Ok(Value::Number(n)),
1859
+ Ok(false) => Err(EvalError::Error(format!("Undefined variable: {}", name))),
1860
+ Err(e) => Err(EvalError::Error(e)),
1861
+ }
1862
+ }
1863
+ Expr::PostfixDec { name, .. } => {
1864
+ let v = self.scope.borrow().get(name.as_ref())
1865
+ .ok_or_else(|| EvalError::Error(format!("Undefined variable: {}", name)))?;
1866
+ let n = match &v {
1867
+ Value::Number(x) => *x,
1868
+ _ => return Err(EvalError::Error(format!("Cannot apply -- to {:?}", v))),
1869
+ };
1870
+ match self.scope.borrow_mut().assign(name.as_ref(), Value::Number(n - 1.0)) {
1871
+ Ok(true) => Ok(Value::Number(n)),
1872
+ Ok(false) => Err(EvalError::Error(format!("Undefined variable: {}", name))),
1873
+ Err(e) => Err(EvalError::Error(e)),
1874
+ }
1875
+ }
1876
+ Expr::PrefixInc { name, .. } => {
1877
+ let v = self.scope.borrow().get(name.as_ref())
1878
+ .ok_or_else(|| EvalError::Error(format!("Undefined variable: {}", name)))?;
1879
+ let n = match &v {
1880
+ Value::Number(x) => *x,
1881
+ _ => return Err(EvalError::Error(format!("Cannot apply ++ to {:?}", v))),
1882
+ };
1883
+ let new_val = Value::Number(n + 1.0);
1884
+ match self.scope.borrow_mut().assign(name.as_ref(), new_val.clone()) {
1885
+ Ok(true) => Ok(new_val),
1886
+ Ok(false) => Err(EvalError::Error(format!("Undefined variable: {}", name))),
1887
+ Err(e) => Err(EvalError::Error(e)),
1888
+ }
1889
+ }
1890
+ Expr::PrefixDec { name, .. } => {
1891
+ let v = self.scope.borrow().get(name.as_ref())
1892
+ .ok_or_else(|| EvalError::Error(format!("Undefined variable: {}", name)))?;
1893
+ let n = match &v {
1894
+ Value::Number(x) => *x,
1895
+ _ => return Err(EvalError::Error(format!("Cannot apply -- to {:?}", v))),
1896
+ };
1897
+ let new_val = Value::Number(n - 1.0);
1898
+ match self.scope.borrow_mut().assign(name.as_ref(), new_val.clone()) {
1899
+ Ok(true) => Ok(new_val),
1900
+ Ok(false) => Err(EvalError::Error(format!("Undefined variable: {}", name))),
1901
+ Err(e) => Err(EvalError::Error(e)),
1902
+ }
1903
+ }
1904
+ Expr::CompoundAssign { name, op, value, .. } => {
1905
+ let current = self.scope.borrow().get(name.as_ref())
1906
+ .ok_or_else(|| EvalError::Error(format!("Undefined variable: {}", name)))?;
1907
+ let rhs = self.eval_expr(value)?;
1908
+ let bin_op = match op {
1909
+ CompoundOp::Add => BinOp::Add,
1910
+ CompoundOp::Sub => BinOp::Sub,
1911
+ CompoundOp::Mul => BinOp::Mul,
1912
+ CompoundOp::Div => BinOp::Div,
1913
+ CompoundOp::Mod => BinOp::Mod,
1914
+ };
1915
+ let result = self.eval_binop(&current, bin_op, &rhs).map_err(EvalError::Error)?;
1916
+ match self.scope.borrow_mut().assign(name.as_ref(), result.clone()) {
1917
+ Ok(true) => Ok(result),
1918
+ Ok(false) => Err(EvalError::Error(format!("Undefined variable: {}", name))),
1919
+ Err(e) => Err(EvalError::Error(e)),
1920
+ }
1921
+ }
1922
+ Expr::LogicalAssign { name, op, value, .. } => {
1923
+ let current = self.scope.borrow().get(name.as_ref())
1924
+ .ok_or_else(|| EvalError::Error(format!("Undefined variable: {}", name)))?;
1925
+ let result = match op {
1926
+ LogicalAssignOp::AndAnd => {
1927
+ if current.is_truthy() {
1928
+ let rhs = self.eval_expr(value)?;
1929
+ let _ = self.scope.borrow_mut().assign(name.as_ref(), rhs.clone());
1930
+ rhs
1931
+ } else {
1932
+ current.clone()
1933
+ }
1934
+ }
1935
+ LogicalAssignOp::OrOr => {
1936
+ if !current.is_truthy() {
1937
+ let rhs = self.eval_expr(value)?;
1938
+ let _ = self.scope.borrow_mut().assign(name.as_ref(), rhs.clone());
1939
+ rhs
1940
+ } else {
1941
+ current.clone()
1942
+ }
1943
+ }
1944
+ LogicalAssignOp::Nullish => {
1945
+ if matches!(current, Value::Null) {
1946
+ let rhs = self.eval_expr(value)?;
1947
+ let _ = self.scope.borrow_mut().assign(name.as_ref(), rhs.clone());
1948
+ rhs
1949
+ } else {
1950
+ current.clone()
1951
+ }
1952
+ }
1953
+ };
1954
+ Ok(result)
1955
+ }
1956
+ Expr::MemberAssign { object, prop, value, .. } => {
1957
+ let obj_val = self.eval_expr(object)?;
1958
+ let val = self.eval_expr(value)?;
1959
+ match obj_val {
1960
+ Value::Object(map) => {
1961
+ map.borrow_mut()
1962
+ .strings
1963
+ .insert(Arc::clone(prop), val.clone());
1964
+ Ok(val)
1965
+ }
1966
+ _ => Err(EvalError::Error(format!(
1967
+ "Cannot assign property '{}' on non-object: {:?}",
1968
+ prop, obj_val
1969
+ ))),
1970
+ }
1971
+ }
1972
+ Expr::IndexAssign { object, index, value, .. } => {
1973
+ let obj_val = self.eval_expr(object)?;
1974
+ let idx_val = self.eval_expr(index)?;
1975
+ let val = self.eval_expr(value)?;
1976
+ match obj_val {
1977
+ Value::Array(arr) => {
1978
+ let idx = match &idx_val {
1979
+ Value::Number(n) => *n as usize,
1980
+ _ => return Err(EvalError::Error(format!(
1981
+ "Array index must be a number, got {:?}",
1982
+ idx_val
1983
+ ))),
1984
+ };
1985
+ let mut arr_mut = arr.borrow_mut();
1986
+ // Extend array if necessary (JS behavior)
1987
+ while arr_mut.len() <= idx {
1988
+ arr_mut.push(Value::Null);
1989
+ }
1990
+ arr_mut[idx] = val.clone();
1991
+ Ok(val)
1992
+ }
1993
+ Value::Object(_) => {
1994
+ eval_object_set(&obj_val, &idx_val, val.clone())
1995
+ .map_err(EvalError::Error)?;
1996
+ Ok(val)
1997
+ }
1998
+ _ => Err(EvalError::Error(format!(
1999
+ "Cannot assign index on non-array/object: {:?}",
2000
+ obj_val
2001
+ ))),
2002
+ }
2003
+ }
2004
+ Expr::ArrowFunction { params, body, .. } => {
2005
+ use tishlang_ast::ArrowBody;
2006
+ let formals: Arc<[FunParam]> = Arc::from(params.clone());
2007
+ let body_stmt = match body {
2008
+ ArrowBody::Expr(expr) => {
2009
+ // Expression body: wrap in implicit return
2010
+ Statement::Return {
2011
+ value: Some(expr.as_ref().clone()),
2012
+ span: Span { start: (0, 0), end: (0, 0) },
2013
+ }
2014
+ }
2015
+ ArrowBody::Block(stmt) => stmt.as_ref().clone(),
2016
+ };
2017
+ Ok(Value::Function {
2018
+ formals,
2019
+ rest_param: None,
2020
+ body: Arc::new(body_stmt),
2021
+ })
2022
+ }
2023
+ Expr::TemplateLiteral { quasis, exprs, .. } => {
2024
+ // Build the string by interleaving quasis and evaluated expressions
2025
+ let mut result = String::new();
2026
+ for (i, quasi) in quasis.iter().enumerate() {
2027
+ result.push_str(quasi);
2028
+ if i < exprs.len() {
2029
+ let val = self.eval_expr(&exprs[i])?;
2030
+ result.push_str(&val.to_string());
2031
+ }
2032
+ }
2033
+ Ok(Value::String(result.into()))
2034
+ }
2035
+ }
2036
+ }
2037
+
2038
+ fn eval_binop(&self, l: &Value, op: BinOp, r: &Value) -> Result<Value, String> {
2039
+ match op {
2040
+ BinOp::Add => match (l, r) {
2041
+ (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b)),
2042
+ (Value::String(a), Value::String(b)) => {
2043
+ let mut s = String::with_capacity(a.len() + b.len());
2044
+ s.push_str(a);
2045
+ s.push_str(b);
2046
+ Ok(Value::String(s.into()))
2047
+ }
2048
+ (Value::String(a), b) => {
2049
+ let b_str = b.to_string();
2050
+ let mut s = String::with_capacity(a.len() + b_str.len());
2051
+ s.push_str(a);
2052
+ s.push_str(&b_str);
2053
+ Ok(Value::String(s.into()))
2054
+ }
2055
+ (a, Value::String(b)) => {
2056
+ let a_str = a.to_string();
2057
+ let mut s = String::with_capacity(a_str.len() + b.len());
2058
+ s.push_str(&a_str);
2059
+ s.push_str(b);
2060
+ Ok(Value::String(s.into()))
2061
+ }
2062
+ _ => Err(format!("Cannot add {:?} and {:?}", l, r)),
2063
+ },
2064
+ BinOp::Sub => self.binop_number(l, r, |a, b| Value::Number(a - b)),
2065
+ BinOp::Mul => self.binop_number(l, r, |a, b| Value::Number(a * b)),
2066
+ BinOp::Div => self.binop_number(l, r, |a, b| Value::Number(a / b)),
2067
+ BinOp::Mod => self.binop_number(l, r, |a, b| Value::Number(a % b)),
2068
+ BinOp::Pow => self.binop_number(l, r, |a, b| Value::Number(a.powf(b))),
2069
+ BinOp::StrictEq => Ok(Value::Bool(l.strict_eq(r))),
2070
+ BinOp::StrictNe => Ok(Value::Bool(!l.strict_eq(r))),
2071
+ BinOp::Lt => self.binop_number(l, r, |a, b| Value::Bool(a < b)),
2072
+ BinOp::Le => self.binop_number(l, r, |a, b| Value::Bool(a <= b)),
2073
+ BinOp::Gt => self.binop_number(l, r, |a, b| Value::Bool(a > b)),
2074
+ BinOp::Ge => self.binop_number(l, r, |a, b| Value::Bool(a >= b)),
2075
+ BinOp::And => Ok(Value::Bool(l.is_truthy() && r.is_truthy())),
2076
+ BinOp::Or => Ok(Value::Bool(l.is_truthy() || r.is_truthy())),
2077
+ BinOp::BitAnd => self.binop_int32(l, r, |a, b| Value::Number((a & b) as f64)),
2078
+ BinOp::BitOr => self.binop_int32(l, r, |a, b| Value::Number((a | b) as f64)),
2079
+ BinOp::BitXor => self.binop_int32(l, r, |a, b| Value::Number((a ^ b) as f64)),
2080
+ BinOp::Shl => self.binop_int32(l, r, |a, b| Value::Number((a << b) as f64)),
2081
+ BinOp::Shr => self.binop_int32(l, r, |a, b| Value::Number((a >> b) as f64)),
2082
+ BinOp::In => {
2083
+ let ok = match r {
2084
+ Value::Object(_) => eval_object_has(r, l),
2085
+ Value::Array(arr) => {
2086
+ let key: Arc<str> = match l {
2087
+ Value::String(s) => Arc::clone(s),
2088
+ Value::Number(n) => n.to_string().into(),
2089
+ _ => {
2090
+ return Err(format!(
2091
+ "'in' requires string or number key on array, got {:?}",
2092
+ l
2093
+ ))
2094
+ }
2095
+ };
2096
+ key.as_ref() == "length"
2097
+ || key
2098
+ .parse::<usize>()
2099
+ .ok()
2100
+ .map(|i| i < arr.borrow().len())
2101
+ .unwrap_or(false)
2102
+ }
2103
+ _ => return Err(format!("'in' requires object or array, got {:?}", r)),
2104
+ };
2105
+ Ok(Value::Bool(ok))
2106
+ }
2107
+ BinOp::Eq | BinOp::Ne => Err("Loose equality not supported".to_string()),
2108
+ }
2109
+ }
2110
+
2111
+ /// Check if a function value is the common numeric sort comparator pattern.
2112
+ /// descending = false: checks for `(a, b) => a - b`
2113
+ /// descending = true: checks for `(a, b) => b - a`
2114
+ fn is_numeric_sort_comparator(f: &Value, descending: bool) -> bool {
2115
+ if let Value::Function {
2116
+ formals,
2117
+ body,
2118
+ rest_param,
2119
+ ..
2120
+ } = f
2121
+ {
2122
+ // Must have exactly 2 simple params, no defaults, no rest
2123
+ if formals.len() != 2 || rest_param.is_some() {
2124
+ return false;
2125
+ }
2126
+ let (param_a, param_b) = match (&formals[0], &formals[1]) {
2127
+ (FunParam::Simple(a), FunParam::Simple(b))
2128
+ if a.default.is_none() && b.default.is_none() =>
2129
+ {
2130
+ (&a.name, &b.name)
2131
+ }
2132
+ _ => return false,
2133
+ };
2134
+
2135
+ // Body must be a return of a - b (or b - a for descending)
2136
+
2137
+ // Check for both Statement::Return and Statement::ExprStmt (arrow implicit return)
2138
+ let expr = match body.as_ref() {
2139
+ Statement::Return { value: Some(e), .. } => e,
2140
+ Statement::ExprStmt { expr: e, .. } => e,
2141
+ _ => return false,
2142
+ };
2143
+
2144
+ // Check for binary subtraction
2145
+ if let Expr::Binary {
2146
+ left,
2147
+ op: BinOp::Sub,
2148
+ right,
2149
+ ..
2150
+ } = expr
2151
+ {
2152
+ // Check left is Ident(a) and right is Ident(b)
2153
+ let (expected_left, expected_right) = if descending {
2154
+ (param_b, param_a) // b - a
2155
+ } else {
2156
+ (param_a, param_b) // a - b
2157
+ };
2158
+
2159
+ if let (
2160
+ Expr::Ident {
2161
+ name: left_name, ..
2162
+ },
2163
+ Expr::Ident {
2164
+ name: right_name, ..
2165
+ },
2166
+ ) = (left.as_ref(), right.as_ref())
2167
+ {
2168
+ return left_name == expected_left && right_name == expected_right;
2169
+ }
2170
+ }
2171
+ }
2172
+ false
2173
+ }
2174
+
2175
+ fn to_int32(v: &Value) -> Result<i32, String> {
2176
+ match v {
2177
+ Value::Number(n) => Ok(*n as i32),
2178
+ _ => Err(format!("Bitwise operands must be numbers, got {:?}", v)),
2179
+ }
2180
+ }
2181
+
2182
+ fn binop_int32<F>(&self, l: &Value, r: &Value, f: F) -> Result<Value, String>
2183
+ where
2184
+ F: FnOnce(i32, i32) -> Value,
2185
+ {
2186
+ let a = Self::to_int32(l)?;
2187
+ let b = Self::to_int32(r)?;
2188
+ Ok(f(a, b))
2189
+ }
2190
+
2191
+ fn binop_number<F>(&self, l: &Value, r: &Value, f: F) -> Result<Value, String>
2192
+ where
2193
+ F: FnOnce(f64, f64) -> Value,
2194
+ {
2195
+ match (l, r) {
2196
+ (Value::Number(a), Value::Number(b)) => Ok(f(*a, *b)),
2197
+ _ => Err(format!("Expected numbers, got {:?} and {:?}", l, r)),
2198
+ }
2199
+ }
2200
+
2201
+ fn eval_unary(&self, op: UnaryOp, v: &Value) -> Result<Value, String> {
2202
+ match op {
2203
+ UnaryOp::Not => Ok(Value::Bool(!v.is_truthy())),
2204
+ UnaryOp::Neg => match v {
2205
+ Value::Number(n) => Ok(Value::Number(-n)),
2206
+ _ => Err(format!("Cannot negate {:?}", v)),
2207
+ },
2208
+ UnaryOp::Pos => match v {
2209
+ Value::Number(n) => Ok(Value::Number(*n)),
2210
+ _ => Err(format!("Cannot apply unary + to {:?}", v)),
2211
+ },
2212
+ UnaryOp::BitNot => {
2213
+ let n = Self::to_int32(v)?;
2214
+ Ok(Value::Number((!n) as f64))
2215
+ }
2216
+ UnaryOp::Void => Ok(Value::Null),
2217
+ }
2218
+ }
2219
+
2220
+ /// Optimized callback invocation for array methods.
2221
+ /// Creates a reusable scope that can be updated for each iteration.
2222
+ fn create_callback_scope(
2223
+ &self,
2224
+ f: &Value,
2225
+ ) -> Option<(Rc<RefCell<Scope>>, Arc<[Arc<str>]>, Arc<Statement>)> {
2226
+ if let Value::Function {
2227
+ formals,
2228
+ body,
2229
+ rest_param,
2230
+ ..
2231
+ } = f
2232
+ {
2233
+ if rest_param.is_some() {
2234
+ return None;
2235
+ }
2236
+ for fp in formals.iter() {
2237
+ match fp {
2238
+ FunParam::Simple(tp) if tp.default.is_none() => {}
2239
+ _ => return None,
2240
+ }
2241
+ }
2242
+ let scope = Scope::child(Rc::clone(&self.scope));
2243
+ {
2244
+ let mut s = scope.borrow_mut();
2245
+ for fp in formals.iter() {
2246
+ for n in fp.bound_names() {
2247
+ s.set(n, Value::Null, true);
2248
+ }
2249
+ }
2250
+ }
2251
+ let flat_names: Arc<[Arc<str>]> = Arc::from(
2252
+ formals
2253
+ .iter()
2254
+ .flat_map(|fp| fp.bound_names())
2255
+ .collect::<Vec<_>>(),
2256
+ );
2257
+ return Some((scope, flat_names, Arc::clone(body)));
2258
+ }
2259
+ None
2260
+ }
2261
+
2262
+ /// Fast callback invocation that reuses an existing scope.
2263
+ fn call_with_scope(
2264
+ &self,
2265
+ scope: &Rc<RefCell<Scope>>,
2266
+ params: &[Arc<str>],
2267
+ body: &Statement,
2268
+ args: &[Value],
2269
+ ) -> Result<Value, EvalError> {
2270
+ {
2271
+ let mut s = scope.borrow_mut();
2272
+ for (i, p) in params.iter().enumerate() {
2273
+ let val = args.get(i).cloned().unwrap_or(Value::Null);
2274
+ // Direct assignment - we know these vars exist and are mutable
2275
+ if let Some(existing) = s.vars.get_mut(p.as_ref()) {
2276
+ *existing = val;
2277
+ }
2278
+ }
2279
+ }
2280
+ let mut eval = Evaluator {
2281
+ scope: Rc::clone(scope),
2282
+ module_cache: Rc::clone(&self.module_cache),
2283
+ current_dir: RefCell::new(self.current_dir.borrow().clone()),
2284
+ virtual_builtins: Rc::clone(&self.virtual_builtins),
2285
+ };
2286
+ match eval.eval_statement(body) {
2287
+ Ok(v) => Ok(v),
2288
+ Err(EvalError::Return(v)) => Ok(v),
2289
+ Err(e) => Err(e),
2290
+ }
2291
+ }
2292
+
2293
+ /// Try to evaluate a simple callback expression directly without creating a scope.
2294
+ /// Returns Some(result) for simple patterns like `x => x * 2` or `x => x > 5`.
2295
+ fn eval_simple_callback(&self, f: &Value, args: &[Value]) -> Option<Result<Value, EvalError>> {
2296
+ if let Value::Function {
2297
+ formals,
2298
+ body,
2299
+ rest_param,
2300
+ ..
2301
+ } = f
2302
+ {
2303
+ if formals.len() != 1 || rest_param.is_some() {
2304
+ return None;
2305
+ }
2306
+ let param_name = match &formals[0] {
2307
+ FunParam::Simple(tp) if tp.default.is_none() => &tp.name,
2308
+ _ => return None,
2309
+ };
2310
+ let arg = args.first().cloned().unwrap_or(Value::Null);
2311
+
2312
+ // Get the expression from the body
2313
+ let expr = match body.as_ref() {
2314
+ Statement::Return { value: Some(e), .. } => e,
2315
+ Statement::ExprStmt { expr: e, .. } => e,
2316
+ _ => return None,
2317
+ };
2318
+
2319
+ // Fast path for common patterns
2320
+ match expr {
2321
+ // x * constant or x + constant, etc.
2322
+ Expr::Binary {
2323
+ left, op, right, ..
2324
+ } => {
2325
+ let left_val = self.eval_simple_operand(left, param_name, &arg)?;
2326
+ let right_val = self.eval_simple_operand(right, param_name, &arg)?;
2327
+ Some(
2328
+ self.eval_binop(&left_val, *op, &right_val)
2329
+ .map_err(EvalError::Error),
2330
+ )
2331
+ }
2332
+ // Just return the parameter
2333
+ Expr::Ident { name, .. } if name == param_name => Some(Ok(arg)),
2334
+ // Property access: x.prop
2335
+ Expr::Member {
2336
+ object,
2337
+ prop,
2338
+ optional,
2339
+ ..
2340
+ } => {
2341
+ if let Expr::Ident { name, .. } = object.as_ref() {
2342
+ if name == param_name {
2343
+ return self.eval_simple_member(&arg, prop, *optional);
2344
+ }
2345
+ }
2346
+ None
2347
+ }
2348
+ _ => None,
2349
+ }
2350
+ } else {
2351
+ None
2352
+ }
2353
+ }
2354
+
2355
+ /// Evaluate a simple operand (identifier or literal).
2356
+ fn eval_simple_operand(
2357
+ &self,
2358
+ expr: &Expr,
2359
+ param_name: &Arc<str>,
2360
+ param_val: &Value,
2361
+ ) -> Option<Value> {
2362
+ match expr {
2363
+ Expr::Ident { name, .. } if name == param_name => Some(param_val.clone()),
2364
+ Expr::Literal { value, .. } => match value {
2365
+ Literal::Number(n) => Some(Value::Number(*n)),
2366
+ Literal::String(s) => Some(Value::String(Arc::clone(s))),
2367
+ Literal::Bool(b) => Some(Value::Bool(*b)),
2368
+ Literal::Null => Some(Value::Null),
2369
+ },
2370
+ _ => None,
2371
+ }
2372
+ }
2373
+
2374
+ /// Evaluate simple member access.
2375
+ fn eval_simple_member(
2376
+ &self,
2377
+ obj: &Value,
2378
+ property: &MemberProp,
2379
+ _optional: bool,
2380
+ ) -> Option<Result<Value, EvalError>> {
2381
+ match property {
2382
+ MemberProp::Name { name, .. } => match obj {
2383
+ Value::Object(o) => {
2384
+ let result = o
2385
+ .borrow()
2386
+ .strings
2387
+ .get(name.as_ref())
2388
+ .cloned()
2389
+ .unwrap_or(Value::Null);
2390
+ Some(Ok(result))
2391
+ }
2392
+ Value::Array(arr) if name.as_ref() == "length" => {
2393
+ Some(Ok(Value::Number(arr.borrow().len() as f64)))
2394
+ }
2395
+ _ => None,
2396
+ },
2397
+ _ => None,
2398
+ }
2399
+ }
2400
+
2401
+ /// Host `new`: `__construct` on objects; otherwise same callables as `call_func`, else null.
2402
+ fn construct_value(&self, callee: &Value, args: &[Value]) -> Result<Value, EvalError> {
2403
+ if let Value::Object(o) = callee {
2404
+ if let Some(ctor) = o
2405
+ .borrow()
2406
+ .strings
2407
+ .get("__construct")
2408
+ .cloned()
2409
+ {
2410
+ return self.call_func(&ctor, args);
2411
+ }
2412
+ }
2413
+ match callee {
2414
+ Value::Native(_) | Value::Function { .. } | Value::CoreFn(_) => {
2415
+ self.call_func(callee, args)
2416
+ }
2417
+ #[cfg(feature = "http")]
2418
+ Value::PromiseConstructor | Value::Serve | Value::BoundPromiseMethod(_, _) => {
2419
+ self.call_func(callee, args)
2420
+ }
2421
+ #[cfg(feature = "timers")]
2422
+ Value::TimerBuiltin(_) => self.call_func(callee, args),
2423
+ Value::OpaqueMethod(_, _) => self.call_func(callee, args),
2424
+ _ => Ok(Value::Null),
2425
+ }
2426
+ }
2427
+
2428
+ fn call_func(&self, f: &Value, args: &[Value]) -> Result<Value, EvalError> {
2429
+ match f {
2430
+ Value::Object(o) => {
2431
+ if let Some(call) = o.borrow().strings.get("__call").cloned() {
2432
+ return self.call_func(&call, args);
2433
+ }
2434
+ Err(EvalError::Error("Not a function".to_string()))
2435
+ }
2436
+ Value::Native(native_fn) => native_fn(args).map_err(EvalError::Error),
2437
+ #[cfg(feature = "http")]
2438
+ Value::PromiseResolver(r) => {
2439
+ let value = args.first().cloned().unwrap_or(Value::Null);
2440
+ let (val, is_fulfilled, reactions) =
2441
+ crate::promise::settle_promise(r, value, r.is_resolve)
2442
+ .map_err(EvalError::Error)?;
2443
+ for reaction in reactions {
2444
+ match reaction {
2445
+ crate::promise::Reaction::Then(
2446
+ on_fulfilled,
2447
+ on_rejected,
2448
+ ref resolve,
2449
+ ref reject,
2450
+ ) => {
2451
+ let handler_result = if is_fulfilled {
2452
+ if let Some(ref h) = on_fulfilled {
2453
+ self.call_func(h, &[val.clone()])
2454
+ } else {
2455
+ Ok(val.clone())
2456
+ }
2457
+ } else {
2458
+ if let Some(ref h) = on_rejected {
2459
+ self.call_func(h, &[val.clone()])
2460
+ } else {
2461
+ Err(EvalError::Throw(val.clone()))
2462
+ }
2463
+ };
2464
+ match handler_result {
2465
+ Ok(v) => {
2466
+ crate::promise::settle_promise(resolve, v, true)
2467
+ .map_err(EvalError::Error)?;
2468
+ }
2469
+ Err(EvalError::Throw(v)) => {
2470
+ crate::promise::settle_promise(reject, v, false)
2471
+ .map_err(EvalError::Error)?;
2472
+ }
2473
+ Err(e) => return Err(e),
2474
+ }
2475
+ }
2476
+ crate::promise::Reaction::Finally(on_finally, ref resolve, ref reject) => {
2477
+ let _ = self.call_func(&on_finally, &[]);
2478
+ if is_fulfilled {
2479
+ crate::promise::settle_promise(resolve, val.clone(), true)
2480
+ .map_err(EvalError::Error)?;
2481
+ } else {
2482
+ crate::promise::settle_promise(reject, val.clone(), false)
2483
+ .map_err(EvalError::Error)?;
2484
+ }
2485
+ }
2486
+ }
2487
+ }
2488
+ Ok(Value::Null)
2489
+ }
2490
+ #[cfg(feature = "http")]
2491
+ Value::PromiseConstructor => {
2492
+ let executor = args.first().ok_or_else(|| {
2493
+ EvalError::Error("Promise requires an executor function".to_string())
2494
+ })?;
2495
+ let (promise, resolve, reject) = crate::promise::create_promise();
2496
+ self.call_func(executor, &[resolve, reject])?;
2497
+ Ok(promise)
2498
+ }
2499
+ #[cfg(feature = "http")]
2500
+ Value::Serve => self.run_http_server(args),
2501
+ Value::CoreFn(f) => {
2502
+ let ca: Result<Vec<tishlang_core::Value>, String> = args
2503
+ .iter()
2504
+ .map(crate::value_convert::eval_to_core)
2505
+ .collect();
2506
+ let ca = ca.map_err(EvalError::Error)?;
2507
+ Ok(crate::value_convert::core_to_eval(f(&ca)))
2508
+ }
2509
+ #[cfg(feature = "regex")]
2510
+ Value::RegExp(_) => Err(EvalError::Error("RegExp is not callable".to_string())),
2511
+ #[cfg(feature = "http")]
2512
+ Value::BoundPromiseMethod(promise_ref, method) => {
2513
+ self.run_promise_method(promise_ref, method.as_ref(), args)
2514
+ }
2515
+ #[cfg(feature = "timers")]
2516
+ Value::TimerBuiltin(name) => self.run_timer_builtin(name.as_ref(), args),
2517
+ Value::OpaqueMethod(opaque, method_name) => {
2518
+ let method = opaque.get_method(method_name.as_ref()).ok_or_else(|| {
2519
+ EvalError::Error(format!(
2520
+ "Method {} not found on {}",
2521
+ method_name,
2522
+ opaque.type_name()
2523
+ ))
2524
+ })?;
2525
+ let core_args: Result<Vec<tishlang_core::Value>, String> = args
2526
+ .iter()
2527
+ .map(crate::value_convert::eval_to_core)
2528
+ .collect();
2529
+ let core_args = core_args.map_err(EvalError::Error)?;
2530
+ let result = method(&core_args);
2531
+ Ok(crate::value_convert::core_to_eval(result))
2532
+ }
2533
+ Value::Function {
2534
+ formals,
2535
+ rest_param,
2536
+ body,
2537
+ } => {
2538
+ let scope = Scope::child(Rc::clone(&self.scope));
2539
+ {
2540
+ let mut s = scope.borrow_mut();
2541
+ for (i, formal) in formals.iter().enumerate() {
2542
+ let val = match args.get(i) {
2543
+ Some(v) => v.clone(),
2544
+ None => {
2545
+ let def = match formal {
2546
+ FunParam::Simple(tp) => tp.default.as_ref(),
2547
+ FunParam::Destructure { default, .. } => default.as_ref(),
2548
+ };
2549
+ if let Some(default_expr) = def {
2550
+ drop(s);
2551
+ let default_val = self.eval_expr(default_expr)?;
2552
+ s = scope.borrow_mut();
2553
+ default_val
2554
+ } else {
2555
+ Value::Null
2556
+ }
2557
+ }
2558
+ };
2559
+ match formal {
2560
+ FunParam::Simple(tp) => {
2561
+ s.set(Arc::clone(&tp.name), val, true);
2562
+ }
2563
+ FunParam::Destructure { pattern, .. } => {
2564
+ drop(s);
2565
+ Self::bind_destruct_pattern_scoped(&scope, pattern, &val, true)?;
2566
+ s = scope.borrow_mut();
2567
+ }
2568
+ }
2569
+ }
2570
+ if let Some(ref rest_name) = rest_param {
2571
+ let rest_vals: Vec<Value> =
2572
+ args.iter().skip(formals.len()).cloned().collect();
2573
+ s.set(
2574
+ Arc::clone(rest_name),
2575
+ Value::Array(Rc::new(RefCell::new(rest_vals))),
2576
+ true,
2577
+ );
2578
+ }
2579
+ }
2580
+ let mut eval = Evaluator {
2581
+ scope,
2582
+ module_cache: Rc::clone(&self.module_cache),
2583
+ current_dir: RefCell::new(self.current_dir.borrow().clone()),
2584
+ virtual_builtins: Rc::clone(&self.virtual_builtins),
2585
+ };
2586
+ match eval.eval_statement(body) {
2587
+ Ok(v) => Ok(v),
2588
+ Err(EvalError::Return(v)) => Ok(v),
2589
+ Err(EvalError::Throw(v)) => Err(EvalError::Throw(v)),
2590
+ Err(EvalError::Error(s)) => Err(EvalError::Error(s)),
2591
+ Err(EvalError::Break) => {
2592
+ Err(EvalError::Error("break outside loop".to_string()))
2593
+ }
2594
+ Err(EvalError::Continue) => {
2595
+ Err(EvalError::Error("continue outside loop".to_string()))
2596
+ }
2597
+ }
2598
+ }
2599
+ _ => Err(EvalError::Error("Not a function".to_string())),
2600
+ }
2601
+ }
2602
+
2603
+ #[cfg(feature = "http")]
2604
+ fn run_promise_method(
2605
+ &self,
2606
+ promise_ref: &crate::promise::PromiseRef,
2607
+ method: &str,
2608
+ args: &[Value],
2609
+ ) -> Result<Value, EvalError> {
2610
+ match method {
2611
+ "then" => {
2612
+ self.run_promise_then_core(promise_ref, args.first().cloned(), args.get(1).cloned())
2613
+ }
2614
+ "catch" => self.run_promise_then_core(promise_ref, None, args.first().cloned()),
2615
+ "finally" => self.run_promise_finally(promise_ref, args.first().cloned()),
2616
+ _ => Err(EvalError::Error(format!(
2617
+ "Unknown promise method: {}",
2618
+ method
2619
+ ))),
2620
+ }
2621
+ }
2622
+
2623
+ #[cfg(feature = "http")]
2624
+ fn run_promise_finally(
2625
+ &self,
2626
+ promise_ref: &crate::promise::PromiseRef,
2627
+ on_finally: Option<Value>,
2628
+ ) -> Result<Value, EvalError> {
2629
+ let (promise, resolve_val, reject_val) = crate::promise::create_promise();
2630
+ let (resolve, reject) = crate::promise::extract_resolvers(&resolve_val, &reject_val);
2631
+ let state = &promise_ref.state;
2632
+ {
2633
+ let s = state.borrow();
2634
+ match &*s {
2635
+ crate::promise::PromiseState::Fulfilled(v) => {
2636
+ let v = v.clone();
2637
+ drop(s);
2638
+ if let Some(ref f) = on_finally {
2639
+ let _ = self.call_func(f, &[]);
2640
+ }
2641
+ crate::promise::settle_promise(&resolve, v, true).map_err(EvalError::Error)?;
2642
+ }
2643
+ crate::promise::PromiseState::Rejected(v) => {
2644
+ let v = v.clone();
2645
+ drop(s);
2646
+ if let Some(ref f) = on_finally {
2647
+ let _ = self.call_func(f, &[]);
2648
+ }
2649
+ crate::promise::settle_promise(&reject, v, false).map_err(EvalError::Error)?;
2650
+ }
2651
+ crate::promise::PromiseState::Pending { .. } => {
2652
+ let reaction = if let Some(ref f) = on_finally {
2653
+ crate::promise::Reaction::Finally(f.clone(), resolve, reject)
2654
+ } else {
2655
+ crate::promise::Reaction::Then(None, None, resolve, reject)
2656
+ };
2657
+ crate::promise::add_reaction(state, reaction);
2658
+ }
2659
+ }
2660
+ }
2661
+ Ok(promise)
2662
+ }
2663
+
2664
+ #[cfg(feature = "http")]
2665
+ fn run_promise_then_core(
2666
+ &self,
2667
+ promise_ref: &crate::promise::PromiseRef,
2668
+ on_fulfilled: Option<Value>,
2669
+ on_rejected: Option<Value>,
2670
+ ) -> Result<Value, EvalError> {
2671
+ let (promise, resolve_val, reject_val) = crate::promise::create_promise();
2672
+ let (resolve, reject) = crate::promise::extract_resolvers(&resolve_val, &reject_val);
2673
+ let state = &promise_ref.state;
2674
+ {
2675
+ let s = state.borrow();
2676
+ match &*s {
2677
+ crate::promise::PromiseState::Fulfilled(v) => {
2678
+ let v = v.clone();
2679
+ drop(s);
2680
+ let result = if let Some(ref h) = on_fulfilled {
2681
+ self.call_func(h, &[v])
2682
+ } else {
2683
+ Ok(v)
2684
+ };
2685
+ match result {
2686
+ Ok(val) => {
2687
+ crate::promise::settle_promise(&resolve, val, true)
2688
+ .map_err(EvalError::Error)?;
2689
+ }
2690
+ Err(EvalError::Throw(val)) => {
2691
+ crate::promise::settle_promise(&reject, val, false)
2692
+ .map_err(EvalError::Error)?;
2693
+ }
2694
+ Err(e) => return Err(e),
2695
+ }
2696
+ }
2697
+ crate::promise::PromiseState::Rejected(v) => {
2698
+ let v = v.clone();
2699
+ drop(s);
2700
+ let result = if let Some(ref h) = on_rejected {
2701
+ self.call_func(h, &[v.clone()])
2702
+ } else {
2703
+ Err(EvalError::Throw(v))
2704
+ };
2705
+ match result {
2706
+ Ok(val) => {
2707
+ crate::promise::settle_promise(&resolve, val, true)
2708
+ .map_err(EvalError::Error)?;
2709
+ }
2710
+ Err(EvalError::Throw(val)) => {
2711
+ crate::promise::settle_promise(&reject, val, false)
2712
+ .map_err(EvalError::Error)?;
2713
+ }
2714
+ Err(e) => return Err(e),
2715
+ }
2716
+ }
2717
+ crate::promise::PromiseState::Pending { .. } => {
2718
+ crate::promise::add_reaction(
2719
+ state,
2720
+ crate::promise::Reaction::Then(
2721
+ on_fulfilled,
2722
+ on_rejected,
2723
+ resolve.clone(),
2724
+ reject.clone(),
2725
+ ),
2726
+ );
2727
+ }
2728
+ }
2729
+ }
2730
+ Ok(promise)
2731
+ }
2732
+
2733
+ #[cfg(feature = "timers")]
2734
+ fn run_timer_builtin(&self, name: &str, args: &[Value]) -> Result<Value, EvalError> {
2735
+ let callback = args
2736
+ .first()
2737
+ .ok_or_else(|| EvalError::Error(format!("{} requires a callback", name)))?
2738
+ .clone();
2739
+ let delay_ms = args
2740
+ .get(1)
2741
+ .and_then(|v| v.as_number())
2742
+ .unwrap_or(0.0)
2743
+ .max(0.0) as u64;
2744
+ let extra_args: Vec<Value> = args.iter().skip(2).cloned().collect();
2745
+
2746
+ let id = match name {
2747
+ "setTimeout" => crate::timers::setTimeout(callback, extra_args, delay_ms),
2748
+ "setInterval" => crate::timers::setInterval(callback, extra_args, delay_ms),
2749
+ _ => return Err(EvalError::Error(format!("Unknown timer: {}", name))),
2750
+ };
2751
+ Ok(Value::Number(id as f64))
2752
+ }
2753
+
2754
+ #[cfg(feature = "timers")]
2755
+ fn clear_timeout_native(args: &[Value]) -> Result<Value, String> {
2756
+ if let Some(Value::Number(n)) = args.first() {
2757
+ crate::timers::clearTimer(*n as u64);
2758
+ }
2759
+ Ok(Value::Null)
2760
+ }
2761
+
2762
+ #[cfg(feature = "timers")]
2763
+ fn clear_interval_native(args: &[Value]) -> Result<Value, String> {
2764
+ if let Some(Value::Number(n)) = args.first() {
2765
+ crate::timers::clearTimer(*n as u64);
2766
+ }
2767
+ Ok(Value::Null)
2768
+ }
2769
+
2770
+ /// Run all due timer callbacks. Called after the script completes so setTimeout/setInterval
2771
+ /// callbacks run without blocking the main script. Loops until no timers are due.
2772
+ #[cfg(feature = "timers")]
2773
+ pub fn run_timer_phase(&mut self) -> Result<(), String> {
2774
+ const MAX_ITERATIONS: u32 = 1_000_000; // avoid infinite loop if setInterval never cleared
2775
+ let mut iterations = 0;
2776
+ while crate::timers::has_pending_timers() && iterations < MAX_ITERATIONS {
2777
+ iterations += 1;
2778
+ let due = crate::timers::take_due_timers();
2779
+ if due.is_empty() {
2780
+ // None due yet; sleep until next timer
2781
+ let next = crate::timers::next_due_instant();
2782
+ if let Some(instant) = next {
2783
+ let now = std::time::Instant::now();
2784
+ if instant > now {
2785
+ std::thread::sleep(instant.duration_since(now));
2786
+ }
2787
+ }
2788
+ continue;
2789
+ }
2790
+ for (id, callback, args, interval_ms) in due {
2791
+ self.call_func(&callback, &args).map_err(|e| match e {
2792
+ EvalError::Error(s) => s,
2793
+ EvalError::Throw(v) => v.to_string(),
2794
+ _ => "timer callback error".to_string(),
2795
+ })?;
2796
+ if interval_ms > 0 {
2797
+ crate::timers::re_register_interval(id, callback, args, interval_ms);
2798
+ }
2799
+ }
2800
+ }
2801
+ Ok(())
2802
+ }
2803
+
2804
+ #[cfg(feature = "http")]
2805
+ fn run_http_server(&self, args: &[Value]) -> Result<Value, EvalError> {
2806
+ use std::io::Write;
2807
+
2808
+ let port = match args.first() {
2809
+ Some(Value::Number(n)) => *n as u16,
2810
+ _ => return Err(EvalError::Error("serve requires a port number".to_string())),
2811
+ };
2812
+
2813
+ let max_requests: Option<usize> = args.get(2).and_then(|v| match v {
2814
+ Value::Number(n) if *n >= 1.0 => Some(*n as usize),
2815
+ _ => None,
2816
+ });
2817
+
2818
+ let handler = match args.get(1) {
2819
+ Some(f @ Value::Function { .. }) | Some(f @ Value::Native(_)) => f.clone(),
2820
+ _ => {
2821
+ return Err(EvalError::Error(
2822
+ "serve requires a handler function".to_string(),
2823
+ ))
2824
+ }
2825
+ };
2826
+
2827
+ let server = crate::http::create_server(port).map_err(EvalError::Error)?;
2828
+ println!("Server listening on http://0.0.0.0:{}", port);
2829
+
2830
+ if max_requests == Some(1) {
2831
+ let port = port;
2832
+ std::thread::spawn(move || {
2833
+ std::thread::sleep(std::time::Duration::from_millis(50));
2834
+ if let Ok(mut stream) = std::net::TcpStream::connect(format!("127.0.0.1:{}", port))
2835
+ {
2836
+ let _ = stream.write_all(
2837
+ b"GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n",
2838
+ );
2839
+ let _ = stream.shutdown(std::net::Shutdown::Write);
2840
+ }
2841
+ });
2842
+ }
2843
+
2844
+ let mut count = 0usize;
2845
+ for mut request in server.incoming_requests() {
2846
+ let req_value = crate::http::request_to_value(&mut request);
2847
+
2848
+ let response_value = match self.call_func(&handler, &[req_value]) {
2849
+ Ok(v) => v,
2850
+ Err(EvalError::Throw(v)) => {
2851
+ let mut err_obj: PropMap = PropMap::with_capacity(2);
2852
+ err_obj.insert(Arc::from("status"), Value::Number(500.0));
2853
+ err_obj.insert(Arc::from("body"), Value::String(v.to_string().into()));
2854
+ Value::object(err_obj)
2855
+ }
2856
+ Err(e) => {
2857
+ let mut err_obj: PropMap = PropMap::with_capacity(2);
2858
+ err_obj.insert(Arc::from("status"), Value::Number(500.0));
2859
+ err_obj.insert(Arc::from("body"), Value::String(e.to_string().into()));
2860
+ Value::object(err_obj)
2861
+ }
2862
+ };
2863
+
2864
+ if let Some((status, headers, file_path)) =
2865
+ crate::http::extract_file_from_response(&response_value)
2866
+ {
2867
+ crate::http::send_file_response(request, status, headers, file_path);
2868
+ } else {
2869
+ let (status, headers, body) = crate::http::value_to_response(&response_value);
2870
+ crate::http::send_response(request, status, headers, body);
2871
+ }
2872
+ count += 1;
2873
+ if max_requests.map(|m| count >= m).unwrap_or(false) {
2874
+ break;
2875
+ }
2876
+ }
2877
+
2878
+ Ok(Value::Null)
2879
+ }
2880
+
2881
+ fn eval_call_args(&self, args: &[tishlang_ast::CallArg]) -> Result<Vec<Value>, EvalError> {
2882
+ let mut result = Vec::with_capacity(args.len());
2883
+ for arg in args {
2884
+ match arg {
2885
+ tishlang_ast::CallArg::Expr(e) => {
2886
+ result.push(self.eval_expr(e)?);
2887
+ }
2888
+ tishlang_ast::CallArg::Spread(e) => {
2889
+ let spread_val = self.eval_expr(e)?;
2890
+ if let Value::Array(arr) = spread_val {
2891
+ result.extend(arr.borrow().iter().cloned());
2892
+ }
2893
+ }
2894
+ }
2895
+ }
2896
+ Ok(result)
2897
+ }
2898
+
2899
+ fn bind_destruct_pattern_scoped(
2900
+ scope: &Rc<RefCell<Scope>>,
2901
+ pattern: &tishlang_ast::DestructPattern,
2902
+ value: &Value,
2903
+ mutable: bool,
2904
+ ) -> Result<(), EvalError> {
2905
+ match pattern {
2906
+ tishlang_ast::DestructPattern::Array(elements) => {
2907
+ let arr = match value {
2908
+ Value::Array(a) => a.borrow().clone(),
2909
+ _ => {
2910
+ return Err(EvalError::Error(
2911
+ "Cannot destructure non-array value".to_string(),
2912
+ ))
2913
+ }
2914
+ };
2915
+
2916
+ for (i, elem) in elements.iter().enumerate() {
2917
+ if let Some(el) = elem {
2918
+ match el {
2919
+ tishlang_ast::DestructElement::Ident(name, _) => {
2920
+ let val = arr.get(i).cloned().unwrap_or(Value::Null);
2921
+ scope.borrow_mut().set(Arc::clone(name), val, mutable);
2922
+ }
2923
+ tishlang_ast::DestructElement::Pattern(nested) => {
2924
+ let val = arr.get(i).cloned().unwrap_or(Value::Null);
2925
+ Self::bind_destruct_pattern_scoped(scope, nested, &val, mutable)?;
2926
+ }
2927
+ tishlang_ast::DestructElement::Rest(name, _) => {
2928
+ let rest: Vec<Value> = arr.iter().skip(i).cloned().collect();
2929
+ scope.borrow_mut().set(
2930
+ Arc::clone(name),
2931
+ Value::Array(Rc::new(RefCell::new(rest))),
2932
+ mutable,
2933
+ );
2934
+ break;
2935
+ }
2936
+ }
2937
+ }
2938
+ }
2939
+ }
2940
+ tishlang_ast::DestructPattern::Object(props) => {
2941
+ let obj = match value {
2942
+ Value::Object(o) => o.borrow().clone(),
2943
+ _ => {
2944
+ return Err(EvalError::Error(
2945
+ "Cannot destructure non-object value".to_string(),
2946
+ ))
2947
+ }
2948
+ };
2949
+
2950
+ for prop in props {
2951
+ let val = obj
2952
+ .strings
2953
+ .get(prop.key.as_ref())
2954
+ .cloned()
2955
+ .unwrap_or(Value::Null);
2956
+ match &prop.value {
2957
+ tishlang_ast::DestructElement::Ident(name, _) => {
2958
+ scope.borrow_mut().set(Arc::clone(name), val, mutable);
2959
+ }
2960
+ tishlang_ast::DestructElement::Pattern(nested) => {
2961
+ Self::bind_destruct_pattern_scoped(scope, nested, &val, mutable)?;
2962
+ }
2963
+ tishlang_ast::DestructElement::Rest(_, _) => {
2964
+ return Err(EvalError::Error(
2965
+ "Rest not supported in object destructuring".to_string(),
2966
+ ));
2967
+ }
2968
+ }
2969
+ }
2970
+ }
2971
+ }
2972
+ Ok(())
2973
+ }
2974
+
2975
+ fn bind_destruct_pattern(
2976
+ &mut self,
2977
+ pattern: &tishlang_ast::DestructPattern,
2978
+ value: &Value,
2979
+ mutable: bool,
2980
+ ) -> Result<(), EvalError> {
2981
+ Self::bind_destruct_pattern_scoped(&self.scope, pattern, value, mutable)
2982
+ }
2983
+
2984
+ /// `String.prototype.lastIndexOf` (interpreter). Kept as a helper so dispatch cannot fall
2985
+ /// through to [`Self::get_prop`] + [`Self::call_func`] for string receivers.
2986
+ fn string_last_index_of_eval(arg_vals: &[Value], receiver: &Arc<str>) -> Value {
2987
+ let search = match arg_vals.first() {
2988
+ Some(Value::String(ss)) => ss.as_ref(),
2989
+ _ => return Value::Number(-1.0),
2990
+ };
2991
+ let position_core: tishlang_core::Value = match arg_vals.get(1) {
2992
+ None => tishlang_core::Value::Number(f64::INFINITY),
2993
+ Some(Value::Null) => tishlang_core::Value::Null,
2994
+ Some(Value::Number(n)) => tishlang_core::Value::Number(*n),
2995
+ Some(Value::Bool(b)) => tishlang_core::Value::Bool(*b),
2996
+ Some(_) => tishlang_core::Value::Number(0.0),
2997
+ };
2998
+ let out =
2999
+ tishlang_builtins::string::last_index_of_str(receiver.as_ref(), search, &position_core);
3000
+ match out {
3001
+ tishlang_core::Value::Number(n) => Value::Number(n),
3002
+ _ => Value::Number(-1.0),
3003
+ }
3004
+ }
3005
+
3006
+ fn get_prop(&self, obj: &Value, key: &str) -> Result<Value, String> {
3007
+ match obj {
3008
+ Value::Object(map) => Ok(map
3009
+ .borrow()
3010
+ .strings
3011
+ .get(key)
3012
+ .cloned()
3013
+ .unwrap_or(Value::Null)),
3014
+ Value::Array(arr) => {
3015
+ if key == "length" {
3016
+ Ok(Value::Number(arr.borrow().len() as f64))
3017
+ } else if let Ok(idx) = key.parse::<usize>() {
3018
+ Ok(arr.borrow().get(idx).cloned().unwrap_or(Value::Null))
3019
+ } else {
3020
+ Ok(Value::Null)
3021
+ }
3022
+ }
3023
+ Value::String(s) => {
3024
+ if key == "length" {
3025
+ Ok(Value::Number(s.chars().count() as f64))
3026
+ } else {
3027
+ Ok(Value::Null)
3028
+ }
3029
+ }
3030
+ #[cfg(feature = "http")]
3031
+ Value::Promise(promise_ref) => match key {
3032
+ "then" => Ok(Value::BoundPromiseMethod(
3033
+ promise_ref.clone(),
3034
+ Arc::from("then"),
3035
+ )),
3036
+ "catch" => Ok(Value::BoundPromiseMethod(
3037
+ promise_ref.clone(),
3038
+ Arc::from("catch"),
3039
+ )),
3040
+ "finally" => Ok(Value::BoundPromiseMethod(
3041
+ promise_ref.clone(),
3042
+ Arc::from("finally"),
3043
+ )),
3044
+ _ => Ok(Value::Null),
3045
+ },
3046
+ #[cfg(feature = "http")]
3047
+ Value::CorePromise(_) => Ok(Value::Null),
3048
+ #[cfg(feature = "http")]
3049
+ Value::PromiseConstructor => match key {
3050
+ "resolve" => Ok(Value::Native(Self::promise_resolve)),
3051
+ "reject" => Ok(Value::Native(Self::promise_reject)),
3052
+ "all" => Ok(Value::Native(Self::promise_all)),
3053
+ "race" => Ok(Value::Native(Self::promise_race)),
3054
+ _ => Ok(Value::Null),
3055
+ },
3056
+ Value::Opaque(o) => {
3057
+ if o.get_method(key).is_some() {
3058
+ Ok(Value::OpaqueMethod(Arc::clone(o), Arc::from(key)))
3059
+ } else {
3060
+ Ok(Value::Null)
3061
+ }
3062
+ }
3063
+ #[cfg(feature = "regex")]
3064
+ Value::RegExp(re) => {
3065
+ let re = re.borrow();
3066
+ match key {
3067
+ "source" => Ok(Value::String(re.source.clone().into())),
3068
+ "flags" => Ok(Value::String(re.flags_string().into())),
3069
+ "lastIndex" => Ok(Value::Number(re.last_index as f64)),
3070
+ "global" => Ok(Value::Bool(re.flags.global)),
3071
+ "ignoreCase" => Ok(Value::Bool(re.flags.ignore_case)),
3072
+ "multiline" => Ok(Value::Bool(re.flags.multiline)),
3073
+ "dotAll" => Ok(Value::Bool(re.flags.dot_all)),
3074
+ "unicode" => Ok(Value::Bool(re.flags.unicode)),
3075
+ "sticky" => Ok(Value::Bool(re.flags.sticky)),
3076
+ _ => Ok(Value::Null),
3077
+ }
3078
+ }
3079
+ _ => Ok(Value::Null),
3080
+ }
3081
+ }
3082
+
3083
+ fn get_index(&self, obj: &Value, index: &Value) -> Result<Value, String> {
3084
+ match obj {
3085
+ Value::Array(arr) => {
3086
+ let idx = match index {
3087
+ Value::Number(n) => *n as usize,
3088
+ _ => return Ok(Value::Null),
3089
+ };
3090
+ Ok(arr.borrow().get(idx).cloned().unwrap_or(Value::Null))
3091
+ }
3092
+ Value::Object(_) => Ok(eval_object_get(obj, index).unwrap_or(Value::Null)),
3093
+ #[cfg(feature = "http")]
3094
+ Value::Promise(_) | Value::CorePromise(_) => {
3095
+ let key = match index {
3096
+ Value::String(s) => s.as_ref(),
3097
+ _ => return Ok(Value::Null),
3098
+ };
3099
+ self.get_prop(obj, key)
3100
+ }
3101
+ _ => Ok(Value::Null),
3102
+ }
3103
+ }
3104
+
3105
+ fn json_parse(s: &str) -> Value {
3106
+ let s = s.trim();
3107
+ if s.is_empty() {
3108
+ return Value::Null;
3109
+ }
3110
+ match Self::json_parse_str(s) {
3111
+ Ok(v) => v,
3112
+ Err(()) => Value::Null,
3113
+ }
3114
+ }
3115
+
3116
+ fn json_parse_str(s: &str) -> Result<Value, ()> {
3117
+ let s = s.trim();
3118
+ if s.is_empty() {
3119
+ return Err(());
3120
+ }
3121
+ if s == "null" {
3122
+ return Ok(Value::Null);
3123
+ }
3124
+ if s == "true" {
3125
+ return Ok(Value::Bool(true));
3126
+ }
3127
+ if s == "false" {
3128
+ return Ok(Value::Bool(false));
3129
+ }
3130
+ if s.starts_with('"') {
3131
+ return Self::json_parse_string_full(s);
3132
+ }
3133
+ if s.starts_with('[') {
3134
+ return Self::json_parse_array(s);
3135
+ }
3136
+ if s.starts_with('{') {
3137
+ return Self::json_parse_object(s);
3138
+ }
3139
+ if let Ok(n) = s.parse::<f64>() {
3140
+ return Ok(Value::Number(n));
3141
+ }
3142
+ Err(())
3143
+ }
3144
+
3145
+ fn json_parse_string(s: &str) -> Result<(Value, &str), ()> {
3146
+ let s = &s[1..];
3147
+ let mut out = String::new();
3148
+ let mut i = 0;
3149
+ let chars: Vec<char> = s.chars().collect();
3150
+ while i < chars.len() {
3151
+ if chars[i] == '"' {
3152
+ let rest_start = s.chars().take(i + 1).map(|c| c.len_utf8()).sum::<usize>();
3153
+ return Ok((Value::String(out.into()), &s[rest_start..]));
3154
+ }
3155
+ if chars[i] == '\\' {
3156
+ i += 1;
3157
+ if i >= chars.len() {
3158
+ return Err(());
3159
+ }
3160
+ match chars[i] {
3161
+ '"' => out.push('"'),
3162
+ '\\' => out.push('\\'),
3163
+ 'n' => out.push('\n'),
3164
+ 'r' => out.push('\r'),
3165
+ 't' => out.push('\t'),
3166
+ _ => return Err(()),
3167
+ }
3168
+ } else {
3169
+ out.push(chars[i]);
3170
+ }
3171
+ i += 1;
3172
+ }
3173
+ Err(())
3174
+ }
3175
+
3176
+ fn json_parse_string_full(s: &str) -> Result<Value, ()> {
3177
+ Self::json_parse_string(s).map(|(v, _)| v)
3178
+ }
3179
+
3180
+ fn json_parse_array(s: &str) -> Result<Value, ()> {
3181
+ let s = s[1..].trim_start();
3182
+ if s.starts_with(']') {
3183
+ return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
3184
+ }
3185
+ let mut vals = Vec::new();
3186
+ let mut rest = s;
3187
+ loop {
3188
+ let (v, next) = Self::json_parse_one(rest)?;
3189
+ vals.push(v);
3190
+ rest = next.trim_start();
3191
+ if rest.starts_with(']') {
3192
+ break;
3193
+ }
3194
+ if !rest.starts_with(',') {
3195
+ return Err(());
3196
+ }
3197
+ rest = rest[1..].trim_start();
3198
+ }
3199
+ Ok(Value::Array(Rc::new(RefCell::new(vals))))
3200
+ }
3201
+
3202
+ fn json_parse_object(s: &str) -> Result<Value, ()> {
3203
+ let s = s[1..].trim_start();
3204
+ if s.starts_with('}') {
3205
+ return Ok(Value::object(PropMap::default()));
3206
+ }
3207
+ let mut map = PropMap::default();
3208
+ let mut rest = s;
3209
+ loop {
3210
+ if !rest.starts_with('"') {
3211
+ return Err(());
3212
+ }
3213
+ let (key_val, next) = Self::json_parse_string(rest)?;
3214
+ let key = match &key_val {
3215
+ Value::String(k) => Arc::clone(k),
3216
+ _ => return Err(()),
3217
+ };
3218
+ rest = next.trim_start();
3219
+ if !rest.starts_with(':') {
3220
+ return Err(());
3221
+ }
3222
+ rest = rest[1..].trim_start();
3223
+ let (val, next) = Self::json_parse_one(rest)?;
3224
+ map.insert(key, val);
3225
+ rest = next.trim_start();
3226
+ if rest.starts_with('}') {
3227
+ break;
3228
+ }
3229
+ if !rest.starts_with(',') {
3230
+ return Err(());
3231
+ }
3232
+ rest = rest[1..].trim_start();
3233
+ }
3234
+ Ok(Value::object(map))
3235
+ }
3236
+
3237
+ fn json_parse_one(s: &str) -> Result<(Value, &str), ()> {
3238
+ let s = s.trim();
3239
+ if s.is_empty() {
3240
+ return Err(());
3241
+ }
3242
+ if s.starts_with('"') {
3243
+ let (v, rest) = Self::json_parse_string(s)?;
3244
+ Ok((v, rest))
3245
+ } else if s.starts_with('[') {
3246
+ let mut depth = 0;
3247
+ for (i, c) in s.char_indices() {
3248
+ if c == '[' {
3249
+ depth += 1;
3250
+ } else if c == ']' {
3251
+ depth -= 1;
3252
+ if depth == 0 {
3253
+ let v = Self::json_parse_array(&s[..=i])?;
3254
+ return Ok((v, &s[i + c.len_utf8()..]));
3255
+ }
3256
+ }
3257
+ }
3258
+ Err(())
3259
+ } else if s.starts_with('{') {
3260
+ let mut depth = 0;
3261
+ for (i, c) in s.char_indices() {
3262
+ if c == '{' {
3263
+ depth += 1;
3264
+ } else if c == '}' {
3265
+ depth -= 1;
3266
+ if depth == 0 {
3267
+ let v = Self::json_parse_object(&s[..=i])?;
3268
+ return Ok((v, &s[i + c.len_utf8()..]));
3269
+ }
3270
+ }
3271
+ }
3272
+ Err(())
3273
+ } else if let Some(rest) = s.strip_prefix("null") {
3274
+ Ok((Value::Null, rest))
3275
+ } else if let Some(rest) = s.strip_prefix("true") {
3276
+ Ok((Value::Bool(true), rest))
3277
+ } else if let Some(rest) = s.strip_prefix("false") {
3278
+ Ok((Value::Bool(false), rest))
3279
+ } else {
3280
+ let end = s
3281
+ .find(|c: char| {
3282
+ !c.is_ascii_digit() && c != '-' && c != '+' && c != '.' && c != 'e' && c != 'E'
3283
+ })
3284
+ .unwrap_or(s.len());
3285
+ let num_str = &s[..end];
3286
+ let n: f64 = num_str.parse().map_err(|_| ())?;
3287
+ Ok((Value::Number(n), &s[end..]))
3288
+ }
3289
+ }
3290
+
3291
+ fn json_stringify_value(v: &Value) -> String {
3292
+ match v {
3293
+ Value::Null => "null".to_string(),
3294
+ Value::Bool(b) => b.to_string(),
3295
+ Value::Number(n) => {
3296
+ if n.is_finite() {
3297
+ n.to_string()
3298
+ } else {
3299
+ "null".to_string()
3300
+ }
3301
+ }
3302
+ Value::String(s) => format!(
3303
+ "\"{}\"",
3304
+ s.replace('\\', "\\\\")
3305
+ .replace('"', "\\\"")
3306
+ .replace('\n', "\\n")
3307
+ .replace('\r', "\\r")
3308
+ .replace('\t', "\\t")
3309
+ ),
3310
+ Value::Array(arr) => {
3311
+ let inner: Vec<String> = arr
3312
+ .borrow()
3313
+ .iter()
3314
+ .map(Self::json_stringify_value)
3315
+ .collect();
3316
+ format!("[{}]", inner.join(","))
3317
+ }
3318
+ Value::Object(map) => {
3319
+ let mut entries: Vec<_> = map
3320
+ .borrow()
3321
+ .strings
3322
+ .iter()
3323
+ .map(|(k, v)| {
3324
+ (
3325
+ k.as_ref().to_string(),
3326
+ format!(
3327
+ "\"{}\":{}",
3328
+ k.replace('\\', "\\\\").replace('"', "\\\""),
3329
+ Self::json_stringify_value(v)
3330
+ ),
3331
+ )
3332
+ })
3333
+ .collect();
3334
+ entries.sort_by(|a, b| a.0.cmp(&b.0));
3335
+ format!(
3336
+ "{{{}}}",
3337
+ entries
3338
+ .into_iter()
3339
+ .map(|(_, s)| s)
3340
+ .collect::<Vec<_>>()
3341
+ .join(",")
3342
+ )
3343
+ }
3344
+ Value::Symbol(_) => "null".to_string(),
3345
+ Value::Function { .. } | Value::Native(_) => "null".to_string(),
3346
+ #[cfg(feature = "http")]
3347
+ Value::CorePromise(_) => "null".to_string(),
3348
+ Value::CoreFn(_) => "null".to_string(),
3349
+ #[cfg(feature = "http")]
3350
+ Value::Serve
3351
+ | Value::Promise(_)
3352
+ | Value::PromiseResolver(_)
3353
+ | Value::PromiseConstructor
3354
+ | Value::BoundPromiseMethod(_, _) => "null".to_string(),
3355
+ #[cfg(feature = "timers")]
3356
+ Value::TimerBuiltin(_) => "null".to_string(),
3357
+ #[cfg(feature = "regex")]
3358
+ Value::RegExp(_) => "null".to_string(),
3359
+ Value::Opaque(_) | Value::OpaqueMethod(_, _) => "null".to_string(),
3360
+ }
3361
+ }
3362
+
3363
+ // Static native wrapper functions (these need to be fn pointers, not closures with &self)
3364
+ fn json_parse_native(args: &[Value]) -> Result<Value, String> {
3365
+ let s = args.first().map(|v| v.to_string()).unwrap_or_default();
3366
+ Ok(Self::json_parse(&s))
3367
+ }
3368
+
3369
+ fn json_stringify_native(args: &[Value]) -> Result<Value, String> {
3370
+ let v = args.first().cloned().unwrap_or(Value::Null);
3371
+ Ok(Value::String(Self::json_stringify_value(&v).into()))
3372
+ }
3373
+
3374
+ fn object_keys(args: &[Value]) -> Result<Value, String> {
3375
+ if let Some(Value::Object(obj)) = args.first() {
3376
+ let keys: Vec<Value> = obj
3377
+ .borrow()
3378
+ .strings
3379
+ .keys()
3380
+ .map(|k| Value::String(Arc::clone(k)))
3381
+ .collect();
3382
+ Ok(Value::Array(Rc::new(RefCell::new(keys))))
3383
+ } else {
3384
+ Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
3385
+ }
3386
+ }
3387
+
3388
+ fn object_values(args: &[Value]) -> Result<Value, String> {
3389
+ if let Some(Value::Object(obj)) = args.first() {
3390
+ let values: Vec<Value> = obj.borrow().strings.values().cloned().collect();
3391
+ Ok(Value::Array(Rc::new(RefCell::new(values))))
3392
+ } else {
3393
+ Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
3394
+ }
3395
+ }
3396
+
3397
+ fn object_entries(args: &[Value]) -> Result<Value, String> {
3398
+ if let Some(Value::Object(obj)) = args.first() {
3399
+ let entries: Vec<Value> = obj
3400
+ .borrow()
3401
+ .strings
3402
+ .iter()
3403
+ .map(|(k, v)| {
3404
+ Value::Array(Rc::new(RefCell::new(vec![
3405
+ Value::String(Arc::clone(k)),
3406
+ v.clone(),
3407
+ ])))
3408
+ })
3409
+ .collect();
3410
+ Ok(Value::Array(Rc::new(RefCell::new(entries))))
3411
+ } else {
3412
+ Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
3413
+ }
3414
+ }
3415
+
3416
+ fn object_assign(args: &[Value]) -> Result<Value, String> {
3417
+ if let Some(Value::Object(target)) = args.first() {
3418
+ let mut t = target.borrow_mut();
3419
+ for src in args.iter().skip(1) {
3420
+ if let Value::Object(src_obj) = src {
3421
+ let s = src_obj.borrow();
3422
+ for (k, v) in s.strings.iter() {
3423
+ t.strings.insert(Arc::clone(k), v.clone());
3424
+ }
3425
+ if let Some(ref sm) = s.symbols {
3426
+ if t.symbols.is_none() {
3427
+ t.symbols = Some(AHashMap::default());
3428
+ }
3429
+ let tm = t.symbols.as_mut().unwrap();
3430
+ for (id, v) in sm.iter() {
3431
+ tm.insert(*id, v.clone());
3432
+ }
3433
+ }
3434
+ }
3435
+ }
3436
+ drop(t);
3437
+ Ok(args.first().cloned().unwrap())
3438
+ } else {
3439
+ Ok(Value::Null)
3440
+ }
3441
+ }
3442
+
3443
+ fn object_from_entries(args: &[Value]) -> Result<Value, String> {
3444
+ if let Some(Value::Array(arr)) = args.first() {
3445
+ let mut map = PropMap::default();
3446
+ for entry in arr.borrow().iter() {
3447
+ if let Value::Array(pair) = entry {
3448
+ let pair = pair.borrow();
3449
+ if let (Some(key), Some(value)) = (pair.first(), pair.get(1)) {
3450
+ let key_str: Arc<str> = key.to_string().into();
3451
+ map.insert(key_str, value.clone());
3452
+ }
3453
+ }
3454
+ }
3455
+ Ok(Value::object(map))
3456
+ } else {
3457
+ Ok(Value::object(PropMap::default()))
3458
+ }
3459
+ }
3460
+
3461
+ #[cfg(feature = "regex")]
3462
+ fn regexp_constructor_native(args: &[Value]) -> Result<Value, String> {
3463
+ crate::regex::regexp_constructor(args)
3464
+ }
3465
+
3466
+ #[cfg(feature = "http")]
3467
+ fn promise_resolve(args: &[Value]) -> Result<Value, String> {
3468
+ let x = args.first().cloned().unwrap_or(Value::Null);
3469
+ let (promise, resolve_val, reject_val) = crate::promise::create_promise();
3470
+ let (resolve, _) = crate::promise::extract_resolvers(&resolve_val, &reject_val);
3471
+ crate::promise::settle_promise(&resolve, x, true)?;
3472
+ Ok(promise)
3473
+ }
3474
+
3475
+ #[cfg(feature = "http")]
3476
+ fn promise_reject(args: &[Value]) -> Result<Value, String> {
3477
+ let r = args.first().cloned().unwrap_or(Value::Null);
3478
+ let (promise, resolve_val, reject_val) = crate::promise::create_promise();
3479
+ let (_, reject) = crate::promise::extract_resolvers(&resolve_val, &reject_val);
3480
+ crate::promise::settle_promise(&reject, r, false)?;
3481
+ Ok(promise)
3482
+ }
3483
+
3484
+ #[cfg(feature = "http")]
3485
+ fn promise_all(args: &[Value]) -> Result<Value, String> {
3486
+ let iterable = args
3487
+ .first()
3488
+ .ok_or_else(|| "Promise.all requires an iterable".to_string())?;
3489
+ let values: Vec<Value> = match iterable {
3490
+ Value::Array(arr) => arr.borrow().clone(),
3491
+ Value::String(s) => s
3492
+ .chars()
3493
+ .map(|c| Value::String(c.to_string().into()))
3494
+ .collect(),
3495
+ _ => return Err("Promise.all requires array or iterable".to_string()),
3496
+ };
3497
+ let mut results = Vec::with_capacity(values.len());
3498
+ for v in values {
3499
+ if let Value::Promise(ref p) = v {
3500
+ match crate::promise::block_until_settled(p) {
3501
+ crate::promise::PromiseAwaitResult::Fulfilled(x) => results.push(x),
3502
+ crate::promise::PromiseAwaitResult::Rejected(x) => {
3503
+ let (promise, resolve_val, reject_val) = crate::promise::create_promise();
3504
+ let (_, reject) =
3505
+ crate::promise::extract_resolvers(&resolve_val, &reject_val);
3506
+ let _ = crate::promise::settle_promise(&reject, x, false);
3507
+ return Ok(promise);
3508
+ }
3509
+ crate::promise::PromiseAwaitResult::Error(e) => return Err(e),
3510
+ }
3511
+ } else if let Value::CorePromise(ref p) = v {
3512
+ match p.block_until_settled() {
3513
+ Ok(x) => results.push(crate::value_convert::core_to_eval(x)),
3514
+ Err(x) => {
3515
+ let (promise, resolve_val, reject_val) = crate::promise::create_promise();
3516
+ let (_, reject) =
3517
+ crate::promise::extract_resolvers(&resolve_val, &reject_val);
3518
+ let _ = crate::promise::settle_promise(
3519
+ &reject,
3520
+ crate::value_convert::core_to_eval(x),
3521
+ false,
3522
+ );
3523
+ return Ok(promise);
3524
+ }
3525
+ }
3526
+ } else {
3527
+ results.push(v);
3528
+ }
3529
+ }
3530
+ let (promise, resolve_val, reject_val) = crate::promise::create_promise();
3531
+ let (resolve, _) = crate::promise::extract_resolvers(&resolve_val, &reject_val);
3532
+ let arr = Value::Array(Rc::new(RefCell::new(results)));
3533
+ crate::promise::settle_promise(&resolve, arr, true)?;
3534
+ Ok(promise)
3535
+ }
3536
+
3537
+ #[cfg(feature = "http")]
3538
+ fn promise_race(args: &[Value]) -> Result<Value, String> {
3539
+ let iterable = args
3540
+ .first()
3541
+ .ok_or_else(|| "Promise.race requires an iterable".to_string())?;
3542
+ let values: Vec<Value> = match iterable {
3543
+ Value::Array(arr) => arr.borrow().clone(),
3544
+ Value::String(s) => s
3545
+ .chars()
3546
+ .map(|c| Value::String(c.to_string().into()))
3547
+ .collect(),
3548
+ _ => return Err("Promise.race requires array or iterable".to_string()),
3549
+ };
3550
+ for v in values {
3551
+ if let Value::CorePromise(ref p) = v {
3552
+ match p.block_until_settled() {
3553
+ Ok(x) => {
3554
+ let (promise, resolve_val, reject_val) = crate::promise::create_promise();
3555
+ let (resolve, _) =
3556
+ crate::promise::extract_resolvers(&resolve_val, &reject_val);
3557
+ crate::promise::settle_promise(
3558
+ &resolve,
3559
+ crate::value_convert::core_to_eval(x),
3560
+ true,
3561
+ )?;
3562
+ return Ok(promise);
3563
+ }
3564
+ Err(x) => {
3565
+ let (promise, resolve_val, reject_val) = crate::promise::create_promise();
3566
+ let (_, reject) =
3567
+ crate::promise::extract_resolvers(&resolve_val, &reject_val);
3568
+ crate::promise::settle_promise(
3569
+ &reject,
3570
+ crate::value_convert::core_to_eval(x),
3571
+ false,
3572
+ )?;
3573
+ return Ok(promise);
3574
+ }
3575
+ }
3576
+ }
3577
+ if let Value::Promise(ref p) = v {
3578
+ match crate::promise::block_until_settled(p) {
3579
+ crate::promise::PromiseAwaitResult::Fulfilled(x) => {
3580
+ let (promise, resolve_val, reject_val) = crate::promise::create_promise();
3581
+ let (resolve, _) =
3582
+ crate::promise::extract_resolvers(&resolve_val, &reject_val);
3583
+ crate::promise::settle_promise(&resolve, x, true)?;
3584
+ return Ok(promise);
3585
+ }
3586
+ crate::promise::PromiseAwaitResult::Rejected(x) => {
3587
+ let (promise, resolve_val, reject_val) = crate::promise::create_promise();
3588
+ let (_, reject) =
3589
+ crate::promise::extract_resolvers(&resolve_val, &reject_val);
3590
+ crate::promise::settle_promise(&reject, x, false)?;
3591
+ return Ok(promise);
3592
+ }
3593
+ crate::promise::PromiseAwaitResult::Error(e) => return Err(e),
3594
+ }
3595
+ }
3596
+ }
3597
+ Err("Promise.race requires at least one promise".to_string())
3598
+ }
3599
+
3600
+ #[cfg(feature = "ws")]
3601
+ fn ws_web_socket_native(args: &[Value]) -> Result<Value, String> {
3602
+ let mut cv = Vec::new();
3603
+ for a in args {
3604
+ cv.push(crate::value_convert::eval_to_core(a)?);
3605
+ }
3606
+ Ok(crate::value_convert::core_to_eval(
3607
+ tishlang_runtime::web_socket_client(&cv),
3608
+ ))
3609
+ }
3610
+
3611
+ #[cfg(feature = "ws")]
3612
+ fn ws_server_native(args: &[Value]) -> Result<Value, String> {
3613
+ let mut cv = Vec::new();
3614
+ for a in args {
3615
+ cv.push(crate::value_convert::eval_to_core(a)?);
3616
+ }
3617
+ Ok(crate::value_convert::core_to_eval(
3618
+ tishlang_runtime::web_socket_server_construct(&cv),
3619
+ ))
3620
+ }
3621
+
3622
+ #[cfg(feature = "ws")]
3623
+ fn ws_send_native(args: &[Value]) -> Result<Value, String> {
3624
+ let conn = args.first().ok_or("wsSend(conn, data) requires conn")?;
3625
+ let conn_core = crate::value_convert::eval_to_core(conn)?;
3626
+ let data = args.get(1).map(|v| v.to_string()).unwrap_or_default();
3627
+ Ok(Value::Bool(tishlang_runtime::ws_send_native(
3628
+ &conn_core, &data,
3629
+ )))
3630
+ }
3631
+
3632
+ #[cfg(feature = "ws")]
3633
+ fn ws_broadcast_native(args: &[Value]) -> Result<Value, String> {
3634
+ let mut cv = Vec::new();
3635
+ for a in args {
3636
+ cv.push(crate::value_convert::eval_to_core(a)?);
3637
+ }
3638
+ Ok(crate::value_convert::core_to_eval(
3639
+ tishlang_runtime::ws_broadcast_native(&cv),
3640
+ ))
3641
+ }
3642
+
3643
+ #[cfg(feature = "http")]
3644
+ fn fetch_native(args: &[Value]) -> Result<Value, String> {
3645
+ let mut cv = Vec::new();
3646
+ for a in args {
3647
+ cv.push(crate::value_convert::eval_to_core(a)?);
3648
+ }
3649
+ match tishlang_runtime::fetch_promise(cv) {
3650
+ tishlang_core::Value::Promise(p) => Ok(Value::CorePromise(p)),
3651
+ _ => Err("internal: fetch did not return Promise".into()),
3652
+ }
3653
+ }
3654
+
3655
+ #[cfg(feature = "http")]
3656
+ fn fetch_all_native(args: &[Value]) -> Result<Value, String> {
3657
+ let mut cv = Vec::new();
3658
+ for a in args {
3659
+ cv.push(crate::value_convert::eval_to_core(a)?);
3660
+ }
3661
+ match tishlang_runtime::fetch_all_promise(cv) {
3662
+ tishlang_core::Value::Promise(p) => Ok(Value::CorePromise(p)),
3663
+ _ => Err("internal: fetchAll did not return Promise".into()),
3664
+ }
3665
+ }
3666
+
3667
+ #[cfg(feature = "http")]
3668
+ fn eval_await(&self, operand: &Expr) -> Result<Value, EvalError> {
3669
+ let val = self.eval_expr(operand)?;
3670
+ if let Value::Promise(ref p) = val {
3671
+ match crate::promise::block_until_settled(p) {
3672
+ crate::promise::PromiseAwaitResult::Fulfilled(v) => Ok(v),
3673
+ crate::promise::PromiseAwaitResult::Rejected(v) => Err(EvalError::Throw(v)),
3674
+ crate::promise::PromiseAwaitResult::Error(e) => Err(EvalError::Error(e)),
3675
+ }
3676
+ } else if let Value::CorePromise(ref p) = val {
3677
+ match p.block_until_settled() {
3678
+ Ok(v) => Ok(crate::value_convert::core_to_eval(v)),
3679
+ Err(v) => Err(EvalError::Throw(crate::value_convert::core_to_eval(v))),
3680
+ }
3681
+ } else {
3682
+ Err(EvalError::Error(
3683
+ "await requires a Promise (use await fetch(...), await reader.read(), etc.)".into(),
3684
+ ))
3685
+ }
3686
+ }
3687
+
3688
+ #[cfg(not(feature = "http"))]
3689
+ fn eval_await(&self, _operand: &Expr) -> Result<Value, EvalError> {
3690
+ Err(EvalError::Error(
3691
+ "await requires the http feature".to_string(),
3692
+ ))
3693
+ }
3694
+ }
3695
+
3696
+ #[derive(Debug)]
3697
+ enum EvalError {
3698
+ Return(Value),
3699
+ Break,
3700
+ Continue,
3701
+ Throw(Value),
3702
+ Error(String),
3703
+ }
3704
+
3705
+ impl std::fmt::Display for EvalError {
3706
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3707
+ match self {
3708
+ EvalError::Return(_) => write!(f, "return"),
3709
+ EvalError::Break => write!(f, "break"),
3710
+ EvalError::Continue => write!(f, "continue"),
3711
+ EvalError::Throw(v) => write!(f, "{}", v),
3712
+ EvalError::Error(s) => write!(f, "{}", s),
3713
+ }
3714
+ }
3715
+ }
3716
+
3717
+ impl std::error::Error for EvalError {}