@tishlang/tish 1.6.0 → 1.7.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 (79) hide show
  1. package/Cargo.toml +1 -0
  2. package/bin/tish +0 -0
  3. package/crates/js_to_tish/src/error.rs +2 -8
  4. package/crates/js_to_tish/src/transform/expr.rs +101 -130
  5. package/crates/js_to_tish/src/transform/stmt.rs +25 -22
  6. package/crates/tish/Cargo.toml +1 -1
  7. package/crates/tish/src/cli_help.rs +76 -29
  8. package/crates/tish/src/main.rs +85 -54
  9. package/crates/tish/tests/cargo_example_compile.rs +3 -1
  10. package/crates/tish/tests/integration_test.rs +197 -47
  11. package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
  12. package/crates/tish/tests/shortcircuit.rs +19 -4
  13. package/crates/tish_ast/src/ast.rs +12 -14
  14. package/crates/tish_build_utils/src/lib.rs +31 -6
  15. package/crates/tish_builtins/src/array.rs +52 -21
  16. package/crates/tish_builtins/src/construct.rs +2 -8
  17. package/crates/tish_builtins/src/globals.rs +30 -15
  18. package/crates/tish_builtins/src/lib.rs +5 -5
  19. package/crates/tish_builtins/src/math.rs +5 -3
  20. package/crates/tish_builtins/src/string.rs +71 -19
  21. package/crates/tish_bytecode/src/chunk.rs +0 -1
  22. package/crates/tish_bytecode/src/compiler.rs +164 -60
  23. package/crates/tish_bytecode/src/opcode.rs +13 -4
  24. package/crates/tish_bytecode/src/peephole.rs +2 -2
  25. package/crates/tish_compile/src/codegen.rs +921 -299
  26. package/crates/tish_compile/src/infer.rs +69 -19
  27. package/crates/tish_compile/src/lib.rs +15 -5
  28. package/crates/tish_compile/src/resolve.rs +112 -69
  29. package/crates/tish_compile/src/types.rs +10 -14
  30. package/crates/tish_compile_js/src/codegen.rs +34 -13
  31. package/crates/tish_compile_js/src/tests_jsx.rs +30 -6
  32. package/crates/tish_compiler_wasm/src/lib.rs +16 -13
  33. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +39 -48
  34. package/crates/tish_core/src/json.rs +5 -3
  35. package/crates/tish_core/src/lib.rs +1 -1
  36. package/crates/tish_core/src/uri.rs +9 -6
  37. package/crates/tish_core/src/value.rs +92 -28
  38. package/crates/tish_cranelift/src/link.rs +6 -9
  39. package/crates/tish_cranelift/src/lower.rs +14 -8
  40. package/crates/tish_eval/src/eval.rs +389 -142
  41. package/crates/tish_eval/src/lib.rs +10 -6
  42. package/crates/tish_eval/src/natives.rs +95 -38
  43. package/crates/tish_eval/src/promise.rs +14 -8
  44. package/crates/tish_eval/src/timers.rs +28 -19
  45. package/crates/tish_eval/src/value.rs +10 -3
  46. package/crates/tish_fmt/src/lib.rs +29 -13
  47. package/crates/tish_lexer/src/lib.rs +217 -63
  48. package/crates/tish_lexer/src/token.rs +6 -6
  49. package/crates/tish_llvm/src/lib.rs +15 -8
  50. package/crates/tish_lsp/src/main.rs +41 -43
  51. package/crates/tish_native/src/build.rs +1 -6
  52. package/crates/tish_native/src/lib.rs +48 -19
  53. package/crates/tish_opt/src/lib.rs +67 -50
  54. package/crates/tish_parser/src/lib.rs +36 -11
  55. package/crates/tish_parser/src/parser.rs +172 -87
  56. package/crates/tish_runtime/src/http.rs +15 -6
  57. package/crates/tish_runtime/src/http_fetch.rs +24 -14
  58. package/crates/tish_runtime/src/lib.rs +224 -168
  59. package/crates/tish_runtime/src/promise.rs +1 -5
  60. package/crates/tish_runtime/src/ws.rs +45 -20
  61. package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
  62. package/crates/tish_ui/src/jsx.rs +41 -22
  63. package/crates/tish_ui/src/lib.rs +2 -2
  64. package/crates/tish_vm/src/vm.rs +309 -112
  65. package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +8 -3
  66. package/crates/tish_wasm/src/lib.rs +38 -28
  67. package/crates/tishlang_cargo_bindgen/Cargo.toml +25 -0
  68. package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
  69. package/crates/tishlang_cargo_bindgen/src/discover.rs +52 -0
  70. package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
  71. package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
  72. package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
  73. package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -0
  74. package/package.json +1 -1
  75. package/platform/darwin-arm64/tish +0 -0
  76. package/platform/darwin-x64/tish +0 -0
  77. package/platform/linux-arm64/tish +0 -0
  78. package/platform/linux-x64/tish +0 -0
  79. package/platform/win32-x64/tish.exe +0 -0
