@tishlang/tish 1.9.2 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/bin/tish +0 -0
  2. package/crates/js_to_tish/src/transform/expr.rs +8 -6
  3. package/crates/js_to_tish/src/transform/stmt.rs +12 -13
  4. package/crates/tish/Cargo.toml +1 -1
  5. package/crates/tish/src/cargo_native_registry.rs +4 -1
  6. package/crates/tish/src/cli_help.rs +9 -1
  7. package/crates/tish/src/main.rs +66 -11
  8. package/crates/tish/tests/integration_test.rs +145 -7
  9. package/crates/tish_ast/src/ast.rs +3 -9
  10. package/crates/tish_build_utils/src/lib.rs +74 -23
  11. package/crates/tish_builtins/src/array.rs +2 -3
  12. package/crates/tish_builtins/src/construct.rs +15 -28
  13. package/crates/tish_builtins/src/globals.rs +18 -16
  14. package/crates/tish_builtins/src/helpers.rs +1 -4
  15. package/crates/tish_builtins/src/lib.rs +1 -0
  16. package/crates/tish_builtins/src/math.rs +7 -0
  17. package/crates/tish_builtins/src/object.rs +10 -10
  18. package/crates/tish_builtins/src/string.rs +27 -3
  19. package/crates/tish_builtins/src/symbol.rs +83 -0
  20. package/crates/tish_compile/src/codegen.rs +324 -158
  21. package/crates/tish_compile/src/lib.rs +39 -7
  22. package/crates/tish_compile/src/resolve.rs +191 -6
  23. package/crates/tish_compile/src/types.rs +6 -6
  24. package/crates/tish_compile_js/src/codegen.rs +8 -5
  25. package/crates/tish_core/src/console_style.rs +9 -0
  26. package/crates/tish_core/src/json.rs +17 -7
  27. package/crates/tish_core/src/macros.rs +2 -2
  28. package/crates/tish_core/src/value.rs +213 -4
  29. package/crates/tish_cranelift/src/link.rs +1 -1
  30. package/crates/tish_cranelift_runtime/Cargo.toml +4 -0
  31. package/crates/tish_eval/src/eval.rs +135 -73
  32. package/crates/tish_eval/src/http.rs +18 -12
  33. package/crates/tish_eval/src/lib.rs +29 -0
  34. package/crates/tish_eval/src/regex.rs +1 -1
  35. package/crates/tish_eval/src/value.rs +89 -4
  36. package/crates/tish_eval/src/value_convert.rs +30 -8
  37. package/crates/tish_fmt/src/lib.rs +4 -1
  38. package/crates/tish_lexer/src/lib.rs +7 -2
  39. package/crates/tish_llvm/src/lib.rs +2 -2
  40. package/crates/tish_lsp/src/builtin_goto.rs +111 -10
  41. package/crates/tish_lsp/src/import_goto.rs +35 -22
  42. package/crates/tish_lsp/src/main.rs +118 -85
  43. package/crates/tish_native/src/build.rs +270 -24
  44. package/crates/tish_native/src/config.rs +48 -0
  45. package/crates/tish_native/src/lib.rs +139 -12
  46. package/crates/tish_parser/src/lib.rs +5 -2
  47. package/crates/tish_parser/src/parser.rs +45 -75
  48. package/crates/tish_pg/src/error.rs +1 -1
  49. package/crates/tish_pg/src/lib.rs +61 -73
  50. package/crates/tish_resolve/src/lib.rs +283 -158
  51. package/crates/tish_resolve/src/pos.rs +10 -2
  52. package/crates/tish_runtime/Cargo.toml +3 -0
  53. package/crates/tish_runtime/src/http.rs +39 -39
  54. package/crates/tish_runtime/src/http_fetch.rs +12 -12
  55. package/crates/tish_runtime/src/lib.rs +35 -44
  56. package/crates/tish_runtime/src/native_promise.rs +0 -11
  57. package/crates/tish_runtime/src/promise.rs +14 -1
  58. package/crates/tish_runtime/src/promise_io.rs +1 -4
  59. package/crates/tish_runtime/src/timers.rs +12 -7
  60. package/crates/tish_runtime/src/ws.rs +40 -27
  61. package/crates/tish_runtime/tests/fetch_readable_stream.rs +10 -8
  62. package/crates/tish_ui/src/jsx.rs +6 -4
  63. package/crates/tish_ui/src/lib.rs +5 -4
  64. package/crates/tish_ui/src/runtime/hooks.rs +123 -37
  65. package/crates/tish_ui/src/runtime/mod.rs +21 -41
  66. package/crates/tish_vm/Cargo.toml +2 -0
  67. package/crates/tish_vm/src/vm.rs +258 -153
  68. package/crates/tish_wasm/src/lib.rs +60 -7
  69. package/crates/tish_wasm_runtime/Cargo.toml +10 -1
  70. package/crates/tish_wasm_runtime/src/gpu.rs +413 -0
  71. package/crates/tish_wasm_runtime/src/lib.rs +7 -1
  72. package/crates/tishlang_cargo_bindgen/src/classify.rs +1 -3
  73. package/crates/tishlang_cargo_bindgen/src/discover.rs +10 -5
  74. package/crates/tishlang_cargo_bindgen/src/infer.rs +18 -8
  75. package/crates/tishlang_cargo_bindgen/src/lib.rs +25 -26
  76. package/crates/tishlang_cargo_bindgen/src/main.rs +41 -38
  77. package/crates/tishlang_cargo_bindgen/src/metadata.rs +4 -1
  78. package/justfile +3 -3
  79. package/package.json +1 -1
  80. package/platform/darwin-arm64/tish +0 -0
  81. package/platform/darwin-x64/tish +0 -0
  82. package/platform/linux-arm64/tish +0 -0
  83. package/platform/linux-x64/tish +0 -0
  84. package/platform/win32-x64/tish.exe +0 -0
