@tishlang/tish-format 3.1.0 → 3.3.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/bin/tish-format +0 -0
- package/crates/tish/src/cli_help.rs +4 -0
- package/crates/tish/src/main.rs +13 -2
- package/crates/tish/tests/rust_lib_crate.rs +140 -0
- package/crates/tish_compile/src/codegen.rs +1395 -107
- package/crates/tish_compile/src/lib.rs +14 -1
- package/crates/tish_compile/src/types.rs +6 -0
- package/crates/tish_compile/tests/perf_codegen_i32_native_abi.rs +113 -0
- package/crates/tish_compile/tests/regr_andor_rhs_move.rs +85 -0
- package/crates/tish_compile/tests/regr_gba_lowered_call_sites.rs +487 -0
- package/crates/tish_compile/tests/regr_rust_lib_emit.rs +150 -0
- package/crates/tish_fmt/Cargo.toml +1 -1
- package/crates/tish_fmt/src/lib.rs +54 -1
- package/crates/tish_native/src/build.rs +151 -6
- package/crates/tish_native/src/config.rs +14 -0
- package/crates/tish_runtime_gba/src/gba.rs +26 -6
- package/crates/tish_runtime_gba/src/lib.rs +46 -0
- package/package.json +1 -1
- package/platform/darwin-arm64/tish-fmt +0 -0
- package/platform/darwin-x64/tish-fmt +0 -0
- package/platform/linux-arm64/tish-fmt +0 -0
- package/platform/linux-x64/tish-fmt +0 -0
- package/platform/win32-x64/tish-fmt.exe +0 -0
package/bin/tish-format
CHANGED
|
Binary file
|
|
@@ -404,6 +404,10 @@ pub fn build_after_help() -> String {
|
|
|
404
404
|
Native executable (see --native-backend)
|
|
405
405
|
{t}js{r}
|
|
406
406
|
JavaScript bundle
|
|
407
|
+
{t}rust-lib{r}
|
|
408
|
+
Rust library source crate (crate-type = \"rlib\") at `-o <dir>` — one `pub fn` per
|
|
409
|
+
`export fn` of the entry module, over `tishlang_runtime::Value`. Cargo is NOT run; the
|
|
410
|
+
crate is the output, for the consumer to depend on. The Rust counterpart of `--target js`
|
|
407
411
|
{t}wasm{r}
|
|
408
412
|
WebAssembly (.tish project; .js source supported on some paths)
|
|
409
413
|
{t}wasm-gpu{r}
|
package/crates/tish/src/main.rs
CHANGED
|
@@ -914,9 +914,9 @@ fn build_file(
|
|
|
914
914
|
.map_err(|e| e.to_string());
|
|
915
915
|
}
|
|
916
916
|
|
|
917
|
-
if target != "native" && target != "gba" {
|
|
917
|
+
if target != "native" && target != "gba" && target != "rust-lib" {
|
|
918
918
|
return Err(format!(
|
|
919
|
-
"Unknown target: {}. Use 'native', 'gba', 'js', 'wasm', 'wasm-gpu', 'wasi', or 'bytecode'.",
|
|
919
|
+
"Unknown target: {}. Use 'native', 'gba', 'rust-lib', 'js', 'wasm', 'wasm-gpu', 'wasi', or 'bytecode'.",
|
|
920
920
|
target
|
|
921
921
|
));
|
|
922
922
|
}
|
|
@@ -935,6 +935,11 @@ fn build_file(
|
|
|
935
935
|
return Err("tish build --target gba requires a .tish entry file.".to_string());
|
|
936
936
|
}
|
|
937
937
|
tishlang_native::NativeBuildConfig::gba()
|
|
938
|
+
} else if target == "rust-lib" {
|
|
939
|
+
if is_js {
|
|
940
|
+
return Err("tish build --target rust-lib requires a .tish entry file.".to_string());
|
|
941
|
+
}
|
|
942
|
+
tishlang_native::NativeBuildConfig::rust_lib()
|
|
938
943
|
} else if let Some(triple) = ios_triple {
|
|
939
944
|
tishlang_native::NativeBuildConfig::ios_staticlib(triple)
|
|
940
945
|
} else if crate_type == "staticlib" {
|
|
@@ -982,6 +987,12 @@ fn build_file(
|
|
|
982
987
|
.map_err(|e| e.to_string())?;
|
|
983
988
|
}
|
|
984
989
|
|
|
990
|
+
// `rust-lib` writes a crate DIRECTORY at `-o`, and already reported it — the artifact-path
|
|
991
|
+
// rewriting below is all about locating a single built file, which does not apply.
|
|
992
|
+
if build_config.artifact == tishlang_native::NativeArtifact::RustLib {
|
|
993
|
+
return Ok(());
|
|
994
|
+
}
|
|
995
|
+
|
|
985
996
|
let out_name = Path::new(output_path)
|
|
986
997
|
.file_stem()
|
|
987
998
|
.and_then(|s| s.to_str())
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
//! `tish build --target rust-lib` end to end: the emitted artifact is a Cargo **source crate**, and
|
|
2
|
+
//! cargo is deliberately not run — the consumer decides what to build it for.
|
|
3
|
+
//!
|
|
4
|
+
//! The fast test pins the crate layout. The behavioural test — module state surviving between
|
|
5
|
+
//! `pub fn` calls, a `clear*()` reassignment being visible to other exports, and the module body
|
|
6
|
+
//! running exactly once — actually builds the crate and a consumer, so it is gated behind
|
|
7
|
+
//! `TISH_SLOW_TESTS=1` to keep the default `cargo test` fast.
|
|
8
|
+
use std::path::{Path, PathBuf};
|
|
9
|
+
|
|
10
|
+
fn fixture() -> PathBuf {
|
|
11
|
+
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
|
12
|
+
.join("../../tests/regression/rust_lib_emit.tish")
|
|
13
|
+
.canonicalize()
|
|
14
|
+
.unwrap()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
fn emit_crate(out: &Path) {
|
|
18
|
+
let entry = fixture();
|
|
19
|
+
tishlang_native::compile_to_native_with_config(
|
|
20
|
+
&entry,
|
|
21
|
+
entry.parent(),
|
|
22
|
+
out,
|
|
23
|
+
&[],
|
|
24
|
+
"rust",
|
|
25
|
+
true,
|
|
26
|
+
&tishlang_native::NativeBuildConfig::rust_lib(),
|
|
27
|
+
)
|
|
28
|
+
.expect("rust-lib emit");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#[test]
|
|
32
|
+
fn emits_an_rlib_source_crate_and_does_not_run_cargo() {
|
|
33
|
+
let out = std::env::temp_dir().join("tish_rustlib_layout");
|
|
34
|
+
let _ = std::fs::remove_dir_all(&out);
|
|
35
|
+
emit_crate(&out);
|
|
36
|
+
|
|
37
|
+
let manifest = std::fs::read_to_string(out.join("Cargo.toml")).expect("Cargo.toml");
|
|
38
|
+
assert!(
|
|
39
|
+
manifest.contains(r#"crate-type = ["rlib"]"#),
|
|
40
|
+
"expected an rlib library crate:\n{manifest}"
|
|
41
|
+
);
|
|
42
|
+
assert!(
|
|
43
|
+
manifest.contains("path = \"src/lib.rs\""),
|
|
44
|
+
"expected a lib entry point:\n{manifest}"
|
|
45
|
+
);
|
|
46
|
+
// Both `path` and `version` on the runtime dep: cargo prefers the path locally and falls back to
|
|
47
|
+
// the registry when the emitted crate is published, so it is publishable as-is.
|
|
48
|
+
assert!(
|
|
49
|
+
manifest.contains("tishlang_runtime = { path =") && manifest.contains("version = \""),
|
|
50
|
+
"the runtime dep must carry both path and version:\n{manifest}"
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
assert!(out.join("src/lib.rs").is_file(), "expected src/lib.rs");
|
|
54
|
+
assert!(
|
|
55
|
+
!out.join("src/main.rs").exists(),
|
|
56
|
+
"a library crate must not emit src/main.rs"
|
|
57
|
+
);
|
|
58
|
+
// Cargo was not invoked: no build output next to the emitted crate.
|
|
59
|
+
assert!(
|
|
60
|
+
!out.join("target").exists(),
|
|
61
|
+
"--target rust-lib must emit the crate and stop, not build it"
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// `TISH_SLOW_TESTS=1 cargo test -p tishlang --test rust_lib_crate` — compiles the emitted crate
|
|
66
|
+
/// plus a consumer that exercises the module-state guarantees.
|
|
67
|
+
#[test]
|
|
68
|
+
fn consumer_observes_persistent_module_state() {
|
|
69
|
+
if std::env::var("TISH_SLOW_TESTS").as_deref() != Ok("1") {
|
|
70
|
+
eprintln!("skipped (set TISH_SLOW_TESTS=1 to run the full cargo build)");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
let root = std::env::temp_dir().join("tish_rustlib_e2e");
|
|
74
|
+
let _ = std::fs::remove_dir_all(&root);
|
|
75
|
+
let lib = root.join("lib");
|
|
76
|
+
emit_crate(&lib);
|
|
77
|
+
|
|
78
|
+
let consumer = root.join("consumer");
|
|
79
|
+
std::fs::create_dir_all(consumer.join("src")).unwrap();
|
|
80
|
+
std::fs::write(
|
|
81
|
+
consumer.join("Cargo.toml"),
|
|
82
|
+
format!(
|
|
83
|
+
"[package]\nname = \"consumer\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n\
|
|
84
|
+
[workspace]\n\n[dependencies]\nlib = {{ package = \"lib\", path = {:?} }}\n",
|
|
85
|
+
lib.display().to_string()
|
|
86
|
+
),
|
|
87
|
+
)
|
|
88
|
+
.unwrap();
|
|
89
|
+
std::fs::write(
|
|
90
|
+
consumer.join("src/main.rs"),
|
|
91
|
+
r#"
|
|
92
|
+
use lib::Value;
|
|
93
|
+
fn num(v: &Value) -> f64 { match v { Value::Number(x) => *x, _ => f64::NAN } }
|
|
94
|
+
fn text(v: &Value) -> String { match v { Value::String(x) => x.to_string(), _ => String::new() } }
|
|
95
|
+
fn main() {
|
|
96
|
+
// First call is not the first declared export: init must already have run.
|
|
97
|
+
assert_eq!(num(&lib::add(Value::Number(2.0), Value::Number(40.0))), 42.0, "add");
|
|
98
|
+
assert_eq!(num(&lib::initRuns()), 1.0, "module top level runs once");
|
|
99
|
+
|
|
100
|
+
lib::register(Value::String("kick".into()), Value::String("noiseBurst".into()));
|
|
101
|
+
assert_eq!(text(&lib::lookup(Value::String("kick".into()))), "noiseBurst", "state persists");
|
|
102
|
+
|
|
103
|
+
// `clearAll` REASSIGNS the module-level binding; other exports must see that.
|
|
104
|
+
assert_eq!(num(&lib::clearAll()), 0.0, "clearAll empties");
|
|
105
|
+
assert_eq!(text(&lib::lookup(Value::String("kick".into()))), "miss", "reassignment visible");
|
|
106
|
+
|
|
107
|
+
assert_eq!(num(&lib::initRuns()), 1.0, "still initialised once");
|
|
108
|
+
|
|
109
|
+
// A throw is parked for the next checkpoint, not unwound. Across the `pub fn` boundary there is
|
|
110
|
+
// no Tish frame to surface it in, so a parked throw used to sit latched and make every LATER
|
|
111
|
+
// call bail and return null. Reading a property off a null is all it takes.
|
|
112
|
+
let _ = lib::runtime::get_index(&Value::Null, &Value::String("nope".into()));
|
|
113
|
+
assert_eq!(
|
|
114
|
+
num(&lib::add(Value::Number(1.0), Value::Number(2.0))),
|
|
115
|
+
3.0,
|
|
116
|
+
"a parked throw must not brick the module"
|
|
117
|
+
);
|
|
118
|
+
for _ in 0..20 {
|
|
119
|
+
let _ = lib::runtime::get_index(&Value::Null, &Value::String("nope".into()));
|
|
120
|
+
}
|
|
121
|
+
assert_eq!(text(&lib::lookup(Value::String("kick".into()))), "miss", "still working after 20");
|
|
122
|
+
|
|
123
|
+
println!("ALL_OK");
|
|
124
|
+
}
|
|
125
|
+
"#,
|
|
126
|
+
)
|
|
127
|
+
.unwrap();
|
|
128
|
+
|
|
129
|
+
let out = std::process::Command::new("cargo")
|
|
130
|
+
.args(["run", "-q"])
|
|
131
|
+
.current_dir(&consumer)
|
|
132
|
+
.output()
|
|
133
|
+
.expect("cargo run");
|
|
134
|
+
assert!(
|
|
135
|
+
String::from_utf8_lossy(&out.stdout).contains("ALL_OK"),
|
|
136
|
+
"consumer failed:\nstdout: {}\nstderr: {}",
|
|
137
|
+
String::from_utf8_lossy(&out.stdout),
|
|
138
|
+
String::from_utf8_lossy(&out.stderr)
|
|
139
|
+
);
|
|
140
|
+
}
|