@@ -48,7 +48,9 @@ fn vm_capabilities_for_cli_run(cli_features: &[String]) -> HashSet<String> {
48
48
  /// `--feature` list for `tish build --target native`: same default as `tish run` (all linked-in caps).
49
49
  fn native_build_features_from_cli(cli_features: &[String]) -> Vec<String> {
50
50
  if cli_features.is_empty() {
51
- let mut v: Vec<String> = tishlang_vm::all_compiled_capabilities().into_iter().collect();
51
+ let mut v: Vec<String> = tishlang_vm::all_compiled_capabilities()
52
+ .into_iter()
53
+ .collect();
52
54
  v.sort();
53
55
  v
54
56
  } else {
@@ -61,8 +63,7 @@ fn argv_with_implicit_run(mut argv: Vec<String>) -> Vec<String> {
61
63
  if argv.len() >= 2 {
62
64
  let first = argv[1].as_str();
63
65
  const SUBCOMMANDS: &[&str] = &["run", "repl", "build", "dump-ast"];
64
- let looks_like_file =
65
- !first.starts_with('-') && !SUBCOMMANDS.iter().any(|&s| s == first);
66
+ let looks_like_file = !first.starts_with('-') && !SUBCOMMANDS.iter().any(|&s| s == first);
66
67
  if looks_like_file {
67
68
  argv.insert(1, "run".to_string());
68
69
  }
@@ -95,7 +96,12 @@ fn main() {
95
96
  let matches = cli_help::build_command().get_matches_from(&argv);
96
97
  let cli = Cli::from_arg_matches(&matches).unwrap_or_else(|e| e.exit());
97
98
  let result = match cli.command {
98
- Some(Commands::Run(a)) => run_file(&a.file, &a.backend, &a.features, a.no_optimize || no_opt_env),
99
+ Some(Commands::Run(a)) => run_file(
100
+ &a.file,
101
+ &a.backend,
102
+ &a.features,
103
+ a.no_optimize || no_opt_env,
104
+ ),
99
105
  Some(Commands::Repl(a)) => run_repl(&a.backend, a.no_optimize || no_opt_env, &a.features),
100
106
  Some(Commands::Build(a)) => build_file(
101
107
  &a.file,
@@ -136,7 +142,8 @@ fn run_stdin_pipe(
136
142
  if source.trim().is_empty() {
137
143
  if fail_on_empty {
138
144
  return Err(
139
- "No source on stdin. Example: echo 'console.log(1)' | tish or tish run -".into(),
145
+ "No source on stdin. Example: echo 'console.log(1)' | tish or tish run -"
146
+ .into(),
140
147
  );
141
148
  }
142
149
  return Ok(());
@@ -162,12 +169,18 @@ fn run_stdin_source(
162
169
  run_program(&program, backend, no_optimize, features)
163
170
  }
164
171
 
165
- fn run_file(path: &str, backend: &str, features: &[String], no_optimize: bool) -> Result<(), String> {
172
+ fn run_file(
173
+ path: &str,
174
+ backend: &str,
175
+ features: &[String],
176
+ no_optimize: bool,
177
+ ) -> Result<(), String> {
166
178
  let program = if path == "-" {
167
179
  return run_stdin_pipe(backend, features, no_optimize, true);
168
180
  } else {
169
- let path =
170
- Path::new(path).canonicalize().map_err(|e| format!("Cannot resolve {}: {}", path, e))?;
181
+ let path = Path::new(path)
182
+ .canonicalize()
183
+ .map_err(|e| format!("Cannot resolve {}: {}", path, e))?;
171
184
  let project_root = path.parent().and_then(|p| {
172
185
  if p.file_name().and_then(|n| n.to_str()) == Some("src") {
173
186
  p.parent()
@@ -177,8 +190,10 @@ fn run_file(path: &str, backend: &str, features: &[String], no_optimize: bool) -
177
190
  });
178
191
 
179
192
  if path.extension().map(|e| e == "js") == Some(true) {
180
- let prog = tishlang_js_to_tish::convert(&fs::read_to_string(&path).map_err(|e| format!("{}", e))?)
181
- .map_err(|e| format!("{}", e))?;
193
+ let prog = tishlang_js_to_tish::convert(
194
+ &fs::read_to_string(&path).map_err(|e| format!("{}", e))?,
195
+ )
196
+ .map_err(|e| format!("{}", e))?;
182
197
  if no_optimize {
183
198
  prog
184
199
  } else {
@@ -209,7 +224,13 @@ fn run_program(
209
224
  let mut eval = tishlang_eval::Evaluator::new();
210
225
  let value = eval.eval_program(program)?;
211
226
  if !matches!(value, tishlang_eval::Value::Null) {
212
- println!("{}", tishlang_eval::format_value_for_console(&value, tishlang_core::use_console_colors()));
227
+ println!(
228
+ "{}",
229
+ tishlang_eval::format_value_for_console(
230
+ &value,
231
+ tishlang_core::use_console_colors()
232
+ )
233
+ );
213
234
  }
214
235
  return Ok(());
215
236
  }
@@ -228,7 +249,10 @@ fn run_program(
228
249
  },
229
250
  )?;
230
251
  if !matches!(value, tishlang_core::Value::Null) {
231
- println!("{}", tishlang_core::format_value_styled(&value, tishlang_core::use_console_colors()));
252
+ println!(
253
+ "{}",
254
+ tishlang_core::format_value_styled(&value, tishlang_core::use_console_colors())
255
+ );
232
256
  }
233
257
  Ok(())
234
258
  }
@@ -246,7 +270,11 @@ fn run_repl(backend: &str, no_optimize: bool, features: &[String]) -> Result<(),
246
270
  print!("{}", prompt);
247
271
  io::stdout().flush().map_err(|e| e.to_string())?;
248
272
  buffer.clear();
249
- if io::stdin().read_line(&mut buffer).map_err(|e| e.to_string())? == 0 {
273
+ if io::stdin()
274
+ .read_line(&mut buffer)
275
+ .map_err(|e| e.to_string())?
276
+ == 0
277
+ {
250
278
  if !multiline.is_empty() {
251
279
  let _ = tishlang_parser::parse(multiline.trim());
252
280
  }
@@ -267,7 +295,13 @@ fn run_repl(backend: &str, no_optimize: bool, features: &[String]) -> Result<(),
267
295
  match eval.eval_program(&program) {
268
296
  Ok(v) => {
269
297
  if !matches!(v, tishlang_eval::Value::Null) {
270
- println!("{}", tishlang_eval::format_value_for_console(&v, tishlang_core::use_console_colors()));
298
+ println!(
299
+ "{}",
300
+ tishlang_eval::format_value_for_console(
301
+ &v,
302
+ tishlang_core::use_console_colors()
303
+ )
304
+ );
271
305
  }
272
306
  }
273
307
  Err(e) => eprintln!("{}", e),
@@ -364,16 +398,20 @@ fn run_repl(backend: &str, no_optimize: bool, features: &[String]) -> Result<(),
364
398
  tishlang_bytecode::compile_for_repl
365
399
  };
366
400
  match compile_fn(&program) {
367
- Ok(chunk) => {
368
- match vm.borrow_mut().run_with_options(&chunk, true) {
369
- Ok(v) => {
370
- if !matches!(v, tishlang_core::Value::Null) {
371
- println!("{}", tishlang_core::format_value_styled(&v, tishlang_core::use_console_colors()));
372
- }
401
+ Ok(chunk) => match vm.borrow_mut().run_with_options(&chunk, true) {
402
+ Ok(v) => {
403
+ if !matches!(v, tishlang_core::Value::Null) {
404
+ println!(
405
+ "{}",
406
+ tishlang_core::format_value_styled(
407
+ &v,
408
+ tishlang_core::use_console_colors()
409
+ )
410
+ );
373
411
  }
374
- Err(e) => eprintln!("{}", e),
375
412
  }
376
- }
413
+ Err(e) => eprintln!("{}", e),
414
+ },
377
415
  Err(e) => eprintln!("Compile error: {}", e),
378
416
  }
379
417
  let _ = rl.add_history_entry(buffer.trim());
@@ -413,8 +451,7 @@ fn repl_prompt(primary: bool) -> String {
413
451
 
414
452
  /// Path to REPL history file (Python-style: ~/.tish_history).
415
453
  fn tish_history_path() -> Option<PathBuf> {
416
- let home = std::env::var_os("HOME")
417
- .or_else(|| std::env::var_os("USERPROFILE"));
454
+ let home = std::env::var_os("HOME").or_else(|| std::env::var_os("USERPROFILE"));
418
455
  home.map(|h| PathBuf::from(h).join(".tish_history"))
419
456
  }
420
457
 
@@ -432,8 +469,8 @@ fn compile_to_js(input_path: &Path, output_path: &str, optimize: bool) -> Result
432
469
  "export fn __TishJsxRoot() {{\n return (\n{}\n )\n}}",
433
470
  source.trim()
434
471
  );
435
- let program = tishlang_parser::parse(&wrapped)
436
- .map_err(|e| format!("JSX wrapper parse: {}", e))?;
472
+ let program =
473
+ tishlang_parser::parse(&wrapped).map_err(|e| format!("JSX wrapper parse: {}", e))?;
437
474
  let p = if optimize {
438
475
  tishlang_opt::optimize(&program)
439
476
  } else {
@@ -477,8 +514,9 @@ fn build_file(
477
514
  no_optimize: bool,
478
515
  ) -> Result<(), String> {
479
516
  let optimize = !no_optimize;
480
- let input_path =
481
- Path::new(input_path).canonicalize().map_err(|e| format!("Cannot resolve {}: {}", input_path, e))?;
517
+ let input_path = Path::new(input_path)
518
+ .canonicalize()
519
+ .map_err(|e| format!("Cannot resolve {}: {}", input_path, e))?;
482
520
 
483
521
  let is_js = input_path.extension().map(|e| e == "js") == Some(true);
484
522
 
@@ -501,8 +539,13 @@ fn build_file(
501
539
  Some(p)
502
540
  }
503
541
  });
504
- return tishlang_wasm::compile_to_wasm(&input_path, project_root, Path::new(output_path), optimize)
505
- .map_err(|e| e.to_string());
542
+ return tishlang_wasm::compile_to_wasm(
543
+ &input_path,
544
+ project_root,
545
+ Path::new(output_path),
546
+ optimize,
547
+ )
548
+ .map_err(|e| e.to_string());
506
549
  }
507
550
 
508
551
  if target == "wasi" {
@@ -513,8 +556,13 @@ fn build_file(
513
556
  Some(p)
514
557
  }
515
558
  });
516
- return tishlang_wasm::compile_to_wasi(&input_path, project_root, Path::new(output_path), optimize)
517
- .map_err(|e| e.to_string());
559
+ return tishlang_wasm::compile_to_wasi(
560
+ &input_path,
561
+ project_root,
562
+ Path::new(output_path),
563
+ optimize,
564
+ )
565
+ .map_err(|e| e.to_string());
518
566
  }
519
567
 
520
568
  if target != "native" {
@@ -570,8 +618,6 @@ fn build_file(
570
618
  Ok(())
571
619
  }
572
620
 
573
-
574
-
575
621
  #[cfg(test)]
576
622
  mod cli_tests {
577
623
  use clap::Parser;
@@ -582,10 +628,7 @@ mod cli_tests {
582
628
 
583
629
  #[test]
584
630
  fn implicit_run_inserts_run_before_file() {
585
- let argv = argv_with_implicit_run(vec![
586
- "tish".to_string(),
587
- "hello.tish".to_string(),
588
- ]);
631
+ let argv = argv_with_implicit_run(vec!["tish".to_string(), "hello.tish".to_string()]);
589
632
  let cli = Cli::try_parse_from(argv).unwrap();
590
633
  match cli.command {
591
634
  Some(Commands::Run(a)) => assert_eq!(a.file, "hello.tish"),
@@ -595,26 +638,15 @@ mod cli_tests {
595
638
 
596
639
  #[test]
597
640
  fn explicit_subcommand_not_treated_as_file() {
598
- let argv = argv_with_implicit_run(vec![
599
- "tish".to_string(),
600
- "repl".to_string(),
601
- ]);
641
+ let argv = argv_with_implicit_run(vec!["tish".to_string(), "repl".to_string()]);
602
642
  let cli = Cli::try_parse_from(argv).unwrap();
603
643
  assert!(matches!(cli.command, Some(Commands::Repl(_))));
604
644
  }
605
645
 
606
646
  #[test]
607
647
  fn build_js_target_parses() {
608
- let cli = Cli::try_parse_from([
609
- "tish",
610
- "build",
611
- "m.tish",
612
- "--target",
613
- "js",
614
- "-o",
615
- "x.js",
616
- ])
617
- .unwrap();
648
+ let cli = Cli::try_parse_from(["tish", "build", "m.tish", "--target", "js", "-o", "x.js"])
649
+ .unwrap();
618
650
  match cli.command {
619
651
  Some(Commands::Build(a)) => assert_eq!(a.file, "m.tish"),
620
652
  _ => panic!("expected Build"),
@@ -632,8 +664,7 @@ mod cli_tests {
632
664
  }
633
665
 
634
666
  fn dump_ast(path: &str) -> Result<(), String> {
635
- let source =
636
- fs::read_to_string(path).map_err(|e| format!("Cannot read {}: {}", path, e))?;
667
+ let source = fs::read_to_string(path).map_err(|e| format!("Cannot read {}: {}", path, e))?;
637
668
  let program = tishlang_parser::parse(&source)?;
638
669
  println!("{:#?}", program);
639
670
  Ok(())
@@ -8,7 +8,9 @@ use tishlang_compile::{compile_project_full, merge_modules, resolve_project};
8
8
 
9
9
  fn native_build_features_from_cli(cli_features: &[String]) -> Vec<String> {
10
10
  if cli_features.is_empty() {
11
- let mut v: Vec<String> = tishlang_vm::all_compiled_capabilities().into_iter().collect();
11
+ let mut v: Vec<String> = tishlang_vm::all_compiled_capabilities()
12
+ .into_iter()
13
+ .collect();
12
14
  v.sort();
13
15
  v
14
16
  } else {