@tishlang/tish-lsp 2.39.0 → 3.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 +4 -1
- package/bin/tish-lsp +0 -0
- package/crates/tish/src/cli_help.rs +28 -0
- package/crates/tish/src/main.rs +41 -5
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +21 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/tish.d.tish +4 -0
- package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +5 -1
- package/crates/tish/tests/regr_typed_externs.rs +59 -0
- package/crates/tish/tests/scope_merge.rs +79 -0
- package/crates/tish_build_utils/src/lib.rs +27 -1
- package/crates/tish_builtins/Cargo.toml +14 -6
- package/crates/tish_builtins/src/array.rs +130 -54
- package/crates/tish_builtins/src/collections.rs +9 -5
- package/crates/tish_builtins/src/construct.rs +7 -1
- package/crates/tish_builtins/src/date.rs +15 -1
- package/crates/tish_builtins/src/globals.rs +65 -3
- package/crates/tish_builtins/src/helpers.rs +7 -3
- package/crates/tish_builtins/src/iterator.rs +5 -1
- package/crates/tish_builtins/src/lib.rs +3 -0
- package/crates/tish_builtins/src/math.rs +14 -0
- package/crates/tish_builtins/src/number.rs +72 -6
- package/crates/tish_builtins/src/object.rs +6 -1
- package/crates/tish_builtins/src/string.rs +13 -2
- package/crates/tish_builtins/src/symbol.rs +17 -17
- package/crates/tish_builtins/src/typedarrays.rs +9 -2
- package/crates/tish_bytecode/src/compiler.rs +76 -2
- package/crates/tish_bytecode/src/lib.rs +1 -1
- package/crates/tish_bytecode/src/opcode.rs +77 -2
- package/crates/tish_compile/src/codegen.rs +2154 -225
- package/crates/tish_compile/src/infer.rs +37 -41
- package/crates/tish_compile/src/lib.rs +13 -1
- package/crates/tish_compile/src/platform_resolve.rs +361 -0
- package/crates/tish_compile/src/resolve.rs +195 -11
- package/crates/tish_compile/src/schemes.rs +333 -0
- package/crates/tish_compile/src/types.rs +205 -34
- package/crates/tish_compile/tests/perf_codegen_320.rs +2 -2
- package/crates/tish_compile/tests/perf_codegen_module_const_forof.rs +3 -1
- package/crates/tish_compile/tests/platform_resolve_cli.rs +164 -0
- package/crates/tish_compile/tests/regr_556_param_name_collision.rs +40 -0
- package/crates/tish_compile/tests/regr_558_struct_array_write.rs +65 -0
- package/crates/tish_compile/tests/regr_558b_struct_field_read.rs +21 -0
- package/crates/tish_compile/tests/regr_558c_wrapped_struct_array.rs +64 -0
- package/crates/tish_compile/tests/regr_558d_native_vec_mutators.rs +156 -0
- package/crates/tish_compile/tests/regr_562_wrapped_struct_array_push.rs +50 -0
- package/crates/tish_compile/tests/regr_fixed_native_coercions.rs +69 -0
- package/crates/tish_compile/tests/regr_gba_numerics_gated.rs +58 -0
- package/crates/tish_compile/tests/regr_gba_struct_reflection.rs +106 -0
- package/crates/tish_compile/tests/regression/name_collision_lib.tish +6 -0
- package/crates/tish_compile/tests/regression/name_collision_main.tish +13 -0
- package/crates/tish_compile/tests/regression/struct_array_write.tish +22 -0
- package/crates/tish_compile/tests/regression/struct_field_return_read.tish +10 -0
- package/crates/tish_compiler_wasm/Cargo.toml +1 -0
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +56 -6
- package/crates/tish_core/Cargo.toml +39 -4
- package/crates/tish_core/src/compat.rs +506 -0
- package/crates/tish_core/src/console_style.rs +23 -1
- package/crates/tish_core/src/json.rs +42 -10
- package/crates/tish_core/src/lib.rs +121 -25
- package/crates/tish_core/src/macros.rs +1 -1
- package/crates/tish_core/src/shape.rs +4 -2
- package/crates/tish_core/src/uri.rs +8 -1
- package/crates/tish_core/src/value.rs +305 -54
- package/crates/tish_core/src/vmref.rs +17 -5
- package/crates/tish_eval/Cargo.toml +2 -0
- package/crates/tish_eval/src/eval.rs +90 -3
- package/crates/tish_eval/src/value.rs +8 -0
- package/crates/tish_eval/src/value_convert.rs +316 -21
- package/crates/tish_lsp/Cargo.toml +3 -1
- package/crates/tish_lsp/src/import_goto.rs +41 -14
- package/crates/tish_lsp/src/main.rs +18 -0
- package/crates/tish_native/src/build.rs +246 -0
- package/crates/tish_native/src/config.rs +20 -0
- package/crates/tish_native/src/lib.rs +16 -6
- package/crates/tish_parser/src/lib.rs +28 -0
- package/crates/tish_parser/src/parser.rs +25 -4
- package/crates/tish_runtime/src/lib.rs +55 -9
- package/crates/tish_runtime_gba/Cargo.lock +837 -0
- package/crates/tish_runtime_gba/Cargo.toml +16 -0
- package/crates/tish_runtime_gba/src/gba.rs +211 -0
- package/crates/tish_runtime_gba/src/lib.rs +804 -0
- package/crates/tish_vm/src/jit.rs +1446 -164
- package/crates/tish_vm/src/vm.rs +583 -173
- package/justfile +9 -0
- package/package.json +1 -1
- package/platform/darwin-arm64/tish-lsp +0 -0
- package/platform/darwin-x64/tish-lsp +0 -0
- package/platform/linux-arm64/tish-lsp +0 -0
- package/platform/linux-x64/tish-lsp +0 -0
- package/platform/win32-x64/tish-lsp.exe +0 -0
|
@@ -0,0 +1,804 @@
|
|
|
1
|
+
//! no_std runtime facade for Tish compiled output on the Game Boy Advance.
|
|
2
|
+
//!
|
|
3
|
+
//! The `Gba` emit mode generates a crate that depends on this one under the name
|
|
4
|
+
//! `tishlang_runtime` (via a Cargo `package =` rename), so every emitted
|
|
5
|
+
//! `tishlang_runtime::…` path resolves here. It re-exports the portable prelude
|
|
6
|
+
//! surface from `tishlang_core` / `tishlang_builtins` and adds the GBA runtime
|
|
7
|
+
//! entry points in [`gba`].
|
|
8
|
+
#![no_std]
|
|
9
|
+
|
|
10
|
+
extern crate alloc;
|
|
11
|
+
|
|
12
|
+
use alloc::boxed::Box;
|
|
13
|
+
use alloc::format;
|
|
14
|
+
use alloc::string::{String, ToString};
|
|
15
|
+
use alloc::vec::Vec;
|
|
16
|
+
|
|
17
|
+
// ── Core value vocabulary (single source of truth in tishlang_core) ──────────
|
|
18
|
+
pub use tishlang_core::{
|
|
19
|
+
js_number_to_string_into, to_int32, to_int32_value, to_number_value, to_uint32,
|
|
20
|
+
to_uint32_value, ArcStr, NumArrayBacking, ObjectData, ObjectMap, PropMap, TishStruct, Value,
|
|
21
|
+
VmReadGuard, VmRef, VmWriteGuard,
|
|
22
|
+
};
|
|
23
|
+
/// `Arc` on GBA is `Rc` (single-core). Emitted code writes `Arc::from(..)`.
|
|
24
|
+
pub use tishlang_core::Arc;
|
|
25
|
+
/// Fixed-point scalar the typed-numeric path lowers `fixed` to (agb `Num<i32,8>`).
|
|
26
|
+
pub type Fixed = agb::fixnum::Num<i32, 8>;
|
|
27
|
+
|
|
28
|
+
// ── Pending-throw + recursion-guard plumbing (shared slot in tishlang_core) ──
|
|
29
|
+
pub use tishlang_core::{
|
|
30
|
+
has_pending_throw, set_pending_throw, stack_overflow_error, take_pending_throw, CallDepthGuard,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/// Enter a boxed user-fn call frame or trip the recursion guard. On GBA there is
|
|
34
|
+
/// no stack-pressure probe (no `std`), so this is just the counted-depth guard.
|
|
35
|
+
#[inline]
|
|
36
|
+
pub fn enter_call_guarded() -> Option<CallDepthGuard> {
|
|
37
|
+
tishlang_core::enter_call_guarded()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ── Error type for throw/return non-local control flow ───────────────────────
|
|
41
|
+
/// Mirrors `tishlang_runtime::TishError` (the host runtime defines its own; kept
|
|
42
|
+
/// in lock-step). `run()` in generated code returns `Result<(), Box<dyn Error>>`.
|
|
43
|
+
#[derive(Debug, Clone)]
|
|
44
|
+
pub enum TishError {
|
|
45
|
+
Throw(Value),
|
|
46
|
+
Return(Value),
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
impl core::fmt::Display for TishError {
|
|
50
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
51
|
+
match self {
|
|
52
|
+
TishError::Throw(v) => write!(f, "{}", v.to_display_string()),
|
|
53
|
+
TishError::Return(v) => write!(f, "return {}", v.to_display_string()),
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
impl core::error::Error for TishError {}
|
|
59
|
+
|
|
60
|
+
/// Convert a boxed error thrown out of `run()` back into a `Value` (throw/return payload).
|
|
61
|
+
pub fn fn_unwind(e: Box<dyn core::error::Error>) -> Value {
|
|
62
|
+
if let Some(te) = e.downcast_ref::<TishError>() {
|
|
63
|
+
match te {
|
|
64
|
+
TishError::Throw(v) | TishError::Return(v) => return v.clone(),
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
Value::String(e.to_string().into())
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ── Value call / concat / read helpers used by native codegen ────────────────
|
|
71
|
+
/// `f()` / `obj()` dispatch, suppressed while a throw is unwinding (#381).
|
|
72
|
+
pub fn value_call(callee: &Value, args: &[Value]) -> Value {
|
|
73
|
+
if has_pending_throw() {
|
|
74
|
+
return Value::Null;
|
|
75
|
+
}
|
|
76
|
+
tishlang_core::value_call(callee, args)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// Append `v`'s JS string-concat form to `buf` (no throwaway `String`).
|
|
80
|
+
#[inline]
|
|
81
|
+
pub fn push_value_str(buf: &mut String, v: &Value) {
|
|
82
|
+
match v {
|
|
83
|
+
Value::String(s) => buf.push_str(s),
|
|
84
|
+
Value::Number(n) => js_number_to_string_into(buf, *n),
|
|
85
|
+
Value::Bool(b) => buf.push_str(if *b { "true" } else { "false" }),
|
|
86
|
+
Value::Null => buf.push_str("null"),
|
|
87
|
+
other => buf.push_str(&other.to_js_string()),
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/// Read a captured `VmRef` cell, releasing the guard before returning (#218).
|
|
92
|
+
#[inline]
|
|
93
|
+
pub fn vm_read<T: Clone>(cell: &VmRef<T>) -> T {
|
|
94
|
+
(*cell.borrow()).clone()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// `for…of` iterable normalization for the native backend.
|
|
98
|
+
pub fn normalize_for_of(v: Value) -> Value {
|
|
99
|
+
if let Value::NumberArray(arr) = &v {
|
|
100
|
+
let items: Vec<Value> = arr.borrow().to_values();
|
|
101
|
+
return Value::Array(VmRef::new(items));
|
|
102
|
+
}
|
|
103
|
+
match tishlang_core::drain_iterator(&v) {
|
|
104
|
+
Some(items) => Value::Array(VmRef::new(items)),
|
|
105
|
+
None => v,
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// JS `in` operator (`key in obj`).
|
|
110
|
+
pub fn in_operator(key: &Value, obj: &Value) -> Value {
|
|
111
|
+
match obj {
|
|
112
|
+
Value::Object(_) => Value::Bool(tishlang_core::object_has(obj, key)),
|
|
113
|
+
Value::Array(arr) => Value::Bool(array_in(key, arr.borrow().len())),
|
|
114
|
+
Value::NumberArray(arr) => Value::Bool(array_in(key, arr.borrow().len())),
|
|
115
|
+
// A by-reference boxed typed struct: `"field" in s` materialises to the struct's object and
|
|
116
|
+
// asks it, so a boxed struct answers `in` like the object it stands for (was always `false`).
|
|
117
|
+
Value::Struct(s) => {
|
|
118
|
+
Value::Bool(tishlang_core::object_has(&s.borrow().tish_to_object(), key))
|
|
119
|
+
}
|
|
120
|
+
_ => Value::Bool(false),
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/// Spread source for `{ ...v }` (GBA emit only — codegen calls this from the Gba object-spread
|
|
125
|
+
/// lowering). Returns the source's own string-keyed props as an owned `PropMap`: a boxed typed struct
|
|
126
|
+
/// is materialised to its ordered object first, so `{ ...s }` carries the struct's fields instead of
|
|
127
|
+
/// dropping them; a non-object yields `None` (spread of `null`/a number contributes nothing, per JS).
|
|
128
|
+
/// Desktop emit keeps its inline `if let Value::Object` path, so this GBA-only helper adds no
|
|
129
|
+
/// non-GBA cost.
|
|
130
|
+
pub fn object_spread_props(v: &Value) -> Option<PropMap> {
|
|
131
|
+
match v {
|
|
132
|
+
Value::Object(o) => Some(o.borrow().strings.clone()),
|
|
133
|
+
Value::Struct(s) => match s.borrow().tish_to_object() {
|
|
134
|
+
Value::Object(o) => Some(o.borrow().strings.clone()),
|
|
135
|
+
_ => None,
|
|
136
|
+
},
|
|
137
|
+
_ => None,
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
fn array_in(key: &Value, len: usize) -> bool {
|
|
142
|
+
let key_str: Arc<str> = match key {
|
|
143
|
+
Value::String(s) => Arc::from(s.as_str()),
|
|
144
|
+
Value::Number(n) => n.to_string().into(),
|
|
145
|
+
_ => return false,
|
|
146
|
+
};
|
|
147
|
+
key_str.as_ref() == "length"
|
|
148
|
+
|| key_str.parse::<usize>().ok().map(|i| i < len).unwrap_or(false)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ── Member / index read + write (ported from the host runtime; the regex/promise
|
|
152
|
+
// arms are feature-gated off on GBA). Method dispatch (`arr.map(f)`) is emitted
|
|
153
|
+
// as direct builtin calls by codegen, not through these. ──────────────────────
|
|
154
|
+
use tishlang_builtins::collections::collection_size;
|
|
155
|
+
use tishlang_builtins::helpers::extract_num;
|
|
156
|
+
|
|
157
|
+
pub fn get_prop(obj: &Value, key: impl AsRef<str>) -> Value {
|
|
158
|
+
let key = key.as_ref();
|
|
159
|
+
match obj {
|
|
160
|
+
Value::Object(map) => {
|
|
161
|
+
if key == "size" {
|
|
162
|
+
if let Some(n) = collection_size(obj) {
|
|
163
|
+
return Value::Number(n);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
map.borrow().strings.get(key).cloned().unwrap_or(Value::Null)
|
|
167
|
+
}
|
|
168
|
+
Value::Array(arr) => {
|
|
169
|
+
if key == "length" {
|
|
170
|
+
Value::Number(arr.borrow().len() as f64)
|
|
171
|
+
} else if let Ok(idx) = key.parse::<usize>() {
|
|
172
|
+
arr.borrow().get(idx).cloned().unwrap_or(Value::Null)
|
|
173
|
+
} else {
|
|
174
|
+
Value::Null
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
Value::NumberArray(arr) => {
|
|
178
|
+
if key == "length" {
|
|
179
|
+
Value::Number(arr.borrow().len() as f64)
|
|
180
|
+
} else if let Ok(idx) = key.parse::<usize>() {
|
|
181
|
+
arr.borrow()
|
|
182
|
+
.get(idx)
|
|
183
|
+
.map(|v| match v {
|
|
184
|
+
Value::Number(n) if n.is_nan() => Value::Null,
|
|
185
|
+
other => other,
|
|
186
|
+
})
|
|
187
|
+
.unwrap_or(Value::Null)
|
|
188
|
+
} else {
|
|
189
|
+
Value::Null
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
Value::String(s) => {
|
|
193
|
+
if key == "length" {
|
|
194
|
+
Value::Number(tishlang_builtins::string::char_count(s) as f64)
|
|
195
|
+
} else {
|
|
196
|
+
Value::Null
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
Value::Opaque(o) => o.get_method(key).map(Value::Function).unwrap_or(Value::Null),
|
|
200
|
+
// A boxed typed struct: read the field natively (a small match, no hashmap). `size`/method
|
|
201
|
+
// sugar isn't relevant here — structs are plain field bags.
|
|
202
|
+
Value::Struct(s) => s.borrow().tish_get(key),
|
|
203
|
+
Value::Null => {
|
|
204
|
+
tishlang_core::set_pending_throw(tishlang_core::cannot_read_property_error(key));
|
|
205
|
+
Value::Null
|
|
206
|
+
}
|
|
207
|
+
_ => Value::Null,
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
pub fn get_index(obj: &Value, index: &Value) -> Value {
|
|
212
|
+
match obj {
|
|
213
|
+
Value::Array(arr) => {
|
|
214
|
+
let idx = match index {
|
|
215
|
+
Value::Number(n) => *n as usize,
|
|
216
|
+
Value::String(s) => match tishlang_core::str_to_array_index(s) {
|
|
217
|
+
Some(i) => i,
|
|
218
|
+
None => return Value::Null,
|
|
219
|
+
},
|
|
220
|
+
_ => return Value::Null,
|
|
221
|
+
};
|
|
222
|
+
arr.borrow().get(idx).cloned().unwrap_or(Value::Null)
|
|
223
|
+
}
|
|
224
|
+
Value::NumberArray(arr) => {
|
|
225
|
+
let idx = match index {
|
|
226
|
+
Value::Number(n) => *n as usize,
|
|
227
|
+
Value::String(s) => match tishlang_core::str_to_array_index(s) {
|
|
228
|
+
Some(i) => i,
|
|
229
|
+
None => return Value::Null,
|
|
230
|
+
},
|
|
231
|
+
_ => return Value::Null,
|
|
232
|
+
};
|
|
233
|
+
arr.borrow()
|
|
234
|
+
.get(idx)
|
|
235
|
+
.map(|v| match v {
|
|
236
|
+
Value::Number(n) if n.is_nan() => Value::Null,
|
|
237
|
+
other => other,
|
|
238
|
+
})
|
|
239
|
+
.unwrap_or(Value::Null)
|
|
240
|
+
}
|
|
241
|
+
Value::String(s) => match index {
|
|
242
|
+
Value::Number(n) if *n >= 0.0 && n.fract() == 0.0 => {
|
|
243
|
+
tishlang_builtins::string::nth_char(s, *n as usize)
|
|
244
|
+
.map(|c| Value::String(c.to_string().into()))
|
|
245
|
+
.unwrap_or(Value::Null)
|
|
246
|
+
}
|
|
247
|
+
_ => Value::Null,
|
|
248
|
+
},
|
|
249
|
+
Value::Object(_) => tishlang_core::object_get(obj, index).unwrap_or(Value::Null),
|
|
250
|
+
Value::Struct(s) => {
|
|
251
|
+
let key = match index {
|
|
252
|
+
Value::String(k) => k.to_string(),
|
|
253
|
+
other => other.to_js_string(),
|
|
254
|
+
};
|
|
255
|
+
s.borrow().tish_get(&key)
|
|
256
|
+
}
|
|
257
|
+
Value::Null => {
|
|
258
|
+
let key = match index {
|
|
259
|
+
Value::String(s) => s.to_string(),
|
|
260
|
+
other => other.to_js_string(),
|
|
261
|
+
};
|
|
262
|
+
tishlang_core::set_pending_throw(tishlang_core::cannot_read_property_error(&key));
|
|
263
|
+
Value::Null
|
|
264
|
+
}
|
|
265
|
+
_ => Value::Null,
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
pub fn delete_property(obj: &Value, key: &Value) -> Value {
|
|
270
|
+
match obj {
|
|
271
|
+
Value::Object(m) => {
|
|
272
|
+
let key_s = match key {
|
|
273
|
+
Value::String(s) => s.to_string(),
|
|
274
|
+
other => other.to_js_string(),
|
|
275
|
+
};
|
|
276
|
+
m.borrow_mut().strings.remove(key_s.as_str());
|
|
277
|
+
}
|
|
278
|
+
Value::Array(arr) => {
|
|
279
|
+
if let Value::Number(n) = key {
|
|
280
|
+
let n = *n;
|
|
281
|
+
if n >= 0.0 && n.fract() == 0.0 {
|
|
282
|
+
let i = n as usize;
|
|
283
|
+
let mut a = arr.borrow_mut();
|
|
284
|
+
if i < a.len() {
|
|
285
|
+
a[i] = Value::Null;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
_ => {}
|
|
291
|
+
}
|
|
292
|
+
Value::Bool(true)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/// A valid JS array index: a non-negative integer `< 2^32-1`. Anything else (negative,
|
|
296
|
+
/// fractional, NaN, or too large) is `None` — on GBA we treat such an assignment as a no-op
|
|
297
|
+
/// rather than densifying/aborting (JS would set a named property, which a `Vec`-backed array
|
|
298
|
+
/// can't hold anyway).
|
|
299
|
+
fn array_index(n: f64) -> Option<usize> {
|
|
300
|
+
if n >= 0.0 && n.fract() == 0.0 && n <= 4_294_967_294.0 {
|
|
301
|
+
Some(n as usize)
|
|
302
|
+
} else {
|
|
303
|
+
None
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/// Grow `v` so `index` is in range, filling with `fill`. Instead of aborting when the
|
|
308
|
+
/// allocation can't be satisfied (a real risk on the GBA's 256KB EWRAM for a large sparse
|
|
309
|
+
/// index), it raises a CATCHABLE tish error and returns `false` so the caller bails out.
|
|
310
|
+
fn grow_or_throw<T: Clone>(v: &mut alloc::vec::Vec<T>, index: usize, fill: T) -> bool {
|
|
311
|
+
if index >= v.len() {
|
|
312
|
+
let extra = index - v.len() + 1;
|
|
313
|
+
if v.try_reserve(extra).is_err() {
|
|
314
|
+
tishlang_core::set_pending_throw(tishlang_core::type_error(format!(
|
|
315
|
+
"array index {index} too large to allocate"
|
|
316
|
+
)));
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
v.resize(index + 1, fill);
|
|
320
|
+
}
|
|
321
|
+
true
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
pub fn set_prop(obj: &Value, key: &str, val: Value) -> Value {
|
|
325
|
+
match obj {
|
|
326
|
+
Value::Object(map) => {
|
|
327
|
+
let mut m = map.borrow_mut();
|
|
328
|
+
if m.frozen {
|
|
329
|
+
tishlang_core::set_pending_throw(tishlang_core::type_error(format!(
|
|
330
|
+
"Cannot assign to read only property '{key}' of a frozen object"
|
|
331
|
+
)));
|
|
332
|
+
return val;
|
|
333
|
+
}
|
|
334
|
+
if let Some(slot) = m.strings.get_mut(key) {
|
|
335
|
+
*slot = val.clone();
|
|
336
|
+
} else {
|
|
337
|
+
m.strings.insert(Arc::from(key), val.clone());
|
|
338
|
+
}
|
|
339
|
+
val
|
|
340
|
+
}
|
|
341
|
+
Value::Array(arr) if key == "length" => {
|
|
342
|
+
let n = extract_num(Some(&val)).unwrap_or(f64::NAN);
|
|
343
|
+
if n.is_nan() || n < 0.0 || n.fract() != 0.0 || n > 4_294_967_295.0 {
|
|
344
|
+
tishlang_core::set_pending_throw(tishlang_core::type_error("Invalid array length"));
|
|
345
|
+
return val;
|
|
346
|
+
}
|
|
347
|
+
let len = n as usize;
|
|
348
|
+
let mut arr_mut = arr.borrow_mut();
|
|
349
|
+
if len > arr_mut.len() {
|
|
350
|
+
// grow_or_throw indexes to len-1 (so len is in range); catchable on OOM.
|
|
351
|
+
let _ = grow_or_throw(&mut arr_mut, len - 1, Value::Null);
|
|
352
|
+
} else {
|
|
353
|
+
arr_mut.truncate(len);
|
|
354
|
+
}
|
|
355
|
+
val
|
|
356
|
+
}
|
|
357
|
+
Value::Struct(s) => {
|
|
358
|
+
s.borrow_mut().tish_set(key, val.clone());
|
|
359
|
+
val
|
|
360
|
+
}
|
|
361
|
+
_ => {
|
|
362
|
+
tishlang_core::set_pending_throw(tishlang_core::type_error("Cannot assign property on a non-object"));
|
|
363
|
+
val
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
pub fn set_index(obj: &Value, idx: &Value, val: Value) -> Value {
|
|
369
|
+
// Resolve the array index, or `None` for a non-index key (negative/fractional/huge number,
|
|
370
|
+
// non-numeric string, or non-string/number key). A `None` on an array is a no-op here
|
|
371
|
+
// rather than an abort: JS would set a named property, which a `Vec`-backed array can't hold.
|
|
372
|
+
let arr_index = |idx: &Value| -> Option<usize> {
|
|
373
|
+
match idx {
|
|
374
|
+
Value::Number(n) => array_index(*n),
|
|
375
|
+
Value::String(s) => tishlang_core::str_to_array_index(s),
|
|
376
|
+
_ => None,
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
match obj {
|
|
380
|
+
Value::Array(arr) => {
|
|
381
|
+
let Some(index) = arr_index(idx) else {
|
|
382
|
+
return val;
|
|
383
|
+
};
|
|
384
|
+
let mut arr_mut = arr.borrow_mut();
|
|
385
|
+
if !grow_or_throw(&mut arr_mut, index, Value::Null) {
|
|
386
|
+
return val;
|
|
387
|
+
}
|
|
388
|
+
arr_mut[index] = val.clone();
|
|
389
|
+
val
|
|
390
|
+
}
|
|
391
|
+
Value::NumberArray(arr) => {
|
|
392
|
+
let Some(index) = arr_index(idx) else {
|
|
393
|
+
return val;
|
|
394
|
+
};
|
|
395
|
+
let mut b = arr.borrow_mut();
|
|
396
|
+
match (b.as_packed_mut(), val.as_number()) {
|
|
397
|
+
(Some(nums), Some(n)) => {
|
|
398
|
+
if grow_or_throw(nums, index, f64::NAN) {
|
|
399
|
+
nums[index] = n;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
_ => {
|
|
403
|
+
let boxed = b.deopt();
|
|
404
|
+
if grow_or_throw(boxed, index, Value::Null) {
|
|
405
|
+
boxed[index] = val.clone();
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
val
|
|
410
|
+
}
|
|
411
|
+
Value::Object(map) => {
|
|
412
|
+
if map.borrow().frozen {
|
|
413
|
+
tishlang_core::set_pending_throw(tishlang_core::type_error(format!(
|
|
414
|
+
"Cannot assign to read only property '{}' of a frozen object",
|
|
415
|
+
idx.to_display_string()
|
|
416
|
+
)));
|
|
417
|
+
return val;
|
|
418
|
+
}
|
|
419
|
+
// object_set only accepts string/number/symbol keys; on any other key raise a
|
|
420
|
+
// CATCHABLE error rather than aborting (`.expect`) the console.
|
|
421
|
+
if tishlang_core::object_set(obj, idx, val.clone()).is_err() {
|
|
422
|
+
tishlang_core::set_pending_throw(tishlang_core::type_error(format!(
|
|
423
|
+
"cannot use {} as an object key",
|
|
424
|
+
idx.to_display_string()
|
|
425
|
+
)));
|
|
426
|
+
}
|
|
427
|
+
val
|
|
428
|
+
}
|
|
429
|
+
_ => {
|
|
430
|
+
tishlang_core::set_pending_throw(tishlang_core::type_error("Cannot index-assign on a non-array/object"));
|
|
431
|
+
val
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// ── console.* → mGBA debug log (agb::println!) ───────────────────────────────
|
|
437
|
+
fn console_line(args: &[Value]) -> String {
|
|
438
|
+
tishlang_core::format_values_for_console(args, false)
|
|
439
|
+
}
|
|
440
|
+
pub fn console_log(args: &[Value]) {
|
|
441
|
+
agb::println!("{}", console_line(args));
|
|
442
|
+
}
|
|
443
|
+
pub fn console_info(args: &[Value]) {
|
|
444
|
+
agb::println!("{}", console_line(args));
|
|
445
|
+
}
|
|
446
|
+
pub fn console_debug(args: &[Value]) {
|
|
447
|
+
agb::println!("{}", console_line(args));
|
|
448
|
+
}
|
|
449
|
+
pub fn console_warn(args: &[Value]) {
|
|
450
|
+
agb::println!("{}", console_line(args));
|
|
451
|
+
}
|
|
452
|
+
pub fn console_error(args: &[Value]) {
|
|
453
|
+
agb::println!("{}", console_line(args));
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// ── JSON (native-ABI wrappers over the core `&str`→Result / `&Value`→String fns) ──
|
|
457
|
+
pub fn json_parse(args: &[Value]) -> Value {
|
|
458
|
+
let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
|
|
459
|
+
tishlang_core::json_parse(&s).unwrap_or(Value::Null)
|
|
460
|
+
}
|
|
461
|
+
pub fn json_stringify(args: &[Value]) -> Value {
|
|
462
|
+
let v = args.first().cloned().unwrap_or(Value::Null);
|
|
463
|
+
Value::String(tishlang_core::json_stringify(&v).into())
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/// `json::{escape_into, write_json_number}` — the helpers codegen-emitted per-struct JSON serialisers
|
|
467
|
+
/// (`__tish_json_TishStruct_*`, #315) call. The std runtime exposes these under `tishlang_runtime::json`;
|
|
468
|
+
/// the GBA facade must mirror them so a game that uses a typed `interface`/`type` compiles. no_std.
|
|
469
|
+
pub mod json {
|
|
470
|
+
pub use tishlang_core::write_json_number;
|
|
471
|
+
|
|
472
|
+
/// Append the JSON-escaped contents of `s` (no surrounding quotes) to `buf`. Same escape rules as
|
|
473
|
+
/// the std runtime's `json::escape_into`; kept local (uses `core::fmt::Write` for `\uXXXX`).
|
|
474
|
+
pub fn escape_into(buf: &mut alloc::string::String, s: &str) {
|
|
475
|
+
use core::fmt::Write;
|
|
476
|
+
let bytes = s.as_bytes();
|
|
477
|
+
let mut start = 0usize;
|
|
478
|
+
for (i, &b) in bytes.iter().enumerate() {
|
|
479
|
+
if b < 0x20 || b == b'"' || b == b'\\' {
|
|
480
|
+
if start < i {
|
|
481
|
+
buf.push_str(&s[start..i]);
|
|
482
|
+
}
|
|
483
|
+
match b {
|
|
484
|
+
b'"' => buf.push_str("\\\""),
|
|
485
|
+
b'\\' => buf.push_str("\\\\"),
|
|
486
|
+
b'\n' => buf.push_str("\\n"),
|
|
487
|
+
b'\r' => buf.push_str("\\r"),
|
|
488
|
+
b'\t' => buf.push_str("\\t"),
|
|
489
|
+
b'\x08' => buf.push_str("\\b"),
|
|
490
|
+
b'\x0c' => buf.push_str("\\f"),
|
|
491
|
+
_ => {
|
|
492
|
+
let _ = write!(buf, "\\u{:04x}", b as u32);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
start = i + 1;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
if start < bytes.len() {
|
|
499
|
+
buf.push_str(&s[start..]);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/// Build an `ObjectMap` from an array of key/value pairs. Generated object literals
|
|
505
|
+
/// emit `ObjectMap::from([...])`; hashbrown's `From<[_; N]>` isn't available with a
|
|
506
|
+
/// custom hasher, so the Gba post-pass rewrites those calls to this.
|
|
507
|
+
pub fn object_map_from<const N: usize>(items: [(Arc<str>, Value); N]) -> ObjectMap {
|
|
508
|
+
let mut m = ObjectMap::default();
|
|
509
|
+
for (k, v) in items {
|
|
510
|
+
m.insert(k, v);
|
|
511
|
+
}
|
|
512
|
+
m
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/// f64 transcendentals for generated inline math (`Math.sqrt(x)` const-folds to
|
|
516
|
+
/// `x.sqrt()`); re-exported so generated code can `use tishlang_runtime::FloatExt`.
|
|
517
|
+
pub use tishlang_core::FloatExt;
|
|
518
|
+
|
|
519
|
+
/// Single-core interior-mutable static helper, re-exported for binding crates
|
|
520
|
+
/// (`tish-agb`) that keep hardware context in a `static`.
|
|
521
|
+
pub use tishlang_core::SingleCore;
|
|
522
|
+
|
|
523
|
+
/// Value arithmetic / comparison operators for generated `+`,`-`,… (ported from the
|
|
524
|
+
/// host runtime's `ops`; pure `Value` math, no_std-safe).
|
|
525
|
+
pub mod ops {
|
|
526
|
+
use crate::Value;
|
|
527
|
+
use alloc::string::String;
|
|
528
|
+
|
|
529
|
+
#[inline]
|
|
530
|
+
pub fn add(left: &Value, right: &Value) -> Value {
|
|
531
|
+
match (left, right) {
|
|
532
|
+
(Value::Number(a), Value::Number(b)) => Value::Number(a + b),
|
|
533
|
+
(Value::String(a), Value::String(b)) => {
|
|
534
|
+
let mut s = String::with_capacity(a.len() + b.len());
|
|
535
|
+
s.push_str(a);
|
|
536
|
+
s.push_str(b);
|
|
537
|
+
Value::String(s.into())
|
|
538
|
+
}
|
|
539
|
+
(Value::String(a), b) => {
|
|
540
|
+
let b_str = b.to_js_string();
|
|
541
|
+
let mut s = String::with_capacity(a.len() + b_str.len());
|
|
542
|
+
s.push_str(a);
|
|
543
|
+
s.push_str(&b_str);
|
|
544
|
+
Value::String(s.into())
|
|
545
|
+
}
|
|
546
|
+
(a, Value::String(b)) => {
|
|
547
|
+
let a_str = a.to_js_string();
|
|
548
|
+
let mut s = String::with_capacity(a_str.len() + b.len());
|
|
549
|
+
s.push_str(&a_str);
|
|
550
|
+
s.push_str(b);
|
|
551
|
+
Value::String(s.into())
|
|
552
|
+
}
|
|
553
|
+
(a, b) => Value::Number(
|
|
554
|
+
a.as_number().unwrap_or(f64::NAN) + b.as_number().unwrap_or(f64::NAN),
|
|
555
|
+
),
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
macro_rules! num_op {
|
|
560
|
+
($name:ident, $op:tt) => {
|
|
561
|
+
#[inline]
|
|
562
|
+
pub fn $name(left: &Value, right: &Value) -> Value {
|
|
563
|
+
match (left, right) {
|
|
564
|
+
(Value::Number(a), Value::Number(b)) => Value::Number(a $op b),
|
|
565
|
+
(a, b) => Value::Number(
|
|
566
|
+
a.as_number().unwrap_or(f64::NAN) $op b.as_number().unwrap_or(f64::NAN),
|
|
567
|
+
),
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
num_op!(sub, -);
|
|
573
|
+
num_op!(mul, *);
|
|
574
|
+
num_op!(div, /);
|
|
575
|
+
num_op!(modulo, %);
|
|
576
|
+
|
|
577
|
+
macro_rules! cmp_op {
|
|
578
|
+
($name:ident, $op:tt) => {
|
|
579
|
+
#[inline]
|
|
580
|
+
pub fn $name(left: &Value, right: &Value) -> Value {
|
|
581
|
+
let b = match (left, right) {
|
|
582
|
+
(Value::Number(a), Value::Number(b)) => a $op b,
|
|
583
|
+
(Value::String(a), Value::String(b)) => a.as_str() $op b.as_str(),
|
|
584
|
+
_ => false,
|
|
585
|
+
};
|
|
586
|
+
Value::Bool(b)
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
cmp_op!(lt, <);
|
|
591
|
+
cmp_op!(le, <=);
|
|
592
|
+
cmp_op!(gt, >);
|
|
593
|
+
cmp_op!(ge, >=);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// ── Globals / object / number / string / uri (all live in builtins::globals) ─
|
|
597
|
+
pub use tishlang_builtins::globals::{
|
|
598
|
+
array_from, array_is_array, array_of, boolean, decode_uri, decode_uri_component, encode_uri,
|
|
599
|
+
encode_uri_component, is_finite, is_nan, number_convert, number_is_finite, number_is_integer,
|
|
600
|
+
number_is_nan, number_is_safe_integer, object_assign, object_entries, object_freeze,
|
|
601
|
+
object_from_entries, object_has_own, object_is, object_is_frozen, object_keys, object_values,
|
|
602
|
+
parse_float, parse_int, string_convert, string_from_char_code, structured_clone,
|
|
603
|
+
};
|
|
604
|
+
pub use tishlang_builtins::string::escape_html as string_escape_html_impl;
|
|
605
|
+
|
|
606
|
+
// ── String methods (codegen lowers `.slice()`/`.indexOf()`/`.toLowerCase()`/… to these prelude
|
|
607
|
+
// names). Mirrors the std `tish_runtime` wrappers; the portable subset (no regex / unicode-normalize).
|
|
608
|
+
pub use tishlang_builtins::string::{
|
|
609
|
+
at as string_at_impl, char_at as string_char_at_impl,
|
|
610
|
+
char_code_at as string_char_code_at_impl, ends_with as string_ends_with_impl,
|
|
611
|
+
includes as string_includes_impl, index_of as string_index_of_impl,
|
|
612
|
+
slice as string_slice_impl, starts_with as string_starts_with_impl,
|
|
613
|
+
substr as string_substr_impl, substring as string_substring_impl,
|
|
614
|
+
to_lower_case as string_to_lower_case, to_upper_case as string_to_upper_case,
|
|
615
|
+
trim as string_trim, trim_end as string_trim_end, trim_start as string_trim_start,
|
|
616
|
+
};
|
|
617
|
+
#[inline]
|
|
618
|
+
pub fn string_index_of(s: &Value, search: &Value, from: &Value) -> Value {
|
|
619
|
+
string_index_of_impl(s, search, Some(from))
|
|
620
|
+
}
|
|
621
|
+
#[inline]
|
|
622
|
+
pub fn string_includes(s: &Value, search: &Value, from: &Value) -> Value {
|
|
623
|
+
string_includes_impl(s, search, Some(from))
|
|
624
|
+
}
|
|
625
|
+
#[inline]
|
|
626
|
+
pub fn string_slice(s: &Value, start: &Value, end: &Value) -> Value {
|
|
627
|
+
string_slice_impl(s, start, end)
|
|
628
|
+
}
|
|
629
|
+
#[inline]
|
|
630
|
+
pub fn string_substring(s: &Value, start: &Value, end: &Value) -> Value {
|
|
631
|
+
string_substring_impl(s, start, end)
|
|
632
|
+
}
|
|
633
|
+
#[inline]
|
|
634
|
+
pub fn string_substr(s: &Value, start: &Value, length: &Value) -> Value {
|
|
635
|
+
string_substr_impl(s, start, length)
|
|
636
|
+
}
|
|
637
|
+
#[inline]
|
|
638
|
+
pub fn string_starts_with(s: &Value, search: &Value, position: Option<&Value>) -> Value {
|
|
639
|
+
string_starts_with_impl(s, search, position)
|
|
640
|
+
}
|
|
641
|
+
#[inline]
|
|
642
|
+
pub fn string_ends_with(s: &Value, search: &Value, end_position: Option<&Value>) -> Value {
|
|
643
|
+
string_ends_with_impl(s, search, end_position)
|
|
644
|
+
}
|
|
645
|
+
#[inline]
|
|
646
|
+
pub fn string_char_at(s: &Value, idx: &Value) -> Value {
|
|
647
|
+
string_char_at_impl(s, idx)
|
|
648
|
+
}
|
|
649
|
+
#[inline]
|
|
650
|
+
pub fn string_at(s: &Value, idx: &Value) -> Value {
|
|
651
|
+
string_at_impl(s, idx)
|
|
652
|
+
}
|
|
653
|
+
#[inline]
|
|
654
|
+
pub fn string_char_code_at(s: &Value, idx: &Value) -> Value {
|
|
655
|
+
string_char_code_at_impl(s, idx)
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// ── Constructors, collections, typed arrays, symbol (re-export as prelude names) ──
|
|
659
|
+
pub use tishlang_builtins::construct::{
|
|
660
|
+
array_construct, audio_context_constructor_value as tish_audio_context_constructor,
|
|
661
|
+
construct as tish_construct, error_constructor_value as tish_error_constructor,
|
|
662
|
+
};
|
|
663
|
+
pub use tishlang_builtins::date::date_constructor_value as tish_date_constructor;
|
|
664
|
+
pub use tishlang_builtins::collections::{
|
|
665
|
+
map_constructor_value as tish_map_constructor, map_get, map_has, map_set, map_values,
|
|
666
|
+
set_constructor_value as tish_set_constructor,
|
|
667
|
+
};
|
|
668
|
+
// Array methods — codegen lowers `.push()`/`.slice()`/`.map()`/… to these `array_*` names. Mirrors
|
|
669
|
+
// the std `tish_runtime` block (minus RNG-dependent `shuffle`). Portable: all operate on Vec<Value>.
|
|
670
|
+
pub use tishlang_builtins::array::{
|
|
671
|
+
at as array_at, concat as array_concat_impl, every as array_every, filter as array_filter,
|
|
672
|
+
find as array_find, find_index as array_find_index, find_last as array_find_last,
|
|
673
|
+
find_last_index as array_find_last_index, flat as array_flat_impl, flat_map as array_flat_map,
|
|
674
|
+
for_each as array_for_each, includes as array_includes_impl, index_of as array_index_of_impl,
|
|
675
|
+
join as array_join_impl, map as array_map, pop as array_pop, push as array_push_impl,
|
|
676
|
+
fill as array_fill, last_index_of as array_last_index_of, copy_within as array_copy_within,
|
|
677
|
+
reduce as array_reduce, reduce_right as array_reduce_right, reverse as array_reverse,
|
|
678
|
+
keys as array_keys, values as array_values, entries as array_entries, shift as array_shift,
|
|
679
|
+
slice as array_slice_impl, snapshot_values as array_snapshot_values,
|
|
680
|
+
as_f64_snapshot as array_as_f64_snapshot, some as array_some,
|
|
681
|
+
sort_by_keys as array_sort_by_keys, sort_default as array_sort_default,
|
|
682
|
+
sort_numeric_asc as array_sort_numeric_asc, sort_numeric_desc as array_sort_numeric_desc,
|
|
683
|
+
sort_with_comparator as array_sort_with_comparator, splice as array_splice_impl,
|
|
684
|
+
to_reversed as array_to_reversed, to_sorted as array_to_sorted,
|
|
685
|
+
to_spliced as array_to_spliced, with as array_with, unshift as array_unshift_impl,
|
|
686
|
+
};
|
|
687
|
+
#[inline]
|
|
688
|
+
pub fn array_push(arr: &Value, args: &[Value]) -> Value { array_push_impl(arr, args) }
|
|
689
|
+
#[inline]
|
|
690
|
+
pub fn array_unshift(arr: &Value, args: &[Value]) -> Value { array_unshift_impl(arr, args) }
|
|
691
|
+
#[inline]
|
|
692
|
+
pub fn array_index_of(arr: &Value, search: &Value, from: Option<&Value>) -> Value {
|
|
693
|
+
array_index_of_impl(arr, search, from)
|
|
694
|
+
}
|
|
695
|
+
#[inline]
|
|
696
|
+
pub fn array_includes(arr: &Value, search: &Value, from: &Value) -> Value {
|
|
697
|
+
array_includes_impl(arr, search, Some(from))
|
|
698
|
+
}
|
|
699
|
+
#[inline]
|
|
700
|
+
pub fn array_join(arr: &Value, sep: &Value) -> Value { array_join_impl(arr, sep) }
|
|
701
|
+
#[inline]
|
|
702
|
+
pub fn array_splice(arr: &Value, start: &Value, delete_count: Option<&Value>, items: &[Value]) -> Value {
|
|
703
|
+
array_splice_impl(arr, start, delete_count, items)
|
|
704
|
+
}
|
|
705
|
+
#[inline]
|
|
706
|
+
pub fn array_slice(arr: &Value, start: &Value, end: &Value) -> Value {
|
|
707
|
+
array_slice_impl(arr, start, end)
|
|
708
|
+
}
|
|
709
|
+
#[inline]
|
|
710
|
+
pub fn array_concat(arr: &Value, args: &[Value]) -> Value { array_concat_impl(arr, args) }
|
|
711
|
+
#[inline]
|
|
712
|
+
pub fn array_flat(arr: &Value, depth: &Value) -> Value { array_flat_impl(arr, depth) }
|
|
713
|
+
#[inline]
|
|
714
|
+
pub fn array_sort(arr: &Value, comparator: Option<&Value>) -> Value {
|
|
715
|
+
match comparator {
|
|
716
|
+
Some(cmp) => array_sort_with_comparator(arr, cmp),
|
|
717
|
+
None => array_sort_default(arr),
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
/// `.at(i)` dispatched on the runtime value — exists on both String and Array (#247).
|
|
721
|
+
#[inline]
|
|
722
|
+
pub fn value_at(recv: &Value, idx: &Value) -> Value {
|
|
723
|
+
match recv {
|
|
724
|
+
Value::String(_) => string_at_impl(recv, idx),
|
|
725
|
+
_ => array_at(recv, idx),
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
pub use tishlang_builtins::symbol::symbol_object;
|
|
729
|
+
pub use tishlang_builtins::typedarrays::{
|
|
730
|
+
float32_array_constructor_value as tish_float32_array_constructor,
|
|
731
|
+
float64_array_constructor_value as tish_float64_array_constructor,
|
|
732
|
+
int16_array_constructor_value as tish_int16_array_constructor,
|
|
733
|
+
int32_array_constructor_value as tish_int32_array_constructor,
|
|
734
|
+
int8_array_constructor_value as tish_int8_array_constructor,
|
|
735
|
+
uint16_array_constructor_value as tish_uint16_array_constructor,
|
|
736
|
+
uint32_array_constructor_value as tish_uint32_array_constructor,
|
|
737
|
+
uint8_array_constructor_value as tish_uint8_array_constructor,
|
|
738
|
+
uint8_clamped_array_constructor_value as tish_uint8_clamped_array_constructor,
|
|
739
|
+
};
|
|
740
|
+
|
|
741
|
+
// ── Math (thin wrappers preserving the `math_*` prelude naming) ──────────────
|
|
742
|
+
macro_rules! math_fwd {
|
|
743
|
+
($($name:ident => $path:path),* $(,)?) => {
|
|
744
|
+
$(
|
|
745
|
+
#[inline]
|
|
746
|
+
pub fn $name(args: &[Value]) -> Value { $path(args) }
|
|
747
|
+
)*
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
math_fwd! {
|
|
751
|
+
math_abs => tishlang_builtins::math::abs,
|
|
752
|
+
math_ceil => tishlang_builtins::math::ceil,
|
|
753
|
+
math_floor => tishlang_builtins::math::floor,
|
|
754
|
+
math_round => tishlang_builtins::math::round,
|
|
755
|
+
math_sqrt => tishlang_builtins::math::sqrt,
|
|
756
|
+
math_sin => tishlang_builtins::math::sin,
|
|
757
|
+
math_cos => tishlang_builtins::math::cos,
|
|
758
|
+
math_tan => tishlang_builtins::math::tan,
|
|
759
|
+
math_asin => tishlang_builtins::math::asin,
|
|
760
|
+
math_acos => tishlang_builtins::math::acos,
|
|
761
|
+
math_atan => tishlang_builtins::math::atan,
|
|
762
|
+
math_atan2 => tishlang_builtins::math::atan2,
|
|
763
|
+
math_log => tishlang_builtins::math::log,
|
|
764
|
+
math_log2 => tishlang_builtins::math::log2,
|
|
765
|
+
math_log10 => tishlang_builtins::math::log10,
|
|
766
|
+
math_exp => tishlang_builtins::math::exp,
|
|
767
|
+
math_expm1 => tishlang_builtins::math::expm1,
|
|
768
|
+
math_log1p => tishlang_builtins::math::log1p,
|
|
769
|
+
math_cbrt => tishlang_builtins::math::cbrt,
|
|
770
|
+
math_trunc => tishlang_builtins::math::trunc,
|
|
771
|
+
math_sign => tishlang_builtins::math::sign,
|
|
772
|
+
math_pow => tishlang_builtins::math::pow,
|
|
773
|
+
math_max => tishlang_builtins::math::max,
|
|
774
|
+
math_min => tishlang_builtins::math::min,
|
|
775
|
+
math_hypot => tishlang_builtins::math::hypot,
|
|
776
|
+
math_imul => tishlang_builtins::math::imul,
|
|
777
|
+
math_clz32 => tishlang_builtins::math::clz32,
|
|
778
|
+
math_fround => tishlang_builtins::math::fround,
|
|
779
|
+
math_random => tishlang_builtins::math::random,
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// Hyperbolic + inverse-hyperbolic: not in builtins::math; computed via FloatExt (libm,
|
|
783
|
+
// already in scope from the `pub use` above).
|
|
784
|
+
macro_rules! math_unary_libm {
|
|
785
|
+
($($name:ident => $m:ident),* $(,)?) => {
|
|
786
|
+
$(
|
|
787
|
+
#[inline]
|
|
788
|
+
pub fn $name(args: &[Value]) -> Value {
|
|
789
|
+
let n = match args.first() { Some(Value::Number(n)) => *n, _ => f64::NAN };
|
|
790
|
+
Value::Number(n.$m())
|
|
791
|
+
}
|
|
792
|
+
)*
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
math_unary_libm! {
|
|
796
|
+
math_sinh => sinh,
|
|
797
|
+
math_cosh => cosh,
|
|
798
|
+
math_tanh => tanh,
|
|
799
|
+
math_asinh => asinh,
|
|
800
|
+
math_acosh => acosh,
|
|
801
|
+
math_atanh => atanh,
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
pub mod gba;
|