@tishlang/tish 1.6.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 +2 -0
- package/README.md +2 -0
- package/bin/tish +0 -0
- package/crates/js_to_tish/src/error.rs +2 -8
- package/crates/js_to_tish/src/transform/expr.rs +128 -137
- package/crates/js_to_tish/src/transform/stmt.rs +62 -32
- package/crates/tish/Cargo.toml +15 -5
- package/crates/tish/src/cargo_native_registry.rs +29 -0
- package/crates/tish/src/cli_help.rs +92 -39
- package/crates/tish/src/main.rs +172 -86
- package/crates/tish/src/repl_completion.rs +3 -3
- package/crates/tish/tests/cargo_example_compile.rs +4 -2
- package/crates/tish/tests/integration_test.rs +216 -54
- package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
- package/crates/tish/tests/shortcircuit.rs +20 -5
- package/crates/tish_ast/src/ast.rs +92 -23
- package/crates/tish_build_utils/Cargo.toml +4 -0
- package/crates/tish_build_utils/src/lib.rs +136 -8
- package/crates/tish_builtins/Cargo.toml +5 -1
- package/crates/tish_builtins/src/array.rs +65 -33
- package/crates/tish_builtins/src/construct.rs +34 -39
- package/crates/tish_builtins/src/globals.rs +42 -26
- package/crates/tish_builtins/src/helpers.rs +2 -1
- package/crates/tish_builtins/src/lib.rs +5 -5
- package/crates/tish_builtins/src/math.rs +5 -3
- package/crates/tish_builtins/src/object.rs +3 -2
- package/crates/tish_builtins/src/string.rs +144 -22
- package/crates/tish_bytecode/src/chunk.rs +0 -1
- package/crates/tish_bytecode/src/compiler.rs +173 -71
- package/crates/tish_bytecode/src/opcode.rs +24 -6
- package/crates/tish_bytecode/src/peephole.rs +2 -2
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/codegen.rs +1621 -453
- package/crates/tish_compile/src/infer.rs +75 -19
- package/crates/tish_compile/src/lib.rs +19 -8
- package/crates/tish_compile/src/resolve.rs +278 -137
- package/crates/tish_compile/src/types.rs +184 -24
- package/crates/tish_compile_js/Cargo.toml +1 -0
- package/crates/tish_compile_js/src/codegen.rs +181 -37
- package/crates/tish_compile_js/src/lib.rs +3 -1
- package/crates/tish_compile_js/src/tests_jsx.rs +30 -6
- package/crates/tish_compiler_wasm/src/lib.rs +16 -13
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +69 -59
- package/crates/tish_core/Cargo.toml +8 -0
- package/crates/tish_core/src/json.rs +107 -56
- package/crates/tish_core/src/lib.rs +4 -2
- package/crates/tish_core/src/macros.rs +5 -5
- package/crates/tish_core/src/uri.rs +9 -6
- package/crates/tish_core/src/value.rs +145 -43
- package/crates/tish_core/src/vmref.rs +178 -0
- package/crates/tish_cranelift/src/link.rs +6 -9
- package/crates/tish_cranelift/src/lower.rs +14 -8
- package/crates/tish_eval/Cargo.toml +17 -2
- package/crates/tish_eval/src/eval.rs +474 -165
- package/crates/tish_eval/src/http.rs +61 -0
- package/crates/tish_eval/src/lib.rs +12 -8
- package/crates/tish_eval/src/natives.rs +136 -38
- package/crates/tish_eval/src/promise.rs +14 -8
- package/crates/tish_eval/src/timers.rs +28 -19
- package/crates/tish_eval/src/value.rs +17 -6
- package/crates/tish_eval/src/value_convert.rs +13 -5
- package/crates/tish_fmt/src/lib.rs +149 -43
- package/crates/tish_lexer/src/lib.rs +232 -63
- package/crates/tish_lexer/src/token.rs +10 -6
- package/crates/tish_llvm/src/lib.rs +17 -8
- 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 +504 -106
- package/crates/tish_native/src/build.rs +4 -8
- package/crates/tish_native/src/lib.rs +54 -21
- package/crates/tish_opt/src/lib.rs +84 -52
- package/crates/tish_parser/src/lib.rs +45 -13
- package/crates/tish_parser/src/parser.rs +505 -130
- 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 +1136 -145
- package/crates/tish_runtime/src/http_fetch.rs +38 -27
- 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 +375 -189
- package/crates/tish_runtime/src/promise.rs +199 -40
- 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 +65 -42
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
- package/crates/tish_ui/src/jsx.rs +317 -27
- 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 +725 -281
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +11 -4
- package/crates/tish_wasm/src/lib.rs +55 -42
- 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 +26 -0
- package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +120 -0
- package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +350 -0
- package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -0
- 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
|
@@ -30,11 +30,11 @@ 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
|
}
|
|
36
37
|
|
|
37
|
-
|
|
38
38
|
/// Single formal parameter: simple identifier or destructuring pattern.
|
|
39
39
|
#[derive(Debug, Clone, PartialEq)]
|
|
40
40
|
pub enum FunParam {
|
|
@@ -46,7 +46,6 @@ pub enum FunParam {
|
|
|
46
46
|
},
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
50
49
|
impl FunParam {
|
|
51
50
|
/// Variable names introduced by this formal parameter.
|
|
52
51
|
pub fn bound_names(&self) -> Vec<Arc<str>> {
|
|
@@ -66,11 +65,11 @@ impl FunParam {
|
|
|
66
65
|
for el in elements {
|
|
67
66
|
if let Some(el) = el {
|
|
68
67
|
match el {
|
|
69
|
-
DestructElement::Ident(n) => out.push(Arc::clone(n)),
|
|
68
|
+
DestructElement::Ident(n, _) => out.push(Arc::clone(n)),
|
|
70
69
|
DestructElement::Pattern(p) => {
|
|
71
70
|
Self::collect_pattern_binding_names(p, out);
|
|
72
71
|
}
|
|
73
|
-
DestructElement::Rest(n) => out.push(Arc::clone(n)),
|
|
72
|
+
DestructElement::Rest(n, _) => out.push(Arc::clone(n)),
|
|
74
73
|
}
|
|
75
74
|
}
|
|
76
75
|
}
|
|
@@ -78,11 +77,11 @@ impl FunParam {
|
|
|
78
77
|
DestructPattern::Object(props) => {
|
|
79
78
|
for prop in props {
|
|
80
79
|
match &prop.value {
|
|
81
|
-
DestructElement::Ident(n) => out.push(Arc::clone(n)),
|
|
80
|
+
DestructElement::Ident(n, _) => out.push(Arc::clone(n)),
|
|
82
81
|
DestructElement::Pattern(p) => {
|
|
83
82
|
Self::collect_pattern_binding_names(p, out);
|
|
84
83
|
}
|
|
85
|
-
DestructElement::Rest(n) => out.push(Arc::clone(n)),
|
|
84
|
+
DestructElement::Rest(n, _) => out.push(Arc::clone(n)),
|
|
86
85
|
}
|
|
87
86
|
}
|
|
88
87
|
}
|
|
@@ -103,11 +102,11 @@ pub enum DestructPattern {
|
|
|
103
102
|
#[derive(Debug, Clone, PartialEq)]
|
|
104
103
|
pub enum DestructElement {
|
|
105
104
|
/// Simple binding: a
|
|
106
|
-
Ident(Arc<str
|
|
105
|
+
Ident(Arc<str>, Span),
|
|
107
106
|
/// Nested pattern: [a, b] or { x, y }
|
|
108
107
|
Pattern(Box<DestructPattern>),
|
|
109
108
|
/// Rest element: ...rest
|
|
110
|
-
Rest(Arc<str
|
|
109
|
+
Rest(Arc<str>, Span),
|
|
111
110
|
}
|
|
112
111
|
|
|
113
112
|
/// Property in object destructuring pattern
|
|
@@ -123,11 +122,22 @@ pub struct DestructProp {
|
|
|
123
122
|
#[derive(Debug, Clone, PartialEq)]
|
|
124
123
|
pub enum ImportSpecifier {
|
|
125
124
|
/// Named: { foo } or { foo as bar }
|
|
126
|
-
Named {
|
|
125
|
+
Named {
|
|
126
|
+
name: Arc<str>,
|
|
127
|
+
name_span: Span,
|
|
128
|
+
alias: Option<Arc<str>>,
|
|
129
|
+
alias_span: Option<Span>,
|
|
130
|
+
},
|
|
127
131
|
/// Namespace: * as M
|
|
128
|
-
Namespace
|
|
132
|
+
Namespace {
|
|
133
|
+
name: Arc<str>,
|
|
134
|
+
name_span: Span,
|
|
135
|
+
},
|
|
129
136
|
/// Default: import X from "..."
|
|
130
|
-
Default
|
|
137
|
+
Default {
|
|
138
|
+
name: Arc<str>,
|
|
139
|
+
name_span: Span,
|
|
140
|
+
},
|
|
131
141
|
}
|
|
132
142
|
|
|
133
143
|
/// Export declaration: named (const/let/fn) or default
|
|
@@ -152,6 +162,7 @@ pub enum Statement {
|
|
|
152
162
|
},
|
|
153
163
|
VarDecl {
|
|
154
164
|
name: Arc<str>,
|
|
165
|
+
name_span: Span,
|
|
155
166
|
mutable: bool, // true for `let`, false for `const`
|
|
156
167
|
type_ann: Option<TypeAnnotation>,
|
|
157
168
|
init: Option<Expr>,
|
|
@@ -188,6 +199,7 @@ pub enum Statement {
|
|
|
188
199
|
},
|
|
189
200
|
ForOf {
|
|
190
201
|
name: Arc<str>,
|
|
202
|
+
name_span: Span,
|
|
191
203
|
iterable: Expr,
|
|
192
204
|
body: Box<Statement>,
|
|
193
205
|
span: Span,
|
|
@@ -205,6 +217,7 @@ pub enum Statement {
|
|
|
205
217
|
FunDecl {
|
|
206
218
|
async_: bool,
|
|
207
219
|
name: Arc<str>,
|
|
220
|
+
name_span: Span,
|
|
208
221
|
params: Vec<FunParam>,
|
|
209
222
|
rest_param: Option<TypedParam>,
|
|
210
223
|
return_type: Option<TypeAnnotation>,
|
|
@@ -229,6 +242,7 @@ pub enum Statement {
|
|
|
229
242
|
Try {
|
|
230
243
|
body: Box<Statement>,
|
|
231
244
|
catch_param: Option<Arc<str>>,
|
|
245
|
+
catch_param_span: Option<Span>,
|
|
232
246
|
catch_body: Option<Box<Statement>>,
|
|
233
247
|
finally_body: Option<Box<Statement>>,
|
|
234
248
|
span: Span,
|
|
@@ -242,6 +256,31 @@ pub enum Statement {
|
|
|
242
256
|
declaration: Box<ExportDeclaration>,
|
|
243
257
|
span: Span,
|
|
244
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
|
+
},
|
|
245
284
|
}
|
|
246
285
|
|
|
247
286
|
#[derive(Debug, Clone, PartialEq)]
|
|
@@ -366,8 +405,8 @@ pub enum Expr {
|
|
|
366
405
|
},
|
|
367
406
|
/// Template literal: `text ${expr} text`
|
|
368
407
|
TemplateLiteral {
|
|
369
|
-
quasis: Vec<Arc<str>>,
|
|
370
|
-
exprs: Vec<Expr>,
|
|
408
|
+
quasis: Vec<Arc<str>>, // Static string parts (n+1 for n expressions)
|
|
409
|
+
exprs: Vec<Expr>, // Interpolated expressions (n)
|
|
371
410
|
span: Span,
|
|
372
411
|
},
|
|
373
412
|
/// Await expression: await operand
|
|
@@ -399,10 +438,7 @@ pub enum Expr {
|
|
|
399
438
|
#[derive(Debug, Clone, PartialEq)]
|
|
400
439
|
pub enum JsxProp {
|
|
401
440
|
/// name="value" or name={expr} or name (boolean shorthand)
|
|
402
|
-
Attr {
|
|
403
|
-
name: Arc<str>,
|
|
404
|
-
value: JsxAttrValue,
|
|
405
|
-
},
|
|
441
|
+
Attr { name: Arc<str>, value: JsxAttrValue },
|
|
406
442
|
/// {...expr}
|
|
407
443
|
Spread(Expr),
|
|
408
444
|
}
|
|
@@ -493,11 +529,11 @@ pub enum CallArg {
|
|
|
493
529
|
|
|
494
530
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
495
531
|
pub enum CompoundOp {
|
|
496
|
-
Add,
|
|
497
|
-
Sub,
|
|
498
|
-
Mul,
|
|
499
|
-
Div,
|
|
500
|
-
Mod,
|
|
532
|
+
Add, // +=
|
|
533
|
+
Sub, // -=
|
|
534
|
+
Mul, // *=
|
|
535
|
+
Div, // /=
|
|
536
|
+
Mod, // %=
|
|
501
537
|
}
|
|
502
538
|
|
|
503
539
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
@@ -552,6 +588,39 @@ pub enum UnaryOp {
|
|
|
552
588
|
|
|
553
589
|
#[derive(Debug, Clone, PartialEq)]
|
|
554
590
|
pub enum MemberProp {
|
|
555
|
-
|
|
591
|
+
/// Property name in `obj.prop` / `obj?.prop` (span covers **prop** only).
|
|
592
|
+
Name {
|
|
593
|
+
name: Arc<str>,
|
|
594
|
+
span: Span,
|
|
595
|
+
},
|
|
556
596
|
Expr(Box<Expr>), // for computed property
|
|
557
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);
|
|
@@ -158,7 +191,9 @@ pub fn find_workspace_root() -> Result<PathBuf, String> {
|
|
|
158
191
|
if let Some(mut current) = exe.parent() {
|
|
159
192
|
for _ in 0..15 {
|
|
160
193
|
let crates_dir = current.join("crates");
|
|
161
|
-
if crates_dir.join("tish_runtime").exists()
|
|
194
|
+
if crates_dir.join("tish_runtime").exists()
|
|
195
|
+
|| crates_dir.join("tish_cranelift_runtime").exists()
|
|
196
|
+
{
|
|
162
197
|
return Ok(current.to_path_buf());
|
|
163
198
|
}
|
|
164
199
|
if let Some(p) = current.parent() {
|
|
@@ -177,6 +212,19 @@ pub fn find_workspace_root() -> Result<PathBuf, String> {
|
|
|
177
212
|
}
|
|
178
213
|
}
|
|
179
214
|
|
|
215
|
+
// Strategy 3b: `node_modules/@tishlang/tish` from cwd or any ancestor (package.json-only apps)
|
|
216
|
+
if let Ok(mut dir) = std::env::current_dir() {
|
|
217
|
+
for _ in 0..32 {
|
|
218
|
+
let npm_pkg = dir.join("node_modules").join("@tishlang").join("tish");
|
|
219
|
+
if is_tish_workspace_root(&npm_pkg) {
|
|
220
|
+
return Ok(npm_pkg);
|
|
221
|
+
}
|
|
222
|
+
if !dir.pop() {
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
180
228
|
// Strategy 4: Walk from current working directory
|
|
181
229
|
if let Ok(mut current) = std::env::current_dir() {
|
|
182
230
|
for _ in 0..15 {
|
|
@@ -276,33 +324,112 @@ pub fn find_crate_path(crate_name: &str) -> Result<PathBuf, String> {
|
|
|
276
324
|
|
|
277
325
|
/// Create a temp build directory with src subdir.
|
|
278
326
|
pub fn create_build_dir(prefix: &str, out_name: &str) -> Result<PathBuf, String> {
|
|
279
|
-
let build_dir =
|
|
327
|
+
let build_dir =
|
|
328
|
+
std::env::temp_dir()
|
|
329
|
+
.join(prefix)
|
|
330
|
+
.join(format!("{}_{}", out_name, std::process::id()));
|
|
280
331
|
fs::create_dir_all(&build_dir).map_err(|e| format!("Cannot create build dir: {}", e))?;
|
|
281
|
-
fs::create_dir_all(build_dir.join("src"))
|
|
332
|
+
fs::create_dir_all(build_dir.join("src"))
|
|
333
|
+
.map_err(|e| format!("Cannot create src dir: {}", e))?;
|
|
282
334
|
Ok(build_dir)
|
|
283
335
|
}
|
|
284
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
|
+
|
|
285
366
|
/// Run cargo build in the given directory.
|
|
286
367
|
/// If target_dir is Some, use that for --target-dir (e.g. workspace target for caching).
|
|
287
368
|
pub fn run_cargo_build(build_dir: &Path, target_dir: Option<&Path>) -> Result<(), String> {
|
|
288
|
-
let target_dir = target_dir
|
|
289
|
-
|
|
290
|
-
.
|
|
369
|
+
let target_dir = target_dir
|
|
370
|
+
.map(|p| p.to_path_buf())
|
|
371
|
+
.unwrap_or_else(|| build_dir.join("target"));
|
|
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"])
|
|
291
389
|
.arg(&target_dir)
|
|
292
390
|
.current_dir(build_dir)
|
|
293
391
|
.env_remove("CARGO_TARGET_DIR")
|
|
392
|
+
.env_remove("RUSTC_WRAPPER")
|
|
393
|
+
.env_remove("RUSTC_WORKSPACE_WRAPPER")
|
|
394
|
+
.env_remove("CARGO_BUILD_RUSTC_WRAPPER")
|
|
294
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
|
|
295
401
|
.output()
|
|
296
402
|
.map_err(|e| format!("Failed to run cargo: {}", e))?;
|
|
297
403
|
|
|
298
404
|
if !output.status.success() {
|
|
299
405
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
300
406
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
301
|
-
return Err(format!(
|
|
407
|
+
return Err(format!(
|
|
408
|
+
"Compilation failed.\nstdout:\n{}\nstderr:\n{}",
|
|
409
|
+
stdout, stderr
|
|
410
|
+
));
|
|
302
411
|
}
|
|
303
412
|
Ok(())
|
|
304
413
|
}
|
|
305
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
|
+
|
|
306
433
|
/// Find the built binary in target/release.
|
|
307
434
|
pub fn find_release_binary(binary_dir: &Path, bin_name: &str) -> Result<PathBuf, String> {
|
|
308
435
|
let binary_no_ext = binary_dir.join(bin_name);
|
|
@@ -351,7 +478,8 @@ pub fn copy_binary_to_output(binary: &Path, output_path: &Path) -> Result<(), St
|
|
|
351
478
|
if let Some(parent) = output_path.parent() {
|
|
352
479
|
fs::create_dir_all(parent).map_err(|e| format!("Cannot create output dir: {}", e))?;
|
|
353
480
|
}
|
|
354
|
-
fs::copy(binary, output_path)
|
|
481
|
+
fs::copy(binary, output_path)
|
|
482
|
+
.map_err(|e| format!("Cannot copy to {}: {}", output_path.display(), e))?;
|
|
355
483
|
Ok(())
|
|
356
484
|
}
|
|
357
485
|
|
|
@@ -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
|
+
use crate::helpers::normalize_index;
|
|
4
|
+
use tishlang_core::VmRef;
|
|
3
5
|
use std::cell::RefCell;
|
|
4
6
|
use std::rc::Rc;
|
|
5
7
|
use tishlang_core::Value;
|
|
6
|
-
use crate::helpers::normalize_index;
|
|
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
|
}
|
|
@@ -153,8 +154,12 @@ pub fn slice(arr: &Value, start: &Value, end: &Value) -> Value {
|
|
|
153
154
|
let len = arr_borrow.len() as i64;
|
|
154
155
|
let start_idx = normalize_index(start, len, 0);
|
|
155
156
|
let end_idx = normalize_index(end, len, len as usize);
|
|
156
|
-
let sliced = if start_idx < end_idx {
|
|
157
|
-
|
|
157
|
+
let sliced = if start_idx < end_idx {
|
|
158
|
+
arr_borrow[start_idx..end_idx].to_vec()
|
|
159
|
+
} else {
|
|
160
|
+
vec![]
|
|
161
|
+
};
|
|
162
|
+
Value::Array(VmRef::new(sliced))
|
|
158
163
|
} else {
|
|
159
164
|
Value::Null
|
|
160
165
|
}
|
|
@@ -170,7 +175,7 @@ pub fn concat(arr: &Value, args: &[Value]) -> Value {
|
|
|
170
175
|
result.push(v.clone());
|
|
171
176
|
}
|
|
172
177
|
}
|
|
173
|
-
Value::Array(
|
|
178
|
+
Value::Array(VmRef::new(result))
|
|
174
179
|
} else {
|
|
175
180
|
Value::Null
|
|
176
181
|
}
|
|
@@ -188,7 +193,7 @@ pub fn flat(arr: &Value, depth: &Value) -> Value {
|
|
|
188
193
|
result.push(v.clone());
|
|
189
194
|
}
|
|
190
195
|
}
|
|
191
|
-
|
|
196
|
+
|
|
192
197
|
if let Value::Array(arr) = arr {
|
|
193
198
|
let d = match depth {
|
|
194
199
|
Value::Number(n) => *n as i32,
|
|
@@ -196,7 +201,7 @@ pub fn flat(arr: &Value, depth: &Value) -> Value {
|
|
|
196
201
|
};
|
|
197
202
|
let mut result = Vec::new();
|
|
198
203
|
flatten(&arr.borrow(), d, &mut result);
|
|
199
|
-
Value::Array(
|
|
204
|
+
Value::Array(VmRef::new(result))
|
|
200
205
|
} else {
|
|
201
206
|
Value::Null
|
|
202
207
|
}
|
|
@@ -208,10 +213,12 @@ pub fn flat(arr: &Value, depth: &Value) -> Value {
|
|
|
208
213
|
pub fn map(arr: &Value, callback: &Value) -> Value {
|
|
209
214
|
if let (Value::Array(arr), Value::Function(cb)) = (arr, callback) {
|
|
210
215
|
let arr_borrow = arr.borrow();
|
|
211
|
-
let result: Vec<Value> = arr_borrow
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
216
|
+
let result: Vec<Value> = arr_borrow
|
|
217
|
+
.iter()
|
|
218
|
+
.enumerate()
|
|
219
|
+
.map(|(i, v)| cb(&[v.clone(), Value::Number(i as f64)]))
|
|
220
|
+
.collect();
|
|
221
|
+
Value::Array(VmRef::new(result))
|
|
215
222
|
} else {
|
|
216
223
|
Value::Null
|
|
217
224
|
}
|
|
@@ -220,11 +227,19 @@ pub fn map(arr: &Value, callback: &Value) -> Value {
|
|
|
220
227
|
pub fn filter(arr: &Value, callback: &Value) -> Value {
|
|
221
228
|
if let (Value::Array(arr), Value::Function(cb)) = (arr, callback) {
|
|
222
229
|
let arr_borrow = arr.borrow();
|
|
223
|
-
let result: Vec<Value> = arr_borrow
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
230
|
+
let result: Vec<Value> = arr_borrow
|
|
231
|
+
.iter()
|
|
232
|
+
.enumerate()
|
|
233
|
+
.filter_map(|(i, v)| {
|
|
234
|
+
let keep = cb(&[v.clone(), Value::Number(i as f64)]);
|
|
235
|
+
if keep.is_truthy() {
|
|
236
|
+
Some(v.clone())
|
|
237
|
+
} else {
|
|
238
|
+
None
|
|
239
|
+
}
|
|
240
|
+
})
|
|
241
|
+
.collect();
|
|
242
|
+
Value::Array(VmRef::new(result))
|
|
228
243
|
} else {
|
|
229
244
|
Value::Null
|
|
230
245
|
}
|
|
@@ -234,9 +249,7 @@ pub fn reduce(arr: &Value, callback: &Value, initial: &Value) -> Value {
|
|
|
234
249
|
if let (Value::Array(arr), Value::Function(cb)) = (arr, callback) {
|
|
235
250
|
let arr_borrow = arr.borrow();
|
|
236
251
|
let len = arr_borrow.len();
|
|
237
|
-
let (start_idx, mut acc) = if matches!(initial, Value::Null)
|
|
238
|
-
&& !arr_borrow.is_empty()
|
|
239
|
-
{
|
|
252
|
+
let (start_idx, mut acc) = if matches!(initial, Value::Null) && !arr_borrow.is_empty() {
|
|
240
253
|
// No initial value: use first element as acc, start from index 1
|
|
241
254
|
(1, arr_borrow[0].clone())
|
|
242
255
|
} else {
|
|
@@ -328,24 +341,28 @@ pub fn flat_map(arr: &Value, callback: &Value) -> Value {
|
|
|
328
341
|
result.push(mapped);
|
|
329
342
|
}
|
|
330
343
|
}
|
|
331
|
-
Value::Array(
|
|
344
|
+
Value::Array(VmRef::new(result))
|
|
332
345
|
} else {
|
|
333
346
|
Value::Null
|
|
334
347
|
}
|
|
335
348
|
}
|
|
336
349
|
|
|
337
350
|
fn sort_by_impl<F>(arr: &Value, cmp: F) -> Value
|
|
338
|
-
where
|
|
351
|
+
where
|
|
352
|
+
F: FnMut(&Value, &Value) -> std::cmp::Ordering,
|
|
353
|
+
{
|
|
339
354
|
if let Value::Array(arr) = arr {
|
|
340
355
|
arr.borrow_mut().sort_by(cmp);
|
|
341
|
-
Value::Array(
|
|
356
|
+
Value::Array(arr.clone())
|
|
342
357
|
} else {
|
|
343
358
|
Value::Null
|
|
344
359
|
}
|
|
345
360
|
}
|
|
346
361
|
|
|
347
362
|
pub fn sort_default(arr: &Value) -> Value {
|
|
348
|
-
sort_by_impl(arr, |a, b|
|
|
363
|
+
sort_by_impl(arr, |a, b| {
|
|
364
|
+
a.to_display_string().cmp(&b.to_display_string())
|
|
365
|
+
})
|
|
349
366
|
}
|
|
350
367
|
|
|
351
368
|
pub fn sort_with_comparator(arr: &Value, comparator: &Value) -> Value {
|
|
@@ -355,7 +372,7 @@ pub fn sort_with_comparator(arr: &Value, comparator: &Value) -> Value {
|
|
|
355
372
|
let mut indices: Vec<usize> = (0..len).collect();
|
|
356
373
|
let mut elements: Vec<Value> = std::mem::take(&mut *arr_mut);
|
|
357
374
|
let mut args_buf: [Value; 2] = [Value::Null, Value::Null];
|
|
358
|
-
|
|
375
|
+
|
|
359
376
|
indices.sort_by(|&a, &b| {
|
|
360
377
|
args_buf[0] = elements[a].clone();
|
|
361
378
|
args_buf[1] = elements[b].clone();
|
|
@@ -365,10 +382,13 @@ pub fn sort_with_comparator(arr: &Value, comparator: &Value) -> Value {
|
|
|
365
382
|
_ => std::cmp::Ordering::Equal,
|
|
366
383
|
}
|
|
367
384
|
});
|
|
368
|
-
|
|
369
|
-
*arr_mut = indices
|
|
385
|
+
|
|
386
|
+
*arr_mut = indices
|
|
387
|
+
.into_iter()
|
|
388
|
+
.map(|i| std::mem::replace(&mut elements[i], Value::Null))
|
|
389
|
+
.collect();
|
|
370
390
|
drop(arr_mut);
|
|
371
|
-
Value::Array(
|
|
391
|
+
Value::Array(arr.clone())
|
|
372
392
|
} else {
|
|
373
393
|
Value::Null
|
|
374
394
|
}
|
|
@@ -380,7 +400,11 @@ fn num_cmp(a: &Value, b: &Value, asc: bool) -> std::cmp::Ordering {
|
|
|
380
400
|
_ => (f64::NAN, f64::NAN),
|
|
381
401
|
};
|
|
382
402
|
let cmp = na.partial_cmp(&nb).unwrap_or(std::cmp::Ordering::Equal);
|
|
383
|
-
if asc {
|
|
403
|
+
if asc {
|
|
404
|
+
cmp
|
|
405
|
+
} else {
|
|
406
|
+
cmp.reverse()
|
|
407
|
+
}
|
|
384
408
|
}
|
|
385
409
|
|
|
386
410
|
pub fn sort_numeric_asc(arr: &Value) -> Value {
|
|
@@ -398,13 +422,21 @@ pub fn sort_by_property_numeric(arr: &Value, prop: &str, asc: bool) -> Value {
|
|
|
398
422
|
let na = get_prop_number(a, &prop_arc);
|
|
399
423
|
let nb = get_prop_number(b, &prop_arc);
|
|
400
424
|
let cmp = na.partial_cmp(&nb).unwrap_or(std::cmp::Ordering::Equal);
|
|
401
|
-
if asc {
|
|
425
|
+
if asc {
|
|
426
|
+
cmp
|
|
427
|
+
} else {
|
|
428
|
+
cmp.reverse()
|
|
429
|
+
}
|
|
402
430
|
})
|
|
403
431
|
}
|
|
404
432
|
|
|
405
433
|
fn get_prop_number(v: &Value, prop: &std::sync::Arc<str>) -> f64 {
|
|
406
434
|
match v {
|
|
407
|
-
Value::Object(o) => o
|
|
435
|
+
Value::Object(o) => o
|
|
436
|
+
.borrow()
|
|
437
|
+
.get(prop.as_ref())
|
|
438
|
+
.map(|v| v.as_number().unwrap_or(f64::NAN))
|
|
439
|
+
.unwrap_or(f64::NAN),
|
|
408
440
|
_ => f64::NAN,
|
|
409
441
|
}
|
|
410
442
|
}
|