@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.
- package/bin/tish +0 -0
- package/crates/js_to_tish/src/transform/expr.rs +8 -6
- package/crates/js_to_tish/src/transform/stmt.rs +12 -13
- package/crates/tish/Cargo.toml +1 -1
- package/crates/tish/src/cargo_native_registry.rs +4 -1
- package/crates/tish/src/cli_help.rs +9 -1
- package/crates/tish/src/main.rs +66 -11
- package/crates/tish/tests/integration_test.rs +145 -7
- package/crates/tish_ast/src/ast.rs +3 -9
- package/crates/tish_build_utils/src/lib.rs +74 -23
- package/crates/tish_builtins/src/array.rs +2 -3
- package/crates/tish_builtins/src/construct.rs +15 -28
- package/crates/tish_builtins/src/globals.rs +18 -16
- package/crates/tish_builtins/src/helpers.rs +1 -4
- package/crates/tish_builtins/src/lib.rs +1 -0
- package/crates/tish_builtins/src/math.rs +7 -0
- package/crates/tish_builtins/src/object.rs +10 -10
- package/crates/tish_builtins/src/string.rs +27 -3
- package/crates/tish_builtins/src/symbol.rs +83 -0
- package/crates/tish_compile/src/codegen.rs +324 -158
- package/crates/tish_compile/src/lib.rs +39 -7
- package/crates/tish_compile/src/resolve.rs +191 -6
- package/crates/tish_compile/src/types.rs +6 -6
- package/crates/tish_compile_js/src/codegen.rs +8 -5
- package/crates/tish_core/src/console_style.rs +9 -0
- package/crates/tish_core/src/json.rs +17 -7
- package/crates/tish_core/src/macros.rs +2 -2
- package/crates/tish_core/src/value.rs +213 -4
- package/crates/tish_cranelift/src/link.rs +1 -1
- package/crates/tish_cranelift_runtime/Cargo.toml +4 -0
- package/crates/tish_eval/src/eval.rs +135 -73
- package/crates/tish_eval/src/http.rs +18 -12
- package/crates/tish_eval/src/lib.rs +29 -0
- package/crates/tish_eval/src/regex.rs +1 -1
- package/crates/tish_eval/src/value.rs +89 -4
- package/crates/tish_eval/src/value_convert.rs +30 -8
- package/crates/tish_fmt/src/lib.rs +4 -1
- package/crates/tish_lexer/src/lib.rs +7 -2
- package/crates/tish_llvm/src/lib.rs +2 -2
- package/crates/tish_lsp/src/builtin_goto.rs +111 -10
- package/crates/tish_lsp/src/import_goto.rs +35 -22
- package/crates/tish_lsp/src/main.rs +118 -85
- package/crates/tish_native/src/build.rs +270 -24
- package/crates/tish_native/src/config.rs +48 -0
- package/crates/tish_native/src/lib.rs +139 -12
- package/crates/tish_parser/src/lib.rs +5 -2
- package/crates/tish_parser/src/parser.rs +45 -75
- package/crates/tish_pg/src/error.rs +1 -1
- package/crates/tish_pg/src/lib.rs +61 -73
- package/crates/tish_resolve/src/lib.rs +283 -158
- package/crates/tish_resolve/src/pos.rs +10 -2
- package/crates/tish_runtime/Cargo.toml +3 -0
- package/crates/tish_runtime/src/http.rs +39 -39
- package/crates/tish_runtime/src/http_fetch.rs +12 -12
- package/crates/tish_runtime/src/lib.rs +35 -44
- package/crates/tish_runtime/src/native_promise.rs +0 -11
- package/crates/tish_runtime/src/promise.rs +14 -1
- package/crates/tish_runtime/src/promise_io.rs +1 -4
- package/crates/tish_runtime/src/timers.rs +12 -7
- package/crates/tish_runtime/src/ws.rs +40 -27
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +10 -8
- package/crates/tish_ui/src/jsx.rs +6 -4
- package/crates/tish_ui/src/lib.rs +5 -4
- package/crates/tish_ui/src/runtime/hooks.rs +123 -37
- package/crates/tish_ui/src/runtime/mod.rs +21 -41
- package/crates/tish_vm/Cargo.toml +2 -0
- package/crates/tish_vm/src/vm.rs +258 -153
- package/crates/tish_wasm/src/lib.rs +60 -7
- package/crates/tish_wasm_runtime/Cargo.toml +10 -1
- package/crates/tish_wasm_runtime/src/gpu.rs +413 -0
- package/crates/tish_wasm_runtime/src/lib.rs +7 -1
- package/crates/tishlang_cargo_bindgen/src/classify.rs +1 -3
- package/crates/tishlang_cargo_bindgen/src/discover.rs +10 -5
- package/crates/tishlang_cargo_bindgen/src/infer.rs +18 -8
- package/crates/tishlang_cargo_bindgen/src/lib.rs +25 -26
- package/crates/tishlang_cargo_bindgen/src/main.rs +41 -38
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +4 -1
- package/justfile +3 -3
- 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
|
@@ -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::
|
|
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::
|
|
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::
|
|
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::
|
|
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::
|
|
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::
|
|
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::
|
|
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::
|
|
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::
|
|
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::
|
|
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().
|
|
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::
|
|
209
|
+
Value::object(obj)
|
|
208
210
|
} else {
|
|
209
|
-
Value::
|
|
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::
|
|
26
|
+
Value::object(obj)
|
|
30
27
|
}
|
|
31
28
|
|
|
32
29
|
/// Extract a number from a Value, returning None for non-numbers.
|
|
@@ -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::
|
|
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(
|
|
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
|
+
}
|