@tishlang/tish 1.6.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/Cargo.toml +2 -0
  2. package/README.md +2 -0
  3. package/bin/tish +0 -0
  4. package/crates/js_to_tish/src/error.rs +2 -8
  5. package/crates/js_to_tish/src/transform/expr.rs +128 -137
  6. package/crates/js_to_tish/src/transform/stmt.rs +62 -32
  7. package/crates/tish/Cargo.toml +15 -5
  8. package/crates/tish/src/cargo_native_registry.rs +29 -0
  9. package/crates/tish/src/cli_help.rs +92 -39
  10. package/crates/tish/src/main.rs +172 -86
  11. package/crates/tish/src/repl_completion.rs +3 -3
  12. package/crates/tish/tests/cargo_example_compile.rs +4 -2
  13. package/crates/tish/tests/integration_test.rs +216 -54
  14. package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
  15. package/crates/tish/tests/shortcircuit.rs +20 -5
  16. package/crates/tish_ast/src/ast.rs +92 -23
  17. package/crates/tish_build_utils/Cargo.toml +4 -0
  18. package/crates/tish_build_utils/src/lib.rs +136 -8
  19. package/crates/tish_builtins/Cargo.toml +5 -1
  20. package/crates/tish_builtins/src/array.rs +65 -33
  21. package/crates/tish_builtins/src/construct.rs +34 -39
  22. package/crates/tish_builtins/src/globals.rs +42 -26
  23. package/crates/tish_builtins/src/helpers.rs +2 -1
  24. package/crates/tish_builtins/src/lib.rs +5 -5
  25. package/crates/tish_builtins/src/math.rs +5 -3
  26. package/crates/tish_builtins/src/object.rs +3 -2
  27. package/crates/tish_builtins/src/string.rs +144 -22
  28. package/crates/tish_bytecode/src/chunk.rs +0 -1
  29. package/crates/tish_bytecode/src/compiler.rs +173 -71
  30. package/crates/tish_bytecode/src/opcode.rs +24 -6
  31. package/crates/tish_bytecode/src/peephole.rs +2 -2
  32. package/crates/tish_compile/Cargo.toml +1 -0
  33. package/crates/tish_compile/src/codegen.rs +1621 -453
  34. package/crates/tish_compile/src/infer.rs +75 -19
  35. package/crates/tish_compile/src/lib.rs +19 -8
  36. package/crates/tish_compile/src/resolve.rs +278 -137
  37. package/crates/tish_compile/src/types.rs +184 -24
  38. package/crates/tish_compile_js/Cargo.toml +1 -0
  39. package/crates/tish_compile_js/src/codegen.rs +181 -37
  40. package/crates/tish_compile_js/src/lib.rs +3 -1
  41. package/crates/tish_compile_js/src/tests_jsx.rs +30 -6
  42. package/crates/tish_compiler_wasm/src/lib.rs +16 -13
  43. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +69 -59
  44. package/crates/tish_core/Cargo.toml +8 -0
  45. package/crates/tish_core/src/json.rs +107 -56
  46. package/crates/tish_core/src/lib.rs +4 -2
  47. package/crates/tish_core/src/macros.rs +5 -5
  48. package/crates/tish_core/src/uri.rs +9 -6
  49. package/crates/tish_core/src/value.rs +145 -43
  50. package/crates/tish_core/src/vmref.rs +178 -0
  51. package/crates/tish_cranelift/src/link.rs +6 -9
  52. package/crates/tish_cranelift/src/lower.rs +14 -8
  53. package/crates/tish_eval/Cargo.toml +17 -2
  54. package/crates/tish_eval/src/eval.rs +474 -165
  55. package/crates/tish_eval/src/http.rs +61 -0
  56. package/crates/tish_eval/src/lib.rs +12 -8
  57. package/crates/tish_eval/src/natives.rs +136 -38
  58. package/crates/tish_eval/src/promise.rs +14 -8
  59. package/crates/tish_eval/src/timers.rs +28 -19
  60. package/crates/tish_eval/src/value.rs +17 -6
  61. package/crates/tish_eval/src/value_convert.rs +13 -5
  62. package/crates/tish_fmt/src/lib.rs +149 -43
  63. package/crates/tish_lexer/src/lib.rs +232 -63
  64. package/crates/tish_lexer/src/token.rs +10 -6
  65. package/crates/tish_llvm/src/lib.rs +17 -8
  66. package/crates/tish_lsp/Cargo.toml +4 -1
  67. package/crates/tish_lsp/README.md +1 -1
  68. package/crates/tish_lsp/src/builtin_goto.rs +261 -0
  69. package/crates/tish_lsp/src/import_goto.rs +549 -0
  70. package/crates/tish_lsp/src/main.rs +504 -106
  71. package/crates/tish_native/src/build.rs +4 -8
  72. package/crates/tish_native/src/lib.rs +54 -21
  73. package/crates/tish_opt/src/lib.rs +84 -52
  74. package/crates/tish_parser/src/lib.rs +45 -13
  75. package/crates/tish_parser/src/parser.rs +505 -130
  76. package/crates/tish_resolve/Cargo.toml +13 -0
  77. package/crates/tish_resolve/src/lib.rs +3436 -0
  78. package/crates/tish_resolve/src/pos.rs +133 -0
  79. package/crates/tish_runtime/Cargo.toml +68 -3
  80. package/crates/tish_runtime/src/http.rs +1136 -145
  81. package/crates/tish_runtime/src/http_fetch.rs +38 -27
  82. package/crates/tish_runtime/src/http_hyper.rs +418 -0
  83. package/crates/tish_runtime/src/http_prefork.rs +189 -0
  84. package/crates/tish_runtime/src/lib.rs +375 -189
  85. package/crates/tish_runtime/src/promise.rs +199 -40
  86. package/crates/tish_runtime/src/promise_io.rs +2 -1
  87. package/crates/tish_runtime/src/timers.rs +37 -1
  88. package/crates/tish_runtime/src/ws.rs +65 -42
  89. package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
  90. package/crates/tish_ui/src/jsx.rs +317 -27
  91. package/crates/tish_ui/src/lib.rs +5 -2
  92. package/crates/tish_ui/src/runtime/hooks.rs +406 -45
  93. package/crates/tish_ui/src/runtime/mod.rs +36 -9
  94. package/crates/tish_vm/Cargo.toml +15 -5
  95. package/crates/tish_vm/src/vm.rs +725 -281
  96. package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +11 -4
  97. package/crates/tish_wasm/src/lib.rs +55 -42
  98. package/crates/tish_wasm_runtime/Cargo.toml +2 -1
  99. package/crates/tish_wasm_runtime/src/lib.rs +1 -1
  100. package/crates/tishlang_cargo_bindgen/Cargo.toml +26 -0
  101. package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
  102. package/crates/tishlang_cargo_bindgen/src/discover.rs +120 -0
  103. package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
  104. package/crates/tishlang_cargo_bindgen/src/lib.rs +350 -0
  105. package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
  106. package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -0
  107. package/justfile +8 -0
  108. package/package.json +1 -1
  109. package/platform/darwin-arm64/tish +0 -0
  110. package/platform/darwin-x64/tish +0 -0
  111. package/platform/linux-arm64/tish +0 -0
  112. package/platform/linux-x64/tish +0 -0
  113. package/platform/win32-x64/tish.exe +0 -0
@@ -8,11 +8,14 @@ use std::path::{Path, PathBuf};
8
8
  use std::rc::Rc;
9
9
  use std::sync::Arc;
10
10
 
11
- use tishlang_ast::{BinOp, CompoundOp, ExportDeclaration, Expr, FunParam, ImportSpecifier, Literal, LogicalAssignOp, MemberProp, Span, Statement, UnaryOp};
11
+ use tishlang_ast::{
12
+ BinOp, CompoundOp, ExportDeclaration, Expr, FunParam, ImportSpecifier, Literal,
13
+ LogicalAssignOp, MemberProp, Span, Statement, UnaryOp,
14
+ };
12
15
 
13
- use crate::value::{PropMap, Value};
14
16
  #[cfg(any(feature = "fs", feature = "process"))]
15
17
  use crate::natives;
18
+ use crate::value::{PropMap, Value};
16
19
 
