@tishlang/tish 1.13.1 → 2.0.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/bin/tish +0 -0
- package/crates/js_to_tish/src/transform/expr.rs +1 -0
- package/crates/tish/Cargo.toml +11 -3
- package/crates/tish/build.rs +21 -0
- package/crates/tish/src/cli_help.rs +15 -4
- package/crates/tish/src/main.rs +93 -21
- package/crates/tish/src/repl_completion.rs +0 -1
- package/crates/tish/tests/error_source_location.rs +36 -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 +402 -91
- package/crates/tish/tests/trycatch_runtime_errors.rs +45 -0
- package/crates/tish/tests/tty_capability.rs +43 -0
- package/crates/tish_ast/src/ast.rs +37 -8
- package/crates/tish_builtins/Cargo.toml +2 -0
- package/crates/tish_builtins/src/array.rs +375 -13
- package/crates/tish_builtins/src/collections.rs +481 -0
- package/crates/tish_builtins/src/construct.rs +59 -19
- package/crates/tish_builtins/src/date.rs +538 -0
- package/crates/tish_builtins/src/globals.rs +86 -6
- package/crates/tish_builtins/src/iterator.rs +129 -0
- package/crates/tish_builtins/src/lib.rs +5 -0
- package/crates/tish_builtins/src/number.rs +96 -0
- package/crates/tish_builtins/src/object.rs +2 -2
- package/crates/tish_builtins/src/string.rs +19 -20
- package/crates/tish_builtins/src/symbol.rs +1 -1
- package/crates/tish_builtins/src/typedarrays.rs +298 -0
- package/crates/tish_bytecode/src/chunk.rs +69 -1
- package/crates/tish_bytecode/src/compiler.rs +933 -89
- package/crates/tish_bytecode/src/encoding.rs +2 -0
- package/crates/tish_bytecode/src/lib.rs +2 -1
- package/crates/tish_bytecode/src/opcode.rs +47 -4
- package/crates/tish_bytecode/src/serialize.rs +31 -1
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/check.rs +774 -0
- package/crates/tish_compile/src/codegen.rs +2334 -349
- package/crates/tish_compile/src/infer.rs +1395 -6
- package/crates/tish_compile/src/lib.rs +50 -8
- package/crates/tish_compile/src/resolve.rs +584 -21
- package/crates/tish_compile/src/types.rs +106 -2
- package/crates/tish_compile_js/src/codegen.rs +67 -0
- package/crates/tish_compile_js/src/tests_jsx.rs +64 -0
- package/crates/tish_core/Cargo.toml +7 -1
- package/crates/tish_core/src/console_style.rs +11 -1
- package/crates/tish_core/src/json.rs +81 -38
- package/crates/tish_core/src/lib.rs +3 -0
- package/crates/tish_core/src/shape.rs +85 -0
- package/crates/tish_core/src/value.rs +679 -25
- package/crates/tish_core/src/vmref.rs +13 -8
- package/crates/tish_cranelift/src/link.rs +17 -4
- package/crates/tish_cranelift_runtime/Cargo.toml +1 -0
- package/crates/tish_eval/Cargo.toml +6 -0
- package/crates/tish_eval/src/eval.rs +665 -117
- package/crates/tish_eval/src/http.rs +4 -1
- package/crates/tish_eval/src/natives.rs +165 -13
- package/crates/tish_eval/src/value.rs +31 -13
- package/crates/tish_eval/src/value_convert.rs +10 -4
- 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/src/lib.rs +43 -5
- package/crates/tish_lexer/src/lib.rs +397 -9
- package/crates/tish_lexer/src/token.rs +7 -0
- package/crates/tish_lint/src/lib.rs +2 -10
- package/crates/tish_lsp/src/import_goto.rs +2 -0
- package/crates/tish_lsp/src/main.rs +439 -26
- package/crates/tish_native/src/build.rs +55 -1
- package/crates/tish_opt/src/lib.rs +126 -23
- package/crates/tish_parser/src/lib.rs +55 -1
- package/crates/tish_parser/src/parser.rs +456 -34
- package/crates/tish_pg/src/lib.rs +3 -3
- package/crates/tish_resolve/src/lib.rs +99 -59
- package/crates/tish_runtime/Cargo.toml +4 -0
- package/crates/tish_runtime/src/http.rs +66 -17
- package/crates/tish_runtime/src/http_fetch.rs +29 -8
- package/crates/tish_runtime/src/http_hyper.rs +25 -2
- package/crates/tish_runtime/src/lib.rs +299 -44
- package/crates/tish_runtime/src/promise.rs +328 -18
- package/crates/tish_runtime/src/timers.rs +13 -7
- package/crates/tish_runtime/src/tty.rs +226 -0
- package/crates/tish_runtime/src/ws.rs +35 -18
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +2 -2
- package/crates/tish_ui/src/jsx.rs +10 -0
- package/crates/tish_ui/src/runtime/hooks.rs +19 -15
- package/crates/tish_ui/src/runtime/mod.rs +15 -12
- package/crates/tish_vm/Cargo.toml +14 -1
- package/crates/tish_vm/src/jit.rs +1050 -0
- package/crates/tish_vm/src/lib.rs +2 -0
- package/crates/tish_vm/src/vm.rs +1546 -202
- package/crates/tish_vm/tests/concurrent_shared_state.rs +140 -0
- package/crates/tish_wasm/src/lib.rs +6 -2
- package/crates/tish_wasm_runtime/src/gpu.rs +17 -1
- package/crates/tishlang_cargo_bindgen/src/classify.rs +1 -3
- package/crates/tishlang_cargo_bindgen/src/lib.rs +2 -2
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +1 -1
- 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
|
@@ -103,10 +103,13 @@ pub fn value_to_response(value: &Value) -> (u16, Vec<(String, String)>, String)
|
|
|
103
103
|
(status, headers, body)
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
/// `(status, headers, file path)` extracted from a response object's `file` key.
|
|
107
|
+
type FileResponse = (u16, Vec<(String, String)>, String);
|
|
108
|
+
|
|
106
109
|
/// If the response value has a `file` key, stream that path (binary-safe). Matches `tishlang_runtime` HTTP behavior.
|
|
107
110
|
pub(crate) fn extract_file_from_response(
|
|
108
111
|
value: &Value,
|
|
109
|
-
) -> Option<
|
|
112
|
+
) -> Option<FileResponse> {
|
|
110
113
|
let Value::Object(obj) = value else {
|
|
111
114
|
return None;
|
|
112
115
|
};
|
|
@@ -80,8 +80,8 @@ pub fn parse_int(args: &[Value]) -> Result<Value, String> {
|
|
|
80
80
|
|
|
81
81
|
pub fn parse_float(args: &[Value]) -> Result<Value, String> {
|
|
82
82
|
let s = args.first().map(|v| v.to_string()).unwrap_or_default();
|
|
83
|
-
|
|
84
|
-
Ok(Value::Number(
|
|
83
|
+
// JS parseFloat parses the longest leading numeric prefix (issue #36); shared with VM/native.
|
|
84
|
+
Ok(Value::Number(tishlang_builtins::globals::js_parse_float(&s)))
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
pub fn is_finite(args: &[Value]) -> Result<Value, String> {
|
|
@@ -215,10 +215,9 @@ pub fn math_round(args: &[Value]) -> Result<Value, String> {
|
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
pub fn math_random(_args: &[Value]) -> Result<Value, String> {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
Ok(Value::Number(random))
|
|
218
|
+
// Match the VM / builtins (`rand::random::<f64>()`) — uniform [0,1). The old
|
|
219
|
+
// RandomState-hash was non-uniform and diverged from every other backend.
|
|
220
|
+
Ok(Value::Number(rand::random::<f64>()))
|
|
222
221
|
}
|
|
223
222
|
|
|
224
223
|
pub fn math_pow(args: &[Value]) -> Result<Value, String> {
|
|
@@ -277,19 +276,94 @@ pub fn math_trunc(args: &[Value]) -> Result<Value, String> {
|
|
|
277
276
|
))
|
|
278
277
|
}
|
|
279
278
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
279
|
+
// Hyperbolic / inverse-hyperbolic / cbrt / base-2/10 logs — these were missing from the
|
|
280
|
+
// interpreter's `Math`, so they returned "Not a function" (issue #61, and an interp↔VM
|
|
281
|
+
// divergence under #67). One macro per unary `f64` method keeps them in lockstep with the VM.
|
|
282
|
+
macro_rules! math_unary {
|
|
283
|
+
($name:ident, $method:ident) => {
|
|
284
|
+
pub fn $name(args: &[Value]) -> Result<Value, String> {
|
|
285
|
+
Ok(Value::Number(
|
|
286
|
+
get_num(args.first().unwrap_or(&Value::Null)).$method(),
|
|
287
|
+
))
|
|
288
|
+
}
|
|
289
|
+
};
|
|
287
290
|
}
|
|
291
|
+
math_unary!(math_sinh, sinh);
|
|
292
|
+
math_unary!(math_cosh, cosh);
|
|
293
|
+
math_unary!(math_tanh, tanh);
|
|
294
|
+
math_unary!(math_asinh, asinh);
|
|
295
|
+
math_unary!(math_acosh, acosh);
|
|
296
|
+
math_unary!(math_atanh, atanh);
|
|
297
|
+
math_unary!(math_cbrt, cbrt);
|
|
298
|
+
math_unary!(math_log2, log2);
|
|
299
|
+
math_unary!(math_log10, log10);
|
|
288
300
|
|
|
289
301
|
pub fn array_is_array(args: &[Value]) -> Result<Value, String> {
|
|
290
302
|
Ok(Value::Bool(matches!(args.first(), Some(Value::Array(_)))))
|
|
291
303
|
}
|
|
292
304
|
|
|
305
|
+
/// Build a JS-style error object `{ name, message }` for the interpreter (issue #60).
|
|
306
|
+
fn make_error_obj(name: &str, args: &[Value]) -> Value {
|
|
307
|
+
let message = args.first().map(|v| v.to_string()).unwrap_or_default();
|
|
308
|
+
let mut m = crate::value::PropMap::with_capacity(2);
|
|
309
|
+
m.insert("name".into(), Value::String(name.into()));
|
|
310
|
+
m.insert("message".into(), Value::String(message.into()));
|
|
311
|
+
Value::object(m)
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
pub fn error_construct(args: &[Value]) -> Result<Value, String> {
|
|
315
|
+
Ok(make_error_obj("Error", args))
|
|
316
|
+
}
|
|
317
|
+
pub fn type_error_construct(args: &[Value]) -> Result<Value, String> {
|
|
318
|
+
Ok(make_error_obj("TypeError", args))
|
|
319
|
+
}
|
|
320
|
+
pub fn range_error_construct(args: &[Value]) -> Result<Value, String> {
|
|
321
|
+
Ok(make_error_obj("RangeError", args))
|
|
322
|
+
}
|
|
323
|
+
pub fn syntax_error_construct(args: &[Value]) -> Result<Value, String> {
|
|
324
|
+
Ok(make_error_obj("SyntaxError", args))
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/// `Array(...)` / `new Array(...)` (issue #72) — mirrors `tishlang_builtins::construct::
|
|
328
|
+
/// array_construct` for the interpreter's `Value`. A single non-negative integer is a length
|
|
329
|
+
/// (null holes); other args become the array's elements.
|
|
330
|
+
pub fn array_construct(args: &[Value]) -> Result<Value, String> {
|
|
331
|
+
if let [Value::Number(n)] = args {
|
|
332
|
+
let n = *n;
|
|
333
|
+
if n >= 0.0 && n.fract() == 0.0 && n <= 4_294_967_295.0 {
|
|
334
|
+
return Ok(Value::array(vec![Value::Null; n as usize]));
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
Ok(Value::array(args.to_vec()))
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/// `String(value)` as a function: JS `ToString` coercion (arrays comma-join recursively,
|
|
341
|
+
/// objects → "[object Object]", null → "null"), matching the VM/native `string_convert`.
|
|
342
|
+
pub fn string_convert(args: &[Value]) -> Result<Value, String> {
|
|
343
|
+
let v = args.first().unwrap_or(&Value::Null);
|
|
344
|
+
Ok(Value::String(v.to_js_string().into()))
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/// `Number(value)` coercion (issue #36) — shares the string parser with the VM/native
|
|
348
|
+
/// backend so the result is byte-identical.
|
|
349
|
+
pub fn number_convert(args: &[Value]) -> Result<Value, String> {
|
|
350
|
+
let v = args.first().unwrap_or(&Value::Null);
|
|
351
|
+
let n = match v {
|
|
352
|
+
Value::Number(n) => *n,
|
|
353
|
+
Value::Bool(b) => {
|
|
354
|
+
if *b {
|
|
355
|
+
1.0
|
|
356
|
+
} else {
|
|
357
|
+
0.0
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
Value::Null => 0.0,
|
|
361
|
+
Value::String(s) => tishlang_builtins::globals::parse_numeric_string(s),
|
|
362
|
+
other => tishlang_builtins::globals::parse_numeric_string(&other.to_js_string()),
|
|
363
|
+
};
|
|
364
|
+
Ok(Value::Number(n))
|
|
365
|
+
}
|
|
366
|
+
|
|
293
367
|
pub fn string_from_char_code(args: &[Value]) -> Result<Value, String> {
|
|
294
368
|
let s: String = args
|
|
295
369
|
.iter()
|
|
@@ -397,3 +471,81 @@ pub fn mkdir(args: &[Value]) -> Result<Value, String> {
|
|
|
397
471
|
};
|
|
398
472
|
Ok(Value::Bool(result.is_ok()))
|
|
399
473
|
}
|
|
474
|
+
|
|
475
|
+
// ── Interactive terminal I/O (issue #101), behind the `tty` feature ──────────────────────
|
|
476
|
+
// Build the interpreter's `Value` from the shared, Value-agnostic core in
|
|
477
|
+
// `tishlang_runtime::tty`, so the interpreter, VM, and native backends behave identically.
|
|
478
|
+
|
|
479
|
+
#[cfg(feature = "tty")]
|
|
480
|
+
fn tty_obj(pairs: Vec<(&str, Value)>) -> Value {
|
|
481
|
+
let mut m = crate::value::PropMap::with_capacity(pairs.len());
|
|
482
|
+
for (k, v) in pairs {
|
|
483
|
+
m.insert(k.into(), v);
|
|
484
|
+
}
|
|
485
|
+
Value::object(m)
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
#[cfg(feature = "tty")]
|
|
489
|
+
pub fn tty_size(_args: &[Value]) -> Result<Value, String> {
|
|
490
|
+
Ok(match tishlang_runtime::tty::size() {
|
|
491
|
+
Some((cols, rows)) => tty_obj(vec![
|
|
492
|
+
("cols", Value::Number(cols as f64)),
|
|
493
|
+
("rows", Value::Number(rows as f64)),
|
|
494
|
+
]),
|
|
495
|
+
None => Value::Null,
|
|
496
|
+
})
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
#[cfg(feature = "tty")]
|
|
500
|
+
pub fn tty_is_tty(_args: &[Value]) -> Result<Value, String> {
|
|
501
|
+
Ok(Value::Bool(tishlang_runtime::tty::is_tty()))
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
#[cfg(feature = "tty")]
|
|
505
|
+
pub fn tty_set_raw_mode(args: &[Value]) -> Result<Value, String> {
|
|
506
|
+
let on = args.first().map(|v| v.is_truthy()).unwrap_or(false);
|
|
507
|
+
Ok(Value::Bool(tishlang_runtime::tty::set_raw_mode(on)))
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
#[cfg(feature = "tty")]
|
|
511
|
+
pub fn tty_enter_alt_screen(_args: &[Value]) -> Result<Value, String> {
|
|
512
|
+
Ok(Value::Bool(tishlang_runtime::tty::enter_alt_screen()))
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
#[cfg(feature = "tty")]
|
|
516
|
+
pub fn tty_leave_alt_screen(_args: &[Value]) -> Result<Value, String> {
|
|
517
|
+
Ok(Value::Bool(tishlang_runtime::tty::leave_alt_screen()))
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
#[cfg(feature = "tty")]
|
|
521
|
+
pub fn tty_read_line(_args: &[Value]) -> Result<Value, String> {
|
|
522
|
+
Ok(match tishlang_runtime::tty::read_line() {
|
|
523
|
+
Some(s) => Value::String(s.into()),
|
|
524
|
+
None => Value::Null,
|
|
525
|
+
})
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
#[cfg(feature = "tty")]
|
|
529
|
+
pub fn tty_read(args: &[Value]) -> Result<Value, String> {
|
|
530
|
+
use tishlang_runtime::tty::TtyEvent;
|
|
531
|
+
let timeout = match args.first() {
|
|
532
|
+
Some(Value::Number(ms)) => Some(ms.max(0.0) as u64),
|
|
533
|
+
_ => None,
|
|
534
|
+
};
|
|
535
|
+
Ok(match tishlang_runtime::tty::read_event(timeout) {
|
|
536
|
+
Some(TtyEvent::Key { key, ctrl, alt, shift }) => tty_obj(vec![
|
|
537
|
+
("type", Value::String("key".into())),
|
|
538
|
+
("key", Value::String(key.into())),
|
|
539
|
+
("ctrl", Value::Bool(ctrl)),
|
|
540
|
+
("alt", Value::Bool(alt)),
|
|
541
|
+
("shift", Value::Bool(shift)),
|
|
542
|
+
]),
|
|
543
|
+
Some(TtyEvent::Resize { cols, rows }) => tty_obj(vec![
|
|
544
|
+
("type", Value::String("resize".into())),
|
|
545
|
+
("cols", Value::Number(cols as f64)),
|
|
546
|
+
("rows", Value::Number(rows as f64)),
|
|
547
|
+
]),
|
|
548
|
+
Some(TtyEvent::Other) => tty_obj(vec![("type", Value::String("other".into()))]),
|
|
549
|
+
None => Value::Null,
|
|
550
|
+
})
|
|
551
|
+
}
|
|
@@ -15,7 +15,9 @@ use tishlang_core::NativeFn as CoreNativeFn;
|
|
|
15
15
|
use tishlang_core::TishSymbol;
|
|
16
16
|
|
|
17
17
|
/// Property map for interpreter object string keys (uses `eval::Value`, not `tishlang_core::Value`).
|
|
18
|
-
|
|
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>;
|
|
19
21
|
|
|
20
22
|
/// Interpreter object: string keys plus optional symbol-keyed properties.
|
|
21
23
|
#[derive(Clone, Debug, Default)]
|
|
@@ -110,11 +112,14 @@ pub enum Value {
|
|
|
110
112
|
Array(Rc<RefCell<Vec<Value>>>),
|
|
111
113
|
Object(Rc<RefCell<EvalObjectData>>),
|
|
112
114
|
Symbol(Arc<TishSymbol>),
|
|
113
|
-
/// User-defined function with AST body
|
|
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.
|
|
114
118
|
Function {
|
|
115
119
|
formals: Arc<[FunParam]>,
|
|
116
120
|
rest_param: Option<Arc<str>>,
|
|
117
121
|
body: Arc<Statement>,
|
|
122
|
+
env: crate::eval::ScopeRef,
|
|
118
123
|
},
|
|
119
124
|
/// Native/builtin function
|
|
120
125
|
Native(NativeFn),
|
|
@@ -192,17 +197,9 @@ impl std::fmt::Debug for Value {
|
|
|
192
197
|
impl std::fmt::Display for Value {
|
|
193
198
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
194
199
|
match self {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
} else if *n == f64::INFINITY {
|
|
199
|
-
write!(f, "Infinity")
|
|
200
|
-
} else if *n == f64::NEG_INFINITY {
|
|
201
|
-
write!(f, "-Infinity")
|
|
202
|
-
} else {
|
|
203
|
-
write!(f, "{}", n)
|
|
204
|
-
}
|
|
205
|
-
}
|
|
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)),
|
|
206
203
|
Value::String(s) => write!(f, "{}", s),
|
|
207
204
|
Value::Bool(b) => write!(f, "{}", b),
|
|
208
205
|
Value::Null => write!(f, "null"),
|
|
@@ -265,6 +262,27 @@ impl Value {
|
|
|
265
262
|
}
|
|
266
263
|
}
|
|
267
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
|
+
|
|
268
286
|
pub fn strict_eq(&self, other: &Value) -> bool {
|
|
269
287
|
match (self, other) {
|
|
270
288
|
(Value::Number(a), Value::Number(b)) => {
|
|
@@ -7,7 +7,7 @@ use std::sync::Arc;
|
|
|
7
7
|
#[cfg(feature = "regex")]
|
|
8
8
|
use tishlang_core::TishRegExp;
|
|
9
9
|
use ahash::AHashMap;
|
|
10
|
-
use tishlang_core::{ObjectData,
|
|
10
|
+
use tishlang_core::{ObjectData, Value as CoreValue, VmRef};
|
|
11
11
|
|
|
12
12
|
use crate::value::{EvalObjectData, PropMap, Value};
|
|
13
13
|
|
|
@@ -15,7 +15,7 @@ use crate::value::{EvalObjectData, PropMap, Value};
|
|
|
15
15
|
pub fn eval_to_core(v: &Value) -> Result<CoreValue, String> {
|
|
16
16
|
match v {
|
|
17
17
|
Value::Number(n) => Ok(CoreValue::Number(*n)),
|
|
18
|
-
Value::String(s) => Ok(CoreValue::String(
|
|
18
|
+
Value::String(s) => Ok(CoreValue::String(tishlang_core::ArcStr::from(s.as_ref()))),
|
|
19
19
|
Value::Bool(b) => Ok(CoreValue::Bool(*b)),
|
|
20
20
|
Value::Null => Ok(CoreValue::Null),
|
|
21
21
|
Value::Array(arr) => {
|
|
@@ -27,7 +27,7 @@ pub fn eval_to_core(v: &Value) -> Result<CoreValue, String> {
|
|
|
27
27
|
}
|
|
28
28
|
Value::Object(map) => {
|
|
29
29
|
let b = map.borrow();
|
|
30
|
-
let mut strings =
|
|
30
|
+
let mut strings = tishlang_core::PropMap::default();
|
|
31
31
|
for (k, v) in b.strings.iter() {
|
|
32
32
|
strings.insert(Arc::clone(k), eval_to_core(v)?);
|
|
33
33
|
}
|
|
@@ -55,7 +55,7 @@ pub fn eval_to_core(v: &Value) -> Result<CoreValue, String> {
|
|
|
55
55
|
pub fn core_to_eval(v: CoreValue) -> Value {
|
|
56
56
|
match v {
|
|
57
57
|
CoreValue::Number(n) => Value::Number(n),
|
|
58
|
-
CoreValue::String(s) => Value::String(s),
|
|
58
|
+
CoreValue::String(s) => Value::String(Arc::from(s.as_str())),
|
|
59
59
|
CoreValue::Bool(b) => Value::Bool(b),
|
|
60
60
|
CoreValue::Null => Value::Null,
|
|
61
61
|
CoreValue::Array(arr) => {
|
|
@@ -87,6 +87,12 @@ pub fn core_to_eval(v: CoreValue) -> Value {
|
|
|
87
87
|
CoreValue::Promise(p) => Value::CorePromise(Arc::clone(&p)),
|
|
88
88
|
#[cfg(not(feature = "http"))]
|
|
89
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
|
+
}
|
|
90
96
|
// `CoreNativeFn` is feature-gated (Rc vs Arc), so use Clone::clone
|
|
91
97
|
// which works for either.
|
|
92
98
|
CoreValue::Function(f) => Value::CoreFn(f.clone()),
|
|
@@ -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"
|