@tishlang/tish-lint 2.39.0 → 3.0.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 +4 -1
- package/bin/tish-lint +0 -0
- package/crates/tish/src/cli_help.rs +28 -0
- package/crates/tish/src/main.rs +41 -5
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +21 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/tish.d.tish +4 -0
- package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +5 -1
- package/crates/tish/tests/regr_typed_externs.rs +59 -0
- package/crates/tish/tests/scope_merge.rs +79 -0
- package/crates/tish_build_utils/src/lib.rs +27 -1
- package/crates/tish_builtins/Cargo.toml +14 -6
- package/crates/tish_builtins/src/array.rs +130 -54
- package/crates/tish_builtins/src/collections.rs +9 -5
- package/crates/tish_builtins/src/construct.rs +7 -1
- package/crates/tish_builtins/src/date.rs +15 -1
- package/crates/tish_builtins/src/globals.rs +65 -3
- package/crates/tish_builtins/src/helpers.rs +7 -3
- package/crates/tish_builtins/src/iterator.rs +5 -1
- package/crates/tish_builtins/src/lib.rs +3 -0
- package/crates/tish_builtins/src/math.rs +14 -0
- package/crates/tish_builtins/src/number.rs +72 -6
- package/crates/tish_builtins/src/object.rs +6 -1
- package/crates/tish_builtins/src/string.rs +13 -2
- package/crates/tish_builtins/src/symbol.rs +17 -17
- package/crates/tish_builtins/src/typedarrays.rs +9 -2
- package/crates/tish_bytecode/src/compiler.rs +76 -2
- package/crates/tish_bytecode/src/lib.rs +1 -1
- package/crates/tish_bytecode/src/opcode.rs +77 -2
- package/crates/tish_compile/src/codegen.rs +2154 -225
- package/crates/tish_compile/src/infer.rs +37 -41
- package/crates/tish_compile/src/lib.rs +13 -1
- package/crates/tish_compile/src/platform_resolve.rs +361 -0
- package/crates/tish_compile/src/resolve.rs +195 -11
- package/crates/tish_compile/src/schemes.rs +333 -0
- package/crates/tish_compile/src/types.rs +205 -34
- package/crates/tish_compile/tests/perf_codegen_320.rs +2 -2
- package/crates/tish_compile/tests/perf_codegen_module_const_forof.rs +3 -1
- package/crates/tish_compile/tests/platform_resolve_cli.rs +164 -0
- package/crates/tish_compile/tests/regr_556_param_name_collision.rs +40 -0
- package/crates/tish_compile/tests/regr_558_struct_array_write.rs +65 -0
- package/crates/tish_compile/tests/regr_558b_struct_field_read.rs +21 -0
- package/crates/tish_compile/tests/regr_558c_wrapped_struct_array.rs +64 -0
- package/crates/tish_compile/tests/regr_558d_native_vec_mutators.rs +156 -0
- package/crates/tish_compile/tests/regr_562_wrapped_struct_array_push.rs +50 -0
- package/crates/tish_compile/tests/regr_fixed_native_coercions.rs +69 -0
- package/crates/tish_compile/tests/regr_gba_numerics_gated.rs +58 -0
- package/crates/tish_compile/tests/regr_gba_struct_reflection.rs +106 -0
- package/crates/tish_compile/tests/regression/name_collision_lib.tish +6 -0
- package/crates/tish_compile/tests/regression/name_collision_main.tish +13 -0
- package/crates/tish_compile/tests/regression/struct_array_write.tish +22 -0
- package/crates/tish_compile/tests/regression/struct_field_return_read.tish +10 -0
- package/crates/tish_compiler_wasm/Cargo.toml +1 -0
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +56 -6
- package/crates/tish_core/Cargo.toml +39 -4
- package/crates/tish_core/src/compat.rs +506 -0
- package/crates/tish_core/src/console_style.rs +23 -1
- package/crates/tish_core/src/json.rs +42 -10
- package/crates/tish_core/src/lib.rs +121 -25
- package/crates/tish_core/src/macros.rs +1 -1
- package/crates/tish_core/src/shape.rs +4 -2
- package/crates/tish_core/src/uri.rs +8 -1
- package/crates/tish_core/src/value.rs +305 -54
- package/crates/tish_core/src/vmref.rs +17 -5
- package/crates/tish_eval/Cargo.toml +2 -0
- package/crates/tish_eval/src/eval.rs +90 -3
- package/crates/tish_eval/src/value.rs +8 -0
- package/crates/tish_eval/src/value_convert.rs +316 -21
- package/crates/tish_lint/Cargo.toml +1 -1
- package/crates/tish_lsp/Cargo.toml +2 -0
- package/crates/tish_lsp/src/import_goto.rs +41 -14
- package/crates/tish_lsp/src/main.rs +18 -0
- package/crates/tish_native/src/build.rs +246 -0
- package/crates/tish_native/src/config.rs +20 -0
- package/crates/tish_native/src/lib.rs +16 -6
- package/crates/tish_parser/src/lib.rs +28 -0
- package/crates/tish_parser/src/parser.rs +25 -4
- package/crates/tish_runtime/src/lib.rs +55 -9
- package/crates/tish_runtime_gba/Cargo.lock +837 -0
- package/crates/tish_runtime_gba/Cargo.toml +16 -0
- package/crates/tish_runtime_gba/src/gba.rs +211 -0
- package/crates/tish_runtime_gba/src/lib.rs +804 -0
- package/crates/tish_vm/src/jit.rs +1446 -164
- package/crates/tish_vm/src/vm.rs +583 -173
- package/justfile +9 -0
- package/package.json +1 -1
- package/platform/darwin-arm64/tish-lint +0 -0
- package/platform/darwin-x64/tish-lint +0 -0
- package/platform/linux-arm64/tish-lint +0 -0
- package/platform/linux-x64/tish-lint +0 -0
- package/platform/win32-x64/tish-lint.exe +0 -0
package/Cargo.toml
CHANGED
|
@@ -36,7 +36,10 @@ members = [
|
|
|
36
36
|
]
|
|
37
37
|
# Generated native-build projects (`tish build`) land under `target/` when TMPDIR points there;
|
|
38
38
|
# never enroll anything beneath `target/` as a workspace member.
|
|
39
|
-
|
|
39
|
+
# GBA-only: depends on `agb`, which builds solely for thumbv4t. Excluded so host
|
|
40
|
+
# `cargo build` doesn't try to cross-compile it; built explicitly with the target
|
|
41
|
+
# (and consumed by path from generated GBA projects, not as a workspace member).
|
|
42
|
+
exclude = ["target", "crates/tish_runtime_gba"]
|
|
40
43
|
resolver = "2"
|
|
41
44
|
|
|
42
45
|
[workspace.package]
|
package/bin/tish-lint
CHANGED
|
Binary file
|
|
@@ -557,6 +557,12 @@ pub(crate) struct BuildArgs {
|
|
|
557
557
|
pub crate_type: String,
|
|
558
558
|
#[arg(long, help_heading = "Options")]
|
|
559
559
|
pub no_optimize: bool,
|
|
560
|
+
/// Platform for RN-style module resolution (`macos`, `ios`, `windows`, `linux`, `web`).
|
|
561
|
+
#[arg(long, value_name = "NAME", help_heading = "Options")]
|
|
562
|
+
pub platform: Option<String>,
|
|
563
|
+
/// Surface for module resolution: `native`, `webview`, or `web` (also `TISH_SURFACE`).
|
|
564
|
+
#[arg(long, value_name = "NAME", help_heading = "Options")]
|
|
565
|
+
pub surface: Option<String>,
|
|
560
566
|
/// 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).
|
|
561
567
|
#[arg(long, help_heading = "Options")]
|
|
562
568
|
pub source_map: bool,
|
|
@@ -599,6 +605,12 @@ pub(crate) struct CompileModuleArgs {
|
|
|
599
605
|
/// Project root for resolving bare specifiers / `node_modules` (defaults to the file's parent).
|
|
600
606
|
#[arg(long, value_name = "DIR", help_heading = "Options")]
|
|
601
607
|
pub project_root: Option<String>,
|
|
608
|
+
/// Platform for RN-style module resolution (`macos`, `ios`, `windows`, `linux`, `web`).
|
|
609
|
+
#[arg(long, value_name = "NAME", help_heading = "Options")]
|
|
610
|
+
pub platform: Option<String>,
|
|
611
|
+
/// Surface for module resolution: `native`, `webview`, or `web`.
|
|
612
|
+
#[arg(long, value_name = "NAME", help_heading = "Options")]
|
|
613
|
+
pub surface: Option<String>,
|
|
602
614
|
/// Disable AST optimizations (forced on when a source map is emitted).
|
|
603
615
|
#[arg(long, help_heading = "Options")]
|
|
604
616
|
pub no_optimize: bool,
|
|
@@ -626,6 +638,22 @@ pub(crate) enum Commands {
|
|
|
626
638
|
/// Compile a single `.tish` module to one ES module (for Vite dev / HMR); prints to stdout
|
|
627
639
|
#[command(name = "compile-module")]
|
|
628
640
|
CompileModule(CompileModuleArgs),
|
|
641
|
+
/// Resolve a relative `.tish` import with platform/surface cascade (for Vite / tooling)
|
|
642
|
+
#[command(name = "resolve-id")]
|
|
643
|
+
ResolveId {
|
|
644
|
+
/// Import specifier (must be relative, e.g. `./Button` or `./Button.tish`).
|
|
645
|
+
#[arg(required = true, value_name = "SOURCE", help_heading = "Arguments")]
|
|
646
|
+
source: String,
|
|
647
|
+
/// Absolute path of the importing file (directory used as resolve base).
|
|
648
|
+
#[arg(long, value_name = "FILE", help_heading = "Options")]
|
|
649
|
+
importer: Option<String>,
|
|
650
|
+
/// Platform token (`macos`, `ios`, `windows`, `linux`, `web`).
|
|
651
|
+
#[arg(long, value_name = "NAME", help_heading = "Options")]
|
|
652
|
+
platform: Option<String>,
|
|
653
|
+
/// Surface token (`native`, `webview`, `web`).
|
|
654
|
+
#[arg(long, value_name = "NAME", help_heading = "Options")]
|
|
655
|
+
surface: Option<String>,
|
|
656
|
+
},
|
|
629
657
|
/// Parse and dump AST
|
|
630
658
|
#[command(name = "dump-ast")]
|
|
631
659
|
DumpAst {
|
package/crates/tish/src/main.rs
CHANGED
|
@@ -73,7 +73,8 @@ fn native_build_features_from_cli(cli_features: &[String]) -> Vec<String> {
|
|
|
73
73
|
fn argv_with_implicit_run(mut argv: Vec<String>) -> Vec<String> {
|
|
74
74
|
if argv.len() >= 2 {
|
|
75
75
|
let first = argv[1].as_str();
|
|
76
|
-
const SUBCOMMANDS: &[&str] =
|
|
76
|
+
const SUBCOMMANDS: &[&str] =
|
|
77
|
+
&["run", "repl", "build", "compile-module", "resolve-id", "dump-ast"];
|
|
77
78
|
let looks_like_file = !first.starts_with('-') && !SUBCOMMANDS.contains(&first);
|
|
78
79
|
if looks_like_file {
|
|
79
80
|
argv.insert(1, "run".to_string());
|
|
@@ -126,6 +127,13 @@ fn main() {
|
|
|
126
127
|
if let Some(mode) = &a.check {
|
|
127
128
|
std::env::set_var("TISH_CHECK", mode);
|
|
128
129
|
}
|
|
130
|
+
if let Err(e) = tishlang_compile::apply_resolve_env(
|
|
131
|
+
a.platform.as_deref(),
|
|
132
|
+
a.surface.as_deref(),
|
|
133
|
+
) {
|
|
134
|
+
eprintln!("Error: {}", e);
|
|
135
|
+
std::process::exit(1);
|
|
136
|
+
}
|
|
129
137
|
build_file(
|
|
130
138
|
&a.file,
|
|
131
139
|
&a.output,
|
|
@@ -140,7 +148,30 @@ fn main() {
|
|
|
140
148
|
&a.jsx_import_source,
|
|
141
149
|
)
|
|
142
150
|
}
|
|
143
|
-
Some(Commands::CompileModule(a)) =>
|
|
151
|
+
Some(Commands::CompileModule(a)) => {
|
|
152
|
+
if let Err(e) = tishlang_compile::apply_resolve_env(
|
|
153
|
+
a.platform.as_deref(),
|
|
154
|
+
a.surface.as_deref(),
|
|
155
|
+
) {
|
|
156
|
+
eprintln!("Error: {}", e);
|
|
157
|
+
std::process::exit(1);
|
|
158
|
+
}
|
|
159
|
+
compile_module(&a)
|
|
160
|
+
}
|
|
161
|
+
Some(Commands::ResolveId {
|
|
162
|
+
source,
|
|
163
|
+
importer,
|
|
164
|
+
platform,
|
|
165
|
+
surface,
|
|
166
|
+
}) => tishlang_compile::resolve_id_public(
|
|
167
|
+
&source,
|
|
168
|
+
importer.as_deref(),
|
|
169
|
+
platform.as_deref(),
|
|
170
|
+
surface.as_deref(),
|
|
171
|
+
)
|
|
172
|
+
.map(|path| {
|
|
173
|
+
println!("{path}");
|
|
174
|
+
}),
|
|
144
175
|
Some(Commands::DumpAst {
|
|
145
176
|
file,
|
|
146
177
|
ignore_indent,
|
|
@@ -883,9 +914,9 @@ fn build_file(
|
|
|
883
914
|
.map_err(|e| e.to_string());
|
|
884
915
|
}
|
|
885
916
|
|
|
886
|
-
if target != "native" {
|
|
917
|
+
if target != "native" && target != "gba" {
|
|
887
918
|
return Err(format!(
|
|
888
|
-
"Unknown target: {}. Use 'native', 'js', 'wasm', 'wasm-gpu', 'wasi', or 'bytecode'.",
|
|
919
|
+
"Unknown target: {}. Use 'native', 'gba', 'js', 'wasm', 'wasm-gpu', 'wasi', or 'bytecode'.",
|
|
889
920
|
target
|
|
890
921
|
));
|
|
891
922
|
}
|
|
@@ -899,7 +930,12 @@ fn build_file(
|
|
|
899
930
|
});
|
|
900
931
|
let features: Vec<String> = native_build_features_from_cli(cli_features);
|
|
901
932
|
|
|
902
|
-
let build_config = if
|
|
933
|
+
let build_config = if target == "gba" {
|
|
934
|
+
if is_js {
|
|
935
|
+
return Err("tish build --target gba requires a .tish entry file.".to_string());
|
|
936
|
+
}
|
|
937
|
+
tishlang_native::NativeBuildConfig::gba()
|
|
938
|
+
} else if let Some(triple) = ios_triple {
|
|
903
939
|
tishlang_native::NativeBuildConfig::ios_staticlib(triple)
|
|
904
940
|
} else if crate_type == "staticlib" {
|
|
905
941
|
tishlang_native::NativeBuildConfig {
|
|
@@ -10,3 +10,24 @@ pub fn greet(args: &[Value]) -> Value {
|
|
|
10
10
|
};
|
|
11
11
|
Value::String(Arc::from(format!("Hello, {}!", name)))
|
|
12
12
|
}
|
|
13
|
+
|
|
14
|
+
/// Boxed shim kept for dynamic call sites (`Value::Function` dispatch / a spread call).
|
|
15
|
+
pub fn add(args: &[Value]) -> Value {
|
|
16
|
+
let a = match args.first() {
|
|
17
|
+
Some(Value::Number(n)) => *n,
|
|
18
|
+
_ => 0.0,
|
|
19
|
+
};
|
|
20
|
+
let b = match args.get(1) {
|
|
21
|
+
Some(Value::Number(n)) => *n,
|
|
22
|
+
_ => 0.0,
|
|
23
|
+
};
|
|
24
|
+
Value::Number(a + b)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// Typed extern (`declare fn add(a: number, b: number): number` in `tish.d.tish`): the direct, unboxed
|
|
28
|
+
/// entry codegen calls as `demo_shim::add_typed(..)` when the args lower to the declared native types.
|
|
29
|
+
/// `number` lowers to `f64` on every native target (unlike `i32`, which is native only under
|
|
30
|
+
/// `--target gba`), so this direct call fires on the default desktop build too.
|
|
31
|
+
pub fn add_typed(a: f64, b: f64) -> f64 {
|
|
32
|
+
a + b
|
|
33
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// Typed externs shipped by the `demo_shim` crate (auto-loaded from beside its source by codegen).
|
|
2
|
+
// Declares the native signatures of exports so a call lowers to a DIRECT `demo_shim::<name>_typed(..)`
|
|
3
|
+
// instead of the boxed `value_call` path — the consuming program never writes these declarations.
|
|
4
|
+
declare fn add(a: number, b: number): number
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
import { greet } from 'cargo:demo_shim'
|
|
1
|
+
import { greet, add } from 'cargo:demo_shim'
|
|
2
2
|
|
|
3
3
|
console.log(greet('from cargo'))
|
|
4
|
+
|
|
5
|
+
// `add` has a typed signature shipped in the crate's `tish.d.tish` (auto-loaded) — this call lowers
|
|
6
|
+
// to a direct `demo_shim::add_typed(2, 3)`, not the boxed namespace-object dispatch.
|
|
7
|
+
console.log(add(2, 3))
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//! Typed externs (perf lever): a native `cargo:` import whose typed signature is shipped in the
|
|
2
|
+
//! crate's `tish.d.tish` (auto-loaded from beside the crate source) must lower a matching call to a
|
|
3
|
+
//! DIRECT `<crate>::<name>_typed(..)` — no `Value` boxing of the args, no `value_call` dispatch —
|
|
4
|
+
//! while a call with no typed signature keeps the boxed namespace path. Uses the in-repo
|
|
5
|
+
//! `tests/fixtures/cargo_example_project/` fixture: `demo_shim` exports `greet` (untyped, stays boxed)
|
|
6
|
+
//! and `add` (declared `add(a: i32, b: i32): i32` in `crates/demo-shim/tish.d.tish`).
|
|
7
|
+
//!
|
|
8
|
+
//! Covers the three branch pieces with no other coverage: the `tish.d.tish` auto-load
|
|
9
|
+
//! (`compile_project_full_emit`), `collect_extern_fns` pairing, and the direct-call emission.
|
|
10
|
+
|
|
11
|
+
use std::path::PathBuf;
|
|
12
|
+
|
|
13
|
+
use tishlang_compile::compile_project_full;
|
|
14
|
+
|
|
15
|
+
fn emit_fixture() -> String {
|
|
16
|
+
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
17
|
+
let input_path = manifest_dir
|
|
18
|
+
.join("tests/fixtures/cargo_example_project/src/main.tish")
|
|
19
|
+
.canonicalize()
|
|
20
|
+
.expect("cargo_example_project test fixture");
|
|
21
|
+
let project_root = input_path.parent().map(|p| {
|
|
22
|
+
if p.file_name().and_then(|n| n.to_str()) == Some("src") {
|
|
23
|
+
p.parent().unwrap_or(p)
|
|
24
|
+
} else {
|
|
25
|
+
p
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
compile_project_full(&input_path, project_root, &[], true)
|
|
29
|
+
.expect("compile_project_full on the cargo example fixture")
|
|
30
|
+
.0
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#[test]
|
|
34
|
+
fn declared_extern_call_lowers_to_a_direct_typed_call() {
|
|
35
|
+
let rust = emit_fixture();
|
|
36
|
+
// The `add(2, 3)` call (typed via the auto-loaded tish.d.tish) must be a direct call into the
|
|
37
|
+
// crate's `add_typed`, not a boxed dispatch through the namespace object.
|
|
38
|
+
assert!(
|
|
39
|
+
rust.contains("add_typed"),
|
|
40
|
+
"typed extern `add` must lower to a direct `demo_shim::add_typed(..)` call; \
|
|
41
|
+
the auto-loaded tish.d.tish declaration was not honored.\n{rust}"
|
|
42
|
+
);
|
|
43
|
+
// The direct call targets the backing crate.
|
|
44
|
+
assert!(
|
|
45
|
+
rust.contains("demo_shim :: add_typed") || rust.contains("demo_shim::add_typed"),
|
|
46
|
+
"the direct typed-extern call must target the `demo_shim` crate.\n{rust}"
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#[test]
|
|
51
|
+
fn untyped_extern_call_stays_boxed() {
|
|
52
|
+
let rust = emit_fixture();
|
|
53
|
+
// `greet` has no typed signature (no tish.d.tish entry), so it must NOT gain a `_typed` call —
|
|
54
|
+
// it keeps the boxed namespace/`value_call` path. Guards against collect_extern_fns over-matching.
|
|
55
|
+
assert!(
|
|
56
|
+
!rust.contains("greet_typed"),
|
|
57
|
+
"untyped extern `greet` must stay boxed (no `greet_typed` direct call).\n{rust}"
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
//! Regression for #38: a merged `@scope/pkg` package (declared via `tish.module`, `exports.tish`,
|
|
2
|
+
//! etc.) must resolve as merged **Tish source**, not be routed to the native Rust-crate path (which
|
|
3
|
+
//! would reject it with "not a Tish native module" or mis-treat it as a crate). Native `@scope`
|
|
4
|
+
//! crates (marked with `tish.crate` / `tish.rustDependencies`) remain native and are exercised by
|
|
5
|
+
//! the downstream regression (tish-apple/tish-macos).
|
|
6
|
+
|
|
7
|
+
use std::fs;
|
|
8
|
+
|
|
9
|
+
use tishlang_ast::{Expr, Statement};
|
|
10
|
+
use tishlang_compile::{merge_modules, resolve_project};
|
|
11
|
+
|
|
12
|
+
/// Build a tempdir project whose `main.tish` imports a merged `@test/greet` package. `tish_field`
|
|
13
|
+
/// is the extra package.json snippet declaring how the package presents itself (`,"tish":{…}` etc.).
|
|
14
|
+
fn setup(tish_field: &str) -> tempfile::TempDir {
|
|
15
|
+
let dir = tempfile::tempdir().unwrap();
|
|
16
|
+
let root = dir.path();
|
|
17
|
+
let pkg = root.join("node_modules/@test/greet");
|
|
18
|
+
fs::create_dir_all(&pkg).unwrap();
|
|
19
|
+
fs::write(
|
|
20
|
+
pkg.join("package.json"),
|
|
21
|
+
format!(r#"{{"name":"@test/greet","version":"1.0.0"{tish_field}}}"#),
|
|
22
|
+
)
|
|
23
|
+
.unwrap();
|
|
24
|
+
fs::write(pkg.join("index.tish"), "export fn greet() { return \"hi\" }\n").unwrap();
|
|
25
|
+
fs::write(
|
|
26
|
+
root.join("main.tish"),
|
|
27
|
+
"import { greet } from \"@test/greet\"\nconsole.log(greet())\n",
|
|
28
|
+
)
|
|
29
|
+
.unwrap();
|
|
30
|
+
dir
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
fn assert_merged_as_source(dir: &tempfile::TempDir) {
|
|
34
|
+
let root = dir.path();
|
|
35
|
+
let modules = resolve_project(&root.join("main.tish"), Some(root)).expect("resolve");
|
|
36
|
+
let merged = merge_modules(modules).expect("merge");
|
|
37
|
+
// Merged as source ⇒ the package's `greet` fn is folded into the top-level program.
|
|
38
|
+
let has_greet = merged
|
|
39
|
+
.program
|
|
40
|
+
.statements
|
|
41
|
+
.iter()
|
|
42
|
+
.any(|s| matches!(s, Statement::FunDecl { name, .. } if name.as_ref() == "greet"));
|
|
43
|
+
assert!(
|
|
44
|
+
has_greet,
|
|
45
|
+
"merged @scope package's fn must be folded into the program (merged as Tish source)"
|
|
46
|
+
);
|
|
47
|
+
// ...and NOT lowered to a native module load (the #38 mis-routing).
|
|
48
|
+
let native_scope = merged.program.statements.iter().any(|s| {
|
|
49
|
+
matches!(
|
|
50
|
+
s,
|
|
51
|
+
Statement::VarDecl { init: Some(Expr::NativeModuleLoad { spec, .. }), .. }
|
|
52
|
+
if spec.contains("@test")
|
|
53
|
+
)
|
|
54
|
+
});
|
|
55
|
+
assert!(
|
|
56
|
+
!native_scope,
|
|
57
|
+
"a merged @scope package must NOT be routed to the native Rust-crate path (#38)"
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
#[test]
|
|
62
|
+
fn scope_tish_module_true_merges_as_source() {
|
|
63
|
+
assert_merged_as_source(&setup(r#","tish":{"module":true}"#));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
#[test]
|
|
67
|
+
fn scope_tish_module_string_path_merges_as_source() {
|
|
68
|
+
assert_merged_as_source(&setup(r#","tish":{"module":"index.tish"}"#));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
#[test]
|
|
72
|
+
fn scope_exports_tish_merges_as_source() {
|
|
73
|
+
assert_merged_as_source(&setup(r#","exports":{"tish":"./index.tish"}"#));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
#[test]
|
|
77
|
+
fn scope_plain_main_merges_as_source() {
|
|
78
|
+
assert_merged_as_source(&setup(r#","main":"index.tish""#));
|
|
79
|
+
}
|
|
@@ -340,7 +340,22 @@ pub fn find_crate_path(crate_name: &str) -> Result<PathBuf, String> {
|
|
|
340
340
|
|
|
341
341
|
/// Sanitize a user-chosen output stem for Cargo `[lib]` / `[[bin]]` target names.
|
|
342
342
|
pub fn cargo_target_name(stem: &str) -> String {
|
|
343
|
-
|
|
343
|
+
// The result is emitted verbatim into a generated `Cargo.toml` (`name = "<stem>"`) and used as
|
|
344
|
+
// the crate/target name, so it must be a valid Rust identifier. Map every char that isn't
|
|
345
|
+
// ASCII-alphanumeric or `_` to `_` — a `-` (cargo normalizes it to `_` anyway), but also a `.`,
|
|
346
|
+
// space, or `"` from an odd `-o` path, which would otherwise break or inject the manifest.
|
|
347
|
+
// A leading digit is prefixed with `_`; an empty result falls back to a fixed default.
|
|
348
|
+
let mut name: String = stem
|
|
349
|
+
.chars()
|
|
350
|
+
.map(|c| if c.is_ascii_alphanumeric() || c == '_' { c } else { '_' })
|
|
351
|
+
.collect();
|
|
352
|
+
if name.is_empty() {
|
|
353
|
+
return "tish_output".to_string();
|
|
354
|
+
}
|
|
355
|
+
if name.starts_with(|c: char| c.is_ascii_digit()) {
|
|
356
|
+
name.insert(0, '_');
|
|
357
|
+
}
|
|
358
|
+
name
|
|
344
359
|
}
|
|
345
360
|
|
|
346
361
|
/// Create a temp build directory with src subdir.
|
|
@@ -428,6 +443,17 @@ mod protoc_tests {
|
|
|
428
443
|
assert_eq!(cargo_target_name("hello-ios"), "hello_ios");
|
|
429
444
|
assert_eq!(cargo_target_name("tish_out"), "tish_out");
|
|
430
445
|
}
|
|
446
|
+
|
|
447
|
+
#[test]
|
|
448
|
+
fn cargo_target_name_sanitizes_invalid_chars() {
|
|
449
|
+
// Quotes / dots / spaces from an odd `-o` stem can't break or inject the manifest.
|
|
450
|
+
assert_eq!(cargo_target_name("a\"b"), "a_b");
|
|
451
|
+
assert_eq!(cargo_target_name("my.app"), "my_app");
|
|
452
|
+
assert_eq!(cargo_target_name("my game"), "my_game");
|
|
453
|
+
// A leading digit is made identifier-safe; empty falls back.
|
|
454
|
+
assert_eq!(cargo_target_name("3d"), "_3d");
|
|
455
|
+
assert_eq!(cargo_target_name(""), "tish_output");
|
|
456
|
+
}
|
|
431
457
|
}
|
|
432
458
|
|
|
433
459
|
/// Find the built static library in target/release (or target/$TRIPLE/release).
|
|
@@ -8,15 +8,23 @@ description = "Shared builtin implementations for Tish (array, string, object, m
|
|
|
8
8
|
license-file = { workspace = true }
|
|
9
9
|
repository = { workspace = true }
|
|
10
10
|
[dependencies]
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
# std-only deps, optional so the `portable` build drops them (rand needs OS
|
|
12
|
+
# entropy; ahash needs atomics — both replaced by tishlang_core hooks/FxHasher).
|
|
13
|
+
ahash = { version = "0.8.11", optional = true }
|
|
14
|
+
indexmap = { version = "2", default-features = false }
|
|
15
|
+
rand = { version = "0.10.1", optional = true }
|
|
16
|
+
# default-features=false keeps it no_std (it works on core+alloc via tinyvec);
|
|
17
|
+
# tish only uses NFC/NFD/NFKC/NFKD, none of which need std.
|
|
18
|
+
unicode-normalization = { version = "0.1.25", default-features = false }
|
|
19
|
+
tishlang_core = { path = "../tish_core", version = ">=0.1", default-features = false }
|
|
16
20
|
|
|
17
21
|
[features]
|
|
18
|
-
default = []
|
|
22
|
+
default = ["std"]
|
|
23
|
+
std = ["tishlang_core/std", "dep:ahash", "dep:rand"]
|
|
19
24
|
regex = ["tishlang_core/regex"]
|
|
25
|
+
# no_std + alloc build for GBA. Inherits tishlang_core's portable vocabulary and
|
|
26
|
+
# its clock/RNG hooks; float math routes through tishlang_core::FloatExt (libm).
|
|
27
|
+
portable = ["tishlang_core/portable"]
|
|
20
28
|
# Propagate the `send-values` feature from `tishlang_core` so that closures
|
|
21
29
|
# handed to us via `NativeFn` pick up the `Send + Sync` bound automatically
|
|
22
30
|
# when multi-threaded `Value`s are enabled upstream.
|