@tishlang/tish 2.10.1 → 2.16.13
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 +3 -0
- package/bin/tish +0 -0
- package/crates/tish/Cargo.toml +1 -1
- package/crates/tish/src/cli_help.rs +16 -0
- package/crates/tish/src/main.rs +24 -4
- package/crates/tish/tests/integration_test.rs +149 -0
- package/crates/tish_ast/src/ast.rs +10 -0
- package/crates/tish_builtins/src/array.rs +529 -56
- package/crates/tish_builtins/src/collections.rs +114 -40
- package/crates/tish_builtins/src/string.rs +95 -8
- package/crates/tish_bytecode/src/chunk.rs +7 -0
- package/crates/tish_bytecode/src/compiler.rs +560 -64
- package/crates/tish_bytecode/src/lib.rs +1 -1
- package/crates/tish_bytecode/src/opcode.rs +154 -2
- package/crates/tish_bytecode/src/serialize.rs +2 -0
- package/crates/tish_bytecode/tests/append_local_string_builder.rs +63 -0
- package/crates/tish_bytecode/tests/math_unary_intrinsic.rs +47 -0
- package/crates/tish_compile/src/codegen.rs +17736 -5269
- package/crates/tish_compile/src/infer.rs +1707 -190
- package/crates/tish_compile/src/lib.rs +29 -4
- package/crates/tish_compile/src/resolve.rs +66 -5
- package/crates/tish_compile/src/types.rs +224 -28
- package/crates/tish_compile/tests/dump_codegen.rs +21 -0
- package/crates/tish_compile/tests/perf_codegen_169_173.rs +8 -17
- package/crates/tish_compile/tests/perf_codegen_173_part3.rs +61 -0
- package/crates/tish_compile/tests/perf_codegen_174.rs +160 -0
- package/crates/tish_compile/tests/perf_codegen_175.rs +190 -0
- package/crates/tish_compile/tests/perf_codegen_176.rs +60 -0
- package/crates/tish_compile/tests/perf_codegen_177.rs +167 -0
- package/crates/tish_compile/tests/perf_codegen_178.rs +114 -0
- package/crates/tish_compile/tests/perf_codegen_178_rec.rs +124 -0
- package/crates/tish_compile/tests/perf_codegen_181.rs +36 -0
- package/crates/tish_compile/tests/perf_codegen_320.rs +211 -0
- package/crates/tish_compile/tests/perf_codegen_module_const_forof.rs +40 -0
- package/crates/tish_compile_js/src/codegen.rs +91 -4
- package/crates/tish_compile_js/src/lib.rs +5 -2
- package/crates/tish_compile_js/src/tests_jsx.rs +175 -7
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +100 -2
- package/crates/tish_core/Cargo.toml +4 -0
- package/crates/tish_core/src/json.rs +104 -5
- package/crates/tish_core/src/lib.rs +174 -0
- package/crates/tish_core/src/shape.rs +4 -2
- package/crates/tish_core/src/value.rs +565 -35
- package/crates/tish_core/src/vmref.rs +14 -0
- package/crates/tish_eval/src/eval.rs +675 -76
- package/crates/tish_eval/src/natives.rs +19 -0
- package/crates/tish_eval/src/value.rs +76 -21
- package/crates/tish_ffi/src/lib.rs +11 -1
- package/crates/tish_ffi/tests/double_free.rs +35 -0
- package/crates/tish_fmt/src/lib.rs +75 -1
- package/crates/tish_lexer/src/lib.rs +76 -0
- package/crates/tish_lexer/src/token.rs +4 -0
- package/crates/tish_lint/src/lib.rs +126 -0
- package/crates/tish_lsp/README.md +2 -1
- package/crates/tish_lsp/src/main.rs +378 -28
- package/crates/tish_native/src/build.rs +41 -0
- package/crates/tish_parser/Cargo.toml +4 -0
- package/crates/tish_parser/src/lib.rs +27 -0
- package/crates/tish_parser/src/parser.rs +302 -20
- package/crates/tish_resolve/src/lib.rs +9 -0
- package/crates/tish_runtime/Cargo.toml +5 -0
- package/crates/tish_runtime/src/http.rs +28 -10
- package/crates/tish_runtime/src/http_fetch.rs +150 -1
- package/crates/tish_runtime/src/http_prefork.rs +72 -11
- package/crates/tish_runtime/src/lib.rs +523 -42
- package/crates/tish_runtime/src/timers.rs +49 -2
- package/crates/tish_ui/src/jsx.rs +253 -0
- package/crates/tish_vm/src/jit.rs +2514 -117
- package/crates/tish_vm/src/vm.rs +1227 -182
- 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/Cargo.toml
CHANGED
|
@@ -34,6 +34,9 @@ members = [
|
|
|
34
34
|
"crates/tish_resolve",
|
|
35
35
|
"crates/tishlang_cargo_bindgen",
|
|
36
36
|
]
|
|
37
|
+
# Generated native-build projects (`tish build`) land under `target/` when TMPDIR points there;
|
|
38
|
+
# never enroll anything beneath `target/` as a workspace member.
|
|
39
|
+
exclude = ["target"]
|
|
37
40
|
resolver = "2"
|
|
38
41
|
|
|
39
42
|
[workspace.package]
|
package/bin/tish
CHANGED
|
Binary file
|
package/crates/tish/Cargo.toml
CHANGED
|
@@ -563,6 +563,14 @@ pub(crate) struct BuildArgs {
|
|
|
563
563
|
/// For `--target js`: `bundle` (default) merges all modules into one file; `esm` emits one `.js` per `.tish` module with real `import`/`export` (so `-o` is a directory) for Vite/Rollup tree-shaking.
|
|
564
564
|
#[arg(long, default_value = "bundle", value_name = "FORMAT", help_heading = "Options")]
|
|
565
565
|
pub format: String,
|
|
566
|
+
/// For `--target js --format esm`: package the JSX runtime (`h`/`Fragment`) is auto-imported from when a module uses JSX without importing them (default `lattish`).
|
|
567
|
+
#[arg(
|
|
568
|
+
long = "jsx-import-source",
|
|
569
|
+
default_value = "lattish",
|
|
570
|
+
value_name = "PKG",
|
|
571
|
+
help_heading = "Options"
|
|
572
|
+
)]
|
|
573
|
+
pub jsx_import_source: String,
|
|
566
574
|
/// Run the gradual type checker: `warn` prints `line:col` type diagnostics; `error` also fails the build on them. (Equivalent to setting `TISH_CHECK`.)
|
|
567
575
|
#[arg(long, value_name = "MODE", help_heading = "Options")]
|
|
568
576
|
pub check: Option<String>,
|
|
@@ -594,6 +602,14 @@ pub(crate) struct CompileModuleArgs {
|
|
|
594
602
|
/// Disable AST optimizations (forced on when a source map is emitted).
|
|
595
603
|
#[arg(long, help_heading = "Options")]
|
|
596
604
|
pub no_optimize: bool,
|
|
605
|
+
/// Package the JSX runtime (`h`/`Fragment`) is auto-imported from when a module uses JSX without importing them (default `lattish`).
|
|
606
|
+
#[arg(
|
|
607
|
+
long = "jsx-import-source",
|
|
608
|
+
default_value = "lattish",
|
|
609
|
+
value_name = "PKG",
|
|
610
|
+
help_heading = "Options"
|
|
611
|
+
)]
|
|
612
|
+
pub jsx_import_source: String,
|
|
597
613
|
/// Entry `.tish` file to compile (only this file is read; dependencies are left to the bundler).
|
|
598
614
|
#[arg(required = true, value_name = "FILE", help_heading = "Arguments")]
|
|
599
615
|
pub file: String,
|
package/crates/tish/src/main.rs
CHANGED
|
@@ -137,6 +137,7 @@ fn main() {
|
|
|
137
137
|
a.ios_triple.as_deref(),
|
|
138
138
|
&a.crate_type,
|
|
139
139
|
&a.format,
|
|
140
|
+
&a.jsx_import_source,
|
|
140
141
|
)
|
|
141
142
|
}
|
|
142
143
|
Some(Commands::CompileModule(a)) => compile_module(&a),
|
|
@@ -544,6 +545,7 @@ fn compile_to_js(
|
|
|
544
545
|
optimize: bool,
|
|
545
546
|
source_map: bool,
|
|
546
547
|
format: &str,
|
|
548
|
+
jsx_import_source: &str,
|
|
547
549
|
) -> Result<(), String> {
|
|
548
550
|
if format != "bundle" && format != "esm" {
|
|
549
551
|
return Err(format!(
|
|
@@ -574,7 +576,14 @@ fn compile_to_js(
|
|
|
574
576
|
}
|
|
575
577
|
});
|
|
576
578
|
if format == "esm" {
|
|
577
|
-
return compile_to_js_esm(
|
|
579
|
+
return compile_to_js_esm(
|
|
580
|
+
input_path,
|
|
581
|
+
output_path,
|
|
582
|
+
optimize,
|
|
583
|
+
source_map,
|
|
584
|
+
project_root,
|
|
585
|
+
jsx_import_source,
|
|
586
|
+
);
|
|
578
587
|
}
|
|
579
588
|
let out_path = Path::new(output_path);
|
|
580
589
|
let out_path = if out_path.extension().is_none()
|
|
@@ -656,6 +665,7 @@ fn compile_to_js_esm(
|
|
|
656
665
|
optimize: bool,
|
|
657
666
|
source_map: bool,
|
|
658
667
|
project_root: Option<&Path>,
|
|
668
|
+
jsx_import_source: &str,
|
|
659
669
|
) -> Result<(), String> {
|
|
660
670
|
if source_map {
|
|
661
671
|
return Err(
|
|
@@ -671,8 +681,9 @@ fn compile_to_js_esm(
|
|
|
671
681
|
.into(),
|
|
672
682
|
);
|
|
673
683
|
}
|
|
674
|
-
let modules =
|
|
675
|
-
|
|
684
|
+
let modules =
|
|
685
|
+
tishlang_compile_js::compile_project_esm(input_path, project_root, optimize, jsx_import_source)
|
|
686
|
+
.map_err(|e| format!("{}", e))?;
|
|
676
687
|
let out_dir = Path::new(output_path);
|
|
677
688
|
fs::create_dir_all(out_dir)
|
|
678
689
|
.map_err(|e| format!("Cannot create output directory {}: {}", out_dir.display(), e))?;
|
|
@@ -753,6 +764,7 @@ fn compile_module(args: &CompileModuleArgs) -> Result<(), String> {
|
|
|
753
764
|
optimize,
|
|
754
765
|
import_rewrite,
|
|
755
766
|
want_map,
|
|
767
|
+
&args.jsx_import_source,
|
|
756
768
|
)
|
|
757
769
|
.map_err(|e| format!("{}", e))?;
|
|
758
770
|
|
|
@@ -781,6 +793,7 @@ fn build_file(
|
|
|
781
793
|
ios_triple: Option<&str>,
|
|
782
794
|
crate_type: &str,
|
|
783
795
|
format: &str,
|
|
796
|
+
jsx_import_source: &str,
|
|
784
797
|
) -> Result<(), String> {
|
|
785
798
|
let optimize = !no_optimize;
|
|
786
799
|
let input_path = Path::new(input_path)
|
|
@@ -790,7 +803,14 @@ fn build_file(
|
|
|
790
803
|
let is_js = input_path.extension().map(|e| e == "js") == Some(true);
|
|
791
804
|
|
|
792
805
|
if target == "js" {
|
|
793
|
-
return compile_to_js(
|
|
806
|
+
return compile_to_js(
|
|
807
|
+
&input_path,
|
|
808
|
+
output_path,
|
|
809
|
+
optimize,
|
|
810
|
+
source_map,
|
|
811
|
+
format,
|
|
812
|
+
jsx_import_source,
|
|
813
|
+
);
|
|
794
814
|
}
|
|
795
815
|
|
|
796
816
|
// `wasm-gpu` (#277): same pipeline as `wasm` but builds the `--features gpu` WebGPU runtime and
|
|
@@ -52,6 +52,28 @@ fn integration_compile_cache_dir() -> PathBuf {
|
|
|
52
52
|
target_dir().join("integration_compile_cache")
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/// Remove cache entries under `dir` whose name starts with `prefix` but is not part of the current
|
|
56
|
+
/// generation `keep_leaf` (the current artifact may have an extension appended, e.g. `<leaf>.wasm`,
|
|
57
|
+
/// so we keep anything that *starts with* `keep_leaf`). Best-effort: caps the per-fixture compile
|
|
58
|
+
/// cache so a `tish` rebuild doesn't leave the previous generation behind forever. Parallel-safe —
|
|
59
|
+
/// see the call site in `compile_cached`.
|
|
60
|
+
fn prune_stale_cache_generations(dir: &Path, prefix: &str, keep_leaf: &str) {
|
|
61
|
+
if let Ok(rd) = std::fs::read_dir(dir) {
|
|
62
|
+
for ent in rd.flatten() {
|
|
63
|
+
let name = ent.file_name();
|
|
64
|
+
let name = name.to_string_lossy();
|
|
65
|
+
if name.starts_with(prefix) && !name.starts_with(keep_leaf) {
|
|
66
|
+
let p = ent.path();
|
|
67
|
+
if p.is_dir() {
|
|
68
|
+
let _ = std::fs::remove_dir_all(&p);
|
|
69
|
+
} else {
|
|
70
|
+
let _ = std::fs::remove_file(&p);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
55
77
|
/// Match `tish build` with no `--feature`: link every capability compiled into this `tish` binary.
|
|
56
78
|
fn native_build_features_for_integration_test() -> Vec<String> {
|
|
57
79
|
let mut v: Vec<String> = tishlang_vm::all_compiled_capabilities()
|
|
@@ -170,6 +192,14 @@ fn compile_cached(bin: &Path, path: &Path, backend: &str) -> PathBuf {
|
|
|
170
192
|
// Include `backend` in the leaf name so nested cargo bin names never collide across backends.
|
|
171
193
|
let leaf = format!("{}__{}__{}", stem, backend, hash8);
|
|
172
194
|
|
|
195
|
+
// Cap the cache: `hash8` mixes in the `tish` binary's mtime, so every recompile of `tish`
|
|
196
|
+
// orphans the previous generation of THIS (stem, backend). Drop those stale siblings now, so the
|
|
197
|
+
// cache holds ~one generation per fixture instead of growing without bound across rebuilds
|
|
198
|
+
// (it reached 14 GB in one session and exhausted the disk). Safe under parallel `nextest`: every
|
|
199
|
+
// live process for this (stem, backend) computes the SAME `hash8`, so only genuinely-stale
|
|
200
|
+
// generations (a different hash) are removed, never an artifact a concurrent run is using.
|
|
201
|
+
prune_stale_cache_generations(&cache_base, &format!("{}__{}__", stem, backend), &leaf);
|
|
202
|
+
|
|
173
203
|
let (artifact_path, compile_args): (PathBuf, Vec<OsString>) = match backend {
|
|
174
204
|
"native" => {
|
|
175
205
|
let ext = if cfg!(target_os = "windows") {
|
|
@@ -588,6 +618,46 @@ fn test_import_alias() {
|
|
|
588
618
|
}
|
|
589
619
|
}
|
|
590
620
|
|
|
621
|
+
/// #305: re-export — `export { foo, bar as inc } from "./dep"` and `export * from "./dep"` in a
|
|
622
|
+
/// middle module, consumed by a named import and a namespace import in the entry. Identical across
|
|
623
|
+
/// the interpreter and the VM (native/cranelift/js verified via the bundle path).
|
|
624
|
+
#[test]
|
|
625
|
+
fn test_reexport() {
|
|
626
|
+
let bin = tish_bin();
|
|
627
|
+
if !bin.exists() {
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
let path = workspace_root()
|
|
631
|
+
.join("tests")
|
|
632
|
+
.join("modules")
|
|
633
|
+
.join("reexport.tish");
|
|
634
|
+
if !path.exists() {
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
let expected = "42\n42\n1.0\n42\n";
|
|
638
|
+
for backend_args in [vec!["run"], vec!["run", "--backend", "vm"]] {
|
|
639
|
+
let mut args = backend_args.clone();
|
|
640
|
+
args.push(path.to_string_lossy().to_string().leak());
|
|
641
|
+
let out = Command::new(&bin)
|
|
642
|
+
.args(&args)
|
|
643
|
+
.current_dir(workspace_root())
|
|
644
|
+
.output()
|
|
645
|
+
.expect("run tish binary");
|
|
646
|
+
assert!(
|
|
647
|
+
out.status.success(),
|
|
648
|
+
"reexport.tish ({:?}) failed: stderr={}",
|
|
649
|
+
backend_args,
|
|
650
|
+
String::from_utf8_lossy(&out.stderr)
|
|
651
|
+
);
|
|
652
|
+
assert_eq!(
|
|
653
|
+
String::from_utf8_lossy(&out.stdout),
|
|
654
|
+
expected,
|
|
655
|
+
"reexport output mismatch on backend {:?}",
|
|
656
|
+
backend_args
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
591
661
|
/// Combined validation: async/await + Promise + setTimeout + multiple HTTP requests.
|
|
592
662
|
/// #97: a module's non-exported top-level bindings stay private — a same-named binding in
|
|
593
663
|
/// another module must not overwrite them (runtime), and the `--target js` bundle must not
|
|
@@ -846,6 +916,75 @@ fn test_compile_module_no_source_map_prints_raw_js() {
|
|
|
846
916
|
);
|
|
847
917
|
}
|
|
848
918
|
|
|
919
|
+
/// #291: `compile-module` auto-imports the JSX runtime (`h` / `Fragment`) when a module uses JSX
|
|
920
|
+
/// but doesn't import them itself, so per-module ESM output doesn't throw `ReferenceError` at load.
|
|
921
|
+
/// `--jsx-import-source` overrides the runtime package.
|
|
922
|
+
#[test]
|
|
923
|
+
fn test_compile_module_jsx_auto_imports_runtime() {
|
|
924
|
+
let bin = tish_bin();
|
|
925
|
+
if !bin.exists() {
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
let widget = workspace_root()
|
|
929
|
+
.join("tests")
|
|
930
|
+
.join("modules")
|
|
931
|
+
.join("esm")
|
|
932
|
+
.join("vite_hmr")
|
|
933
|
+
.join("widget.tish");
|
|
934
|
+
if !widget.exists() {
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
// Default runtime source (`lattish`), Vite-dev so the specifier stays bare.
|
|
938
|
+
let out = Command::new(&bin)
|
|
939
|
+
.args(["compile-module"])
|
|
940
|
+
.arg(&widget)
|
|
941
|
+
.args([
|
|
942
|
+
"--target",
|
|
943
|
+
"js",
|
|
944
|
+
"--format",
|
|
945
|
+
"esm",
|
|
946
|
+
"--vite-dev",
|
|
947
|
+
"--no-source-map",
|
|
948
|
+
])
|
|
949
|
+
.current_dir(workspace_root())
|
|
950
|
+
.output()
|
|
951
|
+
.expect("run compile-module");
|
|
952
|
+
assert!(
|
|
953
|
+
out.status.success(),
|
|
954
|
+
"compile-module failed: stderr={}",
|
|
955
|
+
String::from_utf8_lossy(&out.stderr)
|
|
956
|
+
);
|
|
957
|
+
let stdout = String::from_utf8_lossy(&out.stdout);
|
|
958
|
+
assert!(
|
|
959
|
+
stdout.contains("import { h, Fragment } from \"lattish\";"),
|
|
960
|
+
"JSX module with a fragment auto-imports both h and Fragment:\n{stdout}"
|
|
961
|
+
);
|
|
962
|
+
|
|
963
|
+
// Custom runtime source via --jsx-import-source.
|
|
964
|
+
let out2 = Command::new(&bin)
|
|
965
|
+
.args(["compile-module"])
|
|
966
|
+
.arg(&widget)
|
|
967
|
+
.args([
|
|
968
|
+
"--target",
|
|
969
|
+
"js",
|
|
970
|
+
"--format",
|
|
971
|
+
"esm",
|
|
972
|
+
"--vite-dev",
|
|
973
|
+
"--no-source-map",
|
|
974
|
+
"--jsx-import-source",
|
|
975
|
+
"@tishlang/lattish",
|
|
976
|
+
])
|
|
977
|
+
.current_dir(workspace_root())
|
|
978
|
+
.output()
|
|
979
|
+
.expect("run compile-module");
|
|
980
|
+
assert!(out2.status.success());
|
|
981
|
+
let stdout2 = String::from_utf8_lossy(&out2.stdout);
|
|
982
|
+
assert!(
|
|
983
|
+
stdout2.contains("from \"@tishlang/lattish\";"),
|
|
984
|
+
"--jsx-import-source overrides the runtime package:\n{stdout2}"
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
|
|
849
988
|
/// Ignored: tishlang_eval::run() does not run the event loop.
|
|
850
989
|
#[test]
|
|
851
990
|
#[cfg(feature = "http")]
|
|
@@ -1307,6 +1446,16 @@ fn test_mvp_programs_native() {
|
|
|
1307
1446
|
let combined = combined_mvp_native_inputs_hash(&paths);
|
|
1308
1447
|
let cache_dir = mvp_native_batch_cache_dir(combined);
|
|
1309
1448
|
let _ = std::fs::create_dir_all(&cache_dir);
|
|
1449
|
+
// Cap the native batch cache: a new combined hash (a codegen / inference / source change) orphans
|
|
1450
|
+
// the previous `native_many/<hash>` directory, each of which holds a full nested Cargo `target/`
|
|
1451
|
+
// (~GBs). Drop the stale siblings, keeping only the current generation. Only `test_mvp_programs_
|
|
1452
|
+
// native` builds here, so there is no cross-process race.
|
|
1453
|
+
if let (Some(parent), Some(keep)) = (
|
|
1454
|
+
cache_dir.parent(),
|
|
1455
|
+
cache_dir.file_name().map(|n| n.to_string_lossy().into_owned()),
|
|
1456
|
+
) {
|
|
1457
|
+
prune_stale_cache_generations(parent, "", &keep);
|
|
1458
|
+
}
|
|
1310
1459
|
|
|
1311
1460
|
let ext = if cfg!(target_os = "windows") {
|
|
1312
1461
|
".exe"
|
|
@@ -156,6 +156,16 @@ pub enum ExportDeclaration {
|
|
|
156
156
|
Named(Box<Statement>),
|
|
157
157
|
/// export default expr
|
|
158
158
|
Default(Expr),
|
|
159
|
+
/// Re-export from another module (#305):
|
|
160
|
+
/// `export { foo, bar as inc } from "./m"` -> specifiers, all=false
|
|
161
|
+
/// `export * from "./m"` -> specifiers=[], all=true
|
|
162
|
+
/// Specifiers reuse `ImportSpecifier::Named` (`{ a as b }`).
|
|
163
|
+
ReExport {
|
|
164
|
+
specifiers: Vec<ImportSpecifier>,
|
|
165
|
+
all: bool,
|
|
166
|
+
from: Arc<str>,
|
|
167
|
+
span: Span,
|
|
168
|
+
},
|
|
159
169
|
}
|
|
160
170
|
|
|
161
171
|
#[derive(Debug, Clone)]
|