@tishlang/tish 1.13.2 → 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 +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 +61 -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
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
//! JS-style iterator objects for `Map`/`Set` (`.values()` / `.keys()` / `.entries()`).
|
|
2
|
+
//!
|
|
3
|
+
//! Like [`crate::collections`] and [`crate::date`], an iterator is a plain `Value::Object`
|
|
4
|
+
//! whose `next` method is a per-instance `Value::native` closure capturing a snapshot of the
|
|
5
|
+
//! items plus a shared position cell — so the one implementation works on every backend with
|
|
6
|
+
//! no new `Value` variant. `next()` returns `{ value, done }`; once exhausted it keeps
|
|
7
|
+
//! returning `{ value: null, done: true }`.
|
|
8
|
+
//!
|
|
9
|
+
//! The runtimes drive iteration through [`tishlang_core::drain_iterator`], which calls `next()`
|
|
10
|
+
//! until `done` — that's how these objects work in `for…of`, spread, and `Array.from`. The
|
|
11
|
+
//! snapshot is taken when the iterator is built (`.values()` is called), matching how the old
|
|
12
|
+
//! array-returning version behaved; mid-iteration mutation of the source is not reflected (a
|
|
13
|
+
//! live iterator is a follow-up).
|
|
14
|
+
|
|
15
|
+
use std::sync::Arc;
|
|
16
|
+
|
|
17
|
+
use tishlang_core::{ObjectMap, Value, VmRef};
|
|
18
|
+
|
|
19
|
+
/// Build a single-use iterator object over `items`: `{ next() -> { value, done } }`.
|
|
20
|
+
pub fn array_iterator(items: Vec<Value>) -> Value {
|
|
21
|
+
let items: VmRef<Vec<Value>> = VmRef::new(items);
|
|
22
|
+
let pos: VmRef<usize> = VmRef::new(0);
|
|
23
|
+
|
|
24
|
+
let mut m = ObjectMap::default();
|
|
25
|
+
{
|
|
26
|
+
// Bulk-drain fast path for `for…of` / spread: return all REMAINING items (from the current
|
|
27
|
+
// position) as one array and exhaust the iterator — equivalent to calling `next()` until
|
|
28
|
+
// `done`, but with no per-element `{ value, done }` allocation. `drain_iterator` prefers this
|
|
29
|
+
// when present; manual `.next()` and partial consumption still work (it respects `pos`).
|
|
30
|
+
let items = items.clone();
|
|
31
|
+
let pos = pos.clone();
|
|
32
|
+
m.insert(
|
|
33
|
+
Arc::from("__drain__"),
|
|
34
|
+
Value::native(move |_args: &[Value]| {
|
|
35
|
+
let i = *pos.borrow();
|
|
36
|
+
let b = items.borrow();
|
|
37
|
+
let rest: Vec<Value> = if i < b.len() { b[i..].to_vec() } else { Vec::new() };
|
|
38
|
+
let len = b.len();
|
|
39
|
+
drop(b);
|
|
40
|
+
*pos.borrow_mut() = len;
|
|
41
|
+
Value::Array(VmRef::new(rest))
|
|
42
|
+
}),
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
{
|
|
46
|
+
let items = items.clone();
|
|
47
|
+
let pos = pos.clone();
|
|
48
|
+
m.insert(
|
|
49
|
+
Arc::from("next"),
|
|
50
|
+
Value::native(move |_args: &[Value]| {
|
|
51
|
+
let i = *pos.borrow();
|
|
52
|
+
// Read the element (or note exhaustion) without holding the items borrow
|
|
53
|
+
// across the position write — two different `VmRef`s, but keep it tidy.
|
|
54
|
+
let (value, done) = {
|
|
55
|
+
let b = items.borrow();
|
|
56
|
+
if i < b.len() {
|
|
57
|
+
(b[i].clone(), false)
|
|
58
|
+
} else {
|
|
59
|
+
(Value::Null, true)
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
if !done {
|
|
63
|
+
*pos.borrow_mut() = i + 1;
|
|
64
|
+
}
|
|
65
|
+
let mut r = ObjectMap::default();
|
|
66
|
+
r.insert(Arc::from("value"), value);
|
|
67
|
+
r.insert(Arc::from("done"), Value::Bool(done));
|
|
68
|
+
Value::object(r)
|
|
69
|
+
}),
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
Value::object(m)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#[cfg(test)]
|
|
76
|
+
mod tests {
|
|
77
|
+
use super::*;
|
|
78
|
+
|
|
79
|
+
fn call(obj: &Value, name: &str) -> Value {
|
|
80
|
+
let Value::Object(o) = obj else { panic!("not an object") };
|
|
81
|
+
let m = o.borrow().strings.get(name).cloned().expect("method missing");
|
|
82
|
+
let Value::Function(f) = m else { panic!("{name} is not callable") };
|
|
83
|
+
f.call(&[])
|
|
84
|
+
}
|
|
85
|
+
fn get(obj: &Value, key: &str) -> Value {
|
|
86
|
+
let Value::Object(o) = obj else { return Value::Null };
|
|
87
|
+
o.borrow().strings.get(key).cloned().unwrap_or(Value::Null)
|
|
88
|
+
}
|
|
89
|
+
fn num(v: &Value) -> f64 {
|
|
90
|
+
match v {
|
|
91
|
+
Value::Number(n) => *n,
|
|
92
|
+
_ => f64::NAN,
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
fn nums(v: &Value) -> Vec<f64> {
|
|
96
|
+
match v {
|
|
97
|
+
Value::Array(a) => a.borrow().iter().map(num).collect(),
|
|
98
|
+
_ => vec![],
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
#[test]
|
|
103
|
+
fn next_yields_each_then_done() {
|
|
104
|
+
let it = array_iterator(vec![Value::Number(1.0), Value::Number(2.0)]);
|
|
105
|
+
let r1 = call(&it, "next");
|
|
106
|
+
assert_eq!(num(&get(&r1, "value")), 1.0);
|
|
107
|
+
assert!(!get(&r1, "done").is_truthy());
|
|
108
|
+
assert_eq!(num(&get(&call(&it, "next"), "value")), 2.0);
|
|
109
|
+
// exhausted — and it keeps reporting done.
|
|
110
|
+
assert!(get(&call(&it, "next"), "done").is_truthy());
|
|
111
|
+
assert!(get(&call(&it, "next"), "done").is_truthy());
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
#[test]
|
|
115
|
+
fn drain_returns_all_and_exhausts() {
|
|
116
|
+
let it = array_iterator(vec![Value::Number(1.0), Value::Number(2.0), Value::Number(3.0)]);
|
|
117
|
+
assert_eq!(nums(&call(&it, "__drain__")), vec![1.0, 2.0, 3.0]);
|
|
118
|
+
// draining exhausts the iterator (matches calling next() until done).
|
|
119
|
+
assert!(get(&call(&it, "next"), "done").is_truthy());
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
#[test]
|
|
123
|
+
fn drain_respects_current_position() {
|
|
124
|
+
let it = array_iterator(vec![Value::Number(1.0), Value::Number(2.0), Value::Number(3.0)]);
|
|
125
|
+
call(&it, "next"); // consume the first
|
|
126
|
+
assert_eq!(nums(&call(&it, "__drain__")), vec![2.0, 3.0]);
|
|
127
|
+
assert_eq!(nums(&call(&it, "__drain__")), Vec::<f64>::new()); // already drained
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -5,12 +5,17 @@
|
|
|
5
5
|
//! and native signatures.
|
|
6
6
|
|
|
7
7
|
pub mod array;
|
|
8
|
+
pub mod collections;
|
|
8
9
|
pub mod construct;
|
|
10
|
+
pub mod date;
|
|
9
11
|
pub mod globals;
|
|
10
12
|
pub mod helpers;
|
|
13
|
+
pub mod iterator;
|
|
11
14
|
pub mod math;
|
|
15
|
+
pub mod number;
|
|
12
16
|
pub mod object;
|
|
13
17
|
pub mod string;
|
|
14
18
|
pub mod symbol;
|
|
19
|
+
pub mod typedarrays;
|
|
15
20
|
|
|
16
21
|
pub use tishlang_core::Value;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
//! Number builtin methods.
|
|
2
|
+
//!
|
|
3
|
+
//! Canonical, backend-agnostic implementations of `Number.prototype` methods.
|
|
4
|
+
//! The VM (`get_member`), the Rust runtime (`tishlang_runtime::number_to_fixed`),
|
|
5
|
+
//! and the tree-walk interpreter all route through here so every backend produces
|
|
6
|
+
//! byte-identical output — see `tish/docs/full-backend-parity-plan.md` (Workstream A).
|
|
7
|
+
|
|
8
|
+
use tishlang_core::Value;
|
|
9
|
+
|
|
10
|
+
/// `Number.prototype.toFixed(digits)` — ECMA-262 §21.1.3.3.
|
|
11
|
+
///
|
|
12
|
+
/// Formats the number using fixed-point notation with `digits` fraction digits.
|
|
13
|
+
/// `digits` is clamped to 0–20 (ECMA range) and defaults to 0 when absent/non-numeric,
|
|
14
|
+
/// matching `(1.5).toFixed() === "2"`. A non-number receiver yields `"NaN"`.
|
|
15
|
+
pub fn to_fixed(n: &Value, digits: &Value) -> Value {
|
|
16
|
+
let num = match n {
|
|
17
|
+
Value::Number(x) => *x,
|
|
18
|
+
_ => f64::NAN,
|
|
19
|
+
};
|
|
20
|
+
let d = match digits {
|
|
21
|
+
Value::Number(x) => (*x as i32).clamp(0, 20),
|
|
22
|
+
_ => 0,
|
|
23
|
+
} as usize;
|
|
24
|
+
Value::String(format!("{:.*}", d, num).into())
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// `Number.prototype.toString([radix])` — ECMA-262 §21.1.3.6.
|
|
28
|
+
///
|
|
29
|
+
/// Radix defaults to 10 (canonical JS number formatting). For radix 2–36 the value is
|
|
30
|
+
/// rendered in that base: sign, integer part via repeated division, and a fractional part
|
|
31
|
+
/// (bounded to 52 digits, like V8). NaN / ±Infinity stringify as in base 10 regardless of
|
|
32
|
+
/// radix. An out-of-range radix yields `"RadixError"` so the caller can surface a RangeError.
|
|
33
|
+
pub fn to_string(n: &Value, radix: &Value) -> Value {
|
|
34
|
+
let num = match n {
|
|
35
|
+
Value::Number(x) => *x,
|
|
36
|
+
_ => f64::NAN,
|
|
37
|
+
};
|
|
38
|
+
let r = match radix {
|
|
39
|
+
Value::Number(x) => *x as i64,
|
|
40
|
+
_ => 10,
|
|
41
|
+
};
|
|
42
|
+
match number_to_string_radix(num, r) {
|
|
43
|
+
Some(s) => Value::String(s.into()),
|
|
44
|
+
None => Value::String("RadixError".into()),
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// Backend-agnostic core of `Number.prototype.toString`: works on a plain `f64` so the
|
|
49
|
+
/// tree-walk interpreter (whose `Value` is a distinct type) can share the exact same
|
|
50
|
+
/// formatting. Returns `None` for an out-of-range radix (caller surfaces a RangeError).
|
|
51
|
+
pub fn number_to_string_radix(num: f64, radix: i64) -> Option<String> {
|
|
52
|
+
if !(2..=36).contains(&radix) {
|
|
53
|
+
return None;
|
|
54
|
+
}
|
|
55
|
+
if radix == 10 || num.is_nan() || num.is_infinite() {
|
|
56
|
+
return Some(tishlang_core::js_number_to_string(num));
|
|
57
|
+
}
|
|
58
|
+
let radix = radix as u32;
|
|
59
|
+
const DIGITS: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz";
|
|
60
|
+
let negative = num < 0.0;
|
|
61
|
+
let value = num.abs();
|
|
62
|
+
let int_part = value.trunc();
|
|
63
|
+
let mut frac = value - int_part;
|
|
64
|
+
|
|
65
|
+
// Integer part: collect base-`radix` digits least-significant first, then reverse.
|
|
66
|
+
let mut int_digits = Vec::new();
|
|
67
|
+
let mut i = int_part;
|
|
68
|
+
if i == 0.0 {
|
|
69
|
+
int_digits.push(b'0');
|
|
70
|
+
}
|
|
71
|
+
while i >= 1.0 {
|
|
72
|
+
let d = (i % radix as f64) as usize;
|
|
73
|
+
int_digits.push(DIGITS[d]);
|
|
74
|
+
i = (i / radix as f64).trunc();
|
|
75
|
+
}
|
|
76
|
+
int_digits.reverse();
|
|
77
|
+
let mut out = String::with_capacity(int_digits.len() + 2);
|
|
78
|
+
if negative {
|
|
79
|
+
out.push('-');
|
|
80
|
+
}
|
|
81
|
+
out.push_str(std::str::from_utf8(&int_digits).unwrap());
|
|
82
|
+
|
|
83
|
+
// Fractional part: multiply-by-radix, emitting the integer overflow each step.
|
|
84
|
+
if frac > 0.0 {
|
|
85
|
+
out.push('.');
|
|
86
|
+
let mut count = 0;
|
|
87
|
+
while frac > 0.0 && count < 52 {
|
|
88
|
+
frac *= radix as f64;
|
|
89
|
+
let d = frac.trunc() as usize;
|
|
90
|
+
out.push(DIGITS[d] as char);
|
|
91
|
+
frac -= frac.trunc();
|
|
92
|
+
count += 1;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
Some(out)
|
|
96
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
//! Functions will be migrated here from tishlang_runtime and tishlang_eval.
|
|
5
5
|
|
|
6
6
|
use std::sync::Arc;
|
|
7
|
-
use tishlang_core::{ObjectData,
|
|
7
|
+
use tishlang_core::{ObjectData, PropMap, Value, VmRef};
|
|
8
8
|
|
|
9
9
|
/// Create a new empty object Value.
|
|
10
10
|
pub fn new() -> Value {
|
|
@@ -14,7 +14,7 @@ pub fn new() -> Value {
|
|
|
14
14
|
/// Create a new object Value with a given capacity.
|
|
15
15
|
pub fn with_capacity(capacity: usize) -> Value {
|
|
16
16
|
Value::Object(VmRef::new(ObjectData {
|
|
17
|
-
strings:
|
|
17
|
+
strings: PropMap::with_capacity(capacity),
|
|
18
18
|
symbols: None,
|
|
19
19
|
}))
|
|
20
20
|
}
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
//! JavaScript, matching .length and .charAt(). Byte offsets are never exposed.
|
|
5
5
|
|
|
6
6
|
use crate::helpers::normalize_index;
|
|
7
|
-
use std::sync::Arc;
|
|
8
7
|
use tishlang_core::Value;
|
|
9
8
|
use tishlang_core::VmRef;
|
|
10
9
|
|
|
@@ -25,7 +24,7 @@ fn char_to_byte_offset(s: &str, char_index: usize) -> usize {
|
|
|
25
24
|
|
|
26
25
|
/// Create a new string Value from a string slice.
|
|
27
26
|
pub fn from_str(s: &str) -> Value {
|
|
28
|
-
Value::String(
|
|
27
|
+
Value::String(tishlang_core::ArcStr::from(s))
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
/// Get the length of a string (character count).
|
|
@@ -118,7 +117,7 @@ pub fn index_of(s: &Value, search: &Value, from: Option<&Value>) -> Value {
|
|
|
118
117
|
_ => 0,
|
|
119
118
|
};
|
|
120
119
|
let byte_start = char_to_byte_offset(s, from_char);
|
|
121
|
-
let search_str = search.
|
|
120
|
+
let search_str = search.as_str();
|
|
122
121
|
if let Some(byte_pos) = s[byte_start..].find(search_str) {
|
|
123
122
|
let char_idx = from_char + byte_to_char_index(&s[byte_start..], byte_pos);
|
|
124
123
|
Value::Number(char_idx as f64)
|
|
@@ -141,7 +140,7 @@ pub fn includes(s: &Value, search: &Value, from: Option<&Value>) -> Value {
|
|
|
141
140
|
_ => 0,
|
|
142
141
|
};
|
|
143
142
|
let byte_start = char_to_byte_offset(s, from_char);
|
|
144
|
-
Value::Bool(s[byte_start..].contains(search.
|
|
143
|
+
Value::Bool(s[byte_start..].contains(search.as_str()))
|
|
145
144
|
} else {
|
|
146
145
|
Value::Bool(false)
|
|
147
146
|
}
|
|
@@ -218,8 +217,8 @@ pub fn substr(s: &Value, start: &Value, length: &Value) -> Value {
|
|
|
218
217
|
pub fn split(s: &Value, sep: &Value) -> Value {
|
|
219
218
|
if let Value::String(s) = s {
|
|
220
219
|
let separator = match sep {
|
|
221
|
-
Value::String(ss) => ss.
|
|
222
|
-
_ => return Value::Array(VmRef::new(vec![Value::String(
|
|
220
|
+
Value::String(ss) => ss.as_str(),
|
|
221
|
+
_ => return Value::Array(VmRef::new(vec![Value::String(s.clone())])),
|
|
223
222
|
};
|
|
224
223
|
let parts: Vec<Value> = s
|
|
225
224
|
.split(separator)
|
|
@@ -257,7 +256,7 @@ pub fn to_lower_case(s: &Value) -> Value {
|
|
|
257
256
|
|
|
258
257
|
pub fn starts_with(s: &Value, search: &Value) -> Value {
|
|
259
258
|
if let (Value::String(s), Value::String(search)) = (s, search) {
|
|
260
|
-
Value::Bool(s.starts_with(search.
|
|
259
|
+
Value::Bool(s.starts_with(search.as_str()))
|
|
261
260
|
} else {
|
|
262
261
|
Value::Bool(false)
|
|
263
262
|
}
|
|
@@ -265,7 +264,7 @@ pub fn starts_with(s: &Value, search: &Value) -> Value {
|
|
|
265
264
|
|
|
266
265
|
pub fn ends_with(s: &Value, search: &Value) -> Value {
|
|
267
266
|
if let (Value::String(s), Value::String(search)) = (s, search) {
|
|
268
|
-
Value::Bool(s.ends_with(search.
|
|
267
|
+
Value::Bool(s.ends_with(search.as_str()))
|
|
269
268
|
} else {
|
|
270
269
|
Value::Bool(false)
|
|
271
270
|
}
|
|
@@ -274,11 +273,11 @@ pub fn ends_with(s: &Value, search: &Value) -> Value {
|
|
|
274
273
|
fn replace_impl(s: &Value, search: &Value, replacement: &Value, all: bool) -> Value {
|
|
275
274
|
if let Value::String(s) = s {
|
|
276
275
|
let search_str = match search {
|
|
277
|
-
Value::String(ss) => ss.
|
|
278
|
-
_ => return Value::String(
|
|
276
|
+
Value::String(ss) => ss.as_str(),
|
|
277
|
+
_ => return Value::String(s.clone()),
|
|
279
278
|
};
|
|
280
279
|
let repl_str = match replacement {
|
|
281
|
-
Value::String(ss) => ss.
|
|
280
|
+
Value::String(ss) => ss.as_str(),
|
|
282
281
|
_ => "",
|
|
283
282
|
};
|
|
284
283
|
let result = if all {
|
|
@@ -305,8 +304,8 @@ pub fn replace_all(s: &Value, search: &Value, replacement: &Value) -> Value {
|
|
|
305
304
|
/// character needs escaping. Matches TFB's fortunes verifier byte-for-byte.
|
|
306
305
|
pub fn escape_html(s: &Value) -> Value {
|
|
307
306
|
let input = match s {
|
|
308
|
-
Value::String(s) => s.
|
|
309
|
-
Value::Null => return Value::String(
|
|
307
|
+
Value::String(s) => s.as_str(),
|
|
308
|
+
Value::Null => return Value::String(tishlang_core::ArcStr::from("")),
|
|
310
309
|
_ => return Value::Null,
|
|
311
310
|
};
|
|
312
311
|
let bytes = input.as_bytes();
|
|
@@ -321,10 +320,10 @@ pub fn escape_html(s: &Value) -> Value {
|
|
|
321
320
|
}
|
|
322
321
|
}
|
|
323
322
|
if extra == 0 {
|
|
324
|
-
return Value::String(
|
|
325
|
-
Value::String(s) => s,
|
|
323
|
+
return Value::String(match s {
|
|
324
|
+
Value::String(s) => s.clone(),
|
|
326
325
|
_ => unreachable!(),
|
|
327
|
-
})
|
|
326
|
+
});
|
|
328
327
|
}
|
|
329
328
|
let mut out = String::with_capacity(input.len() + extra);
|
|
330
329
|
let mut last = 0usize;
|
|
@@ -344,7 +343,7 @@ pub fn escape_html(s: &Value) -> Value {
|
|
|
344
343
|
}
|
|
345
344
|
}
|
|
346
345
|
out.push_str(&input[last..]);
|
|
347
|
-
Value::String(
|
|
346
|
+
Value::String(tishlang_core::ArcStr::from(out))
|
|
348
347
|
}
|
|
349
348
|
|
|
350
349
|
fn char_at_idx(s: &str, idx: usize) -> Option<char> {
|
|
@@ -395,15 +394,15 @@ fn pad_impl(s: &Value, target_len: &Value, pad: &Value, at_start: bool) -> Value
|
|
|
395
394
|
if let Value::String(s) = s {
|
|
396
395
|
let target_len = match target_len {
|
|
397
396
|
Value::Number(n) => *n as usize,
|
|
398
|
-
_ => return Value::String(
|
|
397
|
+
_ => return Value::String(s.clone()),
|
|
399
398
|
};
|
|
400
399
|
let pad_str = match pad {
|
|
401
|
-
Value::String(p) if !p.is_empty() => p.
|
|
400
|
+
Value::String(p) if !p.is_empty() => p.as_str(),
|
|
402
401
|
_ => " ",
|
|
403
402
|
};
|
|
404
403
|
let char_count = s.chars().count();
|
|
405
404
|
if char_count >= target_len {
|
|
406
|
-
return Value::String(
|
|
405
|
+
return Value::String(s.clone());
|
|
407
406
|
}
|
|
408
407
|
let needed = target_len - char_count;
|
|
409
408
|
let padding: String = pad_str.chars().cycle().take(needed).collect();
|
|
@@ -44,7 +44,7 @@ fn symbol_key_for_impl(args: &[Value]) -> Value {
|
|
|
44
44
|
Some(Value::Symbol(s)) => s
|
|
45
45
|
.registry_key
|
|
46
46
|
.as_ref()
|
|
47
|
-
.map(|k| Value::String(
|
|
47
|
+
.map(|k| Value::String(tishlang_core::ArcStr::from(k.as_ref())))
|
|
48
48
|
.unwrap_or(Value::Null),
|
|
49
49
|
_ => Value::Null,
|
|
50
50
|
}
|