@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.
- package/Cargo.toml +1 -0
- package/bin/tish +0 -0
- package/crates/js_to_tish/src/error.rs +2 -8
- package/crates/js_to_tish/src/transform/expr.rs +101 -130
- package/crates/js_to_tish/src/transform/stmt.rs +25 -22
- package/crates/tish/Cargo.toml +1 -1
- package/crates/tish/src/cli_help.rs +76 -29
- package/crates/tish/src/main.rs +85 -54
- package/crates/tish/tests/cargo_example_compile.rs +3 -1
- package/crates/tish/tests/integration_test.rs +197 -47
- package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
- package/crates/tish/tests/shortcircuit.rs +19 -4
- package/crates/tish_ast/src/ast.rs +12 -14
- package/crates/tish_build_utils/src/lib.rs +31 -6
- package/crates/tish_builtins/src/array.rs +52 -21
- package/crates/tish_builtins/src/construct.rs +2 -8
- package/crates/tish_builtins/src/globals.rs +30 -15
- package/crates/tish_builtins/src/lib.rs +5 -5
- package/crates/tish_builtins/src/math.rs +5 -3
- package/crates/tish_builtins/src/string.rs +71 -19
- package/crates/tish_bytecode/src/chunk.rs +0 -1
- package/crates/tish_bytecode/src/compiler.rs +164 -60
- package/crates/tish_bytecode/src/opcode.rs +13 -4
- package/crates/tish_bytecode/src/peephole.rs +2 -2
- package/crates/tish_compile/src/codegen.rs +921 -299
- package/crates/tish_compile/src/infer.rs +69 -19
- package/crates/tish_compile/src/lib.rs +15 -5
- package/crates/tish_compile/src/resolve.rs +112 -69
- package/crates/tish_compile/src/types.rs +10 -14
- package/crates/tish_compile_js/src/codegen.rs +34 -13
- package/crates/tish_compile_js/src/tests_jsx.rs +30 -6
- package/crates/tish_compiler_wasm/src/lib.rs +16 -13
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +39 -48
- package/crates/tish_core/src/json.rs +5 -3
- package/crates/tish_core/src/lib.rs +1 -1
- package/crates/tish_core/src/uri.rs +9 -6
- package/crates/tish_core/src/value.rs +92 -28
- package/crates/tish_cranelift/src/link.rs +6 -9
- package/crates/tish_cranelift/src/lower.rs +14 -8
- package/crates/tish_eval/src/eval.rs +389 -142
- package/crates/tish_eval/src/lib.rs +10 -6
- package/crates/tish_eval/src/natives.rs +95 -38
- package/crates/tish_eval/src/promise.rs +14 -8
- package/crates/tish_eval/src/timers.rs +28 -19
- package/crates/tish_eval/src/value.rs +10 -3
- package/crates/tish_fmt/src/lib.rs +29 -13
- package/crates/tish_lexer/src/lib.rs +217 -63
- package/crates/tish_lexer/src/token.rs +6 -6
- package/crates/tish_llvm/src/lib.rs +15 -8
- package/crates/tish_lsp/src/main.rs +41 -43
- package/crates/tish_native/src/build.rs +1 -6
- package/crates/tish_native/src/lib.rs +48 -19
- package/crates/tish_opt/src/lib.rs +67 -50
- package/crates/tish_parser/src/lib.rs +36 -11
- package/crates/tish_parser/src/parser.rs +172 -87
- package/crates/tish_runtime/src/http.rs +15 -6
- package/crates/tish_runtime/src/http_fetch.rs +24 -14
- package/crates/tish_runtime/src/lib.rs +224 -168
- package/crates/tish_runtime/src/promise.rs +1 -5
- package/crates/tish_runtime/src/ws.rs +45 -20
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
- package/crates/tish_ui/src/jsx.rs +41 -22
- package/crates/tish_ui/src/lib.rs +2 -2
- package/crates/tish_vm/src/vm.rs +309 -112
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +8 -3
- package/crates/tish_wasm/src/lib.rs +38 -28
- package/crates/tishlang_cargo_bindgen/Cargo.toml +25 -0
- package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +52 -0
- package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
- package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -0
- package/package.json +1 -1
- package/platform/darwin-arm64/tish +0 -0
- package/platform/darwin-x64/tish +0 -0
- package/platform/linux-arm64/tish +0 -0
- package/platform/linux-x64/tish +0 -0
- package/platform/win32-x64/tish.exe +0 -0
package/crates/tish/src/main.rs
CHANGED
|
@@ -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()
|
|
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(
|
|
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 -"
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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!(
|
|
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!(
|
|
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()
|
|
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!(
|
|
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
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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 =
|
|
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
|
-
|
|
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(
|
|
505
|
-
|
|
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(
|
|
517
|
-
|
|
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
|
-
|
|
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()
|
|
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 {
|