@tishlang/tish-format 1.0.13 → 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.
Files changed (108) hide show
  1. package/Cargo.toml +2 -0
  2. package/bin/tish-format +0 -0
  3. package/crates/js_to_tish/src/transform/expr.rs +1 -0
  4. package/crates/tish/Cargo.toml +10 -2
  5. package/crates/tish/build.rs +21 -0
  6. package/crates/tish/src/cli_help.rs +15 -4
  7. package/crates/tish/src/main.rs +93 -21
  8. package/crates/tish/src/repl_completion.rs +0 -1
  9. package/crates/tish/tests/error_source_location.rs +36 -0
  10. package/crates/tish/tests/fixtures/runtime_error_location.tish +5 -0
  11. package/crates/tish/tests/fixtures/trycatch_runtime_errors.tish +15 -0
  12. package/crates/tish/tests/fixtures/tty_capability.tish +9 -0
  13. package/crates/tish/tests/integration_test.rs +402 -91
  14. package/crates/tish/tests/trycatch_runtime_errors.rs +45 -0
  15. package/crates/tish/tests/tty_capability.rs +43 -0
  16. package/crates/tish_ast/src/ast.rs +37 -8
  17. package/crates/tish_builtins/Cargo.toml +2 -0
  18. package/crates/tish_builtins/src/array.rs +375 -13
  19. package/crates/tish_builtins/src/collections.rs +481 -0
  20. package/crates/tish_builtins/src/construct.rs +59 -19
  21. package/crates/tish_builtins/src/date.rs +538 -0
  22. package/crates/tish_builtins/src/globals.rs +86 -6
  23. package/crates/tish_builtins/src/iterator.rs +129 -0
  24. package/crates/tish_builtins/src/lib.rs +5 -0
  25. package/crates/tish_builtins/src/number.rs +96 -0
  26. package/crates/tish_builtins/src/object.rs +2 -2
  27. package/crates/tish_builtins/src/string.rs +19 -20
  28. package/crates/tish_builtins/src/symbol.rs +1 -1
  29. package/crates/tish_builtins/src/typedarrays.rs +298 -0
  30. package/crates/tish_bytecode/src/chunk.rs +69 -1
  31. package/crates/tish_bytecode/src/compiler.rs +933 -89
  32. package/crates/tish_bytecode/src/encoding.rs +2 -0
  33. package/crates/tish_bytecode/src/lib.rs +2 -1
  34. package/crates/tish_bytecode/src/opcode.rs +47 -4
  35. package/crates/tish_bytecode/src/serialize.rs +31 -1
  36. package/crates/tish_compile/Cargo.toml +1 -0
  37. package/crates/tish_compile/src/check.rs +774 -0
  38. package/crates/tish_compile/src/codegen.rs +2334 -349
  39. package/crates/tish_compile/src/infer.rs +1395 -6
  40. package/crates/tish_compile/src/lib.rs +50 -8
  41. package/crates/tish_compile/src/resolve.rs +584 -21
  42. package/crates/tish_compile/src/types.rs +106 -2
  43. package/crates/tish_compile_js/src/codegen.rs +67 -0
  44. package/crates/tish_compile_js/src/tests_jsx.rs +64 -0
  45. package/crates/tish_core/Cargo.toml +7 -1
  46. package/crates/tish_core/src/console_style.rs +11 -1
  47. package/crates/tish_core/src/json.rs +81 -38
  48. package/crates/tish_core/src/lib.rs +3 -0
  49. package/crates/tish_core/src/shape.rs +85 -0
  50. package/crates/tish_core/src/value.rs +679 -25
  51. package/crates/tish_core/src/vmref.rs +13 -8
  52. package/crates/tish_cranelift/src/link.rs +17 -4
  53. package/crates/tish_cranelift_runtime/Cargo.toml +1 -0
  54. package/crates/tish_eval/Cargo.toml +6 -0
  55. package/crates/tish_eval/src/eval.rs +665 -117
  56. package/crates/tish_eval/src/http.rs +4 -1
  57. package/crates/tish_eval/src/natives.rs +165 -13
  58. package/crates/tish_eval/src/value.rs +31 -13
  59. package/crates/tish_eval/src/value_convert.rs +10 -4
  60. package/crates/tish_ffi/Cargo.toml +26 -0
  61. package/crates/tish_ffi/src/lib.rs +518 -0
  62. package/crates/tish_ffi/tests/fixtures/testmod/Cargo.toml +18 -0
  63. package/crates/tish_ffi/tests/fixtures/testmod/src/lib.rs +46 -0
  64. package/crates/tish_ffi/tests/loader.rs +65 -0
  65. package/crates/tish_fmt/Cargo.toml +1 -1
  66. package/crates/tish_fmt/src/lib.rs +61 -5
  67. package/crates/tish_lexer/src/lib.rs +397 -9
  68. package/crates/tish_lexer/src/token.rs +7 -0
  69. package/crates/tish_lint/src/lib.rs +2 -10
  70. package/crates/tish_lsp/src/import_goto.rs +2 -0
  71. package/crates/tish_lsp/src/main.rs +439 -26
  72. package/crates/tish_native/src/build.rs +55 -1
  73. package/crates/tish_opt/src/lib.rs +126 -23
  74. package/crates/tish_parser/src/lib.rs +55 -1
  75. package/crates/tish_parser/src/parser.rs +456 -34
  76. package/crates/tish_pg/src/lib.rs +3 -3
  77. package/crates/tish_resolve/src/lib.rs +99 -59
  78. package/crates/tish_runtime/Cargo.toml +4 -0
  79. package/crates/tish_runtime/src/http.rs +66 -17
  80. package/crates/tish_runtime/src/http_fetch.rs +29 -8
  81. package/crates/tish_runtime/src/http_hyper.rs +25 -2
  82. package/crates/tish_runtime/src/lib.rs +299 -44
  83. package/crates/tish_runtime/src/promise.rs +328 -18
  84. package/crates/tish_runtime/src/timers.rs +13 -7
  85. package/crates/tish_runtime/src/tty.rs +226 -0
  86. package/crates/tish_runtime/src/ws.rs +35 -18
  87. package/crates/tish_runtime/tests/fetch_readable_stream.rs +2 -2
  88. package/crates/tish_ui/src/jsx.rs +10 -0
  89. package/crates/tish_ui/src/runtime/hooks.rs +19 -15
  90. package/crates/tish_ui/src/runtime/mod.rs +15 -12
  91. package/crates/tish_vm/Cargo.toml +14 -1
  92. package/crates/tish_vm/src/jit.rs +1050 -0
  93. package/crates/tish_vm/src/lib.rs +2 -0
  94. package/crates/tish_vm/src/vm.rs +1546 -202
  95. package/crates/tish_vm/tests/concurrent_shared_state.rs +140 -0
  96. package/crates/tish_wasm/src/lib.rs +6 -2
  97. package/crates/tish_wasm_runtime/src/gpu.rs +17 -1
  98. package/crates/tishlang_cargo_bindgen/src/classify.rs +1 -3
  99. package/crates/tishlang_cargo_bindgen/src/lib.rs +2 -2
  100. package/crates/tishlang_cargo_bindgen/src/metadata.rs +1 -1
  101. package/justfile +8 -0
  102. package/package.json +2 -2
  103. package/platform/darwin-arm64/tish-fmt +0 -0
  104. package/platform/darwin-x64/tish-fmt +0 -0
  105. package/platform/linux-arm64/tish-fmt +0 -0
  106. package/platform/linux-x64/tish-fmt +0 -0
  107. package/platform/win32-x64/tish-fmt.exe +0 -0
  108. package/README.md +0 -138
@@ -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, ObjectMap, Value, VmRef};
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: ObjectMap::with_capacity(capacity),
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(Arc::from(s))
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.as_ref();
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.as_ref()))
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.as_ref(),
222
- _ => return Value::Array(VmRef::new(vec![Value::String(Arc::clone(s))])),
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.as_ref()))
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.as_ref()))
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.as_ref(),
278
- _ => return Value::String(Arc::clone(s)),
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.as_ref(),
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.as_ref(),
309
- Value::Null => return Value::String(Arc::from("")),
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(Arc::clone(match s {
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(Arc::from(out))
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(Arc::clone(s)),
397
+ _ => return Value::String(s.clone()),
399
398
  };
400
399
  let pad_str = match pad {
401
- Value::String(p) if !p.is_empty() => p.as_ref(),
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(Arc::clone(s));
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(Arc::clone(k)))
47
+ .map(|k| Value::String(tishlang_core::ArcStr::from(k.as_ref())))
48
48
  .unwrap_or(Value::Null),
49
49
  _ => Value::Null,
50
50
  }