@tishlang/tish-format 2.39.0 → 2.43.8
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-format +0 -0
- package/crates/tish/src/cli_help.rs +28 -0
- package/crates/tish/src/main.rs +41 -5
- 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 +38 -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 +806 -46
- 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 +148 -10
- package/crates/tish_compile/src/schemes.rs +278 -0
- package/crates/tish_compile/src/types.rs +141 -2
- 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_gba_numerics_gated.rs +58 -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 +19 -1
- package/crates/tish_core/src/json.rs +34 -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 +182 -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_fmt/Cargo.toml +1 -1
- package/crates/tish_lsp/Cargo.toml +2 -0
- 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 +54 -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 +620 -0
- package/crates/tish_vm/src/jit.rs +1446 -164
- package/crates/tish_vm/src/vm.rs +535 -173
- package/justfile +9 -0
- package/package.json +1 -1
- package/platform/darwin-arm64/tish-fmt +0 -0
- package/platform/darwin-x64/tish-fmt +0 -0
- package/platform/linux-arm64/tish-fmt +0 -0
- package/platform/linux-x64/tish-fmt +0 -0
- package/platform/win32-x64/tish-fmt.exe +0 -0
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
//! Portability layer for the `portable` (no_std + alloc) build.
|
|
2
|
+
//!
|
|
3
|
+
//! Several items here (the lock/once shims, the clock/RNG hooks) are consumed by
|
|
4
|
+
//! `tishlang_builtins` and the GBA facade rather than by `tishlang_core` itself,
|
|
5
|
+
//! so within this crate they read as unused.
|
|
6
|
+
#![allow(dead_code, unused_imports)]
|
|
7
|
+
|
|
8
|
+
//!
|
|
9
|
+
//! Every use-site that would otherwise reach for a std-only or atomics-bearing
|
|
10
|
+
//! primitive imports it from here instead (`crate::compat::{Arc, ArcStr,
|
|
11
|
+
//! AHashMap, RandomState, AtomicU64, ...}`), so the *same source* compiles for
|
|
12
|
+
//! the host and for `thumbv4t-none-eabi` (Game Boy Advance).
|
|
13
|
+
//!
|
|
14
|
+
//! - **default (std)**: transparent re-exports of `std::sync::Arc`, `ahash`,
|
|
15
|
+
//! `arcstr`, `std` atomics — behaviour is byte-for-byte the pre-split build.
|
|
16
|
+
//! - **`portable`**: atomics-free, alloc-based equivalents — `Rc` for `Arc`,
|
|
17
|
+
//! `FxHasher` for `ahash` (foldhash/ahash both need atomics; see P0 findings),
|
|
18
|
+
//! `portable-atomic` for the one `AtomicU64`, an `Rc<str>` `ArcStr`, and a
|
|
19
|
+
//! `FloatExt` trait supplying the f64 transcendentals `core` lacks.
|
|
20
|
+
//!
|
|
21
|
+
//! GBA is single-core with cooperative scheduling and no preemptive interrupts
|
|
22
|
+
//! touching interpreter state, so the process-global singletons that are
|
|
23
|
+
//! `thread_local!` on the host become single-threaded statics here.
|
|
24
|
+
|
|
25
|
+
// ===========================================================================
|
|
26
|
+
// Standard (hosted) backing — transparent re-exports.
|
|
27
|
+
// ===========================================================================
|
|
28
|
+
#[cfg(not(feature = "portable"))]
|
|
29
|
+
pub use std_backing::*;
|
|
30
|
+
|
|
31
|
+
#[cfg(not(feature = "portable"))]
|
|
32
|
+
mod std_backing {
|
|
33
|
+
pub use ahash::{AHashMap, RandomState};
|
|
34
|
+
pub use arcstr::ArcStr;
|
|
35
|
+
pub use std::sync::atomic::{AtomicU64, Ordering};
|
|
36
|
+
pub use std::sync::{Arc, Mutex, OnceLock, RwLock};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ===========================================================================
|
|
40
|
+
// Portable (no_std + alloc) backing.
|
|
41
|
+
// ===========================================================================
|
|
42
|
+
#[cfg(feature = "portable")]
|
|
43
|
+
pub use portable_backing::*;
|
|
44
|
+
|
|
45
|
+
#[cfg(feature = "portable")]
|
|
46
|
+
mod portable_backing {
|
|
47
|
+
use alloc::rc::Rc;
|
|
48
|
+
|
|
49
|
+
/// Single-core target: reference counting needs no atomics.
|
|
50
|
+
pub use alloc::rc::Rc as Arc;
|
|
51
|
+
|
|
52
|
+
/// 64-bit atomic via `portable-atomic` (critical-section backed on GBA;
|
|
53
|
+
/// the impl is supplied at final link by agb). Only used for the symbol-id
|
|
54
|
+
/// counter, which on a single core is really just a `Cell`, but keeping the
|
|
55
|
+
/// atomic type keeps the source identical to the std path.
|
|
56
|
+
pub use portable_atomic::{AtomicU64, Ordering};
|
|
57
|
+
|
|
58
|
+
/// FxHasher — deterministic, atomics-free, and exactly what `agb_hashmap`
|
|
59
|
+
/// uses on GBA. Drop-in for `ahash::RandomState`.
|
|
60
|
+
pub type RandomState = rustc_hash::FxBuildHasher;
|
|
61
|
+
|
|
62
|
+
/// Drop-in for `ahash::AHashMap<K, V>` (same `default()` / `get` / `insert`
|
|
63
|
+
/// / `iter` / `entry` surface), hashed with FxHasher.
|
|
64
|
+
pub type AHashMap<K, V> = hashbrown::HashMap<K, V, RandomState>;
|
|
65
|
+
|
|
66
|
+
// -- ArcStr: an immutable, cheaply-cloneable string. On the host this is
|
|
67
|
+
// `arcstr::ArcStr` (thin, atomic-refcounted); here it wraps `Rc<str>`.
|
|
68
|
+
// `Rc<str>` is a 16-byte fat pointer, which still fits the `Value`
|
|
69
|
+
// 24-byte budget (the `Arc<dyn ...>` variants already set that width).
|
|
70
|
+
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
71
|
+
pub struct ArcStr(Rc<str>);
|
|
72
|
+
|
|
73
|
+
impl ArcStr {
|
|
74
|
+
#[inline]
|
|
75
|
+
pub fn as_str(&self) -> &str {
|
|
76
|
+
&self.0
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
impl Default for ArcStr {
|
|
81
|
+
#[inline]
|
|
82
|
+
fn default() -> Self {
|
|
83
|
+
ArcStr(Rc::from(""))
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
impl core::ops::Deref for ArcStr {
|
|
88
|
+
type Target = str;
|
|
89
|
+
#[inline]
|
|
90
|
+
fn deref(&self) -> &str {
|
|
91
|
+
&self.0
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
impl core::convert::AsRef<str> for ArcStr {
|
|
96
|
+
#[inline]
|
|
97
|
+
fn as_ref(&self) -> &str {
|
|
98
|
+
&self.0
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
impl core::borrow::Borrow<str> for ArcStr {
|
|
103
|
+
#[inline]
|
|
104
|
+
fn borrow(&self) -> &str {
|
|
105
|
+
&self.0
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
impl From<&str> for ArcStr {
|
|
110
|
+
#[inline]
|
|
111
|
+
fn from(s: &str) -> Self {
|
|
112
|
+
ArcStr(Rc::from(s))
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
impl From<alloc::string::String> for ArcStr {
|
|
117
|
+
#[inline]
|
|
118
|
+
fn from(s: alloc::string::String) -> Self {
|
|
119
|
+
ArcStr(Rc::from(s.as_str()))
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
impl From<&alloc::string::String> for ArcStr {
|
|
124
|
+
#[inline]
|
|
125
|
+
fn from(s: &alloc::string::String) -> Self {
|
|
126
|
+
ArcStr(Rc::from(s.as_str()))
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
impl From<Rc<str>> for ArcStr {
|
|
131
|
+
#[inline]
|
|
132
|
+
fn from(s: Rc<str>) -> Self {
|
|
133
|
+
ArcStr(s)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
impl core::fmt::Display for ArcStr {
|
|
138
|
+
#[inline]
|
|
139
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
140
|
+
f.write_str(&self.0)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
impl core::fmt::Debug for ArcStr {
|
|
145
|
+
#[inline]
|
|
146
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
147
|
+
core::fmt::Debug::fmt(self.as_str(), f)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
impl PartialEq<str> for ArcStr {
|
|
152
|
+
#[inline]
|
|
153
|
+
fn eq(&self, other: &str) -> bool {
|
|
154
|
+
self.as_str() == other
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
impl PartialEq<&str> for ArcStr {
|
|
159
|
+
#[inline]
|
|
160
|
+
fn eq(&self, other: &&str) -> bool {
|
|
161
|
+
self.as_str() == *other
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// -- Single-core lock/once shims. GBA runs one cooperative thread, so these
|
|
166
|
+
// never actually contend; they exist only to keep the std API surface
|
|
167
|
+
// (`.read()`/`.write()`/`.lock()`/`.get_or_init()`) at call sites.
|
|
168
|
+
use core::cell::{Ref, RefCell, RefMut};
|
|
169
|
+
|
|
170
|
+
/// Never-poisoned, never-contended stand-in for `std::sync::RwLock`.
|
|
171
|
+
pub struct RwLock<T>(RefCell<T>);
|
|
172
|
+
impl<T> RwLock<T> {
|
|
173
|
+
#[inline]
|
|
174
|
+
pub const fn new(v: T) -> Self {
|
|
175
|
+
RwLock(RefCell::new(v))
|
|
176
|
+
}
|
|
177
|
+
#[inline]
|
|
178
|
+
pub fn read(&self) -> Result<Ref<'_, T>, core::convert::Infallible> {
|
|
179
|
+
Ok(self.0.borrow())
|
|
180
|
+
}
|
|
181
|
+
#[inline]
|
|
182
|
+
pub fn write(&self) -> Result<RefMut<'_, T>, core::convert::Infallible> {
|
|
183
|
+
Ok(self.0.borrow_mut())
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/// Stand-in for `std::sync::Mutex` (`.lock()` yields a mut guard).
|
|
188
|
+
pub struct Mutex<T>(RefCell<T>);
|
|
189
|
+
impl<T> Mutex<T> {
|
|
190
|
+
#[inline]
|
|
191
|
+
pub const fn new(v: T) -> Self {
|
|
192
|
+
Mutex(RefCell::new(v))
|
|
193
|
+
}
|
|
194
|
+
#[inline]
|
|
195
|
+
pub fn lock(&self) -> Result<RefMut<'_, T>, core::convert::Infallible> {
|
|
196
|
+
Ok(self.0.borrow_mut())
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/// Stand-in for `std::sync::OnceLock` (`new`/`get`/`get_or_init`/`set`). The GBA has no atomics,
|
|
201
|
+
/// so std's atomic `OnceLock` is unavailable; this wraps the SAFE single-threaded
|
|
202
|
+
/// `core::cell::OnceCell` (which does the write-once interior mutability without hand-rolled
|
|
203
|
+
/// `unsafe`) and only adds `unsafe impl Sync` so it can live in a `static` — sound because the
|
|
204
|
+
/// target is single-core and cooperative, so no concurrent access ever occurs (see `SingleCore`).
|
|
205
|
+
pub struct OnceLock<T>(core::cell::OnceCell<T>);
|
|
206
|
+
// SAFETY: single-core, cooperative; no concurrent access.
|
|
207
|
+
unsafe impl<T> Sync for OnceLock<T> {}
|
|
208
|
+
impl<T> OnceLock<T> {
|
|
209
|
+
#[inline]
|
|
210
|
+
pub const fn new() -> Self {
|
|
211
|
+
OnceLock(core::cell::OnceCell::new())
|
|
212
|
+
}
|
|
213
|
+
#[inline]
|
|
214
|
+
pub fn get(&self) -> Option<&T> {
|
|
215
|
+
self.0.get()
|
|
216
|
+
}
|
|
217
|
+
#[inline]
|
|
218
|
+
pub fn set(&self, value: T) -> Result<(), T> {
|
|
219
|
+
self.0.set(value)
|
|
220
|
+
}
|
|
221
|
+
#[inline]
|
|
222
|
+
pub fn get_or_init<F: FnOnce() -> T>(&self, f: F) -> &T {
|
|
223
|
+
self.0.get_or_init(f)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
impl<T> Default for OnceLock<T> {
|
|
227
|
+
#[inline]
|
|
228
|
+
fn default() -> Self {
|
|
229
|
+
Self::new()
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ===========================================================================
|
|
235
|
+
// f64 transcendentals — `core` lacks them; libm supplies them under `portable`.
|
|
236
|
+
//
|
|
237
|
+
// A trait (not free fns) so the SAME `n.abs()` / `n.floor()` source compiles
|
|
238
|
+
// both ways: on the host the inherent `f64` method wins; under `portable` (no
|
|
239
|
+
// inherent methods exist) the trait method is used. Import it only under
|
|
240
|
+
// `portable` at each float use-site to avoid an unused-import warning on host.
|
|
241
|
+
// ===========================================================================
|
|
242
|
+
#[cfg(feature = "portable")]
|
|
243
|
+
pub trait FloatExt {
|
|
244
|
+
fn abs(self) -> Self;
|
|
245
|
+
fn floor(self) -> Self;
|
|
246
|
+
fn ceil(self) -> Self;
|
|
247
|
+
fn trunc(self) -> Self;
|
|
248
|
+
fn fract(self) -> Self;
|
|
249
|
+
fn sqrt(self) -> Self;
|
|
250
|
+
fn cbrt(self) -> Self;
|
|
251
|
+
fn sin(self) -> Self;
|
|
252
|
+
fn cos(self) -> Self;
|
|
253
|
+
fn tan(self) -> Self;
|
|
254
|
+
fn asin(self) -> Self;
|
|
255
|
+
fn acos(self) -> Self;
|
|
256
|
+
fn atan(self) -> Self;
|
|
257
|
+
fn atan2(self, other: Self) -> Self;
|
|
258
|
+
fn ln(self) -> Self;
|
|
259
|
+
fn log10(self) -> Self;
|
|
260
|
+
fn log2(self) -> Self;
|
|
261
|
+
fn exp(self) -> Self;
|
|
262
|
+
fn exp_m1(self) -> Self;
|
|
263
|
+
fn ln_1p(self) -> Self;
|
|
264
|
+
fn powf(self, n: Self) -> Self;
|
|
265
|
+
fn powi(self, n: i32) -> Self;
|
|
266
|
+
fn rem_euclid(self, m: Self) -> Self;
|
|
267
|
+
fn hypot(self, other: Self) -> Self;
|
|
268
|
+
fn round_ties_even(self) -> Self;
|
|
269
|
+
fn sinh(self) -> Self;
|
|
270
|
+
fn cosh(self) -> Self;
|
|
271
|
+
fn tanh(self) -> Self;
|
|
272
|
+
fn asinh(self) -> Self;
|
|
273
|
+
fn acosh(self) -> Self;
|
|
274
|
+
fn atanh(self) -> Self;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
#[cfg(feature = "portable")]
|
|
278
|
+
impl FloatExt for f64 {
|
|
279
|
+
#[inline]
|
|
280
|
+
fn abs(self) -> f64 {
|
|
281
|
+
libm::fabs(self)
|
|
282
|
+
}
|
|
283
|
+
#[inline]
|
|
284
|
+
fn floor(self) -> f64 {
|
|
285
|
+
libm::floor(self)
|
|
286
|
+
}
|
|
287
|
+
#[inline]
|
|
288
|
+
fn ceil(self) -> f64 {
|
|
289
|
+
libm::ceil(self)
|
|
290
|
+
}
|
|
291
|
+
#[inline]
|
|
292
|
+
fn trunc(self) -> f64 {
|
|
293
|
+
libm::trunc(self)
|
|
294
|
+
}
|
|
295
|
+
#[inline]
|
|
296
|
+
fn fract(self) -> f64 {
|
|
297
|
+
self - libm::trunc(self)
|
|
298
|
+
}
|
|
299
|
+
#[inline]
|
|
300
|
+
fn sqrt(self) -> f64 {
|
|
301
|
+
libm::sqrt(self)
|
|
302
|
+
}
|
|
303
|
+
#[inline]
|
|
304
|
+
fn cbrt(self) -> f64 {
|
|
305
|
+
libm::cbrt(self)
|
|
306
|
+
}
|
|
307
|
+
#[inline]
|
|
308
|
+
fn sin(self) -> f64 {
|
|
309
|
+
libm::sin(self)
|
|
310
|
+
}
|
|
311
|
+
#[inline]
|
|
312
|
+
fn cos(self) -> f64 {
|
|
313
|
+
libm::cos(self)
|
|
314
|
+
}
|
|
315
|
+
#[inline]
|
|
316
|
+
fn tan(self) -> f64 {
|
|
317
|
+
libm::tan(self)
|
|
318
|
+
}
|
|
319
|
+
#[inline]
|
|
320
|
+
fn asin(self) -> f64 {
|
|
321
|
+
libm::asin(self)
|
|
322
|
+
}
|
|
323
|
+
#[inline]
|
|
324
|
+
fn acos(self) -> f64 {
|
|
325
|
+
libm::acos(self)
|
|
326
|
+
}
|
|
327
|
+
#[inline]
|
|
328
|
+
fn atan(self) -> f64 {
|
|
329
|
+
libm::atan(self)
|
|
330
|
+
}
|
|
331
|
+
#[inline]
|
|
332
|
+
fn atan2(self, other: f64) -> f64 {
|
|
333
|
+
libm::atan2(self, other)
|
|
334
|
+
}
|
|
335
|
+
#[inline]
|
|
336
|
+
fn ln(self) -> f64 {
|
|
337
|
+
libm::log(self)
|
|
338
|
+
}
|
|
339
|
+
#[inline]
|
|
340
|
+
fn log10(self) -> f64 {
|
|
341
|
+
libm::log10(self)
|
|
342
|
+
}
|
|
343
|
+
#[inline]
|
|
344
|
+
fn log2(self) -> f64 {
|
|
345
|
+
libm::log2(self)
|
|
346
|
+
}
|
|
347
|
+
#[inline]
|
|
348
|
+
fn exp(self) -> f64 {
|
|
349
|
+
libm::exp(self)
|
|
350
|
+
}
|
|
351
|
+
#[inline]
|
|
352
|
+
fn exp_m1(self) -> f64 {
|
|
353
|
+
libm::expm1(self)
|
|
354
|
+
}
|
|
355
|
+
#[inline]
|
|
356
|
+
fn ln_1p(self) -> f64 {
|
|
357
|
+
libm::log1p(self)
|
|
358
|
+
}
|
|
359
|
+
#[inline]
|
|
360
|
+
fn powf(self, n: f64) -> f64 {
|
|
361
|
+
libm::pow(self, n)
|
|
362
|
+
}
|
|
363
|
+
#[inline]
|
|
364
|
+
fn powi(self, n: i32) -> f64 {
|
|
365
|
+
libm::pow(self, n as f64)
|
|
366
|
+
}
|
|
367
|
+
#[inline]
|
|
368
|
+
fn rem_euclid(self, m: f64) -> f64 {
|
|
369
|
+
let r = libm::fmod(self, m);
|
|
370
|
+
if r < 0.0 {
|
|
371
|
+
r + libm::fabs(m)
|
|
372
|
+
} else {
|
|
373
|
+
r
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
#[inline]
|
|
377
|
+
fn hypot(self, other: f64) -> f64 {
|
|
378
|
+
libm::hypot(self, other)
|
|
379
|
+
}
|
|
380
|
+
#[inline]
|
|
381
|
+
fn round_ties_even(self) -> f64 {
|
|
382
|
+
// Round to nearest, ties to even — matches std's `f64::round_ties_even`.
|
|
383
|
+
libm::rint(self)
|
|
384
|
+
}
|
|
385
|
+
#[inline]
|
|
386
|
+
fn sinh(self) -> f64 {
|
|
387
|
+
libm::sinh(self)
|
|
388
|
+
}
|
|
389
|
+
#[inline]
|
|
390
|
+
fn cosh(self) -> f64 {
|
|
391
|
+
libm::cosh(self)
|
|
392
|
+
}
|
|
393
|
+
#[inline]
|
|
394
|
+
fn tanh(self) -> f64 {
|
|
395
|
+
libm::tanh(self)
|
|
396
|
+
}
|
|
397
|
+
#[inline]
|
|
398
|
+
fn asinh(self) -> f64 {
|
|
399
|
+
libm::asinh(self)
|
|
400
|
+
}
|
|
401
|
+
#[inline]
|
|
402
|
+
fn acosh(self) -> f64 {
|
|
403
|
+
libm::acosh(self)
|
|
404
|
+
}
|
|
405
|
+
#[inline]
|
|
406
|
+
fn atanh(self) -> f64 {
|
|
407
|
+
libm::atanh(self)
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// ===========================================================================
|
|
412
|
+
// Single-core "thread local": a plain static behind a `.with()` API matching
|
|
413
|
+
// `std::thread::LocalKey`, so declaration sites cfg-swap but call sites don't.
|
|
414
|
+
// Sound because GBA runs one cooperative thread and no interrupt handler
|
|
415
|
+
// touches interpreter state.
|
|
416
|
+
// ===========================================================================
|
|
417
|
+
#[cfg(feature = "portable")]
|
|
418
|
+
pub struct SingleCore<T>(T);
|
|
419
|
+
|
|
420
|
+
#[cfg(feature = "portable")]
|
|
421
|
+
// SAFETY: single-core, cooperative, no interrupt handler reenters interpreter
|
|
422
|
+
// state. Access is never concurrent.
|
|
423
|
+
unsafe impl<T> Sync for SingleCore<T> {}
|
|
424
|
+
|
|
425
|
+
#[cfg(feature = "portable")]
|
|
426
|
+
impl<T> SingleCore<T> {
|
|
427
|
+
pub const fn new(value: T) -> Self {
|
|
428
|
+
SingleCore(value)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/// Mirrors `LocalKey::with`: hand out a shared ref to the inner value. Interior mutability lives
|
|
432
|
+
/// in `T` (a `RefCell`), so a plain `&self.0` suffices — no `unsafe` block needed here; only the
|
|
433
|
+
/// `unsafe impl Sync` above, to place a `!Sync` `T` in a `static` on this single-core target.
|
|
434
|
+
#[inline]
|
|
435
|
+
pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
|
|
436
|
+
f(&self.0)
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// ===========================================================================
|
|
441
|
+
// Injectable host hooks (clock + RNG). On the host these are unused (std
|
|
442
|
+
// provides SystemTime / rand); under `portable` the facade (`tishlang_runtime_gba`)
|
|
443
|
+
// installs real sources — agb's timers for the clock, agb's RNG for randomness —
|
|
444
|
+
// and until it does they degrade to deterministic defaults instead of panicking.
|
|
445
|
+
// ===========================================================================
|
|
446
|
+
#[cfg(feature = "portable")]
|
|
447
|
+
mod hooks {
|
|
448
|
+
use super::SingleCore;
|
|
449
|
+
use core::cell::Cell;
|
|
450
|
+
|
|
451
|
+
static NOW_MS: SingleCore<Cell<fn() -> f64>> = SingleCore::new(Cell::new(default_now_ms));
|
|
452
|
+
static RNG_U64: SingleCore<Cell<fn() -> u64>> = SingleCore::new(Cell::new(default_rng));
|
|
453
|
+
static RNG_STATE: SingleCore<Cell<u64>> = SingleCore::new(Cell::new(0x2545_F491_4F6C_DD1D));
|
|
454
|
+
|
|
455
|
+
// Cheap deterministic fallbacks until the facade (tishlang_runtime_gba)
|
|
456
|
+
// installs agb-backed sources.
|
|
457
|
+
fn default_now_ms() -> f64 {
|
|
458
|
+
0.0
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
fn default_rng() -> u64 {
|
|
462
|
+
// xorshift64* — deterministic PRNG for a target with no entropy source.
|
|
463
|
+
RNG_STATE.with(|c| {
|
|
464
|
+
let mut x = c.get();
|
|
465
|
+
x ^= x >> 12;
|
|
466
|
+
x ^= x << 25;
|
|
467
|
+
x ^= x >> 27;
|
|
468
|
+
c.set(x);
|
|
469
|
+
x.wrapping_mul(0x2545_F491_4F6C_DD1D)
|
|
470
|
+
})
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/// Facade hook: install a real millisecond clock (e.g. agb timer derived).
|
|
474
|
+
pub fn install_clock(f: fn() -> f64) {
|
|
475
|
+
NOW_MS.with(|c| c.set(f));
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/// Facade hook: install a real RNG source (e.g. `agb::rng`).
|
|
479
|
+
pub fn install_rng(f: fn() -> u64) {
|
|
480
|
+
RNG_U64.with(|c| c.set(f));
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/// Facade hook: reseed the fallback xorshift PRNG.
|
|
484
|
+
pub fn seed_rng(seed: u64) {
|
|
485
|
+
RNG_STATE.with(|c| c.set(seed | 1));
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/// Milliseconds since an arbitrary epoch, for `Date.now()`.
|
|
489
|
+
pub fn now_ms() -> f64 {
|
|
490
|
+
NOW_MS.with(|c| (c.get())())
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/// A raw random `u64`.
|
|
494
|
+
pub fn next_u64() -> u64 {
|
|
495
|
+
RNG_U64.with(|c| (c.get())())
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/// A uniform `f64` in [0, 1), for `Math.random()`.
|
|
499
|
+
pub fn random_f64() -> f64 {
|
|
500
|
+
// 53 mantissa bits → [0, 1).
|
|
501
|
+
(next_u64() >> 11) as f64 * (1.0 / (1u64 << 53) as f64)
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
#[cfg(feature = "portable")]
|
|
506
|
+
pub use hooks::{install_clock, install_rng, next_u64, now_ms, random_f64, seed_rng};
|
|
@@ -6,11 +6,20 @@
|
|
|
6
6
|
//! `console.log` prints string arguments without surrounding quotes (like Node); nested
|
|
7
7
|
//! strings inside arrays/objects stay quoted. The REPL still quotes string results for clarity.
|
|
8
8
|
|
|
9
|
+
#[cfg(not(feature = "portable"))]
|
|
9
10
|
use std::io::IsTerminal;
|
|
11
|
+
#[cfg(not(feature = "portable"))]
|
|
10
12
|
use std::sync::OnceLock;
|
|
11
13
|
|
|
12
14
|
use crate::Value;
|
|
15
|
+
#[cfg(feature = "portable")]
|
|
16
|
+
use alloc::{
|
|
17
|
+
format,
|
|
18
|
+
string::{String, ToString},
|
|
19
|
+
vec::Vec,
|
|
20
|
+
};
|
|
13
21
|
|
|
22
|
+
#[cfg(not(feature = "portable"))]
|
|
14
23
|
static CONSOLE_USES_COLORS: OnceLock<bool> = OnceLock::new();
|
|
15
24
|
|
|
16
25
|
/// ANSI escape codes (standard 4-bit + bright black for dim).
|
|
@@ -34,10 +43,18 @@ const SPECIAL: &str = "\x1b[90m";
|
|
|
34
43
|
///
|
|
35
44
|
/// Cached for the process lifetime. `is_terminal()` is a syscall; benchmarks and
|
|
36
45
|
/// scripts with many `console.log` calls must not pay it on every line.
|
|
46
|
+
#[cfg(not(feature = "portable"))]
|
|
37
47
|
pub fn use_console_colors() -> bool {
|
|
38
48
|
*CONSOLE_USES_COLORS.get_or_init(|| std::io::stdout().is_terminal())
|
|
39
49
|
}
|
|
40
50
|
|
|
51
|
+
/// No ANSI colors under `portable`: `console.log` goes to the mGBA debug log
|
|
52
|
+
/// (`agb::println!`), which is plain text.
|
|
53
|
+
#[cfg(feature = "portable")]
|
|
54
|
+
pub fn use_console_colors() -> bool {
|
|
55
|
+
false
|
|
56
|
+
}
|
|
57
|
+
|
|
41
58
|
/// Format a single value for console with optional ANSI colors (Node/Bun-style).
|
|
42
59
|
pub fn format_value_styled(value: &Value, colors: bool) -> String {
|
|
43
60
|
if !colors {
|
|
@@ -86,8 +103,9 @@ fn format_value_styled_inner(value: &Value, colors: bool, quote_strings: bool) -
|
|
|
86
103
|
let sep = format!("{PUNCT}, {RESET}");
|
|
87
104
|
let inner: Vec<String> = arr
|
|
88
105
|
.borrow()
|
|
106
|
+
.to_values()
|
|
89
107
|
.iter()
|
|
90
|
-
.map(|
|
|
108
|
+
.map(|v| format_value_styled_inner(v, colors, true))
|
|
91
109
|
.collect();
|
|
92
110
|
format!("{PUNCT}[{RESET}{}{PUNCT}]{RESET}", inner.join(&sep))
|
|
93
111
|
}
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
//! JSON parsing and stringification for Tish values.
|
|
2
2
|
|
|
3
|
-
use crate::{
|
|
4
|
-
use
|
|
3
|
+
use crate::compat::{AHashMap, Arc};
|
|
4
|
+
use crate::{NumArrayBacking, Value, VmRef};
|
|
5
|
+
#[cfg(feature = "portable")]
|
|
6
|
+
use crate::compat::FloatExt;
|
|
7
|
+
#[cfg(feature = "portable")]
|
|
8
|
+
use alloc::{
|
|
9
|
+
boxed::Box,
|
|
10
|
+
format,
|
|
11
|
+
string::{String, ToString},
|
|
12
|
+
vec::Vec,
|
|
13
|
+
};
|
|
5
14
|
|
|
6
15
|
/// Per-`json_parse`-call cache of object-key text → shared `Arc<str>`. A JSON array of records
|
|
7
16
|
/// repeats the same handful of keys across thousands of objects; interning allocates each key
|
|
8
17
|
/// once and `Arc::clone`s it thereafter, instead of a fresh `Arc<str>` allocation per occurrence.
|
|
9
|
-
type KeyCache =
|
|
18
|
+
type KeyCache = AHashMap<Box<str>, Arc<str>>;
|
|
10
19
|
|
|
11
20
|
#[inline]
|
|
12
21
|
fn intern_key(cache: &mut KeyCache, s: &str) -> Arc<str> {
|
|
@@ -133,11 +142,11 @@ fn signal_circular_json_throw() {
|
|
|
133
142
|
if !crate::has_pending_throw() {
|
|
134
143
|
crate::set_pending_throw(Value::object_from_pairs([
|
|
135
144
|
(
|
|
136
|
-
|
|
145
|
+
Arc::from("name"),
|
|
137
146
|
Value::String("TypeError".into()),
|
|
138
147
|
),
|
|
139
148
|
(
|
|
140
|
-
|
|
149
|
+
Arc::from("message"),
|
|
141
150
|
Value::String("Converting circular structure to JSON".into()),
|
|
142
151
|
),
|
|
143
152
|
]));
|
|
@@ -178,11 +187,24 @@ fn json_stringify_into_guarded(buf: &mut String, value: &Value, ancestors: &mut
|
|
|
178
187
|
Value::NumberArray(arr) => {
|
|
179
188
|
let borrowed = arr.borrow();
|
|
180
189
|
buf.push('[');
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
190
|
+
match &*borrowed {
|
|
191
|
+
NumArrayBacking::Packed(nums) => {
|
|
192
|
+
for (i, n) in nums.iter().enumerate() {
|
|
193
|
+
if i > 0 {
|
|
194
|
+
buf.push(',');
|
|
195
|
+
}
|
|
196
|
+
write_json_number(buf, *n);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// A deopted numeric array serializes element-by-element like a boxed Array.
|
|
200
|
+
NumArrayBacking::Boxed(items) => {
|
|
201
|
+
for (i, item) in items.iter().enumerate() {
|
|
202
|
+
if i > 0 {
|
|
203
|
+
buf.push(',');
|
|
204
|
+
}
|
|
205
|
+
json_stringify_into_guarded(buf, item, ancestors);
|
|
206
|
+
}
|
|
184
207
|
}
|
|
185
|
-
write_json_number(buf, *n);
|
|
186
208
|
}
|
|
187
209
|
buf.push(']');
|
|
188
210
|
}
|
|
@@ -253,7 +275,7 @@ pub fn escape_json_string_into(buf: &mut String, s: &str) {
|
|
|
253
275
|
b'\x08' => buf.push_str("\\b"),
|
|
254
276
|
b'\x0c' => buf.push_str("\\f"),
|
|
255
277
|
_ => {
|
|
256
|
-
use
|
|
278
|
+
use core::fmt::Write;
|
|
257
279
|
let _ = write!(buf, "\\u{:04x}", b as u32);
|
|
258
280
|
}
|
|
259
281
|
}
|
|
@@ -509,6 +531,7 @@ fn parse_object<'a>(
|
|
|
509
531
|
Value::Object(VmRef::new(crate::ObjectData {
|
|
510
532
|
strings: map,
|
|
511
533
|
symbols: None,
|
|
534
|
+
frozen: false,
|
|
512
535
|
})),
|
|
513
536
|
rest,
|
|
514
537
|
));
|
|
@@ -539,6 +562,7 @@ fn parse_object<'a>(
|
|
|
539
562
|
Value::Object(VmRef::new(crate::ObjectData {
|
|
540
563
|
strings: map,
|
|
541
564
|
symbols: None,
|
|
565
|
+
frozen: false,
|
|
542
566
|
})),
|
|
543
567
|
&input[1..],
|
|
544
568
|
));
|