@@ -1,10 +1,8 @@
1
1
  //! Array builtin methods.
2
2
 
3
3
  use crate::helpers::normalize_index;
4
- use tishlang_core::VmRef;
5
- use std::cell::RefCell;
6
- use std::rc::Rc;
7
4
  use tishlang_core::Value;
5
+ use tishlang_core::VmRef;
8
6
 
9
7
  /// Create a new array Value from a Vec of Values.
10
8
  pub fn from_vec(v: Vec<Value>) -> Value {
@@ -434,6 +432,7 @@ fn get_prop_number(v: &Value, prop: &std::sync::Arc<str>) -> f64 {
434
432
  match v {
435
433
  Value::Object(o) => o
436
434
  .borrow()
435
+ .strings
437
436
  .get(prop.as_ref())
438
437
  .map(|v| v.as_number().unwrap_or(f64::NAN))
439
438
  .unwrap_or(f64::NAN),
@@ -1,12 +1,8 @@
1
1
  //! `new` lowering for non-JS targets: `construct(callee, args)` approximates JS `[[Construct]]`.
2
2
  //! Browser-exact behavior remains on `tish build --target js`.
3
3
 
4
- use std::cell::RefCell;
5
- use tishlang_core::VmRef;
6
- use std::rc::Rc;
7
4
  use std::sync::Arc;
8
-
9
- use tishlang_core::{ObjectMap, Value};
5
+ use tishlang_core::{ObjectMap, Value, VmRef};
10
6
 
11
7
  const CONSTRUCT: &str = "__construct";
12
8
 
@@ -16,7 +12,7 @@ pub fn construct(callee: &Value, args: &[Value]) -> Value {
16
12
  Value::Function(f) => f(args),
17
13
  Value::Object(o) => {
18
14
  let b = o.borrow();
19
- if let Some(Value::Function(ctor)) = b.get(&Arc::from(CONSTRUCT)) {
15
+ if let Some(Value::Function(ctor)) = b.strings.get(&Arc::from(CONSTRUCT)) {
20
16
  let c = ctor.clone();
21
17
  drop(b);
22
18
  return c(args);
@@ -30,7 +26,7 @@ pub fn construct(callee: &Value, args: &[Value]) -> Value {
30
26
  fn param(initial: f64) -> Value {
31
27
  let mut m = ObjectMap::default();
32
28
  m.insert(Arc::from("value"), Value::Number(initial));
33
- Value::Object(VmRef::new(m))
29
+ Value::object(m)
34
30
  }
35
31
 
36
32
  fn connect_fn() -> Value {
@@ -45,21 +41,21 @@ fn audio_node_stub() -> Value {
45
41
  m.insert(Arc::from("frequency"), param(440.0));
46
42
  m.insert(Arc::from("Q"), param(1.0));
47
43
  m.insert(Arc::from("type"), Value::String("peaking".into()));
48
- Value::Object(VmRef::new(m))
44
+ Value::object(m)
49
45
  }
50
46
 
51
47
  fn analyser_stub() -> Value {
52
48
  let mut m = ObjectMap::default();
53
49
  m.insert(Arc::from("connect"), connect_fn());
54
50
  m.insert(Arc::from("fftSize"), Value::Number(2048.0));
55
- Value::Object(VmRef::new(m))
51
+ Value::object(m)
56
52
  }
57
53
 
58
54
  fn stereo_panner_stub() -> Value {
59
55
  let mut m = ObjectMap::default();
60
56
  m.insert(Arc::from("connect"), connect_fn());
61
57
  m.insert(Arc::from("pan"), param(0.0));
62
- Value::Object(VmRef::new(m))
58
+ Value::object(m)
63
59
  }
64
60
 
65
61
  fn audio_buffer_stub(len: usize) -> Value {
@@ -71,7 +67,7 @@ fn audio_buffer_stub(len: usize) -> Value {
71
67
  Arc::from("getChannelData"),
72
68
  Value::native(move |_args| Value::Array(data2.clone())),
73
69
  );
74
- Value::Object(VmRef::new(m))
70
+ Value::object(m)
75
71
  }
76
72
 
77
73
  fn buffer_source_stub() -> Value {
@@ -79,12 +75,9 @@ fn buffer_source_stub() -> Value {
79
75
  m.insert(Arc::from("buffer"), Value::Null);
80
76
  m.insert(Arc::from("loop"), Value::Bool(false));
81
77
  m.insert(Arc::from("connect"), connect_fn());
82
- m.insert(
83
- Arc::from("start"),
84
- Value::native(|_| Value::Null),
85
- );
78
+ m.insert(Arc::from("start"), Value::native(|_| Value::Null));
86
79
  m.insert(Arc::from("stop"), Value::native(|_| Value::Null));
87
- Value::Object(VmRef::new(m))
80
+ Value::object(m)
88
81
  }
89
82
 
90
83
  fn oscillator_stub() -> Value {
@@ -92,12 +85,9 @@ fn oscillator_stub() -> Value {
92
85
  m.insert(Arc::from("frequency"), param(440.0));
93
86
  m.insert(Arc::from("type"), Value::String("sine".into()));
94
87
  m.insert(Arc::from("connect"), connect_fn());
95
- m.insert(
96
- Arc::from("start"),
97
- Value::native(|_| Value::Null),
98
- );
88
+ m.insert(Arc::from("start"), Value::native(|_| Value::Null));
99
89
  m.insert(Arc::from("stop"), Value::native(|_| Value::Null));
100
- Value::Object(VmRef::new(m))
90
+ Value::object(m)
101
91
  }
102
92
 
103
93
  fn audio_context_instance() -> Value {
@@ -140,12 +130,9 @@ fn audio_context_instance() -> Value {
140
130
  Arc::from("createOscillator"),
141
131
  Value::native(|_| oscillator_stub()),
142
132
  );
143
- ctx.insert(
144
- Arc::from("decodeAudioData"),
145
- Value::native(|_| Value::Null),
146
- );
133
+ ctx.insert(Arc::from("decodeAudioData"), Value::native(|_| Value::Null));
147
134
 
148
- Value::Object(VmRef::new(ctx))
135
+ Value::object(ctx)
149
136
  }
150
137
 
151
138
  /// Global `Uint8Array` for native/VM: `new Uint8Array(n)` → numeric array of zeros (not real bytes).
@@ -160,7 +147,7 @@ pub fn uint8_array_constructor_value() -> Value {
160
147
  });
161
148
  let mut m = ObjectMap::default();
162
149
  m.insert(Arc::from(CONSTRUCT), ctor);
163
- Value::Object(VmRef::new(m))
150
+ Value::object(m)
164
151
  }
165
152
 
166
153
  /// Global `AudioContext` for native/VM: stub graph (no real audio).
@@ -168,5 +155,5 @@ pub fn audio_context_constructor_value() -> Value {
168
155
  let ctor = Value::native(|_args: &[Value]| audio_context_instance());
169
156
  let mut m = ObjectMap::default();
170
157
  m.insert(Arc::from(CONSTRUCT), ctor);
171
- Value::Object(VmRef::new(m))
158
+ Value::object(m)
172
159
  }
@@ -3,10 +3,8 @@
3
3
  //! Used by both tishlang_vm (bytecode) and tishlang_runtime (compiled). Keeps tishlang_vm
4
4
  //! independent of tishlang_runtime.
5
5
 
6
- use std::cell::RefCell;
7
- use tishlang_core::VmRef;
8
- use std::rc::Rc;
9
6
  use std::sync::Arc;
7
+ use tishlang_core::VmRef;
10
8
  use tishlang_core::{percent_decode, percent_encode, ObjectMap, Value};
11
9
 
12
10
  /// Boolean(value) - coerce to bool
@@ -76,6 +74,7 @@ pub fn object_keys(args: &[Value]) -> Value {
76
74
  if let Some(Value::Object(obj)) = args.first() {
77
75
  let obj_borrow = obj.borrow();
78
76
  let keys: Vec<Value> = obj_borrow
77
+ .strings
79
78
  .keys()
80
79
  .map(|k| Value::String(Arc::clone(k)))
81
80
  .collect();
@@ -89,7 +88,7 @@ pub fn object_keys(args: &[Value]) -> Value {
89
88
  pub fn object_values(args: &[Value]) -> Value {
90
89
  if let Some(Value::Object(obj)) = args.first() {
91
90
  let obj_borrow = obj.borrow();
92
- let values: Vec<Value> = obj_borrow.values().cloned().collect();
91
+ let values: Vec<Value> = obj_borrow.strings.values().cloned().collect();
93
92
  Value::Array(VmRef::new(values))
94
93
  } else {
95
94
  Value::Array(VmRef::new(Vec::new()))
@@ -101,13 +100,9 @@ pub fn object_entries(args: &[Value]) -> Value {
101
100
  if let Some(Value::Object(obj)) = args.first() {
102
101
  let obj_borrow = obj.borrow();
103
102
  let entries: Vec<Value> = obj_borrow
103
+ .strings
104
104
  .iter()
105
- .map(|(k, v)| {
106
- Value::Array(VmRef::new(vec![
107
- Value::String(Arc::clone(k)),
108
- v.clone(),
109
- ]))
110
- })
105
+ .map(|(k, v)| Value::Array(VmRef::new(vec![Value::String(Arc::clone(k)), v.clone()])))
111
106
  .collect();
112
107
  Value::Array(VmRef::new(entries))
113
108
  } else {
@@ -127,7 +122,7 @@ pub fn object_assign(args: &[Value]) -> Value {
127
122
  .skip(1)
128
123
  .map(|source| {
129
124
  if let Value::Object(src) = source {
130
- src.borrow().len()
125
+ src.borrow().len_entries()
131
126
  } else {
132
127
  0
133
128
  }
@@ -135,13 +130,20 @@ pub fn object_assign(args: &[Value]) -> Value {
135
130
  .sum();
136
131
 
137
132
  let mut target_mut = target.borrow_mut();
138
- target_mut.reserve(additional_capacity);
133
+ target_mut.strings.reserve(additional_capacity);
139
134
 
140
135
  for source in args.iter().skip(1) {
141
136
  if let Value::Object(src) = source {
142
137
  let src_borrow = src.borrow();
143
- for (k, v) in src_borrow.iter() {
144
- target_mut.insert(Arc::clone(k), v.clone());
138
+ for (k, v) in src_borrow.strings.iter() {
139
+ target_mut.strings.insert(Arc::clone(k), v.clone());
140
+ }
141
+ if let Some(ss) = &src_borrow.symbols {
142
+ if target_mut.symbols.is_none() {
143
+ target_mut.symbols = Some(Default::default());
144
+ }
145
+ let dst = target_mut.symbols.as_mut().unwrap();
146
+ dst.extend(ss.iter().map(|(id, v)| (*id, v.clone())));
145
147
  }
146
148
  }
147
149
  }
@@ -204,8 +206,8 @@ pub fn object_from_entries(args: &[Value]) -> Value {
204
206
  }
205
207
  }
206
208
 
207
- Value::Object(VmRef::new(obj))
209
+ Value::object(obj)
208
210
  } else {
209
- Value::Object(VmRef::new(ObjectMap::default()))
211
+ Value::empty_object()
210
212
  }
211
213
  }
@@ -1,8 +1,5 @@
1
1
  //! Common helper functions used across builtin implementations.
2
2
 
3
- use std::cell::RefCell;
4
- use tishlang_core::VmRef;
5
- use std::rc::Rc;
6
3
  use std::sync::Arc;
7
4
  use tishlang_core::{ObjectMap, Value};
8
5
 
@@ -26,7 +23,7 @@ pub fn normalize_index(idx: &Value, len: i64, default: usize) -> usize {
26
23
  pub fn make_error_value(e: impl std::fmt::Display) -> Value {
27
24
  let mut obj = ObjectMap::with_capacity(1);
28
25
  obj.insert(Arc::from("error"), Value::String(e.to_string().into()));
29
- Value::Object(VmRef::new(obj))
26
+ Value::object(obj)
30
27
  }
31
28
 
32
29
  /// Extract a number from a Value, returning None for non-numbers.
@@ -11,5 +11,6 @@ pub mod helpers;
11
11
  pub mod math;
12
12
  pub mod object;
13
13
  pub mod string;
14
+ pub mod symbol;
14
15
 
15
16
  pub use tishlang_core::Value;
@@ -80,3 +80,10 @@ pub fn hypot(args: &[Value]) -> Value {
80
80
  let y = extract_num(args.get(1)).unwrap_or(0.0);
81
81
  Value::Number(x.hypot(y))
82
82
  }
83
+
84
+ /// ES6 `Math.imul`: 32-bit integer multiply (used by xmur3 PRNG in juke-cards).
85
+ pub fn imul(args: &[Value]) -> Value {
86
+ let a = extract_num(args.first()).unwrap_or(0.0) as i32;
87
+ let b = extract_num(args.get(1)).unwrap_or(0.0) as i32;
88
+ Value::Number(a.wrapping_mul(b) as f64)
89
+ }
@@ -3,34 +3,34 @@
3
3
  //! This module will contain shared object method implementations.
4
4
  //! Functions will be migrated here from tishlang_runtime and tishlang_eval.
5
5
 
6
- use std::cell::RefCell;
7
- use tishlang_core::VmRef;
8
- use std::rc::Rc;
9
6
  use std::sync::Arc;
10
- use tishlang_core::{ObjectMap, Value};
7
+ use tishlang_core::{ObjectData, ObjectMap, Value, VmRef};
11
8
 
12
9
  /// Create a new empty object Value.
13
10
  pub fn new() -> Value {
14
- Value::Object(VmRef::new(ObjectMap::default()))
11
+ Value::empty_object()
15
12
  }
16
13
 
17
14
  /// Create a new object Value with a given capacity.
18
15
  pub fn with_capacity(capacity: usize) -> Value {
19
- Value::Object(VmRef::new(ObjectMap::with_capacity(capacity)))
16
+ Value::Object(VmRef::new(ObjectData {
17
+ strings: ObjectMap::with_capacity(capacity),
18
+ symbols: None,
19
+ }))
20
20
  }
21
21
 
22
- /// Get the keys of an object.
22
+ /// Get the keys of an object (string keys only; matches `Object.keys` in JS).
23
23
  pub fn keys(obj: &Value) -> Option<Vec<Arc<str>>> {
24
24
  match obj {
25
- Value::Object(map) => Some(map.borrow().keys().cloned().collect()),
25
+ Value::Object(map) => Some(map.borrow().strings.keys().cloned().collect()),
26
26
  _ => None,
27
27
  }
28
28
  }
29
29
 
30
- /// Get the values of an object.
30
+ /// Get the values of an object (string-keyed properties only).
31
31
  pub fn values(obj: &Value) -> Option<Vec<Value>> {
32
32
  match obj {
33
- Value::Object(map) => Some(map.borrow().values().cloned().collect()),
33
+ Value::Object(map) => Some(map.borrow().strings.values().cloned().collect()),
34
34
  _ => None,
35
35
  }
36
36
  }
@@ -4,11 +4,9 @@
4
4
  //! JavaScript, matching .length and .charAt(). Byte offsets are never exposed.
5
5
 
6
6
  use crate::helpers::normalize_index;
7
- use tishlang_core::VmRef;
8
- use std::cell::RefCell;
9
- use std::rc::Rc;
10
7
  use std::sync::Arc;
11
8
  use tishlang_core::Value;
9
+ use tishlang_core::VmRef;
12
10
 
13
11
  /// Byte offset -> character index.
14
12
  fn byte_to_char_index(s: &str, byte_offset: usize) -> usize {
@@ -191,6 +189,32 @@ pub fn substring(s: &Value, start: &Value, end: &Value) -> Value {
191
189
  }
192
190
  }
193
191
 
192
+ /// JS `String.prototype.substr(start, length)`.
193
+ pub fn substr(s: &Value, start: &Value, length: &Value) -> Value {
194
+ if let Value::String(s) = s {
195
+ let chars: Vec<char> = s.chars().collect();
196
+ let len = chars.len();
197
+ let mut start_idx = match start {
198
+ Value::Number(n) => *n as i64,
199
+ _ => 0,
200
+ };
201
+ if start_idx < 0 {
202
+ start_idx = (len as i64 + start_idx).max(0);
203
+ }
204
+ let start_idx = (start_idx as usize).min(len);
205
+ let count = match length {
206
+ Value::Null => len - start_idx,
207
+ Value::Number(n) => (*n as i64).max(0) as usize,
208
+ _ => len - start_idx,
209
+ };
210
+ let end_idx = (start_idx + count).min(len);
211
+ let result: String = chars[start_idx..end_idx].iter().collect();
212
+ Value::String(result.into())
213
+ } else {
214
+ Value::Null
215
+ }
216
+ }
217
+
194
218
  pub fn split(s: &Value, sep: &Value) -> Value {
195
219
  if let Value::String(s) = s {
196
220
  let separator = match sep {
@@ -0,0 +1,83 @@
1
+ //! ECMAScript-style `Symbol`, `Symbol.for`, `Symbol.keyFor`.
2
+
3
+ use std::collections::hash_map::Entry;
4
+ use std::sync::{Arc, Mutex, OnceLock};
5
+
6
+ use std::collections::HashMap;
7
+
8
+ use tishlang_core::{alloc_symbol_id, ObjectMap, TishSymbol, Value};
9
+
10
+ static SYMBOL_FOR_REGISTRY: OnceLock<Mutex<HashMap<Arc<str>, Arc<TishSymbol>>>> = OnceLock::new();
11
+
12
+ fn symbol_registry() -> &'static Mutex<HashMap<Arc<str>, Arc<TishSymbol>>> {
13
+ SYMBOL_FOR_REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
14
+ }
15
+
16
+ fn symbol_for_impl(key: &str) -> Value {
17
+ let k: Arc<str> = key.into();
18
+ let mut reg = symbol_registry().lock().unwrap();
19
+ let sym = match reg.entry(Arc::clone(&k)) {
20
+ Entry::Occupied(e) => e.get().clone(),
21
+ Entry::Vacant(v) => {
22
+ let id = alloc_symbol_id();
23
+ let sym = TishSymbol::new_registry(id, Arc::clone(&k), None);
24
+ v.insert(Arc::clone(&sym));
25
+ sym
26
+ }
27
+ };
28
+ Value::Symbol(sym)
29
+ }
30
+
31
+ fn symbol_new(args: &[Value]) -> Value {
32
+ let desc = args.first().and_then(|v| {
33
+ if matches!(v, Value::Null) {
34
+ None
35
+ } else {
36
+ Some(v.to_display_string().into())
37
+ }
38
+ });
39
+ Value::Symbol(TishSymbol::new_unique(desc))
40
+ }
41
+
42
+ fn symbol_key_for_impl(args: &[Value]) -> Value {
43
+ match args.first() {
44
+ Some(Value::Symbol(s)) => s
45
+ .registry_key
46
+ .as_ref()
47
+ .map(|k| Value::String(Arc::clone(k)))
48
+ .unwrap_or(Value::Null),
49
+ _ => Value::Null,
50
+ }
51
+ }
52
+
53
+ /// Global `Symbol`: `Symbol("desc")` via `__call` / `__construct`, `Symbol.for`, `Symbol.keyFor`.
54
+ pub fn symbol_object() -> Value {
55
+ let call = Value::native(symbol_new);
56
+ let for_fn = Value::native(|args: &[Value]| {
57
+ let key = args
58
+ .first()
59
+ .map(|v| v.to_display_string())
60
+ .unwrap_or_default();
61
+ symbol_for_impl(&key)
62
+ });
63
+ let key_for = Value::native(symbol_key_for_impl);
64
+ let mut m = ObjectMap::default();
65
+ m.insert(Arc::from("__call"), call.clone());
66
+ m.insert(Arc::from("__construct"), call);
67
+ m.insert(Arc::from("for"), for_fn);
68
+ m.insert(Arc::from("keyFor"), key_for);
69
+ Value::object(m)
70
+ }
71
+
72
+ #[cfg(test)]
73
+ mod tests {
74
+ use super::*;
75
+ use tishlang_core::value_call;
76
+
77
+ #[test]
78
+ fn symbol_global_value_call() {
79
+ let o = symbol_object();
80
+ let r = value_call(&o, &[Value::String("hi".into())]);
81
+ assert!(matches!(r, Value::Symbol(_)));
82
+ }
83
+ }