@tishlang/tish 1.10.0 → 1.13.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 +0 -0
- package/crates/tish/Cargo.toml +1 -1
- package/crates/tish/src/cli_help.rs +9 -1
- package/crates/tish/src/main.rs +55 -3
- package/crates/tish_build_utils/src/lib.rs +33 -10
- package/crates/tish_builtins/src/math.rs +7 -0
- package/crates/tish_builtins/src/string.rs +26 -0
- package/crates/tish_compile/src/codegen.rs +207 -26
- package/crates/tish_compile/src/lib.rs +19 -9
- package/crates/tish_compile/src/resolve.rs +185 -3
- package/crates/tish_core/src/value.rs +21 -0
- package/crates/tish_cranelift/src/link.rs +1 -1
- package/crates/tish_fmt/src/lib.rs +1201 -132
- package/crates/tish_native/src/build.rs +88 -19
- package/crates/tish_native/src/config.rs +48 -0
- package/crates/tish_native/src/lib.rs +48 -5
- package/crates/tish_runtime/src/lib.rs +9 -1
- package/crates/tish_runtime/src/timers.rs +12 -7
- package/crates/tish_ui/src/lib.rs +5 -4
- package/crates/tish_ui/src/runtime/hooks.rs +119 -23
- package/crates/tish_ui/src/runtime/mod.rs +7 -26
- package/crates/tish_vm/src/vm.rs +40 -0
- package/crates/tish_wasm/src/lib.rs +27 -0
- package/crates/tish_wasm_runtime/Cargo.toml +6 -0
- package/crates/tish_wasm_runtime/src/gpu.rs +413 -0
- package/crates/tish_wasm_runtime/src/lib.rs +5 -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/bin/tish
CHANGED
|
Binary file
|
package/crates/tish/Cargo.toml
CHANGED
|
@@ -405,6 +405,8 @@ pub fn build_after_help() -> String {
|
|
|
405
405
|
WebAssembly (.tish project; .js source supported on some paths)
|
|
406
406
|
{t}wasi{r}
|
|
407
407
|
WASI WebAssembly
|
|
408
|
+
{t}bytecode{r}
|
|
409
|
+
Raw serialized bytecode chunk (no VM binary/JS/HTML); for hosts that already ship the runtime
|
|
408
410
|
|
|
409
411
|
{oh}Native backends{r} (--native-backend, only with --target native, default: rust):
|
|
410
412
|
{t}rust{r}
|
|
@@ -506,7 +508,7 @@ pub(crate) struct BuildArgs {
|
|
|
506
508
|
help_heading = "Options"
|
|
507
509
|
)]
|
|
508
510
|
pub output: String,
|
|
509
|
-
/// `native`, `js`, `wasm`, or `
|
|
511
|
+
/// `native`, `js`, `wasm`, `wasi`, or `bytecode` (see long help below).
|
|
510
512
|
#[arg(
|
|
511
513
|
long,
|
|
512
514
|
default_value = "native",
|
|
@@ -530,6 +532,12 @@ pub(crate) struct BuildArgs {
|
|
|
530
532
|
help_heading = "Options"
|
|
531
533
|
)]
|
|
532
534
|
pub features: Vec<String>,
|
|
535
|
+
/// Cross-compile to an Apple iOS triple (e.g. `aarch64-apple-ios-sim`). Implies `--crate-type staticlib`.
|
|
536
|
+
#[arg(long, value_name = "TRIPLE", help_heading = "Options")]
|
|
537
|
+
pub ios_triple: Option<String>,
|
|
538
|
+
/// Output artifact for `--target native` (default: `bin`; use `staticlib` for embedded iOS).
|
|
539
|
+
#[arg(long, value_name = "TYPE", default_value = "bin", help_heading = "Options")]
|
|
540
|
+
pub crate_type: String,
|
|
533
541
|
#[arg(long, help_heading = "Options")]
|
|
534
542
|
pub no_optimize: bool,
|
|
535
543
|
/// For `--target js` project builds: emit `OUTPUT.js.map` and `//# sourceMappingURL=…` so JS/TS tools can jump to original `.tish` (implies `--no-optimize` for that build).
|
package/crates/tish/src/main.rs
CHANGED
|
@@ -111,6 +111,8 @@ fn main() {
|
|
|
111
111
|
&a.features,
|
|
112
112
|
a.no_optimize || no_opt_env,
|
|
113
113
|
a.source_map,
|
|
114
|
+
a.ios_triple.as_deref(),
|
|
115
|
+
&a.crate_type,
|
|
114
116
|
),
|
|
115
117
|
Some(Commands::DumpAst { file }) => dump_ast(&file),
|
|
116
118
|
None => {
|
|
@@ -568,6 +570,8 @@ fn build_file(
|
|
|
568
570
|
cli_features: &[String],
|
|
569
571
|
no_optimize: bool,
|
|
570
572
|
source_map: bool,
|
|
573
|
+
ios_triple: Option<&str>,
|
|
574
|
+
crate_type: &str,
|
|
571
575
|
) -> Result<(), String> {
|
|
572
576
|
let optimize = !no_optimize;
|
|
573
577
|
let input_path = Path::new(input_path)
|
|
@@ -623,9 +627,26 @@ fn build_file(
|
|
|
623
627
|
.map_err(|e| e.to_string());
|
|
624
628
|
}
|
|
625
629
|
|
|
630
|
+
if target == "bytecode" {
|
|
631
|
+
let project_root = input_path.parent().and_then(|p| {
|
|
632
|
+
if p.file_name().and_then(|n| n.to_str()) == Some("src") {
|
|
633
|
+
p.parent()
|
|
634
|
+
} else {
|
|
635
|
+
Some(p)
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
return tishlang_wasm::compile_to_bytecode(
|
|
639
|
+
&input_path,
|
|
640
|
+
project_root,
|
|
641
|
+
Path::new(output_path),
|
|
642
|
+
optimize,
|
|
643
|
+
)
|
|
644
|
+
.map_err(|e| e.to_string());
|
|
645
|
+
}
|
|
646
|
+
|
|
626
647
|
if target != "native" {
|
|
627
648
|
return Err(format!(
|
|
628
|
-
"Unknown target: {}. Use 'native', 'js', 'wasm', or '
|
|
649
|
+
"Unknown target: {}. Use 'native', 'js', 'wasm', 'wasi', or 'bytecode'.",
|
|
629
650
|
target
|
|
630
651
|
));
|
|
631
652
|
}
|
|
@@ -639,9 +660,31 @@ fn build_file(
|
|
|
639
660
|
});
|
|
640
661
|
let features: Vec<String> = native_build_features_from_cli(cli_features);
|
|
641
662
|
|
|
663
|
+
let build_config = if let Some(triple) = ios_triple {
|
|
664
|
+
tishlang_native::NativeBuildConfig::ios_staticlib(triple)
|
|
665
|
+
} else if crate_type == "staticlib" {
|
|
666
|
+
tishlang_native::NativeBuildConfig {
|
|
667
|
+
artifact: tishlang_native::NativeArtifact::StaticLib,
|
|
668
|
+
cargo_target: None,
|
|
669
|
+
emit_mode: tishlang_compile::NativeEmitMode::EmbeddedLib,
|
|
670
|
+
}
|
|
671
|
+
} else if crate_type != "bin" {
|
|
672
|
+
return Err(format!(
|
|
673
|
+
"Unknown --crate-type: {}. Use 'bin' or 'staticlib'.",
|
|
674
|
+
crate_type
|
|
675
|
+
));
|
|
676
|
+
} else {
|
|
677
|
+
tishlang_native::NativeBuildConfig::desktop()
|
|
678
|
+
};
|
|
679
|
+
|
|
642
680
|
if is_js {
|
|
643
681
|
let source = fs::read_to_string(&input_path).map_err(|e| format!("{}", e))?;
|
|
644
682
|
let program = tishlang_js_to_tish::convert(&source).map_err(|e| format!("{}", e))?;
|
|
683
|
+
if build_config.artifact != tishlang_native::NativeArtifact::Bin {
|
|
684
|
+
return Err(
|
|
685
|
+
"--crate-type staticlib / --ios-triple require a .tish entry file.".to_string(),
|
|
686
|
+
);
|
|
687
|
+
}
|
|
645
688
|
tishlang_native::compile_program_to_native(
|
|
646
689
|
&program,
|
|
647
690
|
project_root,
|
|
@@ -652,13 +695,14 @@ fn build_file(
|
|
|
652
695
|
)
|
|
653
696
|
.map_err(|e| e.to_string())?;
|
|
654
697
|
} else {
|
|
655
|
-
tishlang_native::
|
|
698
|
+
tishlang_native::compile_to_native_with_config(
|
|
656
699
|
&input_path,
|
|
657
700
|
project_root,
|
|
658
701
|
Path::new(output_path),
|
|
659
702
|
&features,
|
|
660
703
|
native_backend,
|
|
661
704
|
optimize,
|
|
705
|
+
&build_config,
|
|
662
706
|
)
|
|
663
707
|
.map_err(|e| e.to_string())?;
|
|
664
708
|
}
|
|
@@ -667,7 +711,15 @@ fn build_file(
|
|
|
667
711
|
.file_stem()
|
|
668
712
|
.and_then(|s| s.to_str())
|
|
669
713
|
.unwrap_or("tish_out");
|
|
670
|
-
let built_path = if
|
|
714
|
+
let built_path = if build_config.artifact == tishlang_native::NativeArtifact::StaticLib {
|
|
715
|
+
if output_path.ends_with('/') || Path::new(output_path).is_dir() {
|
|
716
|
+
Path::new(output_path).join(format!("lib{out_name}.a"))
|
|
717
|
+
} else if output_path.ends_with(".a") {
|
|
718
|
+
Path::new(output_path).to_path_buf()
|
|
719
|
+
} else {
|
|
720
|
+
Path::new(output_path).with_extension("a")
|
|
721
|
+
}
|
|
722
|
+
} else if output_path.ends_with('/') || Path::new(output_path).is_dir() {
|
|
671
723
|
Path::new(output_path).join(out_name)
|
|
672
724
|
} else {
|
|
673
725
|
Path::new(output_path).to_path_buf()
|
|
@@ -338,6 +338,11 @@ pub fn find_crate_path(crate_name: &str) -> Result<PathBuf, String> {
|
|
|
338
338
|
Ok(crate_path)
|
|
339
339
|
}
|
|
340
340
|
|
|
341
|
+
/// Sanitize a user-chosen output stem for Cargo `[lib]` / `[[bin]]` target names.
|
|
342
|
+
pub fn cargo_target_name(stem: &str) -> String {
|
|
343
|
+
stem.replace('-', "_")
|
|
344
|
+
}
|
|
345
|
+
|
|
341
346
|
/// Create a temp build directory with src subdir.
|
|
342
347
|
pub fn create_build_dir(prefix: &str, out_name: &str) -> Result<PathBuf, String> {
|
|
343
348
|
let build_dir =
|
|
@@ -380,8 +385,12 @@ fn protoc_for_nested_cargo() -> Option<PathBuf> {
|
|
|
380
385
|
}
|
|
381
386
|
|
|
382
387
|
/// Run cargo build in the given directory.
|
|
383
|
-
/// If
|
|
384
|
-
pub fn run_cargo_build(
|
|
388
|
+
/// If `cross_target` is Some, passes `--target` and skips `-C target-cpu=native`.
|
|
389
|
+
pub fn run_cargo_build(
|
|
390
|
+
build_dir: &Path,
|
|
391
|
+
target_dir: Option<&Path>,
|
|
392
|
+
cross_target: Option<&str>,
|
|
393
|
+
) -> Result<(), String> {
|
|
385
394
|
let _nested_guard = NESTED_CARGO_MUTEX.lock().unwrap_or_else(|e| e.into_inner());
|
|
386
395
|
|
|
387
396
|
let target_dir = target_dir
|
|
@@ -389,11 +398,10 @@ pub fn run_cargo_build(build_dir: &Path, target_dir: Option<&Path>) -> Result<()
|
|
|
389
398
|
.unwrap_or_else(|| build_dir.join("target"));
|
|
390
399
|
let fast_native = std::env::var("TISH_FAST_NATIVE_BUILD").as_deref() == Ok("1");
|
|
391
400
|
|
|
392
|
-
// Default to target-cpu=native so the emitted binary uses every SIMD / ISA
|
|
393
|
-
// extension the build host supports. Callers can override by pre-setting
|
|
394
|
-
// RUSTFLAGS in the environment. Skipped for fast nested builds (integration tests).
|
|
395
401
|
let mut merged_rustflags = std::env::var("RUSTFLAGS").unwrap_or_default();
|
|
396
|
-
if
|
|
402
|
+
if cross_target.is_some() {
|
|
403
|
+
// Cross-compiling (e.g. iOS): do not use host target-cpu.
|
|
404
|
+
} else if fast_native {
|
|
397
405
|
if cfg!(target_os = "linux")
|
|
398
406
|
&& mold_available()
|
|
399
407
|
&& !merged_rustflags.contains("fuse-ld=mold")
|
|
@@ -407,10 +415,6 @@ pub fn run_cargo_build(build_dir: &Path, target_dir: Option<&Path>) -> Result<()
|
|
|
407
415
|
merged_rustflags = format!("{} -C target-cpu=native", merged_rustflags);
|
|
408
416
|
}
|
|
409
417
|
|
|
410
|
-
// Nested `cargo build` (e.g. `tish build --native-backend rust`) inherits the parent
|
|
411
|
-
// environment. CI often sets `RUSTC_WRAPPER=sccache`; wrapping this inner compile too can
|
|
412
|
-
// cause flaky or failed builds (LTO / temp-crate paths). Use plain rustc here; the main
|
|
413
|
-
// workspace build still benefits from the wrapper.
|
|
414
418
|
let mut cmd = Command::new("cargo");
|
|
415
419
|
cmd.args(["build", "--release", "--target-dir"])
|
|
416
420
|
.arg(&target_dir)
|
|
@@ -421,6 +425,9 @@ pub fn run_cargo_build(build_dir: &Path, target_dir: Option<&Path>) -> Result<()
|
|
|
421
425
|
.env_remove("CARGO_BUILD_RUSTC_WRAPPER")
|
|
422
426
|
.env("CARGO_TERM_PROGRESS", "always")
|
|
423
427
|
.env("RUSTFLAGS", &merged_rustflags);
|
|
428
|
+
if let Some(triple) = cross_target {
|
|
429
|
+
cmd.arg("--target").arg(triple);
|
|
430
|
+
}
|
|
424
431
|
if fast_native {
|
|
425
432
|
cmd.env("CARGO_INCREMENTAL", "1");
|
|
426
433
|
} else {
|
|
@@ -448,6 +455,12 @@ pub fn run_cargo_build(build_dir: &Path, target_dir: Option<&Path>) -> Result<()
|
|
|
448
455
|
mod protoc_tests {
|
|
449
456
|
use super::*;
|
|
450
457
|
|
|
458
|
+
#[test]
|
|
459
|
+
fn cargo_target_name_replaces_hyphens() {
|
|
460
|
+
assert_eq!(cargo_target_name("hello-ios"), "hello_ios");
|
|
461
|
+
assert_eq!(cargo_target_name("tish_out"), "tish_out");
|
|
462
|
+
}
|
|
463
|
+
|
|
451
464
|
#[test]
|
|
452
465
|
fn protoc_for_nested_cargo_without_env_uses_vendored_or_path() {
|
|
453
466
|
let _lock = std::sync::Mutex::new(());
|
|
@@ -458,6 +471,16 @@ mod protoc_tests {
|
|
|
458
471
|
}
|
|
459
472
|
}
|
|
460
473
|
|
|
474
|
+
/// Find the built static library in target/release (or target/$TRIPLE/release).
|
|
475
|
+
pub fn find_release_staticlib(binary_dir: &Path, lib_name: &str) -> Result<PathBuf, String> {
|
|
476
|
+
let path = binary_dir.join(format!("lib{lib_name}.a"));
|
|
477
|
+
if path.exists() {
|
|
478
|
+
Ok(path)
|
|
479
|
+
} else {
|
|
480
|
+
Err(format!("Static library not found at {}", path.display()))
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
461
484
|
/// Find the built binary in target/release.
|
|
462
485
|
pub fn find_release_binary(binary_dir: &Path, bin_name: &str) -> Result<PathBuf, String> {
|
|
463
486
|
let binary_no_ext = binary_dir.join(bin_name);
|
|
@@ -80,3 +80,10 @@ pub fn hypot(args: &[Value]) -> Value {
|
|
|
80
80
|
let y = extract_num(args.get(1)).unwrap_or(0.0);
|
|
81
81
|
Value::Number(x.hypot(y))
|
|
82
82
|
}
|
|
83
|
+
|
|
84
|
+
/// ES6 `Math.imul`: 32-bit integer multiply (used by xmur3 PRNG in juke-cards).
|
|
85
|
+
pub fn imul(args: &[Value]) -> Value {
|
|
86
|
+
let a = extract_num(args.first()).unwrap_or(0.0) as i32;
|
|
87
|
+
let b = extract_num(args.get(1)).unwrap_or(0.0) as i32;
|
|
88
|
+
Value::Number(a.wrapping_mul(b) as f64)
|
|
89
|
+
}
|
|
@@ -189,6 +189,32 @@ pub fn substring(s: &Value, start: &Value, end: &Value) -> Value {
|
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
/// JS `String.prototype.substr(start, length)`.
|
|
193
|
+
pub fn substr(s: &Value, start: &Value, length: &Value) -> Value {
|
|
194
|
+
if let Value::String(s) = s {
|
|
195
|
+
let chars: Vec<char> = s.chars().collect();
|
|
196
|
+
let len = chars.len();
|
|
197
|
+
let mut start_idx = match start {
|
|
198
|
+
Value::Number(n) => *n as i64,
|
|
199
|
+
_ => 0,
|
|
200
|
+
};
|
|
201
|
+
if start_idx < 0 {
|
|
202
|
+
start_idx = (len as i64 + start_idx).max(0);
|
|
203
|
+
}
|
|
204
|
+
let start_idx = (start_idx as usize).min(len);
|
|
205
|
+
let count = match length {
|
|
206
|
+
Value::Null => len - start_idx,
|
|
207
|
+
Value::Number(n) => (*n as i64).max(0) as usize,
|
|
208
|
+
_ => len - start_idx,
|
|
209
|
+
};
|
|
210
|
+
let end_idx = (start_idx + count).min(len);
|
|
211
|
+
let result: String = chars[start_idx..end_idx].iter().collect();
|
|
212
|
+
Value::String(result.into())
|
|
213
|
+
} else {
|
|
214
|
+
Value::Null
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
192
218
|
pub fn split(s: &Value, sep: &Value) -> Value {
|
|
193
219
|
if let Value::String(s) = s {
|
|
194
220
|
let separator = match sep {
|