17
20
  struct Scope {
18
21
  vars: PropMap,
@@ -93,12 +96,25 @@ impl Evaluator {
93
96
  console.insert("log".into(), Value::Native(natives::console_log));
94
97
  console.insert("warn".into(), Value::Native(natives::console_warn));
95
98
  console.insert("error".into(), Value::Native(natives::console_error));
96
- s.set("console".into(), Value::Object(Rc::new(RefCell::new(console))), true);
99
+ s.set(
100
+ "console".into(),
101
+ Value::Object(Rc::new(RefCell::new(console))),
102
+ true,
103
+ );
97
104
  s.set("parseInt".into(), Value::Native(natives::parse_int), true);
98
- s.set("parseFloat".into(), Value::Native(natives::parse_float), true);
105
+ s.set(
106
+ "parseFloat".into(),
107
+ Value::Native(natives::parse_float),
108
+ true,
109
+ );
99
110
  s.set("decodeURI".into(), Value::Native(natives::decode_uri), true);
100
111
  s.set("encodeURI".into(), Value::Native(natives::encode_uri), true);
101
- s.set("Boolean".into(), Value::Native(natives::boolean_native), true);
112
+ s.set("htmlEscape".into(), Value::Native(natives::html_escape), true);
113
+ s.set(
114
+ "Boolean".into(),
115
+ Value::Native(natives::boolean_native),
116
+ true,
117
+ );
102
118
  s.set("isFinite".into(), Value::Native(natives::is_finite), true);
103
119
  s.set("isNaN".into(), Value::Native(natives::is_nan), true);
104
120
  s.set("Infinity".into(), Value::Number(f64::INFINITY), true);
@@ -122,32 +138,65 @@ impl Evaluator {
122
138
  math.insert("trunc".into(), Value::Native(natives::math_trunc));
123
139
  math.insert("PI".into(), Value::Number(std::f64::consts::PI));
124
140
  math.insert("E".into(), Value::Number(std::f64::consts::E));
125
- s.set("Math".into(), Value::Object(Rc::new(RefCell::new(math))), true);
141
+ s.set(
142
+ "Math".into(),
143
+ Value::Object(Rc::new(RefCell::new(math))),
144
+ true,
145
+ );
126
146
 
127
147
  let mut json = PropMap::with_capacity(2);
128
148
  json.insert("parse".into(), Value::Native(Self::json_parse_native));
129
- json.insert("stringify".into(), Value::Native(Self::json_stringify_native));
130
- s.set("JSON".into(), Value::Object(Rc::new(RefCell::new(json))), true);
149
+ json.insert(
150
+ "stringify".into(),
151
+ Value::Native(Self::json_stringify_native),
152
+ );
153
+ s.set(
154
+ "JSON".into(),
155
+ Value::Object(Rc::new(RefCell::new(json))),
156
+ true,
157
+ );
131
158
 
132
159
  let mut object = PropMap::with_capacity(5);
133
160
  object.insert("keys".into(), Value::Native(Self::object_keys));
134
161
  object.insert("values".into(), Value::Native(Self::object_values));
135
162
  object.insert("entries".into(), Value::Native(Self::object_entries));
136
163
  object.insert("assign".into(), Value::Native(Self::object_assign));
137
- object.insert("fromEntries".into(), Value::Native(Self::object_from_entries));
138
- s.set("Object".into(), Value::Object(Rc::new(RefCell::new(object))), true);
164
+ object.insert(
165
+ "fromEntries".into(),
166
+ Value::Native(Self::object_from_entries),
167
+ );
168
+ s.set(
169
+ "Object".into(),
170
+ Value::Object(Rc::new(RefCell::new(object))),
171
+ true,
172
+ );
139
173
 
140
174
  let mut array_obj = PropMap::with_capacity(1);
141
175
  array_obj.insert("isArray".into(), Value::Native(natives::array_is_array));
142
- s.set("Array".into(), Value::Object(Rc::new(RefCell::new(array_obj))), true);
176
+ s.set(
177
+ "Array".into(),
178
+ Value::Object(Rc::new(RefCell::new(array_obj))),
179
+ true,
180
+ );
143
181
 
144
182
  let mut string_obj = PropMap::with_capacity(1);
145
- string_obj.insert("fromCharCode".into(), Value::Native(natives::string_from_char_code));
146
- s.set("String".into(), Value::Object(Rc::new(RefCell::new(string_obj))), true);
183
+ string_obj.insert(
184
+ "fromCharCode".into(),
185
+ Value::Native(natives::string_from_char_code),
186
+ );
187
+ s.set(
188
+ "String".into(),
189
+ Value::Object(Rc::new(RefCell::new(string_obj))),
190
+ true,
191
+ );
147
192
 
148
193
  let mut date = PropMap::with_capacity(1);
149
194
  date.insert("now".into(), Value::Native(natives::date_now));
150
- s.set("Date".into(), Value::Object(Rc::new(RefCell::new(date))), true);
195
+ s.set(
196
+ "Date".into(),
197
+ Value::Object(Rc::new(RefCell::new(date))),
198
+ true,
199
+ );
151
200
 
152
201
  s.set(
153
202
  "Uint8Array".into(),
@@ -166,10 +215,44 @@ impl Evaluator {
166
215
 
167
216
  #[cfg(feature = "regex")]
168
217
  {
169
- s.set("RegExp".into(), Value::Native(Self::regexp_constructor_native), true);
218
+ s.set(
219
+ "RegExp".into(),
220
+ Value::Native(Self::regexp_constructor_native),
221
+ true,
222
+ );
170
223
  }
171
224
 
172
- // fs, http, process: use import { x } from 'tish:fs' etc. No globals.
225
+ // fs, process: prefer `import { x } from 'tish:fs'` etc.
226
+ #[cfg(feature = "timers")]
227
+ {
228
+ s.set(
229
+ "setTimeout".into(),
230
+ Value::TimerBuiltin(Arc::from("setTimeout")),
231
+ true,
232
+ );
233
+ s.set(
234
+ "setInterval".into(),
235
+ Value::TimerBuiltin(Arc::from("setInterval")),
236
+ true,
237
+ );
238
+ s.set(
239
+ "clearTimeout".into(),
240
+ Value::Native(Self::clear_timeout_native),
241
+ true,
242
+ );
243
+ s.set(
244
+ "clearInterval".into(),
245
+ Value::Native(Self::clear_interval_native),
246
+ true,
247
+ );
248
+ }
249
+ #[cfg(feature = "http")]
250
+ {
251
+ s.set("fetch".into(), Value::Native(Self::fetch_native), true);
252
+ s.set("fetchAll".into(), Value::Native(Self::fetch_all_native), true);
253
+ s.set("Promise".into(), Value::PromiseConstructor, true);
254
+ s.set("serve".into(), Value::Serve, true);
255
+ }
173
256
  }
174
257
  Self {
175
258
  scope,
@@ -225,16 +308,28 @@ impl Evaluator {
225
308
  self.scope = prev;
226
309
  Ok(last)
227
310
  }
228
- Statement::VarDecl { name, mutable, init, .. } => {
311
+ Statement::VarDecl {
312
+ name,
313
+ mutable,
314
+ init,
315
+ ..
316
+ } => {
229
317
  let value = init
230
318
  .as_ref()
231
319
  .map(|e| self.eval_expr(e))
232
320
  .transpose()?
233
321
  .unwrap_or(Value::Null);
234
- self.scope.borrow_mut().set(Arc::clone(name), value, *mutable);
322
+ self.scope
323
+ .borrow_mut()
324
+ .set(Arc::clone(name), value, *mutable);
235
325
  Ok(Value::Null)
236
326
  }
237
- Statement::VarDeclDestructure { pattern, mutable, init, .. } => {
327
+ Statement::VarDeclDestructure {
328
+ pattern,
329
+ mutable,
330
+ init,
331
+ ..
332
+ } => {
238
333
  let value = self.eval_expr(init)?;
239
334
  self.bind_destruct_pattern(pattern, &value, *mutable)?;
240
335
  Ok(Value::Null)
@@ -269,15 +364,21 @@ impl Evaluator {
269
364
  }
270
365
  Ok(Value::Null)
271
366
  }
272
- Statement::ForOf { name, iterable, body, .. } => {
367
+ Statement::ForOf {
368
+ name,
369
+ iterable,
370
+ body,
371
+ ..
372
+ } => {
273
373
  let iter_val = self.eval_expr(iterable)?;
274
374
  let elements = match &iter_val {
275
- crate::value::Value::Array(arr) => arr.borrow().iter().cloned().collect::<Vec<_>>(),
276
- crate::value::Value::String(s) => {
277
- s.chars()
278
- .map(|c| crate::value::Value::String(Arc::from(c.to_string())))
279
- .collect::<Vec<_>>()
375
+ crate::value::Value::Array(arr) => {
376
+ arr.borrow().iter().cloned().collect::<Vec<_>>()
280
377
  }
378
+ crate::value::Value::String(s) => s
379
+ .chars()
380
+ .map(|c| crate::value::Value::String(Arc::from(c.to_string())))
381
+ .collect::<Vec<_>>(),
281
382
  _ => {
282
383
  return Err(EvalError::Error(format!(
283
384
  "for-of requires iterable (array or string), got {}",
@@ -360,7 +461,12 @@ impl Evaluator {
360
461
  self.scope.borrow_mut().set(Arc::clone(name), func, true);
361
462
  Ok(Value::Null)
362
463
  }
363
- Statement::Switch { expr, cases, default_body, .. } => {
464
+ Statement::Switch {
465
+ expr,
466
+ cases,
467
+ default_body,
468
+ ..
469
+ } => {
364
470
  let v = self.eval_expr(expr)?;
365
471
  let mut matched = false;
366
472
  for (case_expr, body) in cases {
@@ -438,7 +544,7 @@ impl Evaluator {
438
544
  ..
439
545
  } => {
440
546
  let try_result = self.eval_statement(body);
441
-
547
+
442
548
  let result = match try_result {
443
549
  Ok(v) => Ok(v),
444
550
  Err(EvalError::Throw(thrown)) => {
@@ -459,37 +565,43 @@ impl Evaluator {
459
565
  }
460
566
  Err(e) => Err(e),
461
567
  };
462
-
568
+
463
569
  if let Some(finally_stmt) = finally_body {
464
570
  let _ = self.eval_statement(finally_stmt);
465
571
  }
466
-
572
+
467
573
  result
468
574
  }
469
- Statement::Import { specifiers, from, .. } => {
575
+ Statement::Import {
576
+ specifiers, from, ..
577
+ } => {
470
578
  let exports_val = self.load_module(from)?;
471
579
  let exports = match &exports_val {
472
580
  Value::Object(m) => m.borrow().clone(),
473
- _ => return Err(EvalError::Error("Module exports must be object".to_string())),
581
+ _ => {
582
+ return Err(EvalError::Error(
583
+ "Module exports must be object".to_string(),
584
+ ))
585
+ }
474
586
  };
475
587
  let mut scope = self.scope.borrow_mut();
476
588
  for spec in specifiers {
477
589
  match spec {
478
- ImportSpecifier::Named { name, alias } => {
590
+ ImportSpecifier::Named { name, alias, .. } => {
479
591
  let v = exports.get(name.as_ref()).ok_or_else(|| {
480
592
  EvalError::Error(format!("Module does not export '{}'", name))
481
593
  })?;
482
594
  let bind = alias.as_deref().unwrap_or(name.as_ref());
483
595
  scope.set(Arc::from(bind), v.clone(), false);
484
596
  }
485
- ImportSpecifier::Namespace(ns) => {
486
- scope.set(Arc::clone(ns), exports_val.clone(), false);
597
+ ImportSpecifier::Namespace { name, .. } => {
598
+ scope.set(Arc::clone(name), exports_val.clone(), false);
487
599
  }
488
- ImportSpecifier::Default(bind) => {
600
+ ImportSpecifier::Default { name, .. } => {
489
601
  let v = exports.get("default").ok_or_else(|| {
490
602
  EvalError::Error("Module does not have default export".to_string())
491
603
  })?;
492
- scope.set(Arc::clone(bind), v.clone(), false);
604
+ scope.set(Arc::clone(name), v.clone(), false);
493
605
  }
494
606
  }
495
607
  }
@@ -507,6 +619,9 @@ impl Evaluator {
507
619
  }
508
620
  Ok(Value::Null)
509
621
  }
622
+ Statement::TypeAlias { .. } | Statement::DeclareVar { .. } | Statement::DeclareFun { .. } => {
623
+ Ok(Value::Null)
624
+ }
510
625
  }
511
626
  }
512
627
 
@@ -514,7 +629,8 @@ impl Evaluator {
514
629
  fn load_module(&mut self, from: &str) -> Result<Value, EvalError> {
515
630
  if from.starts_with("cargo:") {
516
631
  return Err(EvalError::Error(
517
- "cargo:… imports are only supported by `tish build` with the Rust native backend.".into(),
632
+ "cargo:… imports are only supported by `tish build` with the Rust native backend."
633
+ .into(),
518
634
  ));
519
635
  }
520
636
  if from.starts_with("tish:") {
@@ -525,24 +641,24 @@ impl Evaluator {
525
641
  return self.load_builtin_module(from);
526
642
  }
527
643
  let dir = self.current_dir.borrow().clone().ok_or_else(|| {
528
- EvalError::Error("Cannot resolve imports: no current file directory (use run_file)".to_string())
644
+ EvalError::Error(
645
+ "Cannot resolve imports: no current file directory (use run_file)".to_string(),
646
+ )
529
647
  })?;
530
648
  let path = Self::resolve_import_path(from, &dir)?;
531
- let path = path.canonicalize().map_err(|e| {
532
- EvalError::Error(format!("Cannot resolve import '{}': {}", from, e))
533
- })?;
649
+ let path = path
650
+ .canonicalize()
651
+ .map_err(|e| EvalError::Error(format!("Cannot resolve import '{}': {}", from, e)))?;
534
652
  {
535
653
  let cache = self.module_cache.borrow();
536
654
  if let Some(m) = cache.get(&path) {
537
655
  return Ok(m.clone());
538
656
  }
539
657
  }
540
- let source = std::fs::read_to_string(&path).map_err(|e| {
541
- EvalError::Error(format!("Cannot read {}: {}", path.display(), e))
542
- })?;
543
- let program = tishlang_parser::parse(&source).map_err(|e| {
544
- EvalError::Error(format!("Parse error in {}: {}", path.display(), e))
545
- })?;
658
+ let source = std::fs::read_to_string(&path)
659
+ .map_err(|e| EvalError::Error(format!("Cannot read {}: {}", path.display(), e)))?;
660
+ let program = tishlang_parser::parse(&source)
661
+ .map_err(|e| EvalError::Error(format!("Parse error in {}: {}", path.display(), e)))?;
546
662
  let module_scope = Scope::child(Rc::clone(&self.scope));
547
663
  let prev_scope = std::mem::replace(&mut self.scope, Rc::clone(&module_scope));
548
664
  let parent_dir = self.current_dir.borrow().clone();
@@ -554,7 +670,9 @@ impl Evaluator {
554
670
  match declaration.as_ref() {
555
671
  ExportDeclaration::Named(s) => {
556
672
  let _ = self.eval_statement(s);
557
- if let Statement::VarDecl { name, .. } | Statement::FunDecl { name, .. } = s.as_ref() {
673
+ if let Statement::VarDecl { name, .. } | Statement::FunDecl { name, .. } =
674
+ s.as_ref()
675
+ {
558
676
  export_names.push(name.to_string());
559
677
  }
560
678
  }
@@ -577,7 +695,9 @@ impl Evaluator {
577
695
  *self.current_dir.borrow_mut() = parent_dir;
578
696
  self.scope = prev_scope;
579
697
  let exports_val = Value::Object(Rc::new(RefCell::new(exports)));
580
- self.module_cache.borrow_mut().insert(path, exports_val.clone());
698
+ self.module_cache
699
+ .borrow_mut()
700
+ .insert(path, exports_val.clone());
581
701
  Ok(exports_val)
582
702
  }
583
703
 
@@ -640,10 +760,6 @@ impl Evaluator {
640
760
  exports.insert("fetchAll".into(), Value::Native(Self::fetch_all_native));
641
761
  exports.insert("serve".into(), Value::Serve);
642
762
  exports.insert("Promise".into(), Value::PromiseConstructor);
643
- exports.insert("setTimeout".into(), Value::TimerBuiltin(Arc::from("setTimeout")));
644
- exports.insert("setInterval".into(), Value::TimerBuiltin(Arc::from("setInterval")));
645
- exports.insert("clearTimeout".into(), Value::Native(Self::clear_timeout_native));
646
- exports.insert("clearInterval".into(), Value::Native(Self::clear_interval_native));
647
763
  return Ok(Value::Object(Rc::new(RefCell::new(exports))));
648
764
  }
649
765
  #[cfg(not(feature = "http"))]
@@ -653,14 +769,49 @@ impl Evaluator {
653
769
  ));
654
770
  }
655
771
  }
772
+ "tish:timers" => {
773
+ #[cfg(feature = "timers")]
774
+ {
775
+ let mut exports: PropMap = PropMap::default();
776
+ exports.insert(
777
+ "setTimeout".into(),
778
+ Value::TimerBuiltin(Arc::from("setTimeout")),
779
+ );
780
+ exports.insert(
781
+ "setInterval".into(),
782
+ Value::TimerBuiltin(Arc::from("setInterval")),
783
+ );
784
+ exports.insert(
785
+ "clearTimeout".into(),
786
+ Value::Native(Self::clear_timeout_native),
787
+ );
788
+ exports.insert(
789
+ "clearInterval".into(),
790
+ Value::Native(Self::clear_interval_native),
791
+ );
792
+ return Ok(Value::Object(Rc::new(RefCell::new(exports))));
793
+ }
794
+ #[cfg(not(feature = "timers"))]
795
+ {
796
+ return Err(EvalError::Error(
797
+ "tish:timers requires the timers feature. Rebuild with: cargo build -p tishlang --features timers".into(),
798
+ ));
799
+ }
800
+ }
656
801
  "tish:ws" => {
657
802
  #[cfg(feature = "ws")]
658
803
  {
659
804
  let mut exports: PropMap = PropMap::default();
660
- exports.insert("WebSocket".into(), Value::Native(Self::ws_web_socket_native));
805
+ exports.insert(
806
+ "WebSocket".into(),
807
+ Value::Native(Self::ws_web_socket_native),
808
+ );
661
809
  exports.insert("Server".into(), Value::Native(Self::ws_server_native));
662
810
  exports.insert("wsSend".into(), Value::Native(Self::ws_send_native));
663
- exports.insert("wsBroadcast".into(), Value::Native(Self::ws_broadcast_native));
811
+ exports.insert(
812
+ "wsBroadcast".into(),
813
+ Value::Native(Self::ws_broadcast_native),
814
+ );
664
815
  return Ok(Value::Object(Rc::new(RefCell::new(exports))));
665
816
  }
666
817
  #[cfg(not(feature = "ws"))]
@@ -677,21 +828,29 @@ impl Evaluator {
677
828
  exports.insert("exit".into(), Value::Native(natives::process_exit));
678
829
  exports.insert("cwd".into(), Value::Native(natives::process_cwd));
679
830
  exports.insert("exec".into(), Value::Native(natives::process_exec));
680
- let argv: Vec<Value> = std::env::args()
681
- .map(|s| Value::String(s.into()))
682
- .collect();
683
- exports.insert("argv".into(), Value::Array(Rc::new(RefCell::new(argv.clone()))));
831
+ let argv: Vec<Value> =
832
+ std::env::args().map(|s| Value::String(s.into())).collect();
833
+ exports.insert(
834
+ "argv".into(),
835
+ Value::Array(Rc::new(RefCell::new(argv.clone()))),
836
+ );
684
837
  let env_obj: PropMap = std::env::vars()
685
838
  .map(|(key, value)| (Arc::from(key.as_str()), Value::String(value.into())))
686
839
  .collect();
687
- exports.insert("env".into(), Value::Object(Rc::new(RefCell::new(env_obj.clone()))));
840
+ exports.insert(
841
+ "env".into(),
842
+ Value::Object(Rc::new(RefCell::new(env_obj.clone()))),
843
+ );
688
844
  let mut process_obj = PropMap::default();
689
845
  process_obj.insert("exit".into(), Value::Native(natives::process_exit));
690
846
  process_obj.insert("cwd".into(), Value::Native(natives::process_cwd));
691
847
  process_obj.insert("exec".into(), Value::Native(natives::process_exec));
692
848
  process_obj.insert("argv".into(), Value::Array(Rc::new(RefCell::new(argv))));
693
849
  process_obj.insert("env".into(), Value::Object(Rc::new(RefCell::new(env_obj))));
694
- exports.insert("process".into(), Value::Object(Rc::new(RefCell::new(process_obj))));
850
+ exports.insert(
851
+ "process".into(),
852
+ Value::Object(Rc::new(RefCell::new(process_obj))),
853
+ );
695
854
  return Ok(Value::Object(Rc::new(RefCell::new(exports))));
696
855
  }
697
856
  #[cfg(not(feature = "process"))]
@@ -703,7 +862,7 @@ impl Evaluator {
703
862
  }
704
863
  _ => {
705
864
  return Err(EvalError::Error(format!(
706
- "Unknown built-in module: {}. Supported: tish:fs, tish:http, tish:process, tish:ws (plus any registered by native modules)",
865
+ "Unknown built-in module: {}. Supported: tish:fs, tish:http, tish:timers, tish:process, tish:ws (plus any registered by native modules)",
707
866
  spec
708
867
  )));
709
868
  }
@@ -750,7 +909,12 @@ impl Evaluator {
750
909
  }
751
910
  Expr::Call { callee, args, .. } => {
752
911
  // Check for built-in method calls on arrays/strings
753
- if let Expr::Member { object, prop: MemberProp::Name(method_name), .. } = callee.as_ref() {
912
+ if let Expr::Member {
913
+ object,
914
+ prop: MemberProp::Name { name: method_name, .. },
915
+ ..
916
+ } = callee.as_ref()
917
+ {
754
918
  let obj = self.eval_expr(object)?;
755
919
  let arg_vals = self.eval_call_args(args)?;
756
920
 
@@ -1517,7 +1681,7 @@ impl Evaluator {
1517
1681
  return Ok(Value::Null);
1518
1682
  }
1519
1683
  let key = match prop {
1520
- MemberProp::Name(n) => Arc::clone(n),
1684
+ MemberProp::Name { name, .. } => Arc::clone(name),
1521
1685
  MemberProp::Expr(e) => {
1522
1686
  let v = self.eval_expr(e)?;
1523
1687
  match v {
@@ -1638,7 +1802,9 @@ impl Evaluator {
1638
1802
  Value::Serve
1639
1803
  | Value::PromiseResolver(_)
1640
1804
  | Value::PromiseConstructor
1641
- | Value::BoundPromiseMethod(_, _) | Value::TimerBuiltin(_) => "function".into(),
1805
+ | Value::BoundPromiseMethod(_, _) => "function".into(),
1806
+ #[cfg(feature = "timers")]
1807
+ Value::TimerBuiltin(_) => "function".into(),
1642
1808
  #[cfg(feature = "http")]
1643
1809
  Value::Promise(_) => "object".into(),
1644
1810
  #[cfg(feature = "regex")]
@@ -1912,7 +2078,13 @@ impl Evaluator {
1912
2078
  /// descending = false: checks for `(a, b) => a - b`
1913
2079
  /// descending = true: checks for `(a, b) => b - a`
1914
2080
  fn is_numeric_sort_comparator(f: &Value, descending: bool) -> bool {
1915
- if let Value::Function { formals, body, rest_param, .. } = f {
2081
+ if let Value::Function {
2082
+ formals,
2083
+ body,
2084
+ rest_param,
2085
+ ..
2086
+ } = f
2087
+ {
1916
2088
  // Must have exactly 2 simple params, no defaults, no rest
1917
2089
  if formals.len() != 2 || rest_param.is_some() {
1918
2090
  return false;
@@ -1936,15 +2108,29 @@ impl Evaluator {
1936
2108
  };
1937
2109
 
1938
2110
  // Check for binary subtraction
1939
- if let Expr::Binary { left, op: BinOp::Sub, right, .. } = expr {
2111
+ if let Expr::Binary {
2112
+ left,
2113
+ op: BinOp::Sub,
2114
+ right,
2115
+ ..
2116
+ } = expr
2117
+ {
1940
2118
  // Check left is Ident(a) and right is Ident(b)
1941
2119
  let (expected_left, expected_right) = if descending {
1942
- (param_b, param_a) // b - a
2120
+ (param_b, param_a) // b - a
1943
2121
  } else {
1944
- (param_a, param_b) // a - b
2122
+ (param_a, param_b) // a - b
1945
2123
  };
1946
2124
 
1947
- if let (Expr::Ident { name: left_name, .. }, Expr::Ident { name: right_name, .. }) = (left.as_ref(), right.as_ref()) {
2125
+ if let (
2126
+ Expr::Ident {
2127
+ name: left_name, ..
2128
+ },
2129
+ Expr::Ident {
2130
+ name: right_name, ..
2131
+ },
2132
+ ) = (left.as_ref(), right.as_ref())
2133
+ {
1948
2134
  return left_name == expected_left && right_name == expected_right;
1949
2135
  }
1950
2136
  }
@@ -1999,8 +2185,17 @@ impl Evaluator {
1999
2185
 
2000
2186
  /// Optimized callback invocation for array methods.
2001
2187
  /// Creates a reusable scope that can be updated for each iteration.
2002
- fn create_callback_scope(&self, f: &Value) -> Option<(Rc<RefCell<Scope>>, Arc<[Arc<str>]>, Arc<Statement>)> {
2003
- if let Value::Function { formals, body, rest_param, .. } = f {
2188
+ fn create_callback_scope(
2189
+ &self,
2190
+ f: &Value,
2191
+ ) -> Option<(Rc<RefCell<Scope>>, Arc<[Arc<str>]>, Arc<Statement>)> {
2192
+ if let Value::Function {
2193
+ formals,
2194
+ body,
2195
+ rest_param,
2196
+ ..
2197
+ } = f
2198
+ {
2004
2199
  if rest_param.is_some() {
2005
2200
  return None;
2006
2201
  }
@@ -2063,12 +2258,14 @@ impl Evaluator {
2063
2258
 
2064
2259
  /// Try to evaluate a simple callback expression directly without creating a scope.
2065
2260
  /// Returns Some(result) for simple patterns like `x => x * 2` or `x => x > 5`.
2066
- fn eval_simple_callback(
2067
- &self,
2068
- f: &Value,
2069
- args: &[Value],
2070
- ) -> Option<Result<Value, EvalError>> {
2071
- if let Value::Function { formals, body, rest_param, .. } = f {
2261
+ fn eval_simple_callback(&self, f: &Value, args: &[Value]) -> Option<Result<Value, EvalError>> {
2262
+ if let Value::Function {
2263
+ formals,
2264
+ body,
2265
+ rest_param,
2266
+ ..
2267
+ } = f
2268
+ {
2072
2269
  if formals.len() != 1 || rest_param.is_some() {
2073
2270
  return None;
2074
2271
  }
@@ -2088,17 +2285,25 @@ impl Evaluator {
2088
2285
  // Fast path for common patterns
2089
2286
  match expr {
2090
2287
  // x * constant or x + constant, etc.
2091
- Expr::Binary { left, op, right, .. } => {
2288
+ Expr::Binary {
2289
+ left, op, right, ..
2290
+ } => {
2092
2291
  let left_val = self.eval_simple_operand(left, param_name, &arg)?;
2093
2292
  let right_val = self.eval_simple_operand(right, param_name, &arg)?;
2094
- Some(self.eval_binop(&left_val, *op, &right_val).map_err(EvalError::Error))
2293
+ Some(
2294
+ self.eval_binop(&left_val, *op, &right_val)
2295
+ .map_err(EvalError::Error),
2296
+ )
2095
2297
  }
2096
2298
  // Just return the parameter
2097
- Expr::Ident { name, .. } if name == param_name => {
2098
- Some(Ok(arg))
2099
- }
2299
+ Expr::Ident { name, .. } if name == param_name => Some(Ok(arg)),
2100
2300
  // Property access: x.prop
2101
- Expr::Member { object, prop, optional, .. } => {
2301
+ Expr::Member {
2302
+ object,
2303
+ prop,
2304
+ optional,
2305
+ ..
2306
+ } => {
2102
2307
  if let Expr::Ident { name, .. } = object.as_ref() {
2103
2308
  if name == param_name {
2104
2309
  return self.eval_simple_member(&arg, prop, *optional);
@@ -2114,7 +2319,12 @@ impl Evaluator {
2114
2319
  }
2115
2320
 
2116
2321
  /// Evaluate a simple operand (identifier or literal).
2117
- fn eval_simple_operand(&self, expr: &Expr, param_name: &Arc<str>, param_val: &Value) -> Option<Value> {
2322
+ fn eval_simple_operand(
2323
+ &self,
2324
+ expr: &Expr,
2325
+ param_name: &Arc<str>,
2326
+ param_val: &Value,
2327
+ ) -> Option<Value> {
2118
2328
  match expr {
2119
2329
  Expr::Ident { name, .. } if name == param_name => Some(param_val.clone()),
2120
2330
  Expr::Literal { value, .. } => match value {
@@ -2128,20 +2338,27 @@ impl Evaluator {
2128
2338
  }
2129
2339
 
2130
2340
  /// Evaluate simple member access.
2131
- fn eval_simple_member(&self, obj: &Value, property: &MemberProp, _optional: bool) -> Option<Result<Value, EvalError>> {
2341
+ fn eval_simple_member(
2342
+ &self,
2343
+ obj: &Value,
2344
+ property: &MemberProp,
2345
+ _optional: bool,
2346
+ ) -> Option<Result<Value, EvalError>> {
2132
2347
  match property {
2133
- MemberProp::Name(name) => {
2134
- match obj {
2135
- Value::Object(o) => {
2136
- let result = o.borrow().get(name.as_ref()).cloned().unwrap_or(Value::Null);
2137
- Some(Ok(result))
2138
- }
2139
- Value::Array(arr) if name.as_ref() == "length" => {
2140
- Some(Ok(Value::Number(arr.borrow().len() as f64)))
2141
- }
2142
- _ => None,
2348
+ MemberProp::Name { name, .. } => match obj {
2349
+ Value::Object(o) => {
2350
+ let result = o
2351
+ .borrow()
2352
+ .get(name.as_ref())
2353
+ .cloned()
2354
+ .unwrap_or(Value::Null);
2355
+ Some(Ok(result))
2356
+ }
2357
+ Value::Array(arr) if name.as_ref() == "length" => {
2358
+ Some(Ok(Value::Number(arr.borrow().len() as f64)))
2143
2359
  }
2144
- }
2360
+ _ => None,
2361
+ },
2145
2362
  _ => None,
2146
2363
  }
2147
2364
  }
@@ -2160,8 +2377,9 @@ impl Evaluator {
2160
2377
  #[cfg(feature = "http")]
2161
2378
  Value::PromiseConstructor
2162
2379
  | Value::Serve
2163
- | Value::BoundPromiseMethod(_, _)
2164
- | Value::TimerBuiltin(_) => self.call_func(callee, args),
2380
+ | Value::BoundPromiseMethod(_, _) => self.call_func(callee, args),
2381
+ #[cfg(feature = "timers")]
2382
+ Value::TimerBuiltin(_) => self.call_func(callee, args),
2165
2383
  Value::OpaqueMethod(_, _) => self.call_func(callee, args),
2166
2384
  _ => Ok(Value::Null),
2167
2385
  }
@@ -2169,9 +2387,7 @@ impl Evaluator {
2169
2387
 
2170
2388
  fn call_func(&self, f: &Value, args: &[Value]) -> Result<Value, EvalError> {
2171
2389
  match f {
2172
- Value::Native(native_fn) => {
2173
- native_fn(args).map_err(EvalError::Error)
2174
- }
2390
+ Value::Native(native_fn) => native_fn(args).map_err(EvalError::Error),
2175
2391
  #[cfg(feature = "http")]
2176
2392
  Value::PromiseResolver(r) => {
2177
2393
  let value = args.first().cloned().unwrap_or(Value::Null);
@@ -2180,7 +2396,12 @@ impl Evaluator {
2180
2396
  .map_err(EvalError::Error)?;
2181
2397
  for reaction in reactions {
2182
2398
  match reaction {
2183
- crate::promise::Reaction::Then(on_fulfilled, on_rejected, ref resolve, ref reject) => {
2399
+ crate::promise::Reaction::Then(
2400
+ on_fulfilled,
2401
+ on_rejected,
2402
+ ref resolve,
2403
+ ref reject,
2404
+ ) => {
2184
2405
  let handler_result = if is_fulfilled {
2185
2406
  if let Some(ref h) = on_fulfilled {
2186
2407
  self.call_func(h, &[val.clone()])
@@ -2232,8 +2453,10 @@ impl Evaluator {
2232
2453
  #[cfg(feature = "http")]
2233
2454
  Value::Serve => self.run_http_server(args),
2234
2455
  Value::CoreFn(f) => {
2235
- let ca: Result<Vec<tishlang_core::Value>, String> =
2236
- args.iter().map(crate::value_convert::eval_to_core).collect();
2456
+ let ca: Result<Vec<tishlang_core::Value>, String> = args
2457
+ .iter()
2458
+ .map(crate::value_convert::eval_to_core)
2459
+ .collect();
2237
2460
  let ca = ca.map_err(EvalError::Error)?;
2238
2461
  Ok(crate::value_convert::core_to_eval(f(&ca)))
2239
2462
  }
@@ -2243,19 +2466,29 @@ impl Evaluator {
2243
2466
  Value::BoundPromiseMethod(promise_ref, method) => {
2244
2467
  self.run_promise_method(promise_ref, method.as_ref(), args)
2245
2468
  }
2246
- #[cfg(feature = "http")]
2469
+ #[cfg(feature = "timers")]
2247
2470
  Value::TimerBuiltin(name) => self.run_timer_builtin(name.as_ref(), args),
2248
2471
  Value::OpaqueMethod(opaque, method_name) => {
2249
2472
  let method = opaque.get_method(method_name.as_ref()).ok_or_else(|| {
2250
- EvalError::Error(format!("Method {} not found on {}", method_name, opaque.type_name()))
2473
+ EvalError::Error(format!(
2474
+ "Method {} not found on {}",
2475
+ method_name,
2476
+ opaque.type_name()
2477
+ ))
2251
2478
  })?;
2252
- let core_args: Result<Vec<tishlang_core::Value>, String> =
2253
- args.iter().map(crate::value_convert::eval_to_core).collect();
2479
+ let core_args: Result<Vec<tishlang_core::Value>, String> = args
2480
+ .iter()
2481
+ .map(crate::value_convert::eval_to_core)
2482
+ .collect();
2254
2483
  let core_args = core_args.map_err(EvalError::Error)?;
2255
2484
  let result = method(&core_args);
2256
2485
  Ok(crate::value_convert::core_to_eval(result))
2257
2486
  }
2258
- Value::Function { formals, rest_param, body } => {
2487
+ Value::Function {
2488
+ formals,
2489
+ rest_param,
2490
+ body,
2491
+ } => {
2259
2492
  let scope = Scope::child(Rc::clone(&self.scope));
2260
2493
  {
2261
2494
  let mut s = scope.borrow_mut();
@@ -2289,8 +2522,13 @@ impl Evaluator {
2289
2522
  }
2290
2523
  }
2291
2524
  if let Some(ref rest_name) = rest_param {
2292
- let rest_vals: Vec<Value> = args.iter().skip(formals.len()).cloned().collect();
2293
- s.set(Arc::clone(rest_name), Value::Array(Rc::new(RefCell::new(rest_vals))), true);
2525
+ let rest_vals: Vec<Value> =
2526
+ args.iter().skip(formals.len()).cloned().collect();
2527
+ s.set(
2528
+ Arc::clone(rest_name),
2529
+ Value::Array(Rc::new(RefCell::new(rest_vals))),
2530
+ true,
2531
+ );
2294
2532
  }
2295
2533
  }
2296
2534
  let mut eval = Evaluator {
@@ -2304,8 +2542,12 @@ impl Evaluator {
2304
2542
  Err(EvalError::Return(v)) => Ok(v),
2305
2543
  Err(EvalError::Throw(v)) => Err(EvalError::Throw(v)),
2306
2544
  Err(EvalError::Error(s)) => Err(EvalError::Error(s)),
2307
- Err(EvalError::Break) => Err(EvalError::Error("break outside loop".to_string())),
2308
- Err(EvalError::Continue) => Err(EvalError::Error("continue outside loop".to_string())),
2545
+ Err(EvalError::Break) => {
2546
+ Err(EvalError::Error("break outside loop".to_string()))
2547
+ }
2548
+ Err(EvalError::Continue) => {
2549
+ Err(EvalError::Error("continue outside loop".to_string()))
2550
+ }
2309
2551
  }
2310
2552
  }
2311
2553
  _ => Err(EvalError::Error("Not a function".to_string())),
@@ -2320,14 +2562,15 @@ impl Evaluator {
2320
2562
  args: &[Value],
2321
2563
  ) -> Result<Value, EvalError> {
2322
2564
  match method {
2323
- "then" => self.run_promise_then_core(
2324
- promise_ref,
2325
- args.first().cloned(),
2326
- args.get(1).cloned(),
2327
- ),
2565
+ "then" => {
2566
+ self.run_promise_then_core(promise_ref, args.first().cloned(), args.get(1).cloned())
2567
+ }
2328
2568
  "catch" => self.run_promise_then_core(promise_ref, None, args.first().cloned()),
2329
2569
  "finally" => self.run_promise_finally(promise_ref, args.first().cloned()),
2330
- _ => Err(EvalError::Error(format!("Unknown promise method: {}", method))),
2570
+ _ => Err(EvalError::Error(format!(
2571
+ "Unknown promise method: {}",
2572
+ method
2573
+ ))),
2331
2574
  }
2332
2575
  }
2333
2576
 
@@ -2428,7 +2671,12 @@ impl Evaluator {
2428
2671
  crate::promise::PromiseState::Pending { .. } => {
2429
2672
  crate::promise::add_reaction(
2430
2673
  state,
2431
- crate::promise::Reaction::Then(on_fulfilled, on_rejected, resolve.clone(), reject.clone()),
2674
+ crate::promise::Reaction::Then(
2675
+ on_fulfilled,
2676
+ on_rejected,
2677
+ resolve.clone(),
2678
+ reject.clone(),
2679
+ ),
2432
2680
  );
2433
2681
  }
2434
2682
  }
@@ -2436,7 +2684,7 @@ impl Evaluator {
2436
2684
  Ok(promise)
2437
2685
  }
2438
2686
 
2439
- #[cfg(feature = "http")]
2687
+ #[cfg(feature = "timers")]
2440
2688
  fn run_timer_builtin(&self, name: &str, args: &[Value]) -> Result<Value, EvalError> {
2441
2689
  let callback = args
2442
2690
  .first()
@@ -2457,7 +2705,7 @@ impl Evaluator {
2457
2705
  Ok(Value::Number(id as f64))
2458
2706
  }
2459
2707
 
2460
- #[cfg(feature = "http")]
2708
+ #[cfg(feature = "timers")]
2461
2709
  fn clear_timeout_native(args: &[Value]) -> Result<Value, String> {
2462
2710
  if let Some(Value::Number(n)) = args.first() {
2463
2711
  crate::timers::clearTimer(*n as u64);
@@ -2465,7 +2713,7 @@ impl Evaluator {
2465
2713
  Ok(Value::Null)
2466
2714
  }
2467
2715
 
2468
- #[cfg(feature = "http")]
2716
+ #[cfg(feature = "timers")]
2469
2717
  fn clear_interval_native(args: &[Value]) -> Result<Value, String> {
2470
2718
  if let Some(Value::Number(n)) = args.first() {
2471
2719
  crate::timers::clearTimer(*n as u64);
@@ -2475,7 +2723,7 @@ impl Evaluator {
2475
2723
 
2476
2724
  /// Run all due timer callbacks. Called after the script completes so setTimeout/setInterval
2477
2725
  /// callbacks run without blocking the main script. Loops until no timers are due.
2478
- #[cfg(feature = "http")]
2726
+ #[cfg(feature = "timers")]
2479
2727
  pub fn run_timer_phase(&mut self) -> Result<(), String> {
2480
2728
  const MAX_ITERATIONS: u32 = 1_000_000; // avoid infinite loop if setInterval never cleared
2481
2729
  let mut iterations = 0;
@@ -2537,8 +2785,11 @@ impl Evaluator {
2537
2785
  let port = port;
2538
2786
  std::thread::spawn(move || {
2539
2787
  std::thread::sleep(std::time::Duration::from_millis(50));
2540
- if let Ok(mut stream) = std::net::TcpStream::connect(format!("127.0.0.1:{}", port)) {
2541
- let _ = stream.write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n");
2788
+ if let Ok(mut stream) = std::net::TcpStream::connect(format!("127.0.0.1:{}", port))
2789
+ {
2790
+ let _ = stream.write_all(
2791
+ b"GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n",
2792
+ );
2542
2793
  let _ = stream.shutdown(std::net::Shutdown::Write);
2543
2794
  }
2544
2795
  });
@@ -2564,8 +2815,14 @@ impl Evaluator {
2564
2815
  }
2565
2816
  };
2566
2817
 
2567
- let (status, headers, body) = crate::http::value_to_response(&response_value);
2568
- crate::http::send_response(request, status, headers, body);
2818
+ if let Some((status, headers, file_path)) =
2819
+ crate::http::extract_file_from_response(&response_value)
2820
+ {
2821
+ crate::http::send_file_response(request, status, headers, file_path);
2822
+ } else {
2823
+ let (status, headers, body) = crate::http::value_to_response(&response_value);
2824
+ crate::http::send_response(request, status, headers, body);
2825
+ }
2569
2826
  count += 1;
2570
2827
  if max_requests.map(|m| count >= m).unwrap_or(false) {
2571
2828
  break;
@@ -2603,13 +2860,17 @@ impl Evaluator {
2603
2860
  tishlang_ast::DestructPattern::Array(elements) => {
2604
2861
  let arr = match value {
2605
2862
  Value::Array(a) => a.borrow().clone(),
2606
- _ => return Err(EvalError::Error("Cannot destructure non-array value".to_string())),
2863
+ _ => {
2864
+ return Err(EvalError::Error(
2865
+ "Cannot destructure non-array value".to_string(),
2866
+ ))
2867
+ }
2607
2868
  };
2608
2869
 
2609
2870
  for (i, elem) in elements.iter().enumerate() {
2610
2871
  if let Some(el) = elem {
2611
2872
  match el {
2612
- tishlang_ast::DestructElement::Ident(name) => {
2873
+ tishlang_ast::DestructElement::Ident(name, _) => {
2613
2874
  let val = arr.get(i).cloned().unwrap_or(Value::Null);
2614
2875
  scope.borrow_mut().set(Arc::clone(name), val, mutable);
2615
2876
  }
@@ -2617,7 +2878,7 @@ impl Evaluator {
2617
2878
  let val = arr.get(i).cloned().unwrap_or(Value::Null);
2618
2879
  Self::bind_destruct_pattern_scoped(scope, nested, &val, mutable)?;
2619
2880
  }
2620
- tishlang_ast::DestructElement::Rest(name) => {
2881
+ tishlang_ast::DestructElement::Rest(name, _) => {
2621
2882
  let rest: Vec<Value> = arr.iter().skip(i).cloned().collect();
2622
2883
  scope.borrow_mut().set(
2623
2884
  Arc::clone(name),
@@ -2633,19 +2894,23 @@ impl Evaluator {
2633
2894
  tishlang_ast::DestructPattern::Object(props) => {
2634
2895
  let obj = match value {
2635
2896
  Value::Object(o) => o.borrow().clone(),
2636
- _ => return Err(EvalError::Error("Cannot destructure non-object value".to_string())),
2897
+ _ => {
2898
+ return Err(EvalError::Error(
2899
+ "Cannot destructure non-object value".to_string(),
2900
+ ))
2901
+ }
2637
2902
  };
2638
2903
 
2639
2904
  for prop in props {
2640
2905
  let val = obj.get(&prop.key).cloned().unwrap_or(Value::Null);
2641
2906
  match &prop.value {
2642
- tishlang_ast::DestructElement::Ident(name) => {
2907
+ tishlang_ast::DestructElement::Ident(name, _) => {
2643
2908
  scope.borrow_mut().set(Arc::clone(name), val, mutable);
2644
2909
  }
2645
2910
  tishlang_ast::DestructElement::Pattern(nested) => {
2646
2911
  Self::bind_destruct_pattern_scoped(scope, nested, &val, mutable)?;
2647
2912
  }
2648
- tishlang_ast::DestructElement::Rest(_) => {
2913
+ tishlang_ast::DestructElement::Rest(_, _) => {
2649
2914
  return Err(EvalError::Error(
2650
2915
  "Rest not supported in object destructuring".to_string(),
2651
2916
  ));
@@ -2657,7 +2922,12 @@ impl Evaluator {
2657
2922
  Ok(())
2658
2923
  }
2659
2924
 
2660
- fn bind_destruct_pattern(&mut self, pattern: &tishlang_ast::DestructPattern, value: &Value, mutable: bool) -> Result<(), EvalError> {
2925
+ fn bind_destruct_pattern(
2926
+ &mut self,
2927
+ pattern: &tishlang_ast::DestructPattern,
2928
+ value: &Value,
2929
+ mutable: bool,
2930
+ ) -> Result<(), EvalError> {
2661
2931
  Self::bind_destruct_pattern_scoped(&self.scope, pattern, value, mutable)
2662
2932
  }
2663
2933
 
@@ -2675,11 +2945,8 @@ impl Evaluator {
2675
2945
  Some(Value::Bool(b)) => tishlang_core::Value::Bool(*b),
2676
2946
  Some(_) => tishlang_core::Value::Number(0.0),
2677
2947
  };
2678
- let out = tishlang_builtins::string::last_index_of_str(
2679
- receiver.as_ref(),
2680
- search,
2681
- &position_core,
2682
- );
2948
+ let out =
2949
+ tishlang_builtins::string::last_index_of_str(receiver.as_ref(), search, &position_core);
2683
2950
  match out {
2684
2951
  tishlang_core::Value::Number(n) => Value::Number(n),
2685
2952
  _ => Value::Number(-1.0),
@@ -2963,7 +3230,9 @@ impl Evaluator {
2963
3230
  Ok((Value::Bool(false), rest))
2964
3231
  } else {
2965
3232
  let end = s
2966
- .find(|c: char| !c.is_ascii_digit() && c != '-' && c != '+' && c != '.' && c != 'e' && c != 'E')
3233
+ .find(|c: char| {
3234
+ !c.is_ascii_digit() && c != '-' && c != '+' && c != '.' && c != 'e' && c != 'E'
3235
+ })
2967
3236
  .unwrap_or(s.len());
2968
3237
  let num_str = &s[..end];
2969
3238
  let n: f64 = num_str.parse().map_err(|_| ())?;
@@ -2991,7 +3260,11 @@ impl Evaluator {
2991
3260
  .replace('\t', "\\t")
2992
3261
  ),
2993
3262
  Value::Array(arr) => {
2994
- let inner: Vec<String> = arr.borrow().iter().map(Self::json_stringify_value).collect();
3263
+ let inner: Vec<String> = arr
3264
+ .borrow()
3265
+ .iter()
3266
+ .map(Self::json_stringify_value)
3267
+ .collect();
2995
3268
  format!("[{}]", inner.join(","))
2996
3269
  }
2997
3270
  Value::Object(map) => {
@@ -3010,7 +3283,14 @@ impl Evaluator {
3010
3283
  })
3011
3284
  .collect();
3012
3285
  entries.sort_by(|a, b| a.0.cmp(&b.0));
3013
- format!("{{{}}}", entries.into_iter().map(|(_, s)| s).collect::<Vec<_>>().join(","))
3286
+ format!(
3287
+ "{{{}}}",
3288
+ entries
3289
+ .into_iter()
3290
+ .map(|(_, s)| s)
3291
+ .collect::<Vec<_>>()
3292
+ .join(",")
3293
+ )
3014
3294
  }
3015
3295
  Value::Function { .. } | Value::Native(_) => "null".to_string(),
3016
3296
  #[cfg(feature = "http")]
@@ -3021,7 +3301,9 @@ impl Evaluator {
3021
3301
  | Value::Promise(_)
3022
3302
  | Value::PromiseResolver(_)
3023
3303
  | Value::PromiseConstructor
3024
- | Value::BoundPromiseMethod(_, _) | Value::TimerBuiltin(_) => "null".to_string(),
3304
+ | Value::BoundPromiseMethod(_, _) => "null".to_string(),
3305
+ #[cfg(feature = "timers")]
3306
+ Value::TimerBuiltin(_) => "null".to_string(),
3025
3307
  #[cfg(feature = "regex")]
3026
3308
  Value::RegExp(_) => "null".to_string(),
3027
3309
  Value::Opaque(_) | Value::OpaqueMethod(_, _) => "null".to_string(),
@@ -3041,7 +3323,11 @@ impl Evaluator {
3041
3323
 
3042
3324
  fn object_keys(args: &[Value]) -> Result<Value, String> {
3043
3325
  if let Some(Value::Object(obj)) = args.first() {
3044
- let keys: Vec<Value> = obj.borrow().keys().map(|k| Value::String(Arc::clone(k))).collect();
3326
+ let keys: Vec<Value> = obj
3327
+ .borrow()
3328
+ .keys()
3329
+ .map(|k| Value::String(Arc::clone(k)))
3330
+ .collect();
3045
3331
  Ok(Value::Array(Rc::new(RefCell::new(keys))))
3046
3332
  } else {
3047
3333
  Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
@@ -3059,12 +3345,16 @@ impl Evaluator {
3059
3345
 
3060
3346
  fn object_entries(args: &[Value]) -> Result<Value, String> {
3061
3347
  if let Some(Value::Object(obj)) = args.first() {
3062
- let entries: Vec<Value> = obj.borrow().iter().map(|(k, v)| {
3063
- Value::Array(Rc::new(RefCell::new(vec![
3064
- Value::String(Arc::clone(k)),
3065
- v.clone(),
3066
- ])))
3067
- }).collect();
3348
+ let entries: Vec<Value> = obj
3349
+ .borrow()
3350
+ .iter()
3351
+ .map(|(k, v)| {
3352
+ Value::Array(Rc::new(RefCell::new(vec![
3353
+ Value::String(Arc::clone(k)),
3354
+ v.clone(),
3355
+ ])))
3356
+ })
3357
+ .collect();
3068
3358
  Ok(Value::Array(Rc::new(RefCell::new(entries))))
3069
3359
  } else {
3070
3360
  Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
@@ -3136,7 +3426,10 @@ impl Evaluator {
3136
3426
  .ok_or_else(|| "Promise.all requires an iterable".to_string())?;
3137
3427
  let values: Vec<Value> = match iterable {
3138
3428
  Value::Array(arr) => arr.borrow().clone(),
3139
- Value::String(s) => s.chars().map(|c| Value::String(c.to_string().into())).collect(),
3429
+ Value::String(s) => s
3430
+ .chars()
3431
+ .map(|c| Value::String(c.to_string().into()))
3432
+ .collect(),
3140
3433
  _ => return Err("Promise.all requires array or iterable".to_string()),
3141
3434
  };
3142
3435
  let mut results = Vec::with_capacity(values.len());
@@ -3146,7 +3439,8 @@ impl Evaluator {
3146
3439
  crate::promise::PromiseAwaitResult::Fulfilled(x) => results.push(x),
3147
3440
  crate::promise::PromiseAwaitResult::Rejected(x) => {
3148
3441
  let (promise, resolve_val, reject_val) = crate::promise::create_promise();
3149
- let (_, reject) = crate::promise::extract_resolvers(&resolve_val, &reject_val);
3442
+ let (_, reject) =
3443
+ crate::promise::extract_resolvers(&resolve_val, &reject_val);
3150
3444
  let _ = crate::promise::settle_promise(&reject, x, false);
3151
3445
  return Ok(promise);
3152
3446
  }
@@ -3157,7 +3451,8 @@ impl Evaluator {
3157
3451
  Ok(x) => results.push(crate::value_convert::core_to_eval(x)),
3158
3452
  Err(x) => {
3159
3453
  let (promise, resolve_val, reject_val) = crate::promise::create_promise();
3160
- let (_, reject) = crate::promise::extract_resolvers(&resolve_val, &reject_val);
3454
+ let (_, reject) =
3455
+ crate::promise::extract_resolvers(&resolve_val, &reject_val);
3161
3456
  let _ = crate::promise::settle_promise(
3162
3457
  &reject,
3163
3458
  crate::value_convert::core_to_eval(x),
@@ -3184,7 +3479,10 @@ impl Evaluator {
3184
3479
  .ok_or_else(|| "Promise.race requires an iterable".to_string())?;
3185
3480
  let values: Vec<Value> = match iterable {
3186
3481
  Value::Array(arr) => arr.borrow().clone(),
3187
- Value::String(s) => s.chars().map(|c| Value::String(c.to_string().into())).collect(),
3482
+ Value::String(s) => s
3483
+ .chars()
3484
+ .map(|c| Value::String(c.to_string().into()))
3485
+ .collect(),
3188
3486
  _ => return Err("Promise.race requires array or iterable".to_string()),
3189
3487
  };
3190
3488
  for v in values {
@@ -3192,7 +3490,8 @@ impl Evaluator {
3192
3490
  match p.block_until_settled() {
3193
3491
  Ok(x) => {
3194
3492
  let (promise, resolve_val, reject_val) = crate::promise::create_promise();
3195
- let (resolve, _) = crate::promise::extract_resolvers(&resolve_val, &reject_val);
3493
+ let (resolve, _) =
3494
+ crate::promise::extract_resolvers(&resolve_val, &reject_val);
3196
3495
  crate::promise::settle_promise(
3197
3496
  &resolve,
3198
3497
  crate::value_convert::core_to_eval(x),
@@ -3202,7 +3501,8 @@ impl Evaluator {
3202
3501
  }
3203
3502
  Err(x) => {
3204
3503
  let (promise, resolve_val, reject_val) = crate::promise::create_promise();
3205
- let (_, reject) = crate::promise::extract_resolvers(&resolve_val, &reject_val);
3504
+ let (_, reject) =
3505
+ crate::promise::extract_resolvers(&resolve_val, &reject_val);
3206
3506
  crate::promise::settle_promise(
3207
3507
  &reject,
3208
3508
  crate::value_convert::core_to_eval(x),
@@ -3216,13 +3516,15 @@ impl Evaluator {
3216
3516
  match crate::promise::block_until_settled(p) {
3217
3517
  crate::promise::PromiseAwaitResult::Fulfilled(x) => {
3218
3518
  let (promise, resolve_val, reject_val) = crate::promise::create_promise();
3219
- let (resolve, _) = crate::promise::extract_resolvers(&resolve_val, &reject_val);
3519
+ let (resolve, _) =
3520
+ crate::promise::extract_resolvers(&resolve_val, &reject_val);
3220
3521
  crate::promise::settle_promise(&resolve, x, true)?;
3221
3522
  return Ok(promise);
3222
3523
  }
3223
3524
  crate::promise::PromiseAwaitResult::Rejected(x) => {
3224
3525
  let (promise, resolve_val, reject_val) = crate::promise::create_promise();
3225
- let (_, reject) = crate::promise::extract_resolvers(&resolve_val, &reject_val);
3526
+ let (_, reject) =
3527
+ crate::promise::extract_resolvers(&resolve_val, &reject_val);
3226
3528
  crate::promise::settle_promise(&reject, x, false)?;
3227
3529
  return Ok(promise);
3228
3530
  }
@@ -3239,7 +3541,9 @@ impl Evaluator {
3239
3541
  for a in args {
3240
3542
  cv.push(crate::value_convert::eval_to_core(a)?);
3241
3543
  }
3242
- Ok(crate::value_convert::core_to_eval(tishlang_runtime::web_socket_client(&cv)))
3544
+ Ok(crate::value_convert::core_to_eval(
3545
+ tishlang_runtime::web_socket_client(&cv),
3546
+ ))
3243
3547
  }
3244
3548
 
3245
3549
  #[cfg(feature = "ws")]
@@ -3248,7 +3552,9 @@ impl Evaluator {
3248
3552
  for a in args {
3249
3553
  cv.push(crate::value_convert::eval_to_core(a)?);
3250
3554
  }
3251
- Ok(crate::value_convert::core_to_eval(tishlang_runtime::web_socket_server_construct(&cv)))
3555
+ Ok(crate::value_convert::core_to_eval(
3556
+ tishlang_runtime::web_socket_server_construct(&cv),
3557
+ ))
3252
3558
  }
3253
3559
 
3254
3560
  #[cfg(feature = "ws")]
@@ -3256,7 +3562,9 @@ impl Evaluator {
3256
3562
  let conn = args.first().ok_or("wsSend(conn, data) requires conn")?;
3257
3563
  let conn_core = crate::value_convert::eval_to_core(conn)?;
3258
3564
  let data = args.get(1).map(|v| v.to_string()).unwrap_or_default();
3259
- Ok(Value::Bool(tishlang_runtime::ws_send_native(&conn_core, &data)))
3565
+ Ok(Value::Bool(tishlang_runtime::ws_send_native(
3566
+ &conn_core, &data,
3567
+ )))
3260
3568
  }
3261
3569
 
3262
3570
  #[cfg(feature = "ws")]
@@ -3265,7 +3573,9 @@ impl Evaluator {
3265
3573
  for a in args {
3266
3574
  cv.push(crate::value_convert::eval_to_core(a)?);
3267
3575
  }
3268
- Ok(crate::value_convert::core_to_eval(tishlang_runtime::ws_broadcast_native(&cv)))
3576
+ Ok(crate::value_convert::core_to_eval(
3577
+ tishlang_runtime::ws_broadcast_native(&cv),
3578
+ ))
3269
3579
  }
3270
3580
 
3271
3581
  #[cfg(feature = "http")]
@@ -3319,7 +3629,6 @@ impl Evaluator {
3319
3629
  "await requires the http feature".to_string(),
3320
3630
  ))
3321
3631
  }
3322
-
3323
3632
  }
3324
3633
 
3325
3634
  #[derive(Debug)]