@tishlang/tish 1.7.0 → 1.8.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/README.md +2 -0
- package/bin/tish +0 -0
- package/crates/js_to_tish/src/transform/expr.rs +28 -8
- package/crates/js_to_tish/src/transform/stmt.rs +49 -22
- package/crates/tish/Cargo.toml +15 -5
- package/crates/tish/src/cargo_native_registry.rs +29 -0
- package/crates/tish/src/cli_help.rs +16 -10
- package/crates/tish/src/main.rs +87 -32
- package/crates/tish/src/repl_completion.rs +3 -3
- package/crates/tish/tests/cargo_example_compile.rs +1 -1
- package/crates/tish/tests/integration_test.rs +19 -7
- package/crates/tish/tests/shortcircuit.rs +1 -1
- package/crates/tish_ast/src/ast.rs +80 -9
- package/crates/tish_build_utils/Cargo.toml +4 -0
- package/crates/tish_build_utils/src/lib.rs +105 -2
- package/crates/tish_builtins/Cargo.toml +5 -1
- package/crates/tish_builtins/src/array.rs +13 -12
- package/crates/tish_builtins/src/construct.rs +34 -33
- package/crates/tish_builtins/src/globals.rs +12 -11
- package/crates/tish_builtins/src/helpers.rs +2 -1
- package/crates/tish_builtins/src/object.rs +3 -2
- package/crates/tish_builtins/src/string.rs +73 -3
- package/crates/tish_bytecode/src/compiler.rs +12 -14
- package/crates/tish_bytecode/src/opcode.rs +12 -3
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/codegen.rs +745 -199
- package/crates/tish_compile/src/infer.rs +6 -0
- package/crates/tish_compile/src/lib.rs +4 -3
- package/crates/tish_compile/src/resolve.rs +180 -82
- package/crates/tish_compile/src/types.rs +175 -11
- package/crates/tish_compile_js/Cargo.toml +1 -0
- package/crates/tish_compile_js/src/codegen.rs +152 -29
- package/crates/tish_compile_js/src/lib.rs +3 -1
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +31 -12
- package/crates/tish_core/Cargo.toml +8 -0
- package/crates/tish_core/src/json.rs +102 -53
- package/crates/tish_core/src/lib.rs +3 -1
- package/crates/tish_core/src/macros.rs +5 -5
- package/crates/tish_core/src/value.rs +53 -15
- package/crates/tish_core/src/vmref.rs +178 -0
- package/crates/tish_eval/Cargo.toml +17 -2
- package/crates/tish_eval/src/eval.rs +90 -28
- package/crates/tish_eval/src/http.rs +61 -0
- package/crates/tish_eval/src/lib.rs +3 -3
- package/crates/tish_eval/src/natives.rs +41 -0
- package/crates/tish_eval/src/value.rs +7 -3
- package/crates/tish_eval/src/value_convert.rs +13 -5
- package/crates/tish_fmt/src/lib.rs +120 -30
- package/crates/tish_lexer/src/lib.rs +20 -5
- package/crates/tish_lexer/src/token.rs +4 -0
- package/crates/tish_llvm/src/lib.rs +3 -1
- package/crates/tish_lsp/Cargo.toml +4 -1
- package/crates/tish_lsp/README.md +1 -1
- package/crates/tish_lsp/src/builtin_goto.rs +261 -0
- package/crates/tish_lsp/src/import_goto.rs +549 -0
- package/crates/tish_lsp/src/main.rs +502 -102
- package/crates/tish_native/src/build.rs +3 -2
- package/crates/tish_native/src/lib.rs +6 -2
- package/crates/tish_opt/src/lib.rs +17 -2
- package/crates/tish_parser/src/lib.rs +10 -3
- package/crates/tish_parser/src/parser.rs +346 -56
- package/crates/tish_resolve/Cargo.toml +13 -0
- package/crates/tish_resolve/src/lib.rs +3436 -0
- package/crates/tish_resolve/src/pos.rs +133 -0
- package/crates/tish_runtime/Cargo.toml +68 -3
- package/crates/tish_runtime/src/http.rs +1123 -141
- package/crates/tish_runtime/src/http_fetch.rs +15 -14
- package/crates/tish_runtime/src/http_hyper.rs +418 -0
- package/crates/tish_runtime/src/http_prefork.rs +189 -0
- package/crates/tish_runtime/src/lib.rs +159 -29
- package/crates/tish_runtime/src/promise.rs +199 -36
- package/crates/tish_runtime/src/promise_io.rs +2 -1
- package/crates/tish_runtime/src/timers.rs +37 -1
- package/crates/tish_runtime/src/ws.rs +26 -28
- package/crates/tish_ui/src/jsx.rs +279 -8
- package/crates/tish_ui/src/lib.rs +5 -2
- package/crates/tish_ui/src/runtime/hooks.rs +406 -45
- package/crates/tish_ui/src/runtime/mod.rs +36 -9
- package/crates/tish_vm/Cargo.toml +15 -5
- package/crates/tish_vm/src/vm.rs +506 -259
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +3 -1
- package/crates/tish_wasm/src/lib.rs +17 -14
- package/crates/tish_wasm_runtime/Cargo.toml +2 -1
- package/crates/tish_wasm_runtime/src/lib.rs +1 -1
- package/crates/tishlang_cargo_bindgen/Cargo.toml +1 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +68 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +5 -4
- package/justfile +8 -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
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
//! Grey preview hint below the line (like Node) and Tab for full list.
|
|
3
3
|
|
|
4
4
|
use std::borrow::Cow;
|
|
5
|
-
|
|
6
|
-
use
|
|
5
|
+
|
|
6
|
+
use tishlang_core::VmRef;
|
|
7
7
|
|
|
8
8
|
use rustyline::completion::{Completer, Pair};
|
|
9
9
|
use rustyline::highlight::Highlighter;
|
|
@@ -29,7 +29,7 @@ const ANSI_RESET: &str = "\x1b[0m";
|
|
|
29
29
|
|
|
30
30
|
/// Tab completer that evaluates the expression before the last `.` and suggests property/method names.
|
|
31
31
|
pub struct ReplCompleter {
|
|
32
|
-
pub vm:
|
|
32
|
+
pub vm: VmRef<Vm>,
|
|
33
33
|
pub no_optimize: bool,
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -66,12 +66,20 @@ fn file_content_hash(path: &Path) -> u64 {
|
|
|
66
66
|
///
|
|
67
67
|
/// Cache is keyed by backend (native, cranelift, js, wasi) so e.g. cranelift and wasi
|
|
68
68
|
/// compiles of the same file do not overwrite each other: .../cranelift/<stem>_<hash> vs .../wasi/<stem>_<hash>.wasm.
|
|
69
|
+
///
|
|
70
|
+
/// The artifact **basename** must be unique per `(stem, hash, backend)`: nested `tish build`
|
|
71
|
+
/// uses it as the Cargo binary name under the workspace `target/release/`. If native and
|
|
72
|
+
/// cranelift both used `strict_equality_<hash>`, parallel `cargo nextest` could run those
|
|
73
|
+
/// tests concurrently and corrupt the same `target/release/...` output (Linux: ETXTBSY when
|
|
74
|
+
/// executing a binary still being written).
|
|
69
75
|
fn compile_cached(bin: &Path, path: &Path, backend: &str) -> PathBuf {
|
|
70
76
|
let stem = path.file_stem().unwrap().to_string_lossy();
|
|
71
77
|
let hash = file_content_hash(path);
|
|
72
78
|
let hash8 = &format!("{:016x}", hash)[..8];
|
|
73
79
|
let cache_base = integration_compile_cache_dir().join(backend);
|
|
74
80
|
let _ = std::fs::create_dir_all(&cache_base);
|
|
81
|
+
// Include `backend` in the leaf name so nested cargo bin names never collide across backends.
|
|
82
|
+
let leaf = format!("{}__{}__{}", stem, backend, hash8);
|
|
75
83
|
|
|
76
84
|
let (artifact_path, compile_args): (PathBuf, Vec<OsString>) = match backend {
|
|
77
85
|
"native" => {
|
|
@@ -80,7 +88,7 @@ fn compile_cached(bin: &Path, path: &Path, backend: &str) -> PathBuf {
|
|
|
80
88
|
} else {
|
|
81
89
|
""
|
|
82
90
|
};
|
|
83
|
-
let cached = cache_base.join(format!("{}
|
|
91
|
+
let cached = cache_base.join(format!("{}{}", leaf, ext));
|
|
84
92
|
let args = vec![
|
|
85
93
|
OsString::from("build"),
|
|
86
94
|
OsString::from(path),
|
|
@@ -95,7 +103,7 @@ fn compile_cached(bin: &Path, path: &Path, backend: &str) -> PathBuf {
|
|
|
95
103
|
} else {
|
|
96
104
|
""
|
|
97
105
|
};
|
|
98
|
-
let cached = cache_base.join(format!("{}
|
|
106
|
+
let cached = cache_base.join(format!("{}{}", leaf, ext));
|
|
99
107
|
let args = vec![
|
|
100
108
|
OsString::from("build"),
|
|
101
109
|
OsString::from(path),
|
|
@@ -107,7 +115,7 @@ fn compile_cached(bin: &Path, path: &Path, backend: &str) -> PathBuf {
|
|
|
107
115
|
(cached, args)
|
|
108
116
|
}
|
|
109
117
|
"js" => {
|
|
110
|
-
let cached = cache_base.join(format!("{}
|
|
118
|
+
let cached = cache_base.join(format!("{}.js", leaf));
|
|
111
119
|
let args = vec![
|
|
112
120
|
OsString::from("build"),
|
|
113
121
|
OsString::from(path),
|
|
@@ -119,7 +127,7 @@ fn compile_cached(bin: &Path, path: &Path, backend: &str) -> PathBuf {
|
|
|
119
127
|
(cached, args)
|
|
120
128
|
}
|
|
121
129
|
"wasi" => {
|
|
122
|
-
let out_base = cache_base.join(
|
|
130
|
+
let out_base = cache_base.join(&leaf);
|
|
123
131
|
let artifact = out_base.with_extension("wasm");
|
|
124
132
|
let args = vec![
|
|
125
133
|
OsString::from("build"),
|
|
@@ -397,7 +405,7 @@ fn test_async_await_run() {
|
|
|
397
405
|
}
|
|
398
406
|
}
|
|
399
407
|
|
|
400
|
-
/// Run Promise and setTimeout module tests (
|
|
408
|
+
/// Run Promise and setTimeout module tests (`promise` needs `http`; `settimeout` needs `timers`, which `http` enables).
|
|
401
409
|
/// Ignored: tishlang_eval::run() does not run the event loop.
|
|
402
410
|
#[test]
|
|
403
411
|
#[cfg(feature = "http")]
|
|
@@ -455,7 +463,9 @@ fn test_vm_date_now() {
|
|
|
455
463
|
// Library path
|
|
456
464
|
let modules = tishlang_compile::resolve_project(&path, path.parent()).expect("resolve");
|
|
457
465
|
tishlang_compile::detect_cycles(&modules).expect("cycles");
|
|
458
|
-
let program = tishlang_compile::merge_modules(modules)
|
|
466
|
+
let program = tishlang_compile::merge_modules(modules)
|
|
467
|
+
.expect("merge")
|
|
468
|
+
.program;
|
|
459
469
|
let chunk = tishlang_bytecode::compile(&program).expect("compile");
|
|
460
470
|
let result = tishlang_vm::run(&chunk);
|
|
461
471
|
assert!(
|
|
@@ -499,7 +509,9 @@ fn test_vm_index_assign_via_resolve() {
|
|
|
499
509
|
.join("array_sort_minimal.tish");
|
|
500
510
|
let modules = tishlang_compile::resolve_project(&path, path.parent()).expect("resolve");
|
|
501
511
|
tishlang_compile::detect_cycles(&modules).expect("cycles");
|
|
502
|
-
let program = tishlang_compile::merge_modules(modules)
|
|
512
|
+
let program = tishlang_compile::merge_modules(modules)
|
|
513
|
+
.expect("merge")
|
|
514
|
+
.program;
|
|
503
515
|
let chunk = tishlang_bytecode::compile(&program).expect("compile");
|
|
504
516
|
let result = tishlang_vm::run(&chunk);
|
|
505
517
|
assert!(
|
|
@@ -53,7 +53,7 @@ fn test_and_shortcircuit_via_resolve_project() {
|
|
|
53
53
|
let path = path.canonicalize().expect("path");
|
|
54
54
|
let project_root = path.parent().unwrap();
|
|
55
55
|
let modules = resolve_project(&path, Some(project_root)).expect("resolve");
|
|
56
|
-
let program = merge_modules(modules).expect("merge");
|
|
56
|
+
let program = merge_modules(modules).expect("merge").program;
|
|
57
57
|
let program = tishlang_opt::optimize(&program); // Mirror CLI
|
|
58
58
|
let chunk = compile(&program).expect("compile");
|
|
59
59
|
let result = tishlang_vm::run(&chunk);
|
|
@@ -30,6 +30,7 @@ pub enum TypeAnnotation {
|
|
|
30
30
|
#[derive(Debug, Clone, PartialEq)]
|
|
31
31
|
pub struct TypedParam {
|
|
32
32
|
pub name: Arc<str>,
|
|
33
|
+
pub name_span: Span,
|
|
33
34
|
pub type_ann: Option<TypeAnnotation>,
|
|
34
35
|
pub default: Option<Expr>,
|
|
35
36
|
}
|
|
@@ -64,11 +65,11 @@ impl FunParam {
|
|
|
64
65
|
for el in elements {
|
|
65
66
|
if let Some(el) = el {
|
|
66
67
|
match el {
|
|
67
|
-
DestructElement::Ident(n) => out.push(Arc::clone(n)),
|
|
68
|
+
DestructElement::Ident(n, _) => out.push(Arc::clone(n)),
|
|
68
69
|
DestructElement::Pattern(p) => {
|
|
69
70
|
Self::collect_pattern_binding_names(p, out);
|
|
70
71
|
}
|
|
71
|
-
DestructElement::Rest(n) => out.push(Arc::clone(n)),
|
|
72
|
+
DestructElement::Rest(n, _) => out.push(Arc::clone(n)),
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
75
|
}
|
|
@@ -76,11 +77,11 @@ impl FunParam {
|
|
|
76
77
|
DestructPattern::Object(props) => {
|
|
77
78
|
for prop in props {
|
|
78
79
|
match &prop.value {
|
|
79
|
-
DestructElement::Ident(n) => out.push(Arc::clone(n)),
|
|
80
|
+
DestructElement::Ident(n, _) => out.push(Arc::clone(n)),
|
|
80
81
|
DestructElement::Pattern(p) => {
|
|
81
82
|
Self::collect_pattern_binding_names(p, out);
|
|
82
83
|
}
|
|
83
|
-
DestructElement::Rest(n) => out.push(Arc::clone(n)),
|
|
84
|
+
DestructElement::Rest(n, _) => out.push(Arc::clone(n)),
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
87
|
}
|
|
@@ -101,11 +102,11 @@ pub enum DestructPattern {
|
|
|
101
102
|
#[derive(Debug, Clone, PartialEq)]
|
|
102
103
|
pub enum DestructElement {
|
|
103
104
|
/// Simple binding: a
|
|
104
|
-
Ident(Arc<str
|
|
105
|
+
Ident(Arc<str>, Span),
|
|
105
106
|
/// Nested pattern: [a, b] or { x, y }
|
|
106
107
|
Pattern(Box<DestructPattern>),
|
|
107
108
|
/// Rest element: ...rest
|
|
108
|
-
Rest(Arc<str
|
|
109
|
+
Rest(Arc<str>, Span),
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
/// Property in object destructuring pattern
|
|
@@ -123,12 +124,20 @@ pub enum ImportSpecifier {
|
|
|
123
124
|
/// Named: { foo } or { foo as bar }
|
|
124
125
|
Named {
|
|
125
126
|
name: Arc<str>,
|
|
127
|
+
name_span: Span,
|
|
126
128
|
alias: Option<Arc<str>>,
|
|
129
|
+
alias_span: Option<Span>,
|
|
127
130
|
},
|
|
128
131
|
/// Namespace: * as M
|
|
129
|
-
Namespace
|
|
132
|
+
Namespace {
|
|
133
|
+
name: Arc<str>,
|
|
134
|
+
name_span: Span,
|
|
135
|
+
},
|
|
130
136
|
/// Default: import X from "..."
|
|
131
|
-
Default
|
|
137
|
+
Default {
|
|
138
|
+
name: Arc<str>,
|
|
139
|
+
name_span: Span,
|
|
140
|
+
},
|
|
132
141
|
}
|
|
133
142
|
|
|
134
143
|
/// Export declaration: named (const/let/fn) or default
|
|
@@ -153,6 +162,7 @@ pub enum Statement {
|
|
|
153
162
|
},
|
|
154
163
|
VarDecl {
|
|
155
164
|
name: Arc<str>,
|
|
165
|
+
name_span: Span,
|
|
156
166
|
mutable: bool, // true for `let`, false for `const`
|
|
157
167
|
type_ann: Option<TypeAnnotation>,
|
|
158
168
|
init: Option<Expr>,
|
|
@@ -189,6 +199,7 @@ pub enum Statement {
|
|
|
189
199
|
},
|
|
190
200
|
ForOf {
|
|
191
201
|
name: Arc<str>,
|
|
202
|
+
name_span: Span,
|
|
192
203
|
iterable: Expr,
|
|
193
204
|
body: Box<Statement>,
|
|
194
205
|
span: Span,
|
|
@@ -206,6 +217,7 @@ pub enum Statement {
|
|
|
206
217
|
FunDecl {
|
|
207
218
|
async_: bool,
|
|
208
219
|
name: Arc<str>,
|
|
220
|
+
name_span: Span,
|
|
209
221
|
params: Vec<FunParam>,
|
|
210
222
|
rest_param: Option<TypedParam>,
|
|
211
223
|
return_type: Option<TypeAnnotation>,
|
|
@@ -230,6 +242,7 @@ pub enum Statement {
|
|
|
230
242
|
Try {
|
|
231
243
|
body: Box<Statement>,
|
|
232
244
|
catch_param: Option<Arc<str>>,
|
|
245
|
+
catch_param_span: Option<Span>,
|
|
233
246
|
catch_body: Option<Box<Statement>>,
|
|
234
247
|
finally_body: Option<Box<Statement>>,
|
|
235
248
|
span: Span,
|
|
@@ -243,6 +256,31 @@ pub enum Statement {
|
|
|
243
256
|
declaration: Box<ExportDeclaration>,
|
|
244
257
|
span: Span,
|
|
245
258
|
},
|
|
259
|
+
/// `type Name = Type` (erased at runtime; for checker / declaration files).
|
|
260
|
+
TypeAlias {
|
|
261
|
+
name: Arc<str>,
|
|
262
|
+
name_span: Span,
|
|
263
|
+
ty: TypeAnnotation,
|
|
264
|
+
span: Span,
|
|
265
|
+
},
|
|
266
|
+
/// `declare let name: T` or `declare const name: T`
|
|
267
|
+
DeclareVar {
|
|
268
|
+
name: Arc<str>,
|
|
269
|
+
name_span: Span,
|
|
270
|
+
type_ann: Option<TypeAnnotation>,
|
|
271
|
+
const_: bool,
|
|
272
|
+
span: Span,
|
|
273
|
+
},
|
|
274
|
+
/// `declare [async] function name(...): R` (no body).
|
|
275
|
+
DeclareFun {
|
|
276
|
+
async_: bool,
|
|
277
|
+
name: Arc<str>,
|
|
278
|
+
name_span: Span,
|
|
279
|
+
params: Vec<FunParam>,
|
|
280
|
+
rest_param: Option<TypedParam>,
|
|
281
|
+
return_type: Option<TypeAnnotation>,
|
|
282
|
+
span: Span,
|
|
283
|
+
},
|
|
246
284
|
}
|
|
247
285
|
|
|
248
286
|
#[derive(Debug, Clone, PartialEq)]
|
|
@@ -550,6 +588,39 @@ pub enum UnaryOp {
|
|
|
550
588
|
|
|
551
589
|
#[derive(Debug, Clone, PartialEq)]
|
|
552
590
|
pub enum MemberProp {
|
|
553
|
-
|
|
591
|
+
/// Property name in `obj.prop` / `obj?.prop` (span covers **prop** only).
|
|
592
|
+
Name {
|
|
593
|
+
name: Arc<str>,
|
|
594
|
+
span: Span,
|
|
595
|
+
},
|
|
554
596
|
Expr(Box<Expr>), // for computed property
|
|
555
597
|
}
|
|
598
|
+
|
|
599
|
+
impl Statement {
|
|
600
|
+
/// Source span covering this statement (including nested bodies where applicable).
|
|
601
|
+
pub fn span(&self) -> Span {
|
|
602
|
+
match self {
|
|
603
|
+
Statement::Block { span, .. }
|
|
604
|
+
| Statement::VarDecl { span, .. }
|
|
605
|
+
| Statement::VarDeclDestructure { span, .. }
|
|
606
|
+
| Statement::ExprStmt { span, .. }
|
|
607
|
+
| Statement::If { span, .. }
|
|
608
|
+
| Statement::While { span, .. }
|
|
609
|
+
| Statement::For { span, .. }
|
|
610
|
+
| Statement::ForOf { span, .. }
|
|
611
|
+
| Statement::Return { span, .. }
|
|
612
|
+
| Statement::Break { span, .. }
|
|
613
|
+
| Statement::Continue { span, .. }
|
|
614
|
+
| Statement::FunDecl { span, .. }
|
|
615
|
+
| Statement::Switch { span, .. }
|
|
616
|
+
| Statement::DoWhile { span, .. }
|
|
617
|
+
| Statement::Throw { span, .. }
|
|
618
|
+
| Statement::Try { span, .. }
|
|
619
|
+
| Statement::Import { span, .. }
|
|
620
|
+
| Statement::Export { span, .. }
|
|
621
|
+
| Statement::TypeAlias { span, .. }
|
|
622
|
+
| Statement::DeclareVar { span, .. }
|
|
623
|
+
| Statement::DeclareFun { span, .. } => *span,
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
@@ -5,3 +5,7 @@ edition = "2021"
|
|
|
5
5
|
description = "Shared build utilities for Tish (workspace discovery, path resolution)"
|
|
6
6
|
license-file = { workspace = true }
|
|
7
7
|
repository = { workspace = true }
|
|
8
|
+
|
|
9
|
+
[dependencies]
|
|
10
|
+
# Bundled `protoc` for nested `cargo build` (Lance / prost-build) when none is on PATH.
|
|
11
|
+
protoc-bin-vendored = "3"
|
|
@@ -137,6 +137,39 @@ fn tish_root_from_project_cargo_files(mut start: PathBuf) -> Option<PathBuf> {
|
|
|
137
137
|
/// Returns the directory containing the workspace Cargo.toml (with [workspace]).
|
|
138
138
|
/// Used when building native binaries, WASM, or locating runtime crates.
|
|
139
139
|
pub fn find_workspace_root() -> Result<PathBuf, String> {
|
|
140
|
+
// Strategy 0: explicit checkout (e.g. tish-hub `npm run dev` / native build from a JS-only tree)
|
|
141
|
+
if let Ok(root) = std::env::var("TISHLANG_WORKSPACE") {
|
|
142
|
+
let trimmed = root.trim();
|
|
143
|
+
if !trimmed.is_empty() {
|
|
144
|
+
let p = PathBuf::from(trimmed);
|
|
145
|
+
if p.is_dir() && is_tish_workspace_root(&p) {
|
|
146
|
+
return p.canonicalize().map_err(|e| {
|
|
147
|
+
format!(
|
|
148
|
+
"TISHLANG_WORKSPACE is set but not a usable directory: {}",
|
|
149
|
+
e
|
|
150
|
+
)
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
// Non-empty but invalid: fall through (sibling `tish/` discovery may still apply).
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Strategy 0b: monorepo layout `…/<parent>/tish-hub` (cwd) next to `…/<parent>/tish` (language repo).
|
|
158
|
+
// Works without `TISHLANG_WORKSPACE` so older `tish` binaries still find the compiler tree.
|
|
159
|
+
if let Ok(mut dir) = std::env::current_dir() {
|
|
160
|
+
for _ in 0..24 {
|
|
161
|
+
let candidate = dir.join("tish");
|
|
162
|
+
if is_tish_workspace_root(&candidate) {
|
|
163
|
+
return candidate.canonicalize().map_err(|e| {
|
|
164
|
+
format!("Cannot canonicalize Tish workspace {}: {}", candidate.display(), e)
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
if !dir.pop() {
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
140
173
|
// Strategy 1: CARGO_MANIFEST_DIR (works during cargo build/run from workspace)
|
|
141
174
|
if let Ok(manifest_dir) = std::env::var("CARGO_MANIFEST_DIR") {
|
|
142
175
|
let path = PathBuf::from(&manifest_dir);
|
|
@@ -301,18 +334,70 @@ pub fn create_build_dir(prefix: &str, out_name: &str) -> Result<PathBuf, String>
|
|
|
301
334
|
Ok(build_dir)
|
|
302
335
|
}
|
|
303
336
|
|
|
337
|
+
/// `PROTOC` for prost-build in transitive crates (e.g. lance-encoding) during nested `cargo build`.
|
|
338
|
+
/// Respects an existing `PROTOC`, then `protoc` on `PATH`, then `protoc-bin-vendored`.
|
|
339
|
+
fn protoc_for_nested_cargo() -> Option<PathBuf> {
|
|
340
|
+
// Empty or non-file PROTOC must not short-circuit: prost-build treats "" like a missing binary.
|
|
341
|
+
if let Some(v) = std::env::var_os("PROTOC") {
|
|
342
|
+
if !v.is_empty() {
|
|
343
|
+
let p = PathBuf::from(&v);
|
|
344
|
+
if p.is_file() {
|
|
345
|
+
return None;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
// Prefer vendored protoc over PATH: a broken or non-executable `protoc` on PATH still passes `is_file()`.
|
|
350
|
+
if let Ok(p) = protoc_bin_vendored::protoc_bin_path() {
|
|
351
|
+
return Some(p);
|
|
352
|
+
}
|
|
353
|
+
let ext = if cfg!(windows) { ".exe" } else { "" };
|
|
354
|
+
let name = format!("protoc{}", ext);
|
|
355
|
+
if let Some(paths) = std::env::var_os("PATH") {
|
|
356
|
+
for dir in std::env::split_paths(&paths) {
|
|
357
|
+
let candidate = dir.join(&name);
|
|
358
|
+
if candidate.is_file() {
|
|
359
|
+
return Some(candidate);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
None
|
|
364
|
+
}
|
|
365
|
+
|
|
304
366
|
/// Run cargo build in the given directory.
|
|
305
367
|
/// If target_dir is Some, use that for --target-dir (e.g. workspace target for caching).
|
|
306
368
|
pub fn run_cargo_build(build_dir: &Path, target_dir: Option<&Path>) -> Result<(), String> {
|
|
307
369
|
let target_dir = target_dir
|
|
308
370
|
.map(|p| p.to_path_buf())
|
|
309
371
|
.unwrap_or_else(|| build_dir.join("target"));
|
|
310
|
-
|
|
311
|
-
|
|
372
|
+
// Default to target-cpu=native so the emitted binary uses every SIMD / ISA
|
|
373
|
+
// extension the build host supports. Callers can override by pre-setting
|
|
374
|
+
// RUSTFLAGS in the environment.
|
|
375
|
+
let existing_rustflags = std::env::var("RUSTFLAGS").unwrap_or_default();
|
|
376
|
+
let merged_rustflags = if existing_rustflags.is_empty() {
|
|
377
|
+
"-C target-cpu=native".to_string()
|
|
378
|
+
} else if existing_rustflags.contains("target-cpu") {
|
|
379
|
+
existing_rustflags
|
|
380
|
+
} else {
|
|
381
|
+
format!("{} -C target-cpu=native", existing_rustflags)
|
|
382
|
+
};
|
|
383
|
+
// Nested `cargo build` (e.g. `tish build --native-backend rust`) inherits the parent
|
|
384
|
+
// environment. CI often sets `RUSTC_WRAPPER=sccache`; wrapping this inner compile too can
|
|
385
|
+
// cause flaky or failed builds (LTO / temp-crate paths). Use plain rustc here; the main
|
|
386
|
+
// workspace build still benefits from the wrapper.
|
|
387
|
+
let mut cmd = Command::new("cargo");
|
|
388
|
+
cmd.args(["build", "--release", "--target-dir"])
|
|
312
389
|
.arg(&target_dir)
|
|
313
390
|
.current_dir(build_dir)
|
|
314
391
|
.env_remove("CARGO_TARGET_DIR")
|
|
392
|
+
.env_remove("RUSTC_WRAPPER")
|
|
393
|
+
.env_remove("RUSTC_WORKSPACE_WRAPPER")
|
|
394
|
+
.env_remove("CARGO_BUILD_RUSTC_WRAPPER")
|
|
315
395
|
.env("CARGO_TERM_PROGRESS", "always")
|
|
396
|
+
.env("RUSTFLAGS", &merged_rustflags);
|
|
397
|
+
if let Some(protoc) = protoc_for_nested_cargo() {
|
|
398
|
+
cmd.env("PROTOC", protoc);
|
|
399
|
+
}
|
|
400
|
+
let output = cmd
|
|
316
401
|
.output()
|
|
317
402
|
.map_err(|e| format!("Failed to run cargo: {}", e))?;
|
|
318
403
|
|
|
@@ -327,6 +412,24 @@ pub fn run_cargo_build(build_dir: &Path, target_dir: Option<&Path>) -> Result<()
|
|
|
327
412
|
Ok(())
|
|
328
413
|
}
|
|
329
414
|
|
|
415
|
+
#[cfg(test)]
|
|
416
|
+
mod protoc_tests {
|
|
417
|
+
use super::*;
|
|
418
|
+
|
|
419
|
+
#[test]
|
|
420
|
+
fn protoc_for_nested_cargo_without_env_uses_vendored_or_path() {
|
|
421
|
+
let _lock = std::sync::Mutex::new(());
|
|
422
|
+
let _guard = _lock.lock().unwrap();
|
|
423
|
+
std::env::remove_var("PROTOC");
|
|
424
|
+
let p = protoc_for_nested_cargo().expect("expected vendored or PATH protoc");
|
|
425
|
+
assert!(
|
|
426
|
+
p.exists(),
|
|
427
|
+
"resolved protoc should exist: {}",
|
|
428
|
+
p.display()
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
330
433
|
/// Find the built binary in target/release.
|
|
331
434
|
pub fn find_release_binary(binary_dir: &Path, bin_name: &str) -> Result<PathBuf, String> {
|
|
332
435
|
let binary_no_ext = binary_dir.join(bin_name);
|
|
@@ -8,9 +8,13 @@ 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
|
-
rand = "0.10.
|
|
11
|
+
rand = "0.10.1"
|
|
12
12
|
tishlang_core = { path = "../tish_core", version = ">=0.1" }
|
|
13
13
|
|
|
14
14
|
[features]
|
|
15
15
|
default = []
|
|
16
16
|
regex = ["tishlang_core/regex"]
|
|
17
|
+
# Propagate the `send-values` feature from `tishlang_core` so that closures
|
|
18
|
+
# handed to us via `NativeFn` pick up the `Send + Sync` bound automatically
|
|
19
|
+
# when multi-threaded `Value`s are enabled upstream.
|
|
20
|
+
send-values = ["tishlang_core/send-values"]
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
//! Array builtin methods.
|
|
2
2
|
|
|
3
3
|
use crate::helpers::normalize_index;
|
|
4
|
+
use tishlang_core::VmRef;
|
|
4
5
|
use std::cell::RefCell;
|
|
5
6
|
use std::rc::Rc;
|
|
6
7
|
use tishlang_core::Value;
|
|
7
8
|
|
|
8
9
|
/// Create a new array Value from a Vec of Values.
|
|
9
10
|
pub fn from_vec(v: Vec<Value>) -> Value {
|
|
10
|
-
Value::Array(
|
|
11
|
+
Value::Array(VmRef::new(v))
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
/// Get the length of an array.
|
|
@@ -110,7 +111,7 @@ pub fn join(arr: &Value, sep: &Value) -> Value {
|
|
|
110
111
|
pub fn reverse(arr: &Value) -> Value {
|
|
111
112
|
if let Value::Array(arr) = arr {
|
|
112
113
|
arr.borrow_mut().reverse();
|
|
113
|
-
Value::Array(
|
|
114
|
+
Value::Array(arr.clone())
|
|
114
115
|
} else {
|
|
115
116
|
Value::Null
|
|
116
117
|
}
|
|
@@ -122,7 +123,7 @@ pub fn shuffle(arr: &Value) -> Value {
|
|
|
122
123
|
let mut v = arr.borrow().clone();
|
|
123
124
|
use rand::seq::SliceRandom;
|
|
124
125
|
v.shuffle(&mut rand::rng());
|
|
125
|
-
Value::Array(
|
|
126
|
+
Value::Array(VmRef::new(v))
|
|
126
127
|
} else {
|
|
127
128
|
Value::Null
|
|
128
129
|
}
|
|
@@ -141,7 +142,7 @@ pub fn splice(arr: &Value, start: &Value, delete_count: Option<&Value>, items: &
|
|
|
141
142
|
let removed: Vec<Value> = arr_mut
|
|
142
143
|
.splice(start_idx..start_idx + actual_delete, items.iter().cloned())
|
|
143
144
|
.collect();
|
|
144
|
-
Value::Array(
|
|
145
|
+
Value::Array(VmRef::new(removed))
|
|
145
146
|
} else {
|
|
146
147
|
Value::Null
|
|
147
148
|
}
|
|
@@ -158,7 +159,7 @@ pub fn slice(arr: &Value, start: &Value, end: &Value) -> Value {
|
|
|
158
159
|
} else {
|
|
159
160
|
vec![]
|
|
160
161
|
};
|
|
161
|
-
Value::Array(
|
|
162
|
+
Value::Array(VmRef::new(sliced))
|
|
162
163
|
} else {
|
|
163
164
|
Value::Null
|
|
164
165
|
}
|
|
@@ -174,7 +175,7 @@ pub fn concat(arr: &Value, args: &[Value]) -> Value {
|
|
|
174
175
|
result.push(v.clone());
|
|
175
176
|
}
|
|
176
177
|
}
|
|
177
|
-
Value::Array(
|
|
178
|
+
Value::Array(VmRef::new(result))
|
|
178
179
|
} else {
|
|
179
180
|
Value::Null
|
|
180
181
|
}
|
|
@@ -200,7 +201,7 @@ pub fn flat(arr: &Value, depth: &Value) -> Value {
|
|
|
200
201
|
};
|
|
201
202
|
let mut result = Vec::new();
|
|
202
203
|
flatten(&arr.borrow(), d, &mut result);
|
|
203
|
-
Value::Array(
|
|
204
|
+
Value::Array(VmRef::new(result))
|
|
204
205
|
} else {
|
|
205
206
|
Value::Null
|
|
206
207
|
}
|
|
@@ -217,7 +218,7 @@ pub fn map(arr: &Value, callback: &Value) -> Value {
|
|
|
217
218
|
.enumerate()
|
|
218
219
|
.map(|(i, v)| cb(&[v.clone(), Value::Number(i as f64)]))
|
|
219
220
|
.collect();
|
|
220
|
-
Value::Array(
|
|
221
|
+
Value::Array(VmRef::new(result))
|
|
221
222
|
} else {
|
|
222
223
|
Value::Null
|
|
223
224
|
}
|
|
@@ -238,7 +239,7 @@ pub fn filter(arr: &Value, callback: &Value) -> Value {
|
|
|
238
239
|
}
|
|
239
240
|
})
|
|
240
241
|
.collect();
|
|
241
|
-
Value::Array(
|
|
242
|
+
Value::Array(VmRef::new(result))
|
|
242
243
|
} else {
|
|
243
244
|
Value::Null
|
|
244
245
|
}
|
|
@@ -340,7 +341,7 @@ pub fn flat_map(arr: &Value, callback: &Value) -> Value {
|
|
|
340
341
|
result.push(mapped);
|
|
341
342
|
}
|
|
342
343
|
}
|
|
343
|
-
Value::Array(
|
|
344
|
+
Value::Array(VmRef::new(result))
|
|
344
345
|
} else {
|
|
345
346
|
Value::Null
|
|
346
347
|
}
|
|
@@ -352,7 +353,7 @@ where
|
|
|
352
353
|
{
|
|
353
354
|
if let Value::Array(arr) = arr {
|
|
354
355
|
arr.borrow_mut().sort_by(cmp);
|
|
355
|
-
Value::Array(
|
|
356
|
+
Value::Array(arr.clone())
|
|
356
357
|
} else {
|
|
357
358
|
Value::Null
|
|
358
359
|
}
|
|
@@ -387,7 +388,7 @@ pub fn sort_with_comparator(arr: &Value, comparator: &Value) -> Value {
|
|
|
387
388
|
.map(|i| std::mem::replace(&mut elements[i], Value::Null))
|
|
388
389
|
.collect();
|
|
389
390
|
drop(arr_mut);
|
|
390
|
-
Value::Array(
|
|
391
|
+
Value::Array(arr.clone())
|
|
391
392
|
} else {
|
|
392
393
|
Value::Null
|
|
393
394
|
}
|