@tishlang/tish 1.0.6 → 1.0.10

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