@tishlang/tish 1.0.29 → 1.0.34
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/crates/js_to_tish/src/transform/expr.rs +15 -6
- package/crates/tish/Cargo.toml +1 -1
- package/crates/tish/src/main.rs +1 -1
- package/crates/tish/tests/integration_test.rs +4 -3
- package/crates/tish_ast/src/ast.rs +65 -2
- package/crates/tish_build_utils/src/lib.rs +10 -2
- package/crates/tish_builtins/src/construct.rs +177 -0
- package/crates/tish_builtins/src/globals.rs +3 -5
- package/crates/tish_builtins/src/helpers.rs +2 -3
- package/crates/tish_builtins/src/lib.rs +1 -0
- package/crates/tish_builtins/src/object.rs +3 -4
- package/crates/tish_bytecode/src/compiler.rs +85 -11
- package/crates/tish_bytecode/src/opcode.rs +7 -3
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/codegen.rs +604 -106
- package/crates/tish_compile/src/infer.rs +236 -0
- package/crates/tish_compile/src/lib.rs +52 -5
- package/crates/tish_compile/src/types.rs +42 -5
- package/crates/tish_compile_js/Cargo.toml +1 -0
- package/crates/tish_compile_js/src/codegen.rs +38 -94
- package/crates/tish_compile_js/src/lib.rs +0 -1
- package/crates/tish_compile_js/src/tests_jsx.rs +68 -0
- package/crates/tish_core/Cargo.toml +4 -0
- package/crates/tish_core/src/console_style.rs +7 -1
- package/crates/tish_core/src/json.rs +1 -2
- package/crates/tish_core/src/macros.rs +2 -3
- package/crates/tish_core/src/value.rs +13 -5
- package/crates/tish_cranelift/src/lib.rs +6 -4
- package/crates/tish_cranelift/src/lower.rs +5 -3
- package/crates/tish_cranelift_runtime/src/lib.rs +4 -2
- package/crates/tish_eval/Cargo.toml +2 -0
- package/crates/tish_eval/src/eval.rs +172 -79
- package/crates/tish_eval/src/http.rs +3 -4
- package/crates/tish_eval/src/lib.rs +7 -0
- package/crates/tish_eval/src/regex.rs +3 -2
- package/crates/tish_eval/src/value.rs +11 -13
- package/crates/tish_eval/src/value_convert.rs +4 -8
- package/crates/tish_fmt/src/lib.rs +49 -10
- package/crates/tish_lexer/src/token.rs +2 -0
- package/crates/tish_lint/src/lib.rs +9 -0
- package/crates/tish_llvm/src/lib.rs +4 -4
- package/crates/tish_lsp/README.md +1 -1
- package/crates/tish_native/src/build.rs +16 -2
- package/crates/tish_native/src/lib.rs +10 -11
- package/crates/tish_opt/src/lib.rs +15 -0
- package/crates/tish_parser/src/lib.rs +101 -1
- package/crates/tish_parser/src/parser.rs +168 -51
- package/crates/tish_runtime/src/http.rs +4 -5
- package/crates/tish_runtime/src/http_fetch.rs +17 -10
- package/crates/tish_runtime/src/lib.rs +9 -2
- package/crates/tish_runtime/src/promise.rs +2 -3
- package/crates/tish_runtime/src/promise_io.rs +2 -3
- package/crates/tish_runtime/src/ws.rs +7 -7
- package/crates/tish_ui/Cargo.toml +17 -0
- package/crates/tish_ui/src/jsx.rs +390 -0
- package/crates/tish_ui/src/lib.rs +16 -0
- package/crates/tish_ui/src/runtime/hooks.rs +122 -0
- package/crates/tish_ui/src/runtime/mod.rs +173 -0
- package/crates/tish_vm/src/vm.rs +121 -27
- package/justfile +3 -3
- 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/crates/tish_compile_js/src/js_intrinsics.rs +0 -82
|
@@ -211,4 +211,72 @@ fn FileList() {
|
|
|
211
211
|
&js[..900.min(js.len())]
|
|
212
212
|
);
|
|
213
213
|
}
|
|
214
|
+
|
|
215
|
+
#[test]
|
|
216
|
+
fn new_date_global_emits_valid_js_with_and_without_optimize() {
|
|
217
|
+
let src = "let epoch = new Date(0)\nconsole.log(epoch.getTime())";
|
|
218
|
+
let program = parse(src).expect("parse");
|
|
219
|
+
for optimize in [false, true] {
|
|
220
|
+
let js = compile_with_jsx(&program, optimize).expect("compile");
|
|
221
|
+
assert!(
|
|
222
|
+
js.contains("new Date(0)"),
|
|
223
|
+
"optimize={optimize}: expected `new Date(0)` in JS output:\n{js}"
|
|
224
|
+
);
|
|
225
|
+
assert!(
|
|
226
|
+
!js.contains("let epoch = new;"),
|
|
227
|
+
"optimize={optimize}: broken `new` emission (missing constructor):\n{js}"
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
#[test]
|
|
233
|
+
fn new_uint8array_emits_direct_new_no_preamble() {
|
|
234
|
+
let src = "fn f(n) { return new Uint8Array(n) }";
|
|
235
|
+
let program = parse(src).expect("parse");
|
|
236
|
+
let js = compile_with_jsx(&program, false).expect("compile");
|
|
237
|
+
assert!(
|
|
238
|
+
js.contains("new Uint8Array("),
|
|
239
|
+
"expected direct new Uint8Array, got: {}",
|
|
240
|
+
&js[..500.min(js.len())]
|
|
241
|
+
);
|
|
242
|
+
assert!(
|
|
243
|
+
!js.contains("__tishUint8Array"),
|
|
244
|
+
"should not emit legacy intrinsic helper"
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
#[test]
|
|
249
|
+
fn new_audio_context_emits_direct_new_no_preamble() {
|
|
250
|
+
let src = "fn f() { return new AudioContext() }";
|
|
251
|
+
let program = parse(src).expect("parse");
|
|
252
|
+
let js = compile_with_jsx(&program, false).expect("compile");
|
|
253
|
+
assert!(
|
|
254
|
+
js.contains("new AudioContext("),
|
|
255
|
+
"expected new AudioContext, got: {}",
|
|
256
|
+
&js[..500.min(js.len())]
|
|
257
|
+
);
|
|
258
|
+
assert!(
|
|
259
|
+
!js.contains("__tishWebAudioCreateContext"),
|
|
260
|
+
"should not emit legacy intrinsic helper"
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
#[test]
|
|
265
|
+
fn new_class_name_emits_direct_new_js() {
|
|
266
|
+
let src = r#"
|
|
267
|
+
fn ClassName(x) {
|
|
268
|
+
return x
|
|
269
|
+
}
|
|
270
|
+
fn factory() {
|
|
271
|
+
return new ClassName(42)
|
|
272
|
+
}
|
|
273
|
+
"#;
|
|
274
|
+
let program = parse(src).expect("parse");
|
|
275
|
+
let js = compile_with_jsx(&program, false).expect("compile");
|
|
276
|
+
assert!(
|
|
277
|
+
js.contains("new ClassName("),
|
|
278
|
+
"expected new ClassName( in JS output, got: {}",
|
|
279
|
+
&js[..800.min(js.len())]
|
|
280
|
+
);
|
|
281
|
+
}
|
|
214
282
|
}
|
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
//! booleans, null, and object structure are easier to scan.
|
|
5
5
|
|
|
6
6
|
use std::io::IsTerminal;
|
|
7
|
+
use std::sync::OnceLock;
|
|
7
8
|
|
|
8
9
|
use crate::Value;
|
|
9
10
|
|
|
11
|
+
static CONSOLE_USES_COLORS: OnceLock<bool> = OnceLock::new();
|
|
12
|
+
|
|
10
13
|
/// ANSI escape codes (standard 4-bit + bright black for dim).
|
|
11
14
|
const RESET: &str = "\x1b[0m";
|
|
12
15
|
/// Number: yellow (Node-style)
|
|
@@ -25,8 +28,11 @@ const PUNCT: &str = "\x1b[90m";
|
|
|
25
28
|
const SPECIAL: &str = "\x1b[90m";
|
|
26
29
|
|
|
27
30
|
/// Returns whether console output should use colors (stdout is a TTY).
|
|
31
|
+
///
|
|
32
|
+
/// Cached for the process lifetime. `is_terminal()` is a syscall; benchmarks and
|
|
33
|
+
/// scripts with many `console.log` calls must not pay it on every line.
|
|
28
34
|
pub fn use_console_colors() -> bool {
|
|
29
|
-
std::io::stdout().is_terminal()
|
|
35
|
+
*CONSOLE_USES_COLORS.get_or_init(|| std::io::stdout().is_terminal())
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
/// Format a single value for console with optional ANSI colors (Node/Bun-style).
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
use crate::Value;
|
|
4
4
|
use std::cell::RefCell;
|
|
5
|
-
use std::collections::HashMap;
|
|
6
5
|
use std::rc::Rc;
|
|
7
6
|
use std::sync::Arc;
|
|
8
7
|
|
|
@@ -260,7 +259,7 @@ fn parse_array(input: &str) -> Result<(Value, &str), String> {
|
|
|
260
259
|
|
|
261
260
|
fn parse_object(input: &str) -> Result<(Value, &str), String> {
|
|
262
261
|
let mut input = &input[1..]; // skip '{'
|
|
263
|
-
let mut map =
|
|
262
|
+
let mut map = crate::ObjectMap::default();
|
|
264
263
|
|
|
265
264
|
input = input.trim_start();
|
|
266
265
|
if let Some(rest) = input.strip_prefix('}') {
|
|
@@ -24,11 +24,10 @@
|
|
|
24
24
|
macro_rules! tish_module {
|
|
25
25
|
($($name:expr => $fn:expr),* $(,)?) => {{
|
|
26
26
|
use std::cell::RefCell;
|
|
27
|
-
use std::collections::HashMap;
|
|
28
27
|
use std::rc::Rc;
|
|
29
28
|
use std::sync::Arc;
|
|
30
|
-
use $crate::Value;
|
|
31
|
-
let mut map =
|
|
29
|
+
use $crate::{ObjectMap, Value};
|
|
30
|
+
let mut map = ObjectMap::default();
|
|
32
31
|
$(
|
|
33
32
|
map.insert(Arc::from($name), Value::Function(Rc::new($fn)));
|
|
34
33
|
)*
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
//! Unified Value type for Tish runtime values.
|
|
2
2
|
|
|
3
3
|
use std::cell::RefCell;
|
|
4
|
-
use std::collections::HashMap;
|
|
5
4
|
use std::rc::Rc;
|
|
6
5
|
use std::sync::Arc;
|
|
7
6
|
|
|
7
|
+
use ahash::AHashMap;
|
|
8
|
+
|
|
9
|
+
/// Property map for objects and other `Arc<str>` → `Value` tables (VM globals, scopes).
|
|
10
|
+
/// Uses a faster hasher than `std::collections::HashMap` for string-heavy workloads.
|
|
11
|
+
pub type ObjectMap = AHashMap<Arc<str>, Value>;
|
|
12
|
+
|
|
8
13
|
#[cfg(feature = "regex")]
|
|
9
14
|
use fancy_regex::Regex;
|
|
10
15
|
|
|
@@ -20,6 +25,9 @@ pub trait TishOpaque: Send + Sync {
|
|
|
20
25
|
|
|
21
26
|
/// Get a method by name. Returns a native function if the method exists.
|
|
22
27
|
fn get_method(&self, name: &str) -> Option<NativeFn>;
|
|
28
|
+
|
|
29
|
+
/// For downcasting `Arc<dyn TishOpaque>` in native crates (e.g. Polars → `DataFrame`).
|
|
30
|
+
fn as_any(&self) -> &dyn std::any::Any;
|
|
23
31
|
}
|
|
24
32
|
|
|
25
33
|
/// Trait for Promise-like values that can be awaited (block until settled).
|
|
@@ -147,7 +155,7 @@ pub enum Value {
|
|
|
147
155
|
Bool(bool),
|
|
148
156
|
Null,
|
|
149
157
|
Array(Rc<RefCell<Vec<Value>>>),
|
|
150
|
-
Object(Rc<RefCell<
|
|
158
|
+
Object(Rc<RefCell<ObjectMap>>),
|
|
151
159
|
Function(NativeFn),
|
|
152
160
|
#[cfg(feature = "regex")]
|
|
153
161
|
RegExp(Rc<RefCell<TishRegExp>>),
|
|
@@ -256,8 +264,8 @@ impl Value {
|
|
|
256
264
|
Value::Array(Rc::new(RefCell::new(items)))
|
|
257
265
|
}
|
|
258
266
|
|
|
259
|
-
/// Create a new object Value from a
|
|
260
|
-
pub fn object(map:
|
|
267
|
+
/// Create a new object Value from a property map.
|
|
268
|
+
pub fn object(map: ObjectMap) -> Self {
|
|
261
269
|
Value::Object(Rc::new(RefCell::new(map)))
|
|
262
270
|
}
|
|
263
271
|
|
|
@@ -268,7 +276,7 @@ impl Value {
|
|
|
268
276
|
|
|
269
277
|
/// Create an empty object Value.
|
|
270
278
|
pub fn empty_object() -> Self {
|
|
271
|
-
Value::Object(Rc::new(RefCell::new(
|
|
279
|
+
Value::Object(Rc::new(RefCell::new(ObjectMap::default())))
|
|
272
280
|
}
|
|
273
281
|
|
|
274
282
|
/// Extract the number value, if this is a Number.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
//!
|
|
1
|
+
//! Standalone native binary: embedded bytecode + VM (Cranelift used as object builder).
|
|
2
2
|
//!
|
|
3
|
-
//!
|
|
3
|
+
//! Produces an executable that runs **`tishlang_vm`** on serialized bytecode embedded in
|
|
4
|
+
//! the binary — not lowering of Tish opcodes to CLIF/machine code (see module docs in
|
|
5
|
+
//! `lower.rs`). For Rust transpile + `tishlang_runtime`, use `--native-backend rust`.
|
|
4
6
|
|
|
5
7
|
mod link;
|
|
6
8
|
mod lower;
|
|
@@ -25,8 +27,8 @@ impl std::fmt::Display for CraneliftError {
|
|
|
25
27
|
|
|
26
28
|
impl std::error::Error for CraneliftError {}
|
|
27
29
|
|
|
28
|
-
///
|
|
29
|
-
/// `features` are passed to tishlang_cranelift_runtime (e.g. fs, process, http
|
|
30
|
+
/// Build a native binary that embeds `chunk` and runs it with the bytecode VM.
|
|
31
|
+
/// `features` are passed to `tishlang_cranelift_runtime` (e.g. fs, process, http).
|
|
30
32
|
pub fn compile_chunk_to_native(
|
|
31
33
|
chunk: &Chunk,
|
|
32
34
|
output_path: &Path,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
//!
|
|
1
|
+
//! Embed serialized bytecode in an object file for the standalone native binary.
|
|
2
2
|
//!
|
|
3
|
-
//!
|
|
4
|
-
//!
|
|
3
|
+
//! **This is not AOT compilation of Tish into Cranelift IR.** The chunk is stored as
|
|
4
|
+
//! read-only data (`tish_chunk_data`, `tish_chunk_len`). The link step produces an
|
|
5
|
+
//! executable that **deserializes the chunk and runs `tishlang_vm`** — same VM as
|
|
6
|
+
//! `tish run --backend vm`. Cranelift is only the object-file emitter for that blob.
|
|
5
7
|
|
|
6
8
|
use std::path::Path;
|
|
7
9
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
//! Runtime
|
|
1
|
+
//! Runtime linked into the `tish compile --native-backend cranelift` executable.
|
|
2
2
|
//!
|
|
3
|
-
//!
|
|
3
|
+
//! **`tish_run_chunk`** deserializes embedded bytecode and runs **`tishlang_vm`** — the same
|
|
4
|
+
//! execution engine as `tish run --backend vm`. The crate name is historical; this is not
|
|
5
|
+
//! running CLIF-emitted machine code for each Tish opcode.
|
|
4
6
|
|
|
5
7
|
use tishlang_bytecode::deserialize;
|
|
6
8
|
use tishlang_vm::Vm;
|
|
@@ -16,8 +16,10 @@ tokio = ["dep:tokio"]
|
|
|
16
16
|
ws = ["dep:tishlang_runtime", "tishlang_runtime/ws"]
|
|
17
17
|
|
|
18
18
|
[dependencies]
|
|
19
|
+
ahash = "0.8.12"
|
|
19
20
|
rand = "0.10.0"
|
|
20
21
|
tishlang_ast = { path = "../tish_ast", version = ">=0.1" }
|
|
22
|
+
tishlang_builtins = { path = "../tish_builtins", version = ">=0.1" }
|
|
21
23
|
tishlang_parser = { path = "../tish_parser", version = ">=0.1" }
|
|
22
24
|
tishlang_core = { path = "../tish_core", version = ">=0.1" }
|
|
23
25
|
reqwest = { version = "0.13.2", default-features = false, features = ["rustls", "json", "stream"], optional = true }
|