@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
@@ -30,7 +30,6 @@ impl Constant {
30
30
  }
31
31
  }
32
32
 
33
-
34
33
  /// A bytecode chunk: instructions and associated data.
35
34
  #[derive(Debug, Clone)]
36
35
  pub struct Chunk {
@@ -4,9 +4,9 @@ use std::collections::HashMap;
4
4
  use std::sync::Arc;
5
5
 
6
6
  use tishlang_ast::{
7
- ArrayElement, ArrowBody, BinOp, CallArg, DestructElement, DestructPattern, Expr,
8
- ExportDeclaration, FunParam, JsxAttrValue, JsxChild, JsxProp, Literal, LogicalAssignOp,
9
- MemberProp, ObjectProp, Program, Span, Statement,
7
+ ArrayElement, ArrowBody, BinOp, CallArg, DestructElement, DestructPattern, ExportDeclaration,
8
+ Expr, FunParam, JsxAttrValue, JsxChild, JsxProp, Literal, LogicalAssignOp, MemberProp,
9
+ ObjectProp, Program, Span, Statement,
10
10
  };
11
11
 
12
12
  use crate::chunk::{Chunk, Constant};
@@ -120,9 +120,7 @@ impl<'a> Compiler<'a> {
120
120
  slots.push(None);
121
121
  }
122
122
  FunParam::Destructure {
123
- pattern,
124
- default,
125
- ..
123
+ pattern, default, ..
126
124
  } => {
127
125
  if default.is_some() {
128
126
  return Err(CompileError {
@@ -274,8 +272,18 @@ impl<'a> Compiler<'a> {
274
272
  ..
275
273
  } = body_expr
276
274
  {
277
- if let (Expr::Member { object: lo, prop: MemberProp::Name(p), .. }, Expr::Member { object: ro, prop: MemberProp::Name(pr), .. }) =
278
- (left.as_ref(), right.as_ref())
275
+ if let (
276
+ Expr::Member {
277
+ object: lo,
278
+ prop: MemberProp::Name { name: p, .. },
279
+ ..
280
+ },
281
+ Expr::Member {
282
+ object: ro,
283
+ prop: MemberProp::Name { name: pr, .. },
284
+ ..
285
+ },
286
+ ) = (left.as_ref(), right.as_ref())
279
287
  {
280
288
  if p != pr {
281
289
  return None;
@@ -327,8 +335,14 @@ impl<'a> Compiler<'a> {
327
335
  ..
328
336
  } = body_expr
329
337
  {
330
- if let (Expr::Ident { name: left_name, .. }, Expr::Ident { name: right_name, .. }) =
331
- (left.as_ref(), right.as_ref())
338
+ if let (
339
+ Expr::Ident {
340
+ name: left_name, ..
341
+ },
342
+ Expr::Ident {
343
+ name: right_name, ..
344
+ },
345
+ ) = (left.as_ref(), right.as_ref())
332
346
  {
333
347
  if left_name.as_ref() == param_a && right_name.as_ref() == param_b {
334
348
  return Some(true);
@@ -360,7 +374,10 @@ impl<'a> Compiler<'a> {
360
374
  ArrowBody::Expr(e) => e.as_ref(),
361
375
  ArrowBody::Block(stmt) => {
362
376
  let s = stmt.as_ref();
363
- if let Statement::Return { value: Some(ref e), .. } = s {
377
+ if let Statement::Return {
378
+ value: Some(ref e), ..
379
+ } = s
380
+ {
364
381
  e
365
382
  } else if let Statement::ExprStmt { expr: ref e, .. } = s {
366
383
  e
@@ -376,9 +393,14 @@ impl<'a> Compiler<'a> {
376
393
  }
377
394
  }
378
395
  // Binary: x op const or const op x
379
- if let Expr::Binary { left, op, right, .. } = expr_ref {
380
- let left_is_param = matches!(left.as_ref(), Expr::Ident { name, .. } if name.as_ref() == param_name);
381
- let right_is_param = matches!(right.as_ref(), Expr::Ident { name, .. } if name.as_ref() == param_name);
396
+ if let Expr::Binary {
397
+ left, op, right, ..
398
+ } = expr_ref
399
+ {
400
+ let left_is_param =
401
+ matches!(left.as_ref(), Expr::Ident { name, .. } if name.as_ref() == param_name);
402
+ let right_is_param =
403
+ matches!(right.as_ref(), Expr::Ident { name, .. } if name.as_ref() == param_name);
382
404
  let left_is_literal = matches!(left.as_ref(), Expr::Literal { .. });
383
405
  let right_is_literal = matches!(right.as_ref(), Expr::Literal { .. });
384
406
  if left_is_param && right_is_literal {
@@ -412,7 +434,10 @@ impl<'a> Compiler<'a> {
412
434
  ArrowBody::Expr(e) => e.as_ref(),
413
435
  ArrowBody::Block(stmt) => {
414
436
  let s = stmt.as_ref();
415
- if let Statement::Return { value: Some(ref e), .. } = s {
437
+ if let Statement::Return {
438
+ value: Some(ref e), ..
439
+ } = s
440
+ {
416
441
  e
417
442
  } else if let Statement::ExprStmt { expr: ref e, .. } = s {
418
443
  e
@@ -421,12 +446,29 @@ impl<'a> Compiler<'a> {
421
446
  }
422
447
  }
423
448
  };
424
- if let Expr::Binary { left, op, right, .. } = expr_ref {
425
- if !matches!(op, BinOp::Eq | BinOp::Ne | BinOp::StrictEq | BinOp::StrictNe | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge | BinOp::And | BinOp::Or) {
449
+ if let Expr::Binary {
450
+ left, op, right, ..
451
+ } = expr_ref
452
+ {
453
+ if !matches!(
454
+ op,
455
+ BinOp::Eq
456
+ | BinOp::Ne
457
+ | BinOp::StrictEq
458
+ | BinOp::StrictNe
459
+ | BinOp::Lt
460
+ | BinOp::Le
461
+ | BinOp::Gt
462
+ | BinOp::Ge
463
+ | BinOp::And
464
+ | BinOp::Or
465
+ ) {
426
466
  return None;
427
467
  }
428
- let left_is_param = matches!(left.as_ref(), Expr::Ident { name, .. } if name.as_ref() == param_name);
429
- let right_is_param = matches!(right.as_ref(), Expr::Ident { name, .. } if name.as_ref() == param_name);
468
+ let left_is_param =
469
+ matches!(left.as_ref(), Expr::Ident { name, .. } if name.as_ref() == param_name);
470
+ let right_is_param =
471
+ matches!(right.as_ref(), Expr::Ident { name, .. } if name.as_ref() == param_name);
430
472
  let left_is_literal = matches!(left.as_ref(), Expr::Literal { .. });
431
473
  let right_is_literal = matches!(right.as_ref(), Expr::Literal { .. });
432
474
  if left_is_param && right_is_literal {
@@ -534,10 +576,9 @@ impl<'a> Compiler<'a> {
534
576
  continue_patches: Vec::new(),
535
577
  continue_is_forward_jump: false,
536
578
  });
537
- self.breakable_stack
538
- .push(Breakable::Loop {
539
- unwind_depth: self.block_depth,
540
- });
579
+ self.breakable_stack.push(Breakable::Loop {
580
+ unwind_depth: self.block_depth,
581
+ });
541
582
  self.compile_expr(cond)?;
542
583
  let jump_out = self.emit_jump(Opcode::JumpIfFalse);
543
584
  // JumpIfFalse already pops condition when taking body
@@ -580,10 +621,9 @@ impl<'a> Compiler<'a> {
580
621
  continue_patches: Vec::new(),
581
622
  continue_is_forward_jump: true,
582
623
  });
583
- self.breakable_stack
584
- .push(Breakable::Loop {
585
- unwind_depth: self.block_depth,
586
- });
624
+ self.breakable_stack.push(Breakable::Loop {
625
+ unwind_depth: self.block_depth,
626
+ });
587
627
  self.compile_statement(body)?;
588
628
  let update_start = self.chunk.code.len();
589
629
  if let Some(u) = update {
@@ -604,7 +644,12 @@ impl<'a> Compiler<'a> {
604
644
  self.breakable_stack.pop();
605
645
  self.scope.pop();
606
646
  }
607
- Statement::ForOf { name, iterable, body, .. } => {
647
+ Statement::ForOf {
648
+ name,
649
+ iterable,
650
+ body,
651
+ ..
652
+ } => {
608
653
  self.compile_expr(iterable)?;
609
654
  self.scope.push(HashMap::new());
610
655
  let arr_name = Arc::from("__forof_arr__");
@@ -615,12 +660,18 @@ impl<'a> Compiler<'a> {
615
660
  let len_idx = self.name_idx(&len_name);
616
661
  let name_idx = self.name_idx(name);
617
662
  self.emit_u16(Opcode::DeclareVar, arr_idx);
618
- self.scope.last_mut().unwrap().insert(arr_name.clone(), false);
663
+ self.scope
664
+ .last_mut()
665
+ .unwrap()
666
+ .insert(arr_name.clone(), false);
619
667
  self.emit_u16(Opcode::LoadVar, arr_idx);
620
668
  let len_name_idx = self.name_idx(&Arc::from("length"));
621
669
  self.emit_u16(Opcode::GetMember, len_name_idx);
622
670
  self.emit_u16(Opcode::DeclareVar, len_idx);
623
- self.scope.last_mut().unwrap().insert(len_name.clone(), false);
671
+ self.scope
672
+ .last_mut()
673
+ .unwrap()
674
+ .insert(len_name.clone(), false);
624
675
  let zero_idx = self.constant_idx(Constant::Number(0.0));
625
676
  self.emit(Opcode::LoadConst);
626
677
  self.chunk.write_u16(zero_idx);
@@ -632,15 +683,17 @@ impl<'a> Compiler<'a> {
632
683
  continue_patches: Vec::new(),
633
684
  continue_is_forward_jump: false,
634
685
  });
635
- self.breakable_stack
636
- .push(Breakable::Loop {
637
- unwind_depth: self.block_depth,
638
- });
686
+ self.breakable_stack.push(Breakable::Loop {
687
+ unwind_depth: self.block_depth,
688
+ });
639
689
  self.emit_u16(Opcode::LoadVar, arr_idx);
640
690
  self.emit_u16(Opcode::LoadVar, i_idx);
641
691
  self.emit(Opcode::GetIndex);
642
692
  self.emit_u16(Opcode::DeclareVar, name_idx);
643
- self.scope.last_mut().unwrap().insert(Arc::clone(name), false);
693
+ self.scope
694
+ .last_mut()
695
+ .unwrap()
696
+ .insert(Arc::clone(name), false);
644
697
  self.compile_statement(body)?;
645
698
  self.emit_u16(Opcode::LoadVar, i_idx);
646
699
  let one_idx = self.constant_idx(Constant::Number(1.0));
@@ -678,9 +731,8 @@ impl<'a> Compiler<'a> {
678
731
  }
679
732
  Statement::Break { .. } => {
680
733
  let unwind_depth = match self.breakable_stack.last() {
681
- Some(Breakable::Loop { unwind_depth }) | Some(Breakable::Switch { unwind_depth }) => {
682
- *unwind_depth
683
- }
734
+ Some(Breakable::Loop { unwind_depth })
735
+ | Some(Breakable::Switch { unwind_depth }) => *unwind_depth,
684
736
  None => {
685
737
  return Err(CompileError {
686
738
  message: "break not inside a loop or switch".to_string(),
@@ -694,7 +746,11 @@ impl<'a> Compiler<'a> {
694
746
  self.loop_stack.last_mut().unwrap().break_patches.push(pos);
695
747
  }
696
748
  Some(Breakable::Switch { .. }) => {
697
- self.switch_stack.last_mut().unwrap().break_patches.push(pos);
749
+ self.switch_stack
750
+ .last_mut()
751
+ .unwrap()
752
+ .break_patches
753
+ .push(pos);
698
754
  }
699
755
  None => {}
700
756
  }
@@ -764,7 +820,10 @@ impl<'a> Compiler<'a> {
764
820
  self.chunk.write_u16(idx);
765
821
  let idx = self.name_idx(name);
766
822
  self.emit_u16(Opcode::DeclareVar, idx);
767
- self.scope.last_mut().unwrap().insert(Arc::clone(name), false);
823
+ self.scope
824
+ .last_mut()
825
+ .unwrap()
826
+ .insert(Arc::clone(name), false);
768
827
  }
769
828
  Statement::DoWhile { body, cond, .. } => {
770
829
  let start = self.chunk.code.len();
@@ -773,10 +832,9 @@ impl<'a> Compiler<'a> {
773
832
  continue_patches: Vec::new(),
774
833
  continue_is_forward_jump: false,
775
834
  });
776
- self.breakable_stack
777
- .push(Breakable::Loop {
778
- unwind_depth: self.block_depth,
779
- });
835
+ self.breakable_stack.push(Breakable::Loop {
836
+ unwind_depth: self.block_depth,
837
+ });
780
838
  self.compile_statement(body)?;
781
839
  let cond_start = self.chunk.code.len();
782
840
  self.compile_expr(cond)?;
@@ -794,7 +852,12 @@ impl<'a> Compiler<'a> {
794
852
  self.patch_jump(p, end);
795
853
  }
796
854
  }
797
- Statement::Switch { expr, cases, default_body, .. } => {
855
+ Statement::Switch {
856
+ expr,
857
+ cases,
858
+ default_body,
859
+ ..
860
+ } => {
798
861
  let switch_unwind_depth = self.block_depth;
799
862
  self.switch_stack.push(SwitchInfo {
800
863
  break_patches: Vec::new(),
@@ -899,7 +962,8 @@ impl<'a> Compiler<'a> {
899
962
  if let Some(finally) = finally_body {
900
963
  self.compile_statement(finally)?;
901
964
  }
902
- let catch_offset = catch_start.wrapping_sub(catch_offset_pos).wrapping_sub(3) as u16;
965
+ let catch_offset =
966
+ catch_start.wrapping_sub(catch_offset_pos).wrapping_sub(3) as u16;
903
967
  self.chunk.code[catch_offset_pos + 1] = (catch_offset >> 8) as u8;
904
968
  self.chunk.code[catch_offset_pos + 2] = (catch_offset & 0xff) as u8;
905
969
  }
@@ -918,6 +982,9 @@ impl<'a> Compiler<'a> {
918
982
  });
919
983
  }
920
984
  },
985
+ Statement::TypeAlias { .. }
986
+ | Statement::DeclareVar { .. }
987
+ | Statement::DeclareFun { .. } => {}
921
988
  }
922
989
  Ok(())
923
990
  }
@@ -937,7 +1004,7 @@ impl<'a> Compiler<'a> {
937
1004
  DestructPattern::Array(elements) => {
938
1005
  for (i, elem) in elements.iter().enumerate() {
939
1006
  match elem {
940
- Some(DestructElement::Ident(name)) => {
1007
+ Some(DestructElement::Ident(name, _)) => {
941
1008
  self.emit(Opcode::Dup);
942
1009
  let idx = self.constant_idx(Constant::Number(i as f64));
943
1010
  self.emit(Opcode::LoadConst);
@@ -967,7 +1034,7 @@ impl<'a> Compiler<'a> {
967
1034
  self.chunk.write_u16(key_idx);
968
1035
  self.emit(Opcode::GetIndex); // GetIndex pops obj, index and uses get_member
969
1036
  match &prop.value {
970
- DestructElement::Ident(name) => {
1037
+ DestructElement::Ident(name, _) => {
971
1038
  let idx = self.name_idx(name);
972
1039
  self.emit_u16(decl_op, idx);
973
1040
  if mutable {
@@ -1008,7 +1075,9 @@ impl<'a> Compiler<'a> {
1008
1075
  let idx = self.name_idx(name);
1009
1076
  self.emit_u16(Opcode::LoadVar, idx);
1010
1077
  }
1011
- Expr::Binary { left, op, right, .. } => {
1078
+ Expr::Binary {
1079
+ left, op, right, ..
1080
+ } => {
1012
1081
  match op {
1013
1082
  BinOp::And => {
1014
1083
  // Short-circuit: a && b => if !a then a else b
@@ -1049,16 +1118,29 @@ impl<'a> Compiler<'a> {
1049
1118
  && args.len() == 1
1050
1119
  && matches!(args[0], CallArg::Expr(_))
1051
1120
  {
1052
- if let (Expr::Member { object, prop: MemberProp::Name(key), optional: false, .. }, CallArg::Expr(cmp_expr)) =
1053
- (callee.as_ref(), &args[0])
1121
+ if let (
1122
+ Expr::Member {
1123
+ object,
1124
+ prop: MemberProp::Name { name: key, .. },
1125
+ optional: false,
1126
+ ..
1127
+ },
1128
+ CallArg::Expr(cmp_expr),
1129
+ ) = (callee.as_ref(), &args[0])
1054
1130
  {
1055
1131
  if key.as_ref() == "sort" {
1056
- if let Some(ascending) = Self::detect_numeric_sort_comparator(cmp_expr) {
1132
+ if let Some(ascending) = Self::detect_numeric_sort_comparator(cmp_expr)
1133
+ {
1057
1134
  self.compile_expr(object)?;
1058
- self.emit_u8(Opcode::ArraySortNumeric, if ascending { 0 } else { 1 });
1135
+ self.emit_u8(
1136
+ Opcode::ArraySortNumeric,
1137
+ if ascending { 0 } else { 1 },
1138
+ );
1059
1139
  return Ok(());
1060
1140
  }
1061
- if let Some((prop, ascending)) = Self::detect_property_sort_comparator(cmp_expr) {
1141
+ if let Some((prop, ascending)) =
1142
+ Self::detect_property_sort_comparator(cmp_expr)
1143
+ {
1062
1144
  self.compile_expr(object)?;
1063
1145
  let prop_idx = self.constant_idx(Constant::String(prop));
1064
1146
  self.emit(Opcode::ArraySortByProperty);
@@ -1152,7 +1234,7 @@ impl<'a> Compiler<'a> {
1152
1234
  let jump_end = self.emit_jump(Opcode::Jump);
1153
1235
  self.patch_jump(jump_to_get, self.chunk.code.len());
1154
1236
  match prop {
1155
- MemberProp::Name(key) => {
1237
+ MemberProp::Name { name: key, .. } => {
1156
1238
  let idx = self.name_idx(key);
1157
1239
  self.emit_u16(Opcode::GetMemberOptional, idx);
1158
1240
  }
@@ -1164,7 +1246,7 @@ impl<'a> Compiler<'a> {
1164
1246
  self.patch_jump(jump_end, self.chunk.code.len());
1165
1247
  } else {
1166
1248
  match prop {
1167
- MemberProp::Name(key) => {
1249
+ MemberProp::Name { name: key, .. } => {
1168
1250
  let idx = self.name_idx(key);
1169
1251
  self.emit_u16(Opcode::GetMember, idx);
1170
1252
  }
@@ -1211,7 +1293,9 @@ impl<'a> Compiler<'a> {
1211
1293
  self.patch_jump(jump_end, self.chunk.code.len());
1212
1294
  }
1213
1295
  Expr::Array { elements, .. } => {
1214
- let has_spread = elements.iter().any(|e| matches!(e, ArrayElement::Spread(_)));
1296
+ let has_spread = elements
1297
+ .iter()
1298
+ .any(|e| matches!(e, ArrayElement::Spread(_)));
1215
1299
  if has_spread {
1216
1300
  // Build array incrementally: start with [], concat each element
1217
1301
  self.emit_u16(Opcode::NewArray, 0);
@@ -1376,7 +1460,9 @@ impl<'a> Compiler<'a> {
1376
1460
  self.emit(Opcode::Dup);
1377
1461
  self.emit_u16(Opcode::StoreVar, idx);
1378
1462
  }
1379
- Expr::CompoundAssign { name, op, value, .. } => {
1463
+ Expr::CompoundAssign {
1464
+ name, op, value, ..
1465
+ } => {
1380
1466
  let idx = self.name_idx(name);
1381
1467
  self.emit_u16(Opcode::LoadVar, idx);
1382
1468
  self.compile_expr(value)?;
@@ -1384,20 +1470,32 @@ impl<'a> Compiler<'a> {
1384
1470
  self.emit(Opcode::Dup);
1385
1471
  self.emit_u16(Opcode::StoreVar, idx);
1386
1472
  }
1387
- Expr::MemberAssign { object, prop, value, .. } => {
1473
+ Expr::MemberAssign {
1474
+ object,
1475
+ prop,
1476
+ value,
1477
+ ..
1478
+ } => {
1388
1479
  self.compile_expr(object)?;
1389
1480
  self.compile_expr(value)?;
1390
1481
  let idx = self.name_idx(prop);
1391
1482
  self.emit_u16(Opcode::SetMember, idx); // SetMember pops obj, val and pushes val back
1392
1483
  }
1393
- Expr::IndexAssign { object, index, value, .. } => {
1484
+ Expr::IndexAssign {
1485
+ object,
1486
+ index,
1487
+ value,
1488
+ ..
1489
+ } => {
1394
1490
  self.compile_expr(object)?;
1395
1491
  self.compile_expr(index)?;
1396
1492
  self.compile_expr(value)?;
1397
1493
  self.emit(Opcode::Dup); // leave copy for assignment expression result
1398
1494
  self.emit(Opcode::SetIndex);
1399
1495
  }
1400
- Expr::NativeModuleLoad { spec, export_name, .. } => {
1496
+ Expr::NativeModuleLoad {
1497
+ spec, export_name, ..
1498
+ } => {
1401
1499
  let spec_idx = self.constant_idx(Constant::String(Arc::clone(spec)));
1402
1500
  let export_idx = self.constant_idx(Constant::String(Arc::clone(export_name)));
1403
1501
  self.emit(Opcode::LoadNativeExport);
@@ -1405,7 +1503,10 @@ impl<'a> Compiler<'a> {
1405
1503
  self.chunk.write_u16(export_idx);
1406
1504
  }
1407
1505
  Expr::JsxElement {
1408
- tag, props, children, ..
1506
+ tag,
1507
+ props,
1508
+ children,
1509
+ ..
1409
1510
  } => {
1410
1511
  self.compile_jsx_element(tag, props, children)?;
1411
1512
  }
@@ -1413,16 +1514,13 @@ impl<'a> Compiler<'a> {
1413
1514
  self.compile_jsx_fragment(children)?;
1414
1515
  }
1415
1516
  Expr::Await { operand, .. } => {
1416
- // await expr => LoadNativeExport("tish:http","await"), compile(operand), Call(1)
1417
- let spec_idx = self.constant_idx(Constant::String(Arc::from("tish:http")));
1418
- let await_idx = self.constant_idx(Constant::String(Arc::from("await")));
1419
- self.emit(Opcode::LoadNativeExport);
1420
- self.chunk.write_u16(spec_idx);
1421
- self.chunk.write_u16(await_idx);
1517
+ // await expr => evaluate operand, then VM Opcode::AwaitPromise (throw on reject).
1422
1518
  self.compile_expr(operand)?;
1423
- self.emit_u16(Opcode::Call, 1);
1519
+ self.emit(Opcode::AwaitPromise);
1424
1520
  }
1425
- Expr::LogicalAssign { name, op, value, .. } => {
1521
+ Expr::LogicalAssign {
1522
+ name, op, value, ..
1523
+ } => {
1426
1524
  let idx = self.name_idx(name);
1427
1525
  match op {
1428
1526
  LogicalAssignOp::OrOr => {
@@ -1515,7 +1613,11 @@ impl<'a> Compiler<'a> {
1515
1613
  let h_idx = self.name_idx(&Arc::from("h"));
1516
1614
  self.emit_u16(Opcode::LoadGlobal, h_idx);
1517
1615
  let tag_str = tag.as_ref();
1518
- let is_component = tag_str.chars().next().map(|c| c.is_uppercase()).unwrap_or(false);
1616
+ let is_component = tag_str
1617
+ .chars()
1618
+ .next()
1619
+ .map(|c| c.is_uppercase())
1620
+ .unwrap_or(false);
1519
1621
  if is_component {
1520
1622
  let tag_idx = self.name_idx(tag);
1521
1623
  self.emit_u16(Opcode::LoadGlobal, tag_idx);
@@ -93,13 +93,16 @@ pub enum Opcode {
93
93
  ExitBlock = 41,
94
94
  /// Like [`DeclareVar`] but does not record block-scope undo (for `for`/`for-of` header bindings).
95
95
  DeclareVarPlain = 42,
96
+ /// Pop the `await` operand value; if it is a `Promise`, block until settled, push the result,
97
+ /// or unwind to `catch` like `Throw` on rejection.
98
+ AwaitPromise = 43,
96
99
  }
97
100
 
98
101
  impl Opcode {
99
- /// Decode byte to opcode. Safe for b in 0..=42 (matches #[repr(u8)] discriminants).
102
+ /// Decode byte to opcode. Safe for b in 0..=43 (matches #[repr(u8)] discriminants).
100
103
  #[inline]
101
104
  pub fn from_u8(b: u8) -> Option<Opcode> {
102
- if b <= 42 {
105
+ if b <= 43 {
103
106
  Some(unsafe { std::mem::transmute(b) })
104
107
  } else {
105
108
  None
@@ -109,10 +112,25 @@ impl Opcode {
109
112
  /// Size in bytes of this instruction at `ip` (including operands). Returns None if truncated.
110
113
  pub fn instruction_size(self, code: &[u8], ip: usize) -> Option<usize> {
111
114
  let size = match self {
112
- Opcode::Nop | Opcode::Pop | Opcode::Dup | Opcode::Return | Opcode::ExitTry
113
- | Opcode::ArrayMapIdentity | Opcode::CallSpread | Opcode::ConstructSpread
114
- | Opcode::EnterBlock | Opcode::ExitBlock => 1,
115
- Opcode::ArraySortByProperty | Opcode::ArrayMapBinOp | Opcode::ArrayFilterBinOp
115
+ Opcode::Nop
116
+ | Opcode::Pop
117
+ | Opcode::Dup
118
+ | Opcode::Return
119
+ | Opcode::ExitTry
120
+ | Opcode::ConcatArray
121
+ | Opcode::MergeObject
122
+ | Opcode::GetIndex
123
+ | Opcode::SetIndex
124
+ | Opcode::Throw
125
+ | Opcode::ArrayMapIdentity
126
+ | Opcode::CallSpread
127
+ | Opcode::ConstructSpread
128
+ | Opcode::EnterBlock
129
+ | Opcode::ExitBlock
130
+ | Opcode::AwaitPromise => 1,
131
+ Opcode::ArraySortByProperty
132
+ | Opcode::ArrayMapBinOp
133
+ | Opcode::ArrayFilterBinOp
116
134
  | Opcode::LoadNativeExport => 5,
117
135
  _ => 3,
118
136
  };
@@ -162,8 +162,8 @@ fn chain_jumps(code: &mut [u8]) {
162
162
  let current_offset = read_i16(code, ip + 1) as isize;
163
163
  let current_target = (ip as isize + 3 + current_offset).max(0) as usize;
164
164
  if let Some(final_target) = final_jump_target(code, ip) {
165
- let target_ok = final_target == code.len()
166
- || insn_starts.contains(&final_target);
165
+ let target_ok =
166
+ final_target == code.len() || insn_starts.contains(&final_target);
167
167
  if final_target != current_target && target_ok {
168
168
  let new_offset = final_target as i32 - (ip + 3) as i32;
169
169
  if (i16::MIN as i32..=i16::MAX as i32).contains(&new_offset) {
@@ -9,6 +9,7 @@ repository = { workspace = true }
9
9
  [features]
10
10
  default = []
11
11
  http = []
12
+ timers = []
12
13
  fs = []
13
14
  process = []
14
15
  regex = []