@tishlang/tish 1.9.2 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/tish +0 -0
- package/crates/js_to_tish/src/transform/expr.rs +8 -6
- package/crates/js_to_tish/src/transform/stmt.rs +12 -13
- package/crates/tish/Cargo.toml +1 -1
- package/crates/tish/src/cargo_native_registry.rs +4 -1
- package/crates/tish/src/cli_help.rs +9 -1
- package/crates/tish/src/main.rs +66 -11
- package/crates/tish/tests/integration_test.rs +145 -7
- package/crates/tish_ast/src/ast.rs +3 -9
- package/crates/tish_build_utils/src/lib.rs +74 -23
- package/crates/tish_builtins/src/array.rs +2 -3
- package/crates/tish_builtins/src/construct.rs +15 -28
- package/crates/tish_builtins/src/globals.rs +18 -16
- package/crates/tish_builtins/src/helpers.rs +1 -4
- package/crates/tish_builtins/src/lib.rs +1 -0
- package/crates/tish_builtins/src/math.rs +7 -0
- package/crates/tish_builtins/src/object.rs +10 -10
- package/crates/tish_builtins/src/string.rs +27 -3
- package/crates/tish_builtins/src/symbol.rs +83 -0
- package/crates/tish_compile/src/codegen.rs +324 -158
- package/crates/tish_compile/src/lib.rs +39 -7
- package/crates/tish_compile/src/resolve.rs +191 -6
- package/crates/tish_compile/src/types.rs +6 -6
- package/crates/tish_compile_js/src/codegen.rs +8 -5
- package/crates/tish_core/src/console_style.rs +9 -0
- package/crates/tish_core/src/json.rs +17 -7
- package/crates/tish_core/src/macros.rs +2 -2
- package/crates/tish_core/src/value.rs +213 -4
- package/crates/tish_cranelift/src/link.rs +1 -1
- package/crates/tish_cranelift_runtime/Cargo.toml +4 -0
- package/crates/tish_eval/src/eval.rs +135 -73
- package/crates/tish_eval/src/http.rs +18 -12
- package/crates/tish_eval/src/lib.rs +29 -0
- package/crates/tish_eval/src/regex.rs +1 -1
- package/crates/tish_eval/src/value.rs +89 -4
- package/crates/tish_eval/src/value_convert.rs +30 -8
- package/crates/tish_fmt/src/lib.rs +4 -1
- package/crates/tish_lexer/src/lib.rs +7 -2
- package/crates/tish_llvm/src/lib.rs +2 -2
- package/crates/tish_lsp/src/builtin_goto.rs +111 -10
- package/crates/tish_lsp/src/import_goto.rs +35 -22
- package/crates/tish_lsp/src/main.rs +118 -85
- package/crates/tish_native/src/build.rs +270 -24
- package/crates/tish_native/src/config.rs +48 -0
- package/crates/tish_native/src/lib.rs +139 -12
- package/crates/tish_parser/src/lib.rs +5 -2
- package/crates/tish_parser/src/parser.rs +45 -75
- package/crates/tish_pg/src/error.rs +1 -1
- package/crates/tish_pg/src/lib.rs +61 -73
- package/crates/tish_resolve/src/lib.rs +283 -158
- package/crates/tish_resolve/src/pos.rs +10 -2
- package/crates/tish_runtime/Cargo.toml +3 -0
- package/crates/tish_runtime/src/http.rs +39 -39
- package/crates/tish_runtime/src/http_fetch.rs +12 -12
- package/crates/tish_runtime/src/lib.rs +35 -44
- package/crates/tish_runtime/src/native_promise.rs +0 -11
- package/crates/tish_runtime/src/promise.rs +14 -1
- package/crates/tish_runtime/src/promise_io.rs +1 -4
- package/crates/tish_runtime/src/timers.rs +12 -7
- package/crates/tish_runtime/src/ws.rs +40 -27
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +10 -8
- package/crates/tish_ui/src/jsx.rs +6 -4
- package/crates/tish_ui/src/lib.rs +5 -4
- package/crates/tish_ui/src/runtime/hooks.rs +123 -37
- package/crates/tish_ui/src/runtime/mod.rs +21 -41
- package/crates/tish_vm/Cargo.toml +2 -0
- package/crates/tish_vm/src/vm.rs +258 -153
- package/crates/tish_wasm/src/lib.rs +60 -7
- package/crates/tish_wasm_runtime/Cargo.toml +10 -1
- package/crates/tish_wasm_runtime/src/gpu.rs +413 -0
- package/crates/tish_wasm_runtime/src/lib.rs +7 -1
- package/crates/tishlang_cargo_bindgen/src/classify.rs +1 -3
- package/crates/tishlang_cargo_bindgen/src/discover.rs +10 -5
- package/crates/tishlang_cargo_bindgen/src/infer.rs +18 -8
- package/crates/tishlang_cargo_bindgen/src/lib.rs +25 -26
- package/crates/tishlang_cargo_bindgen/src/main.rs +41 -38
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +4 -1
- 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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
//! Unified Value type for Tish runtime values.
|
|
2
2
|
|
|
3
|
+
use std::sync::atomic::{AtomicU64, Ordering};
|
|
3
4
|
use std::sync::Arc;
|
|
4
5
|
|
|
5
6
|
use ahash::AHashMap;
|
|
@@ -10,6 +11,47 @@ use crate::vmref::VmRef;
|
|
|
10
11
|
/// Uses a faster hasher than `std::collections::HashMap` for string-heavy workloads.
|
|
11
12
|
pub type ObjectMap = AHashMap<Arc<str>, Value>;
|
|
12
13
|
|
|
14
|
+
static NEXT_SYMBOL_ID: AtomicU64 = AtomicU64::new(1);
|
|
15
|
+
|
|
16
|
+
fn next_symbol_id() -> u64 {
|
|
17
|
+
NEXT_SYMBOL_ID.fetch_add(1, Ordering::Relaxed)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/// Allocate a unique symbol id (for `Symbol()` and first-time `Symbol.for` entries).
|
|
21
|
+
#[inline]
|
|
22
|
+
pub fn alloc_symbol_id() -> u64 {
|
|
23
|
+
next_symbol_id()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/// Primitive Symbol (ECMAScript-style): identity is `Arc` pointer equality.
|
|
27
|
+
#[derive(Debug)]
|
|
28
|
+
pub struct TishSymbol {
|
|
29
|
+
pub id: u64,
|
|
30
|
+
pub description: Option<Arc<str>>,
|
|
31
|
+
/// Set when created via `Symbol.for(key)` (global registry).
|
|
32
|
+
pub registry_key: Option<Arc<str>>,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
impl TishSymbol {
|
|
36
|
+
/// Unique symbol (`Symbol("desc")`).
|
|
37
|
+
pub fn new_unique(description: Option<Arc<str>>) -> Arc<Self> {
|
|
38
|
+
Arc::new(Self {
|
|
39
|
+
id: next_symbol_id(),
|
|
40
|
+
description,
|
|
41
|
+
registry_key: None,
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// Registry symbol (`Symbol.for`): stable `id` for this registry key.
|
|
46
|
+
pub fn new_registry(id: u64, registry_key: Arc<str>, description: Option<Arc<str>>) -> Arc<Self> {
|
|
47
|
+
Arc::new(Self {
|
|
48
|
+
id,
|
|
49
|
+
description,
|
|
50
|
+
registry_key: Some(registry_key),
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
13
55
|
#[cfg(feature = "regex")]
|
|
14
56
|
use fancy_regex::Regex;
|
|
15
57
|
|
|
@@ -28,6 +70,14 @@ pub type NativeFn = std::rc::Rc<dyn Fn(&[Value]) -> Value>;
|
|
|
28
70
|
|
|
29
71
|
/// Trait for opaque Rust types exposed to Tish (e.g. Polars DataFrame).
|
|
30
72
|
/// Implementors provide method dispatch so Tish can call methods on the value.
|
|
73
|
+
///
|
|
74
|
+
/// The `Send + Sync` supertrait bound is conditional on the `send-values`
|
|
75
|
+
/// feature. When `send-values` is off (single-threaded VMs: wasm browser /
|
|
76
|
+
/// wasi / interpreter / cranelift), `NativeFn` is already `Rc<dyn Fn>`, so
|
|
77
|
+
/// `Value` is `!Send` anyway — dropping the bound here loses nothing and lets
|
|
78
|
+
/// `!Send` opaques like `JsHandle(wasm_bindgen::JsValue)` be stored in a
|
|
79
|
+
/// `Value::Opaque` on the browser runtime.
|
|
80
|
+
#[cfg(feature = "send-values")]
|
|
31
81
|
pub trait TishOpaque: Send + Sync {
|
|
32
82
|
/// Display name for the type (e.g. "DataFrame").
|
|
33
83
|
fn type_name(&self) -> &'static str;
|
|
@@ -39,6 +89,19 @@ pub trait TishOpaque: Send + Sync {
|
|
|
39
89
|
fn as_any(&self) -> &dyn std::any::Any;
|
|
40
90
|
}
|
|
41
91
|
|
|
92
|
+
/// Single-threaded variant (no `Send + Sync` bound); see the `send-values` doc above.
|
|
93
|
+
#[cfg(not(feature = "send-values"))]
|
|
94
|
+
pub trait TishOpaque {
|
|
95
|
+
/// Display name for the type (e.g. "DataFrame").
|
|
96
|
+
fn type_name(&self) -> &'static str;
|
|
97
|
+
|
|
98
|
+
/// Get a method by name. Returns a native function if the method exists.
|
|
99
|
+
fn get_method(&self, name: &str) -> Option<NativeFn>;
|
|
100
|
+
|
|
101
|
+
/// For downcasting `Arc<dyn TishOpaque>` in native crates (e.g. Polars → `DataFrame`).
|
|
102
|
+
fn as_any(&self) -> &dyn std::any::Any;
|
|
103
|
+
}
|
|
104
|
+
|
|
42
105
|
/// Trait for Promise-like values that can be awaited (block until settled).
|
|
43
106
|
/// Implemented by the runtime for native compile; interpreter uses its own Promise.
|
|
44
107
|
pub trait TishPromise: Send + Sync {
|
|
@@ -222,7 +285,9 @@ pub enum Value {
|
|
|
222
285
|
Bool(bool),
|
|
223
286
|
Null,
|
|
224
287
|
Array(VmRef<Vec<Value>>),
|
|
225
|
-
Object(VmRef<
|
|
288
|
+
Object(VmRef<ObjectData>),
|
|
289
|
+
/// ECMAScript-style primitive symbol (identity by `Arc`).
|
|
290
|
+
Symbol(Arc<TishSymbol>),
|
|
226
291
|
Function(NativeFn),
|
|
227
292
|
#[cfg(feature = "regex")]
|
|
228
293
|
RegExp(VmRef<TishRegExp>),
|
|
@@ -232,6 +297,134 @@ pub enum Value {
|
|
|
232
297
|
Opaque(Arc<dyn TishOpaque>),
|
|
233
298
|
}
|
|
234
299
|
|
|
300
|
+
/// Ordinary object: string-keyed properties plus optional symbol-keyed side map.
|
|
301
|
+
#[derive(Clone, Debug, Default)]
|
|
302
|
+
pub struct ObjectData {
|
|
303
|
+
pub strings: ObjectMap,
|
|
304
|
+
pub symbols: Option<AHashMap<u64, Value>>,
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
impl ObjectData {
|
|
308
|
+
#[inline]
|
|
309
|
+
pub fn from_strings(strings: ObjectMap) -> Self {
|
|
310
|
+
Self {
|
|
311
|
+
strings,
|
|
312
|
+
symbols: None,
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
#[inline]
|
|
317
|
+
pub fn len_entries(&self) -> usize {
|
|
318
|
+
self.strings.len() + self.symbols.as_ref().map(|s| s.len()).unwrap_or(0)
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/// Read a property from an object value.
|
|
323
|
+
pub fn object_get(obj: &Value, key: &Value) -> Option<Value> {
|
|
324
|
+
let Value::Object(od) = obj else {
|
|
325
|
+
return None;
|
|
326
|
+
};
|
|
327
|
+
let b = od.borrow();
|
|
328
|
+
match key {
|
|
329
|
+
Value::Symbol(s) => b.symbols.as_ref()?.get(&s.id).cloned(),
|
|
330
|
+
Value::Number(n) => {
|
|
331
|
+
let k: Arc<str> = n.to_string().into();
|
|
332
|
+
b.strings.get(&k).cloned()
|
|
333
|
+
}
|
|
334
|
+
Value::String(k) => b.strings.get(k.as_ref()).cloned(),
|
|
335
|
+
_ => None,
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/// Set a property on an object.
|
|
340
|
+
pub fn object_set(obj: &Value, key: &Value, val: Value) -> Result<(), String> {
|
|
341
|
+
let Value::Object(od) = obj else {
|
|
342
|
+
return Err(format!("Cannot set property on {}", obj.type_name()));
|
|
343
|
+
};
|
|
344
|
+
let mut b = od.borrow_mut();
|
|
345
|
+
match key {
|
|
346
|
+
Value::Symbol(s) => {
|
|
347
|
+
if b.symbols.is_none() {
|
|
348
|
+
b.symbols = Some(AHashMap::default());
|
|
349
|
+
}
|
|
350
|
+
b.symbols.as_mut().unwrap().insert(s.id, val);
|
|
351
|
+
Ok(())
|
|
352
|
+
}
|
|
353
|
+
Value::Number(n) => {
|
|
354
|
+
b.strings.insert(n.to_string().into(), val);
|
|
355
|
+
Ok(())
|
|
356
|
+
}
|
|
357
|
+
Value::String(k) => {
|
|
358
|
+
b.strings.insert(Arc::clone(k), val);
|
|
359
|
+
Ok(())
|
|
360
|
+
}
|
|
361
|
+
_ => Err(format!(
|
|
362
|
+
"Object key must be string, number, or symbol, got {}",
|
|
363
|
+
key.type_name()
|
|
364
|
+
)),
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/// `key in obj` for objects.
|
|
369
|
+
pub fn object_has(obj: &Value, key: &Value) -> bool {
|
|
370
|
+
let Value::Object(od) = obj else {
|
|
371
|
+
return false;
|
|
372
|
+
};
|
|
373
|
+
let b = od.borrow();
|
|
374
|
+
match key {
|
|
375
|
+
Value::Symbol(s) => b.symbols.as_ref().is_some_and(|m| m.contains_key(&s.id)),
|
|
376
|
+
Value::Number(n) => {
|
|
377
|
+
let k: Arc<str> = n.to_string().into();
|
|
378
|
+
b.strings.contains_key(&k)
|
|
379
|
+
}
|
|
380
|
+
Value::String(k) => b.strings.contains_key(k.as_ref()),
|
|
381
|
+
_ => false,
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/// Invoke a callable [`Value`]: [`Value::Function`], or an object exposing `__call` (e.g. `Symbol`).
|
|
386
|
+
pub fn value_call(callee: &Value, args: &[Value]) -> Value {
|
|
387
|
+
match callee {
|
|
388
|
+
Value::Function(f) => f(args),
|
|
389
|
+
Value::Object(o) => {
|
|
390
|
+
let inner = o.borrow().strings.get("__call").cloned();
|
|
391
|
+
if let Some(inner) = inner {
|
|
392
|
+
return value_call(&inner, args);
|
|
393
|
+
}
|
|
394
|
+
panic!(
|
|
395
|
+
"Not a function: tried to call {:?} as a function (e.g. method on Null when read failed)",
|
|
396
|
+
callee
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
_ => panic!(
|
|
400
|
+
"Not a function: tried to call {:?} as a function (e.g. method on Null when read failed)",
|
|
401
|
+
callee
|
|
402
|
+
),
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/// Merge two object payloads (spread / VM MergeObject).
|
|
407
|
+
pub fn merge_object_data(left: &VmRef<ObjectData>, right: &VmRef<ObjectData>) -> ObjectData {
|
|
408
|
+
let l = left.borrow();
|
|
409
|
+
let r = right.borrow();
|
|
410
|
+
let mut strings = ObjectMap::with_capacity(l.strings.len() + r.strings.len());
|
|
411
|
+
strings.extend(l.strings.iter().map(|(k, v)| (Arc::clone(k), v.clone())));
|
|
412
|
+
strings.extend(r.strings.iter().map(|(k, v)| (Arc::clone(k), v.clone())));
|
|
413
|
+
let mut symbols: Option<AHashMap<u64, Value>> = None;
|
|
414
|
+
if let Some(ls) = &l.symbols {
|
|
415
|
+
symbols = Some(ls.clone());
|
|
416
|
+
}
|
|
417
|
+
if let Some(rs) = &r.symbols {
|
|
418
|
+
match &mut symbols {
|
|
419
|
+
Some(m) => {
|
|
420
|
+
m.extend(rs.iter().map(|(k, v)| (*k, v.clone())));
|
|
421
|
+
}
|
|
422
|
+
None => symbols = Some(rs.clone()),
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
ObjectData { strings, symbols }
|
|
426
|
+
}
|
|
427
|
+
|
|
235
428
|
impl std::fmt::Debug for Value {
|
|
236
429
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
237
430
|
match self {
|
|
@@ -241,6 +434,7 @@ impl std::fmt::Debug for Value {
|
|
|
241
434
|
Value::Null => write!(f, "Null"),
|
|
242
435
|
Value::Array(arr) => write!(f, "Array({:?})", arr.borrow()),
|
|
243
436
|
Value::Object(obj) => write!(f, "Object({:?})", obj.borrow()),
|
|
437
|
+
Value::Symbol(s) => write!(f, "Symbol({})", s.id),
|
|
244
438
|
Value::Function(_) => write!(f, "Function"),
|
|
245
439
|
#[cfg(feature = "regex")]
|
|
246
440
|
Value::RegExp(re) => write!(
|
|
@@ -281,11 +475,19 @@ impl Value {
|
|
|
281
475
|
Value::Object(obj) => {
|
|
282
476
|
let inner: Vec<String> = obj
|
|
283
477
|
.borrow()
|
|
478
|
+
.strings
|
|
284
479
|
.iter()
|
|
285
480
|
.map(|(k, v)| format!("{}: {}", k.as_ref(), v.to_display_string()))
|
|
286
481
|
.collect();
|
|
287
482
|
format!("{{{}}}", inner.join(", "))
|
|
288
483
|
}
|
|
484
|
+
Value::Symbol(s) => {
|
|
485
|
+
if let Some(d) = &s.description {
|
|
486
|
+
format!("Symbol({})", d)
|
|
487
|
+
} else {
|
|
488
|
+
"Symbol()".to_string()
|
|
489
|
+
}
|
|
490
|
+
}
|
|
289
491
|
Value::Function(_) => "[Function]".to_string(),
|
|
290
492
|
Value::Promise(_) => "[object Promise]".to_string(),
|
|
291
493
|
Value::Opaque(o) => format!("[object {}]", o.type_name()),
|
|
@@ -331,6 +533,7 @@ impl Value {
|
|
|
331
533
|
(Value::RegExp(a), Value::RegExp(b)) => VmRef::ptr_eq(a, b),
|
|
332
534
|
(Value::Promise(a), Value::Promise(b)) => Arc::ptr_eq(a, b),
|
|
333
535
|
(Value::Opaque(a), Value::Opaque(b)) => Arc::ptr_eq(a, b),
|
|
536
|
+
(Value::Symbol(a), Value::Symbol(b)) => Arc::ptr_eq(a, b),
|
|
334
537
|
_ => false,
|
|
335
538
|
}
|
|
336
539
|
}
|
|
@@ -364,7 +567,7 @@ impl Value {
|
|
|
364
567
|
|
|
365
568
|
/// Create a new object Value from a property map.
|
|
366
569
|
pub fn object(map: ObjectMap) -> Self {
|
|
367
|
-
Value::Object(VmRef::new(map))
|
|
570
|
+
Value::Object(VmRef::new(ObjectData::from_strings(map)))
|
|
368
571
|
}
|
|
369
572
|
|
|
370
573
|
/// Create an empty array Value.
|
|
@@ -374,7 +577,7 @@ impl Value {
|
|
|
374
577
|
|
|
375
578
|
/// Create an empty object Value.
|
|
376
579
|
pub fn empty_object() -> Self {
|
|
377
|
-
Value::Object(VmRef::new(
|
|
580
|
+
Value::Object(VmRef::new(ObjectData::default()))
|
|
378
581
|
}
|
|
379
582
|
|
|
380
583
|
/// Extract the number value, if this is a Number.
|
|
@@ -399,6 +602,7 @@ impl Value {
|
|
|
399
602
|
Value::RegExp(_) => "object",
|
|
400
603
|
Value::Promise(_) => "object",
|
|
401
604
|
Value::Opaque(o) => o.type_name(),
|
|
605
|
+
Value::Symbol(_) => "symbol",
|
|
402
606
|
}
|
|
403
607
|
}
|
|
404
608
|
|
|
@@ -406,7 +610,12 @@ impl Value {
|
|
|
406
610
|
pub fn completion_keys(&self) -> Vec<String> {
|
|
407
611
|
match self {
|
|
408
612
|
Value::Object(m) => {
|
|
409
|
-
let mut keys: Vec<String> = m
|
|
613
|
+
let mut keys: Vec<String> = m
|
|
614
|
+
.borrow()
|
|
615
|
+
.strings
|
|
616
|
+
.keys()
|
|
617
|
+
.map(|k| k.to_string())
|
|
618
|
+
.collect();
|
|
410
619
|
keys.sort();
|
|
411
620
|
keys
|
|
412
621
|
}
|
|
@@ -103,7 +103,7 @@ fn main() {{
|
|
|
103
103
|
message: format!("Cannot write build.rs: {}", e),
|
|
104
104
|
})?;
|
|
105
105
|
|
|
106
|
-
tishlang_build_utils::run_cargo_build(&build_dir, None)
|
|
106
|
+
tishlang_build_utils::run_cargo_build(&build_dir, None, None)
|
|
107
107
|
.map_err(|e| CraneliftError { message: e })?;
|
|
108
108
|
|
|
109
109
|
let binary_dir = build_dir.join("target").join("release");
|
|
@@ -11,6 +11,10 @@ default = []
|
|
|
11
11
|
fs = ["tishlang_vm/fs"]
|
|
12
12
|
process = ["tishlang_vm/process"]
|
|
13
13
|
http = ["tishlang_vm/http"]
|
|
14
|
+
promise = ["tishlang_vm/promise"]
|
|
15
|
+
timers = ["tishlang_vm/timers"]
|
|
16
|
+
ws = ["tishlang_vm/ws"]
|
|
17
|
+
regex = ["tishlang_vm/regex"]
|
|
14
18
|
|
|
15
19
|
[lib]
|
|
16
20
|
crate-type = ["staticlib", "rlib"]
|