@tishlang/tish-format 1.0.12 → 2.0.1
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 +51 -0
- package/LICENSE +13 -0
- package/bin/tish-format +0 -0
- package/crates/js_to_tish/Cargo.toml +11 -0
- package/crates/js_to_tish/README.md +18 -0
- package/crates/js_to_tish/src/error.rs +55 -0
- package/crates/js_to_tish/src/lib.rs +11 -0
- package/crates/js_to_tish/src/span_util.rs +35 -0
- package/crates/js_to_tish/src/transform/expr.rs +611 -0
- package/crates/js_to_tish/src/transform/stmt.rs +503 -0
- package/crates/js_to_tish/src/transform.rs +60 -0
- package/crates/tish/Cargo.toml +62 -0
- package/crates/tish/build.rs +21 -0
- package/crates/tish/src/cargo_native_registry.rs +32 -0
- package/crates/tish/src/cli_help.rs +576 -0
- package/crates/tish/src/main.rs +853 -0
- package/crates/tish/src/repl_completion.rs +199 -0
- package/crates/tish/tests/cargo_example_compile.rs +67 -0
- package/crates/tish/tests/error_source_location.rs +36 -0
- package/crates/tish/tests/fixtures/cargo_example_project/Cargo.toml +3 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/Cargo.toml +11 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +12 -0
- package/crates/tish/tests/fixtures/cargo_example_project/package.json +10 -0
- package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +3 -0
- package/crates/tish/tests/fixtures/runtime_error_location.tish +5 -0
- package/crates/tish/tests/fixtures/trycatch_runtime_errors.tish +15 -0
- package/crates/tish/tests/fixtures/tty_capability.tish +9 -0
- package/crates/tish/tests/integration_test.rs +1406 -0
- package/crates/tish/tests/run_optimize_stdout_parity.rs +50 -0
- package/crates/tish/tests/shortcircuit.rs +65 -0
- package/crates/tish/tests/trycatch_runtime_errors.rs +45 -0
- package/crates/tish/tests/tty_capability.rs +43 -0
- package/crates/tish_ast/Cargo.toml +9 -0
- package/crates/tish_ast/src/ast.rs +649 -0
- package/crates/tish_ast/src/lib.rs +5 -0
- package/crates/tish_build_utils/Cargo.toml +11 -0
- package/crates/tish_build_utils/src/lib.rs +577 -0
- package/crates/tish_builtins/Cargo.toml +22 -0
- package/crates/tish_builtins/src/array.rs +803 -0
- package/crates/tish_builtins/src/collections.rs +481 -0
- package/crates/tish_builtins/src/construct.rs +199 -0
- package/crates/tish_builtins/src/date.rs +538 -0
- package/crates/tish_builtins/src/globals.rs +293 -0
- package/crates/tish_builtins/src/helpers.rs +35 -0
- package/crates/tish_builtins/src/iterator.rs +129 -0
- package/crates/tish_builtins/src/lib.rs +21 -0
- package/crates/tish_builtins/src/math.rs +89 -0
- package/crates/tish_builtins/src/number.rs +96 -0
- package/crates/tish_builtins/src/object.rs +36 -0
- package/crates/tish_builtins/src/string.rs +646 -0
- package/crates/tish_builtins/src/symbol.rs +83 -0
- package/crates/tish_builtins/src/typedarrays.rs +298 -0
- package/crates/tish_bytecode/Cargo.toml +17 -0
- package/crates/tish_bytecode/src/chunk.rs +164 -0
- package/crates/tish_bytecode/src/compiler.rs +2604 -0
- package/crates/tish_bytecode/src/encoding.rs +102 -0
- package/crates/tish_bytecode/src/lib.rs +20 -0
- package/crates/tish_bytecode/src/opcode.rs +185 -0
- package/crates/tish_bytecode/src/peephole.rs +189 -0
- package/crates/tish_bytecode/src/serialize.rs +193 -0
- package/crates/tish_bytecode/tests/break_continue_bytecode.rs +44 -0
- package/crates/tish_bytecode/tests/constant_folding.rs +84 -0
- package/crates/tish_bytecode/tests/sort_optimization.rs +31 -0
- package/crates/tish_compile/Cargo.toml +27 -0
- package/crates/tish_compile/src/check.rs +774 -0
- package/crates/tish_compile/src/codegen.rs +7317 -0
- package/crates/tish_compile/src/infer.rs +1681 -0
- package/crates/tish_compile/src/lib.rs +206 -0
- package/crates/tish_compile/src/resolve.rs +1951 -0
- package/crates/tish_compile/src/types.rs +605 -0
- package/crates/tish_compile_js/Cargo.toml +18 -0
- package/crates/tish_compile_js/examples/jsx_vdom_smoke.tish +8 -0
- package/crates/tish_compile_js/src/codegen.rs +938 -0
- package/crates/tish_compile_js/src/error.rs +20 -0
- package/crates/tish_compile_js/src/lib.rs +26 -0
- package/crates/tish_compile_js/src/tests_jsx.rs +414 -0
- package/crates/tish_compiler_wasm/Cargo.toml +21 -0
- package/crates/tish_compiler_wasm/src/lib.rs +57 -0
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +473 -0
- package/crates/tish_core/Cargo.toml +32 -0
- package/crates/tish_core/src/console_style.rs +170 -0
- package/crates/tish_core/src/json.rs +430 -0
- package/crates/tish_core/src/lib.rs +20 -0
- package/crates/tish_core/src/macros.rs +36 -0
- package/crates/tish_core/src/shape.rs +85 -0
- package/crates/tish_core/src/uri.rs +118 -0
- package/crates/tish_core/src/value.rs +1350 -0
- package/crates/tish_core/src/vmref.rs +183 -0
- package/crates/tish_cranelift/Cargo.toml +19 -0
- package/crates/tish_cranelift/src/lib.rs +43 -0
- package/crates/tish_cranelift/src/link.rs +130 -0
- package/crates/tish_cranelift/src/lower.rs +85 -0
- package/crates/tish_cranelift_runtime/Cargo.toml +26 -0
- package/crates/tish_cranelift_runtime/src/lib.rs +45 -0
- package/crates/tish_eval/Cargo.toml +51 -0
- package/crates/tish_eval/src/eval.rs +4265 -0
- package/crates/tish_eval/src/http.rs +191 -0
- package/crates/tish_eval/src/lib.rs +99 -0
- package/crates/tish_eval/src/natives.rs +551 -0
- package/crates/tish_eval/src/promise.rs +179 -0
- package/crates/tish_eval/src/regex.rs +299 -0
- package/crates/tish_eval/src/timers.rs +120 -0
- package/crates/tish_eval/src/value.rs +336 -0
- package/crates/tish_eval/src/value_convert.rs +117 -0
- package/crates/tish_ffi/Cargo.toml +26 -0
- package/crates/tish_ffi/src/lib.rs +518 -0
- package/crates/tish_ffi/tests/fixtures/testmod/Cargo.toml +18 -0
- package/crates/tish_ffi/tests/fixtures/testmod/src/lib.rs +46 -0
- package/crates/tish_ffi/tests/loader.rs +65 -0
- package/crates/tish_fmt/Cargo.toml +16 -0
- package/crates/tish_fmt/src/bin/tish-fmt.rs +41 -0
- package/crates/tish_fmt/src/lib.rs +2157 -0
- package/crates/tish_jsx_web/Cargo.toml +9 -0
- package/crates/tish_jsx_web/README.md +5 -0
- package/crates/tish_jsx_web/src/lib.rs +2 -0
- package/crates/tish_lexer/Cargo.toml +9 -0
- package/crates/tish_lexer/src/lib.rs +1104 -0
- package/crates/tish_lexer/src/token.rs +170 -0
- package/crates/tish_lint/Cargo.toml +18 -0
- package/crates/tish_lint/src/bin/tish-lint.rs +195 -0
- package/crates/tish_lint/src/lib.rs +281 -0
- package/crates/tish_llvm/Cargo.toml +13 -0
- package/crates/tish_llvm/src/lib.rs +115 -0
- package/crates/tish_lsp/Cargo.toml +25 -0
- package/crates/tish_lsp/README.md +26 -0
- package/crates/tish_lsp/src/builtin_goto.rs +362 -0
- package/crates/tish_lsp/src/import_goto.rs +564 -0
- package/crates/tish_lsp/src/main.rs +1459 -0
- package/crates/tish_native/Cargo.toml +16 -0
- package/crates/tish_native/src/build.rs +481 -0
- package/crates/tish_native/src/config.rs +48 -0
- package/crates/tish_native/src/lib.rs +416 -0
- package/crates/tish_opt/Cargo.toml +13 -0
- package/crates/tish_opt/src/lib.rs +1046 -0
- package/crates/tish_parser/Cargo.toml +11 -0
- package/crates/tish_parser/src/lib.rs +386 -0
- package/crates/tish_parser/src/parser.rs +2726 -0
- package/crates/tish_pg/Cargo.toml +34 -0
- package/crates/tish_pg/README.md +38 -0
- package/crates/tish_pg/src/error.rs +52 -0
- package/crates/tish_pg/src/lib.rs +955 -0
- package/crates/tish_resolve/Cargo.toml +13 -0
- package/crates/tish_resolve/src/lib.rs +3601 -0
- package/crates/tish_resolve/src/pos.rs +141 -0
- package/crates/tish_runtime/Cargo.toml +100 -0
- package/crates/tish_runtime/src/http.rs +1347 -0
- package/crates/tish_runtime/src/http_fetch.rs +492 -0
- package/crates/tish_runtime/src/http_hyper.rs +441 -0
- package/crates/tish_runtime/src/http_prefork.rs +189 -0
- package/crates/tish_runtime/src/lib.rs +1447 -0
- package/crates/tish_runtime/src/native_promise.rs +15 -0
- package/crates/tish_runtime/src/promise.rs +558 -0
- package/crates/tish_runtime/src/promise_io.rs +38 -0
- package/crates/tish_runtime/src/timers.rs +172 -0
- package/crates/tish_runtime/src/tty.rs +226 -0
- package/crates/tish_runtime/src/ws.rs +778 -0
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +102 -0
- package/crates/tish_ui/Cargo.toml +17 -0
- package/crates/tish_ui/src/jsx.rs +692 -0
- package/crates/tish_ui/src/lib.rs +20 -0
- package/crates/tish_ui/src/runtime/hooks.rs +573 -0
- package/crates/tish_ui/src/runtime/mod.rs +183 -0
- package/crates/tish_vm/Cargo.toml +60 -0
- package/crates/tish_vm/src/jit.rs +1050 -0
- package/crates/tish_vm/src/lib.rs +41 -0
- package/crates/tish_vm/src/vm.rs +3536 -0
- package/crates/tish_vm/tests/concurrent_shared_state.rs +140 -0
- package/crates/tish_vm/tests/fixtures/or_string_cmd.tish +2 -0
- package/crates/tish_vm/tests/lexical_scope_declare.rs +34 -0
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +150 -0
- package/crates/tish_wasm/Cargo.toml +15 -0
- package/crates/tish_wasm/src/lib.rs +428 -0
- package/crates/tish_wasm_runtime/Cargo.toml +37 -0
- package/crates/tish_wasm_runtime/src/gpu.rs +429 -0
- package/crates/tish_wasm_runtime/src/lib.rs +42 -0
- package/crates/tishlang_cargo_bindgen/Cargo.toml +26 -0
- package/crates/tishlang_cargo_bindgen/src/classify.rs +261 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +125 -0
- package/crates/tishlang_cargo_bindgen/src/infer.rs +382 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
- package/crates/tishlang_cargo_bindgen/src/main.rs +167 -0
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +117 -0
- package/justfile +276 -0
- package/package.json +2 -2
- package/platform/darwin-arm64/tish-fmt +0 -0
- package/platform/darwin-x64/tish-fmt +0 -0
- package/platform/linux-arm64/tish-fmt +0 -0
- package/platform/linux-x64/tish-fmt +0 -0
- package/platform/win32-x64/tish-fmt.exe +0 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
//! Runtime values for the Tish interpreter.
|
|
2
|
+
//!
|
|
3
|
+
//! This module defines the interpreter's `Value` type, which includes variants
|
|
4
|
+
//! like `Function`, `Native`, and `Serve` that hold AST or interpreter-specific
|
|
5
|
+
//! data. The compiled runtime uses `tishlang_core::Value` instead, which has a
|
|
6
|
+
//! different shape (no AST-carrying variants). The split is intentional.
|
|
7
|
+
|
|
8
|
+
use std::cell::RefCell;
|
|
9
|
+
use std::rc::Rc;
|
|
10
|
+
use std::sync::Arc;
|
|
11
|
+
|
|
12
|
+
use ahash::AHashMap;
|
|
13
|
+
use tishlang_ast::{FunParam, Statement};
|
|
14
|
+
use tishlang_core::NativeFn as CoreNativeFn;
|
|
15
|
+
use tishlang_core::TishSymbol;
|
|
16
|
+
|
|
17
|
+
/// Property map for interpreter object string keys (uses `eval::Value`, not `tishlang_core::Value`).
|
|
18
|
+
/// `IndexMap` preserves insertion order so `Object.keys` / `JSON.stringify` match JS/Node
|
|
19
|
+
/// (and the VM/rust backends, which use `tishlang_core`'s insertion-ordered `PropMap`).
|
|
20
|
+
pub type PropMap = indexmap::IndexMap<Arc<str>, Value>;
|
|
21
|
+
|
|
22
|
+
/// Interpreter object: string keys plus optional symbol-keyed properties.
|
|
23
|
+
#[derive(Clone, Debug, Default)]
|
|
24
|
+
pub struct EvalObjectData {
|
|
25
|
+
pub strings: PropMap,
|
|
26
|
+
pub symbols: Option<AHashMap<u64, Value>>,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
impl EvalObjectData {
|
|
30
|
+
pub fn from_strings(strings: PropMap) -> Self {
|
|
31
|
+
Self {
|
|
32
|
+
strings,
|
|
33
|
+
symbols: None,
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
pub fn eval_object_get(obj: &Value, key: &Value) -> Option<Value> {
|
|
39
|
+
let Value::Object(od) = obj else {
|
|
40
|
+
return None;
|
|
41
|
+
};
|
|
42
|
+
let b = od.borrow();
|
|
43
|
+
match key {
|
|
44
|
+
Value::Symbol(s) => b.symbols.as_ref()?.get(&s.id).cloned(),
|
|
45
|
+
Value::Number(n) => {
|
|
46
|
+
let k: Arc<str> = n.to_string().into();
|
|
47
|
+
b.strings.get(&k).cloned()
|
|
48
|
+
}
|
|
49
|
+
Value::String(k) => b.strings.get(k.as_ref()).cloned(),
|
|
50
|
+
_ => None,
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
pub fn eval_object_set(obj: &Value, key: &Value, val: Value) -> Result<(), String> {
|
|
55
|
+
let Value::Object(od) = obj else {
|
|
56
|
+
return Err("Cannot set property on non-object".to_string());
|
|
57
|
+
};
|
|
58
|
+
let mut b = od.borrow_mut();
|
|
59
|
+
match key {
|
|
60
|
+
Value::Symbol(s) => {
|
|
61
|
+
if b.symbols.is_none() {
|
|
62
|
+
b.symbols = Some(AHashMap::default());
|
|
63
|
+
}
|
|
64
|
+
b.symbols.as_mut().unwrap().insert(s.id, val);
|
|
65
|
+
Ok(())
|
|
66
|
+
}
|
|
67
|
+
Value::Number(n) => {
|
|
68
|
+
b.strings.insert(n.to_string().into(), val);
|
|
69
|
+
Ok(())
|
|
70
|
+
}
|
|
71
|
+
Value::String(k) => {
|
|
72
|
+
b.strings.insert(Arc::clone(k), val);
|
|
73
|
+
Ok(())
|
|
74
|
+
}
|
|
75
|
+
_ => Err("Object key must be string, number, or symbol".to_string()),
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
pub fn eval_object_has(obj: &Value, key: &Value) -> bool {
|
|
80
|
+
let Value::Object(od) = obj else {
|
|
81
|
+
return false;
|
|
82
|
+
};
|
|
83
|
+
let b = od.borrow();
|
|
84
|
+
match key {
|
|
85
|
+
Value::Symbol(s) => b.symbols.as_ref().is_some_and(|m| m.contains_key(&s.id)),
|
|
86
|
+
Value::Number(n) => {
|
|
87
|
+
let k: Arc<str> = n.to_string().into();
|
|
88
|
+
b.strings.contains_key(&k)
|
|
89
|
+
}
|
|
90
|
+
Value::String(k) => b.strings.contains_key(k.as_ref()),
|
|
91
|
+
_ => false,
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
use tishlang_core::TishOpaque;
|
|
95
|
+
#[cfg(feature = "http")]
|
|
96
|
+
use tishlang_core::TishPromise;
|
|
97
|
+
|
|
98
|
+
#[cfg(feature = "http")]
|
|
99
|
+
pub use crate::promise::PromiseResolver;
|
|
100
|
+
#[cfg(feature = "regex")]
|
|
101
|
+
pub use crate::regex::TishRegExp;
|
|
102
|
+
|
|
103
|
+
/// Native function type - takes args, returns Result<Value, String>
|
|
104
|
+
pub type NativeFn = fn(&[Value]) -> Result<Value, String>;
|
|
105
|
+
|
|
106
|
+
#[derive(Clone)]
|
|
107
|
+
pub enum Value {
|
|
108
|
+
Number(f64),
|
|
109
|
+
String(Arc<str>),
|
|
110
|
+
Bool(bool),
|
|
111
|
+
Null,
|
|
112
|
+
Array(Rc<RefCell<Vec<Value>>>),
|
|
113
|
+
Object(Rc<RefCell<EvalObjectData>>),
|
|
114
|
+
Symbol(Arc<TishSymbol>),
|
|
115
|
+
/// User-defined function with AST body. `env` is the lexical scope captured at definition
|
|
116
|
+
/// time, so the body resolves free variables against where it was DEFINED (a real closure),
|
|
117
|
+
/// not where it is called.
|
|
118
|
+
Function {
|
|
119
|
+
formals: Arc<[FunParam]>,
|
|
120
|
+
rest_param: Option<Arc<str>>,
|
|
121
|
+
body: Arc<Statement>,
|
|
122
|
+
env: crate::eval::ScopeRef,
|
|
123
|
+
},
|
|
124
|
+
/// Native/builtin function
|
|
125
|
+
Native(NativeFn),
|
|
126
|
+
/// HTTP serve function (needs special handling for callbacks)
|
|
127
|
+
#[cfg(feature = "http")]
|
|
128
|
+
Serve,
|
|
129
|
+
#[cfg(feature = "regex")]
|
|
130
|
+
RegExp(Rc<RefCell<TishRegExp>>),
|
|
131
|
+
/// Promise (ECMA-262 §27.2). Requires http feature for tokio.
|
|
132
|
+
#[cfg(feature = "http")]
|
|
133
|
+
Promise(crate::promise::PromiseRef),
|
|
134
|
+
/// Internal: resolve/reject functions passed to executor. Not user-facing.
|
|
135
|
+
#[cfg(feature = "http")]
|
|
136
|
+
PromiseResolver(PromiseResolver),
|
|
137
|
+
/// Promise constructor: Promise(executor). Requires special call handling.
|
|
138
|
+
#[cfg(feature = "http")]
|
|
139
|
+
PromiseConstructor,
|
|
140
|
+
/// Bound promise method: promise.then/catch/finally - captures the promise for the call.
|
|
141
|
+
#[cfg(feature = "http")]
|
|
142
|
+
BoundPromiseMethod(crate::promise::PromiseRef, std::sync::Arc<str>),
|
|
143
|
+
/// Timer builtins: setTimeout, setInterval. Need evaluator for callback.
|
|
144
|
+
#[cfg(feature = "timers")]
|
|
145
|
+
TimerBuiltin(std::sync::Arc<str>),
|
|
146
|
+
/// Native `tishlang_core` Promise (fetch / reader.read / response.text).
|
|
147
|
+
#[cfg(feature = "http")]
|
|
148
|
+
CorePromise(Arc<dyn TishPromise>),
|
|
149
|
+
/// `tishlang_core::Value::Function` (native callbacks, `new` constructors, fetch/ws when enabled).
|
|
150
|
+
CoreFn(CoreNativeFn),
|
|
151
|
+
/// Opaque handle to a native Rust type (e.g. Polars DataFrame).
|
|
152
|
+
Opaque(Arc<dyn TishOpaque>),
|
|
153
|
+
/// Bound method on an opaque value (opaque, method_name). Callable.
|
|
154
|
+
OpaqueMethod(Arc<dyn TishOpaque>, Arc<str>),
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
impl std::fmt::Debug for Value {
|
|
158
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
159
|
+
match self {
|
|
160
|
+
Value::Number(n) => write!(f, "Number({})", n),
|
|
161
|
+
Value::String(s) => write!(f, "String({:?})", s.as_ref()),
|
|
162
|
+
Value::Bool(b) => write!(f, "Bool({})", b),
|
|
163
|
+
Value::Null => write!(f, "Null"),
|
|
164
|
+
Value::Array(arr) => write!(f, "Array({:?})", arr.borrow()),
|
|
165
|
+
Value::Object(obj) => write!(f, "Object({:?})", obj.borrow()),
|
|
166
|
+
Value::Function { .. } => write!(f, "Function"),
|
|
167
|
+
Value::Native(_) => write!(f, "Native"),
|
|
168
|
+
#[cfg(feature = "http")]
|
|
169
|
+
Value::Serve => write!(f, "Serve"),
|
|
170
|
+
#[cfg(feature = "regex")]
|
|
171
|
+
Value::RegExp(re) => write!(
|
|
172
|
+
f,
|
|
173
|
+
"RegExp(/{}/{})",
|
|
174
|
+
re.borrow().source,
|
|
175
|
+
re.borrow().flags_string()
|
|
176
|
+
),
|
|
177
|
+
#[cfg(feature = "http")]
|
|
178
|
+
Value::Promise(_) => write!(f, "Promise"),
|
|
179
|
+
#[cfg(feature = "http")]
|
|
180
|
+
Value::PromiseResolver(_) => write!(f, "[PromiseResolver]"),
|
|
181
|
+
#[cfg(feature = "http")]
|
|
182
|
+
Value::PromiseConstructor => write!(f, "[Function: Promise]"),
|
|
183
|
+
#[cfg(feature = "http")]
|
|
184
|
+
Value::BoundPromiseMethod(_, _) => write!(f, "[Function]"),
|
|
185
|
+
#[cfg(feature = "timers")]
|
|
186
|
+
Value::TimerBuiltin(_) => write!(f, "[Function]"),
|
|
187
|
+
#[cfg(feature = "http")]
|
|
188
|
+
Value::CorePromise(_) => write!(f, "Promise"),
|
|
189
|
+
Value::CoreFn(_) => write!(f, "CoreFn"),
|
|
190
|
+
Value::Opaque(o) => write!(f, "{}(opaque)", o.type_name()),
|
|
191
|
+
Value::OpaqueMethod(_, _) => write!(f, "[Function]"),
|
|
192
|
+
Value::Symbol(s) => write!(f, "Symbol({})", s.id),
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
impl std::fmt::Display for Value {
|
|
198
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
199
|
+
match self {
|
|
200
|
+
// Match JS `Number.prototype.toString` (exponential past digit 21 / before −6),
|
|
201
|
+
// shared with the VM/native path via `tishlang_core`.
|
|
202
|
+
Value::Number(n) => write!(f, "{}", tishlang_core::js_number_to_string(*n)),
|
|
203
|
+
Value::String(s) => write!(f, "{}", s),
|
|
204
|
+
Value::Bool(b) => write!(f, "{}", b),
|
|
205
|
+
Value::Null => write!(f, "null"),
|
|
206
|
+
Value::Array(arr) => {
|
|
207
|
+
let inner: Vec<String> = arr.borrow().iter().map(|v| v.to_string()).collect();
|
|
208
|
+
write!(f, "[{}]", inner.join(", "))
|
|
209
|
+
}
|
|
210
|
+
Value::Object(obj) => {
|
|
211
|
+
let inner: Vec<String> = obj
|
|
212
|
+
.borrow()
|
|
213
|
+
.strings
|
|
214
|
+
.iter()
|
|
215
|
+
.map(|(k, v)| format!("{}: {}", k.as_ref(), v))
|
|
216
|
+
.collect();
|
|
217
|
+
write!(f, "{{{}}}", inner.join(", "))
|
|
218
|
+
}
|
|
219
|
+
Value::Symbol(s) => {
|
|
220
|
+
if let Some(d) = &s.description {
|
|
221
|
+
write!(f, "Symbol({})", d)
|
|
222
|
+
} else {
|
|
223
|
+
write!(f, "Symbol()")
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
Value::Function { .. } => write!(f, "[Function]"),
|
|
227
|
+
Value::Native(_) => write!(f, "[NativeFunction]"),
|
|
228
|
+
#[cfg(feature = "http")]
|
|
229
|
+
Value::Serve => write!(f, "[NativeFunction: serve]"),
|
|
230
|
+
#[cfg(feature = "regex")]
|
|
231
|
+
Value::RegExp(re) => {
|
|
232
|
+
let re = re.borrow();
|
|
233
|
+
write!(f, "/{}/{}", re.source, re.flags_string())
|
|
234
|
+
}
|
|
235
|
+
#[cfg(feature = "http")]
|
|
236
|
+
Value::Promise(_) => write!(f, "[Promise]"),
|
|
237
|
+
#[cfg(feature = "http")]
|
|
238
|
+
Value::PromiseResolver(_) => write!(f, "[Function]"),
|
|
239
|
+
#[cfg(feature = "http")]
|
|
240
|
+
Value::PromiseConstructor => write!(f, "function Promise() {{ [native code] }}"),
|
|
241
|
+
#[cfg(feature = "http")]
|
|
242
|
+
Value::BoundPromiseMethod(_, _) => write!(f, "[Function]"),
|
|
243
|
+
#[cfg(feature = "timers")]
|
|
244
|
+
Value::TimerBuiltin(_) => write!(f, "[Function]"),
|
|
245
|
+
#[cfg(feature = "http")]
|
|
246
|
+
Value::CorePromise(_) => write!(f, "[Promise]"),
|
|
247
|
+
Value::CoreFn(_) => write!(f, "[Function]"),
|
|
248
|
+
Value::Opaque(o) => write!(f, "[object {}]", o.type_name()),
|
|
249
|
+
Value::OpaqueMethod(_, _) => write!(f, "[Function]"),
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
impl Value {
|
|
255
|
+
pub fn is_truthy(&self) -> bool {
|
|
256
|
+
match self {
|
|
257
|
+
Value::Null => false,
|
|
258
|
+
Value::Bool(b) => *b,
|
|
259
|
+
Value::Number(n) => *n != 0.0 && !n.is_nan(),
|
|
260
|
+
Value::String(s) => !s.is_empty(),
|
|
261
|
+
_ => true,
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/// JavaScript `ToString` coercion (as used by `Array.prototype.join`), distinct from the
|
|
266
|
+
/// `Display`/inspect form: a nested **array** stringifies to its own comma-joined `toString`
|
|
267
|
+
/// (recursively, always `,`), an **object** becomes `"[object Object]"`, and `null`/`undefined`
|
|
268
|
+
/// elements elide to `""`. Mirrors `tishlang_core::Value::to_js_string` so interp output matches
|
|
269
|
+
/// the VM/rust/cranelift/wasi backends (and Node) for join/coercion.
|
|
270
|
+
pub fn to_js_string(&self) -> String {
|
|
271
|
+
match self {
|
|
272
|
+
Value::Array(arr) => arr
|
|
273
|
+
.borrow()
|
|
274
|
+
.iter()
|
|
275
|
+
.map(|v| match v {
|
|
276
|
+
Value::Null => String::new(),
|
|
277
|
+
other => other.to_js_string(),
|
|
278
|
+
})
|
|
279
|
+
.collect::<Vec<_>>()
|
|
280
|
+
.join(","),
|
|
281
|
+
Value::Object(_) => "[object Object]".to_string(),
|
|
282
|
+
_ => self.to_string(),
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
pub fn strict_eq(&self, other: &Value) -> bool {
|
|
287
|
+
match (self, other) {
|
|
288
|
+
(Value::Number(a), Value::Number(b)) => {
|
|
289
|
+
if a.is_nan() || b.is_nan() {
|
|
290
|
+
false
|
|
291
|
+
} else {
|
|
292
|
+
a == b
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
(Value::String(a), Value::String(b)) => a == b,
|
|
296
|
+
(Value::Bool(a), Value::Bool(b)) => a == b,
|
|
297
|
+
(Value::Null, Value::Null) => true,
|
|
298
|
+
(Value::Array(a), Value::Array(b)) => Rc::ptr_eq(a, b),
|
|
299
|
+
(Value::Object(a), Value::Object(b)) => Rc::ptr_eq(a, b),
|
|
300
|
+
(Value::Symbol(a), Value::Symbol(b)) => Arc::ptr_eq(a, b),
|
|
301
|
+
(Value::Opaque(a), Value::Opaque(b)) => Arc::ptr_eq(a, b),
|
|
302
|
+
(Value::OpaqueMethod(a, ak), Value::OpaqueMethod(b, bk)) => {
|
|
303
|
+
Arc::ptr_eq(a, b) && ak == bk
|
|
304
|
+
}
|
|
305
|
+
_ => false,
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/// Create a new array Value from a Vec.
|
|
310
|
+
pub fn array(items: Vec<Value>) -> Self {
|
|
311
|
+
Value::Array(Rc::new(RefCell::new(items)))
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/// Create a new object Value from a property map.
|
|
315
|
+
pub fn object(map: PropMap) -> Self {
|
|
316
|
+
Value::Object(Rc::new(RefCell::new(EvalObjectData::from_strings(map))))
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/// Create an empty array Value.
|
|
320
|
+
pub fn empty_array() -> Self {
|
|
321
|
+
Value::Array(Rc::new(RefCell::new(Vec::new())))
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/// Create an empty object Value.
|
|
325
|
+
pub fn empty_object() -> Self {
|
|
326
|
+
Value::Object(Rc::new(RefCell::new(EvalObjectData::default())))
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/// Extract the number value, if this is a Number.
|
|
330
|
+
pub fn as_number(&self) -> Option<f64> {
|
|
331
|
+
match self {
|
|
332
|
+
Value::Number(n) => Some(*n),
|
|
333
|
+
_ => None,
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
//! Conversion between tishlang_eval::Value and tishlang_core::Value for opaque method calls.
|
|
2
|
+
|
|
3
|
+
use std::cell::RefCell;
|
|
4
|
+
use std::rc::Rc;
|
|
5
|
+
use std::sync::Arc;
|
|
6
|
+
|
|
7
|
+
#[cfg(feature = "regex")]
|
|
8
|
+
use tishlang_core::TishRegExp;
|
|
9
|
+
use ahash::AHashMap;
|
|
10
|
+
use tishlang_core::{ObjectData, Value as CoreValue, VmRef};
|
|
11
|
+
|
|
12
|
+
use crate::value::{EvalObjectData, PropMap, Value};
|
|
13
|
+
|
|
14
|
+
/// Convert interpreter Value to core Value. Fails for interpreter-only variants.
|
|
15
|
+
pub fn eval_to_core(v: &Value) -> Result<CoreValue, String> {
|
|
16
|
+
match v {
|
|
17
|
+
Value::Number(n) => Ok(CoreValue::Number(*n)),
|
|
18
|
+
Value::String(s) => Ok(CoreValue::String(tishlang_core::ArcStr::from(s.as_ref()))),
|
|
19
|
+
Value::Bool(b) => Ok(CoreValue::Bool(*b)),
|
|
20
|
+
Value::Null => Ok(CoreValue::Null),
|
|
21
|
+
Value::Array(arr) => {
|
|
22
|
+
let mut out = Vec::new();
|
|
23
|
+
for item in arr.borrow().iter() {
|
|
24
|
+
out.push(eval_to_core(item)?);
|
|
25
|
+
}
|
|
26
|
+
Ok(CoreValue::Array(VmRef::new(out)))
|
|
27
|
+
}
|
|
28
|
+
Value::Object(map) => {
|
|
29
|
+
let b = map.borrow();
|
|
30
|
+
let mut strings = tishlang_core::PropMap::default();
|
|
31
|
+
for (k, v) in b.strings.iter() {
|
|
32
|
+
strings.insert(Arc::clone(k), eval_to_core(v)?);
|
|
33
|
+
}
|
|
34
|
+
let symbols = if let Some(ss) = &b.symbols {
|
|
35
|
+
let mut sm = AHashMap::default();
|
|
36
|
+
for (id, v) in ss.iter() {
|
|
37
|
+
sm.insert(*id, eval_to_core(v)?);
|
|
38
|
+
}
|
|
39
|
+
Some(sm)
|
|
40
|
+
} else {
|
|
41
|
+
None
|
|
42
|
+
};
|
|
43
|
+
Ok(CoreValue::Object(VmRef::new(ObjectData { strings, symbols })))
|
|
44
|
+
}
|
|
45
|
+
Value::Symbol(s) => Ok(CoreValue::Symbol(Arc::clone(s))),
|
|
46
|
+
Value::Opaque(o) => Ok(CoreValue::Opaque(Arc::clone(o))),
|
|
47
|
+
_ => Err(format!(
|
|
48
|
+
"Cannot pass {:?} to native function (unsupported type)",
|
|
49
|
+
std::mem::discriminant(v)
|
|
50
|
+
)),
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// Convert core Value to interpreter Value.
|
|
55
|
+
pub fn core_to_eval(v: CoreValue) -> Value {
|
|
56
|
+
match v {
|
|
57
|
+
CoreValue::Number(n) => Value::Number(n),
|
|
58
|
+
CoreValue::String(s) => Value::String(Arc::from(s.as_str())),
|
|
59
|
+
CoreValue::Bool(b) => Value::Bool(b),
|
|
60
|
+
CoreValue::Null => Value::Null,
|
|
61
|
+
CoreValue::Array(arr) => {
|
|
62
|
+
let mut out = Vec::new();
|
|
63
|
+
for item in arr.borrow().iter() {
|
|
64
|
+
out.push(core_to_eval(item.clone()));
|
|
65
|
+
}
|
|
66
|
+
Value::Array(Rc::new(RefCell::new(out)))
|
|
67
|
+
}
|
|
68
|
+
CoreValue::Object(map) => {
|
|
69
|
+
let b = map.borrow();
|
|
70
|
+
let mut out = PropMap::default();
|
|
71
|
+
for (k, v) in b.strings.iter() {
|
|
72
|
+
out.insert(Arc::clone(k), core_to_eval(v.clone()));
|
|
73
|
+
}
|
|
74
|
+
let mut eod = EvalObjectData::from_strings(out);
|
|
75
|
+
if let Some(ss) = &b.symbols {
|
|
76
|
+
let mut es = AHashMap::default();
|
|
77
|
+
for (id, v) in ss.iter() {
|
|
78
|
+
es.insert(*id, core_to_eval(v.clone()));
|
|
79
|
+
}
|
|
80
|
+
eod.symbols = Some(es);
|
|
81
|
+
}
|
|
82
|
+
Value::Object(Rc::new(RefCell::new(eod)))
|
|
83
|
+
}
|
|
84
|
+
CoreValue::Symbol(s) => Value::Symbol(Arc::clone(&s)),
|
|
85
|
+
CoreValue::Opaque(o) => Value::Opaque(o),
|
|
86
|
+
#[cfg(feature = "http")]
|
|
87
|
+
CoreValue::Promise(p) => Value::CorePromise(Arc::clone(&p)),
|
|
88
|
+
#[cfg(not(feature = "http"))]
|
|
89
|
+
CoreValue::Promise(_) => Value::Null,
|
|
90
|
+
// NumberArray: materialize to boxed Array for the interpreter (it has no packed path).
|
|
91
|
+
CoreValue::NumberArray(arr) => {
|
|
92
|
+
let nums = arr.borrow();
|
|
93
|
+
let out: Vec<Value> = nums.iter().map(|&n| Value::Number(n)).collect();
|
|
94
|
+
Value::Array(Rc::new(RefCell::new(out)))
|
|
95
|
+
}
|
|
96
|
+
// `CoreNativeFn` is feature-gated (Rc vs Arc), so use Clone::clone
|
|
97
|
+
// which works for either.
|
|
98
|
+
CoreValue::Function(f) => Value::CoreFn(f.clone()),
|
|
99
|
+
// tishlang_core gets regex from http or regex features; handle RegExp when it exists
|
|
100
|
+
#[cfg(any(feature = "http", feature = "regex"))]
|
|
101
|
+
CoreValue::RegExp(re) => {
|
|
102
|
+
#[cfg(feature = "regex")]
|
|
103
|
+
{
|
|
104
|
+
// Core uses `VmRef<TishRegExp>` (potentially `Arc<Mutex>`),
|
|
105
|
+
// interpreter uses `Rc<RefCell<TishRegExp>>`. Clone the
|
|
106
|
+
// inner state across so the two storage shapes can coexist.
|
|
107
|
+
let inner: TishRegExp = re.borrow().clone();
|
|
108
|
+
Value::RegExp(Rc::new(RefCell::new(inner)))
|
|
109
|
+
}
|
|
110
|
+
#[cfg(not(feature = "regex"))]
|
|
111
|
+
{
|
|
112
|
+
let _ = re;
|
|
113
|
+
Value::Null
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "tishlang_ffi"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
description = "Stable C ABI for tish native extensions — opaque value handles + extern \"C\" accessors so a cdylib (native) or wasm host-import module can interoperate with any tish backend without sharing tish_core's Rust types. Workstream B (full-backend-parity-plan.md)."
|
|
6
|
+
|
|
7
|
+
license-file = { workspace = true }
|
|
8
|
+
repository = { workspace = true }
|
|
9
|
+
|
|
10
|
+
[features]
|
|
11
|
+
default = []
|
|
12
|
+
# An extension that LINKS tishlang_core must match the host's value representation/layout exactly.
|
|
13
|
+
# `send-values` flips arrays/objects to Arc<Mutex<…>>; `regex` adds the `Value::RegExp` variant
|
|
14
|
+
# (which changes the enum's niche/discriminant layout). The shipped `tish` (default="full") enables
|
|
15
|
+
# both, so a linked extension targeting it must too. (The decoupled, host-exported-accessor model
|
|
16
|
+
# avoids all of this — there is then a single tish_core.)
|
|
17
|
+
send-values = ["tishlang_core/send-values"]
|
|
18
|
+
regex = ["tishlang_core/regex"]
|
|
19
|
+
|
|
20
|
+
[dependencies]
|
|
21
|
+
tishlang_core = { path = "../tish_core", version = ">=0.1" }
|
|
22
|
+
|
|
23
|
+
# Native cdylib loading (B2). Not for wasm — there is no dlopen in wasm; wasi resolves the SAME
|
|
24
|
+
# handle+accessor contract via host imports (B4), so the loader is gated to non-wasm targets.
|
|
25
|
+
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
|
26
|
+
libloading = "0.8"
|