@rust-js/runtime 0.0.1 → 0.0.3
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/index.js +619 -13
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @rust-js/runtime 0.0.
|
|
1
|
+
// @rust-js/runtime 0.0.3: the helpers the JS rust-js writes imports (ADR 0103).
|
|
2
2
|
// Generated by `rust-js --runtime-module`. Do not edit.
|
|
3
3
|
export function $traitImpl(cache, keys, make) {
|
|
4
4
|
// Each key is a dictionary or a drop, held weakly: a drop made for one call
|
|
@@ -93,6 +93,172 @@ export function $displayF64(value) {
|
|
|
93
93
|
throw new Error('No f64 round-trip decimal found');
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
// The shortest digits of the positive `f32` `x` that read back as `x`, the
|
|
97
|
+
// nearest to it of those, as Rust's: `[digits, point]`, `x` being `0.digits`
|
|
98
|
+
// times 10 to `point` (ADR 0122). A decimal reads back as `x` when it's in
|
|
99
|
+
// `x`'s rounding interval, at an end of it only when `x`'s significand is
|
|
100
|
+
// even, as a tie rounds to an even one. Tested exactly: through an `f64`,
|
|
101
|
+
// `Math.fround(Number(text))` rounds twice, which can miss `x`.
|
|
102
|
+
export function $f32Digits(x) {
|
|
103
|
+
const view = new DataView(new ArrayBuffer(4));
|
|
104
|
+
view.setFloat32(0, x);
|
|
105
|
+
const bits = view.getUint32(0);
|
|
106
|
+
const exponentBits = bits >>> 23;
|
|
107
|
+
const fraction = bits & 0x7fffff;
|
|
108
|
+
const significand = BigInt(exponentBits === 0 ? fraction : fraction | 0x800000);
|
|
109
|
+
const exponent2 = exponentBits === 0 ? -149 : exponentBits - 150;
|
|
110
|
+
// In quarters of the last place: `x` is 4m of them, and its interval's ends
|
|
111
|
+
// 2 either side, or 1 below a power of two, where the gap below is half.
|
|
112
|
+
const quarters = 4n * significand;
|
|
113
|
+
const below = fraction === 0 && exponentBits > 1 ? 1n : 2n;
|
|
114
|
+
const even = (significand & 1n) === 0n;
|
|
115
|
+
const quarterNum = exponent2 >= 2 ? 1n << BigInt(exponent2 - 2) : 1n;
|
|
116
|
+
const quarterDen = exponent2 < 2 ? 1n << BigInt(2 - exponent2) : 1n;
|
|
117
|
+
// `c` times 10 to `p`, against `k` quarters.
|
|
118
|
+
const compare = (c, p, k) => {
|
|
119
|
+
const left = p >= 0 ? c * 10n ** BigInt(p) * quarterDen : c * quarterDen;
|
|
120
|
+
const right = p >= 0 ? k * quarterNum : k * quarterNum * 10n ** BigInt(-p);
|
|
121
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
122
|
+
};
|
|
123
|
+
const readsBack = (c, p) => {
|
|
124
|
+
const low = compare(c, p, quarters - below);
|
|
125
|
+
const high = compare(c, p, quarters + 2n);
|
|
126
|
+
return (low > 0 || (low === 0 && even)) && (high < 0 || (high === 0 && even));
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// The first digit's power of ten, estimated, then made exact.
|
|
130
|
+
let exponent = Math.floor(Math.log10(x));
|
|
131
|
+
while (compare(1n, exponent, quarters) > 0) exponent--;
|
|
132
|
+
while (compare(1n, exponent + 1, quarters) <= 0) exponent++;
|
|
133
|
+
|
|
134
|
+
for (let precision = 1; precision <= 9; precision++) {
|
|
135
|
+
const power = exponent - precision + 1;
|
|
136
|
+
// `x` over 10 to `power`, as `n / d`, and the integer nearest it.
|
|
137
|
+
const n = power >= 0 ? quarters * quarterNum : quarters * quarterNum * 10n ** BigInt(-power);
|
|
138
|
+
const d = power >= 0 ? quarterDen * 10n ** BigInt(power) : quarterDen;
|
|
139
|
+
const rounded = n / d + (2n * (n % d) >= d ? 1n : 0n);
|
|
140
|
+
let best;
|
|
141
|
+
let bestDistance;
|
|
142
|
+
for (const candidate of [rounded, rounded - 1n, rounded + 1n]) {
|
|
143
|
+
if (candidate > 0n && readsBack(candidate, power)) {
|
|
144
|
+
const delta = candidate * d - n;
|
|
145
|
+
const distance = delta < 0n ? -delta : delta;
|
|
146
|
+
if (best === undefined || distance < bestDistance || (distance === bestDistance && candidate > best)) {
|
|
147
|
+
best = candidate;
|
|
148
|
+
bestDistance = distance;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (best !== undefined) {
|
|
153
|
+
let digits = best.toString();
|
|
154
|
+
let decimalPower = power;
|
|
155
|
+
while (digits.endsWith("0")) {
|
|
156
|
+
digits = digits.slice(0, -1);
|
|
157
|
+
decimalPower++;
|
|
158
|
+
}
|
|
159
|
+
return [digits, digits.length + decimalPower];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
throw new Error("No f32 round-trip decimal found");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function $displayF32(value) {
|
|
166
|
+
if (Number.isNaN(value)) return "NaN";
|
|
167
|
+
if (value === Infinity) return "inf";
|
|
168
|
+
if (value === -Infinity) return "-inf";
|
|
169
|
+
const sign = value < 0 || Object.is(value, -0) ? "-" : "";
|
|
170
|
+
if (value === 0) return sign + "0";
|
|
171
|
+
const [digits, point] = $f32Digits(Math.abs(value));
|
|
172
|
+
const body =
|
|
173
|
+
point <= 0
|
|
174
|
+
? "0." + "0".repeat(-point) + digits
|
|
175
|
+
: point >= digits.length
|
|
176
|
+
? digits + "0".repeat(point - digits.length)
|
|
177
|
+
: digits.slice(0, point) + "." + digits.slice(point);
|
|
178
|
+
return sign + body;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function $debugF32(value) {
|
|
182
|
+
const size = Math.abs(value);
|
|
183
|
+
if (Number.isFinite(value) && size !== 0 && (size < Math.fround(1e-4) || size >= Math.fround(1e16))) {
|
|
184
|
+
const [digits, point] = $f32Digits(size);
|
|
185
|
+
const mantissa = digits.length > 1 ? digits[0] + "." + digits.slice(1) : digits;
|
|
186
|
+
return (value < 0 ? "-" : "") + mantissa + "e" + (point - 1);
|
|
187
|
+
}
|
|
188
|
+
const text = $displayF32(value);
|
|
189
|
+
return Number.isFinite(value) && !text.includes(".") ? text + ".0" : text;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function $bigToF32(n) {
|
|
193
|
+
const negative = n < 0n;
|
|
194
|
+
let size = negative ? -n : n;
|
|
195
|
+
const length = size.toString(2).length;
|
|
196
|
+
if (length > 24) {
|
|
197
|
+
const shift = BigInt(length - 24);
|
|
198
|
+
const rest = size & ((1n << shift) - 1n);
|
|
199
|
+
const half = 1n << (shift - 1n);
|
|
200
|
+
size >>= shift;
|
|
201
|
+
if (rest > half || (rest === half && (size & 1n) === 1n)) size++;
|
|
202
|
+
size <<= shift;
|
|
203
|
+
}
|
|
204
|
+
const x = Number(size);
|
|
205
|
+
return negative ? -x : x;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function $powiF32(x, n) {
|
|
209
|
+
const reciprocal = n < 0;
|
|
210
|
+
let result = 1;
|
|
211
|
+
while (true) {
|
|
212
|
+
if (n & 1) {
|
|
213
|
+
result = Math.fround(result * x);
|
|
214
|
+
}
|
|
215
|
+
n = (n / 2) | 0;
|
|
216
|
+
if (n === 0) {
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
x = Math.fround(x * x);
|
|
220
|
+
}
|
|
221
|
+
return reciprocal ? Math.fround(1 / result) : result;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function $parseF32(s) {
|
|
225
|
+
const read = $parseF64(s);
|
|
226
|
+
const d = read._0;
|
|
227
|
+
if (read.TAG === "Err" || !Number.isFinite(d) || Math.fround(d) === d) {
|
|
228
|
+
return read.TAG === "Err" ? read : { TAG: "Ok", _0: Math.fround(d) };
|
|
229
|
+
}
|
|
230
|
+
const rounded = Math.fround(d);
|
|
231
|
+
// The `f32` this side of `d`: the largest where it rounds to infinity.
|
|
232
|
+
const near = Number.isFinite(rounded) ? rounded : Math.sign(d) * 3.4028234663852886e38;
|
|
233
|
+
const size = Math.abs(near);
|
|
234
|
+
// The `f32` on `d`'s other side, and the tie between them: past the
|
|
235
|
+
// largest, where infinity is, it's as far above as the one below is.
|
|
236
|
+
const view = new DataView(new ArrayBuffer(8));
|
|
237
|
+
view.setFloat32(0, size);
|
|
238
|
+
const bits = view.getUint32(0);
|
|
239
|
+
view.setUint32(0, Math.abs(d) > size ? bits + 1 : bits - 1);
|
|
240
|
+
const other = view.getFloat32(0);
|
|
241
|
+
view.setUint32(0, bits - 1);
|
|
242
|
+
const tie = other === Infinity ? size + (size - view.getFloat32(0)) / 2 : (size + other) / 2;
|
|
243
|
+
if (Math.abs(d) !== tie) return { TAG: "Ok", _0: rounded };
|
|
244
|
+
// The digits, `digits` times 10 to `exponent`, against the tie, `m`
|
|
245
|
+
// times 2 to `k`, in integers.
|
|
246
|
+
const [mantissa, power = "0"] = s.toLowerCase().replace(/^[+-]/, "").split("e");
|
|
247
|
+
const [whole, fraction = ""] = mantissa.split(".");
|
|
248
|
+
const digits = BigInt(whole + fraction || "0");
|
|
249
|
+
const exponent = Number(power) - fraction.length;
|
|
250
|
+
view.setFloat64(0, tie);
|
|
251
|
+
const tieBits = view.getBigUint64(0);
|
|
252
|
+
const m = (tieBits & ((1n << 52n) - 1n)) | (1n << 52n);
|
|
253
|
+
const k = Number(tieBits >> 52n) - 1075;
|
|
254
|
+
const left = digits * 10n ** BigInt(Math.max(exponent, 0)) * 2n ** BigInt(Math.max(-k, 0));
|
|
255
|
+
const right = m * 2n ** BigInt(Math.max(k, 0)) * 10n ** BigInt(Math.max(-exponent, 0));
|
|
256
|
+
// The tie itself goes to the even one, as `Math.fround` takes it.
|
|
257
|
+
if (left === right) return { TAG: "Ok", _0: rounded };
|
|
258
|
+
const chosen = left > right ? Math.max(size, other) : Math.min(size, other);
|
|
259
|
+
return { TAG: "Ok", _0: d < 0 ? -chosen : chosen };
|
|
260
|
+
}
|
|
261
|
+
|
|
96
262
|
export function $f64Max(a, b) {
|
|
97
263
|
return Number.isNaN(a) ? b : Number.isNaN(b) ? a : Math.max(a, b);
|
|
98
264
|
}
|
|
@@ -265,6 +431,129 @@ export function $orInsertWith(map, key, make) {
|
|
|
265
431
|
return map.get(key);
|
|
266
432
|
}
|
|
267
433
|
|
|
434
|
+
export function $key(value) {
|
|
435
|
+
if (value == null) {
|
|
436
|
+
return "~";
|
|
437
|
+
}
|
|
438
|
+
switch (typeof value) {
|
|
439
|
+
case "string":
|
|
440
|
+
return JSON.stringify(value);
|
|
441
|
+
case "bigint":
|
|
442
|
+
return value + "n";
|
|
443
|
+
case "object":
|
|
444
|
+
if (Array.isArray(value)) {
|
|
445
|
+
return "[" + value.map($key).join(",") + "]";
|
|
446
|
+
}
|
|
447
|
+
return "{" + Object.keys(value).sort().map((k) => JSON.stringify(k) + ":" + $key(value[k])).join(",") + "}";
|
|
448
|
+
default:
|
|
449
|
+
return String(value);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
export class $KeyMap extends Map {
|
|
454
|
+
constructor(entries) {
|
|
455
|
+
super();
|
|
456
|
+
if (entries) {
|
|
457
|
+
for (const [key, value] of entries) {
|
|
458
|
+
this.set(key, value);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
get(key) {
|
|
463
|
+
const entry = super.get($key(key));
|
|
464
|
+
return entry === undefined ? undefined : entry[1];
|
|
465
|
+
}
|
|
466
|
+
set(key, value) {
|
|
467
|
+
const found = $key(key);
|
|
468
|
+
const entry = super.get(found);
|
|
469
|
+
if (entry === undefined) {
|
|
470
|
+
super.set(found, [key, value]);
|
|
471
|
+
} else {
|
|
472
|
+
entry[1] = value;
|
|
473
|
+
}
|
|
474
|
+
return this;
|
|
475
|
+
}
|
|
476
|
+
has(key) {
|
|
477
|
+
return super.has($key(key));
|
|
478
|
+
}
|
|
479
|
+
delete(key) {
|
|
480
|
+
return super.delete($key(key));
|
|
481
|
+
}
|
|
482
|
+
*entries() {
|
|
483
|
+
for (const [key, value] of super.values()) {
|
|
484
|
+
yield [key, value];
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
*keys() {
|
|
488
|
+
for (const [key] of super.values()) {
|
|
489
|
+
yield key;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
*values() {
|
|
493
|
+
for (const [, value] of super.values()) {
|
|
494
|
+
yield value;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
[Symbol.iterator]() {
|
|
498
|
+
return this.entries();
|
|
499
|
+
}
|
|
500
|
+
forEach(f, that) {
|
|
501
|
+
for (const [key, value] of this.entries()) {
|
|
502
|
+
f.call(that, value, key, this);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
export class $KeySet extends Set {
|
|
508
|
+
#items = new Map();
|
|
509
|
+
constructor(items) {
|
|
510
|
+
super();
|
|
511
|
+
if (items) {
|
|
512
|
+
for (const item of items) {
|
|
513
|
+
this.add(item);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
get size() {
|
|
518
|
+
return this.#items.size;
|
|
519
|
+
}
|
|
520
|
+
add(item) {
|
|
521
|
+
const found = $key(item);
|
|
522
|
+
if (!this.#items.has(found)) {
|
|
523
|
+
this.#items.set(found, item);
|
|
524
|
+
}
|
|
525
|
+
return this;
|
|
526
|
+
}
|
|
527
|
+
has(item) {
|
|
528
|
+
return this.#items.has($key(item));
|
|
529
|
+
}
|
|
530
|
+
delete(item) {
|
|
531
|
+
return this.#items.delete($key(item));
|
|
532
|
+
}
|
|
533
|
+
clear() {
|
|
534
|
+
this.#items.clear();
|
|
535
|
+
}
|
|
536
|
+
values() {
|
|
537
|
+
return this.#items.values();
|
|
538
|
+
}
|
|
539
|
+
keys() {
|
|
540
|
+
return this.#items.values();
|
|
541
|
+
}
|
|
542
|
+
*entries() {
|
|
543
|
+
for (const item of this.#items.values()) {
|
|
544
|
+
yield [item, item];
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
[Symbol.iterator]() {
|
|
548
|
+
return this.#items.values();
|
|
549
|
+
}
|
|
550
|
+
forEach(f, that) {
|
|
551
|
+
for (const item of this.#items.values()) {
|
|
552
|
+
f.call(that, item, item, this);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
268
557
|
export function $sortedEntries(map, cmp) {
|
|
269
558
|
return Array.from(map).sort((a, b) => cmp(a[0], b[0]));
|
|
270
559
|
}
|
|
@@ -571,6 +860,56 @@ export function $bigRange(start, end) {
|
|
|
571
860
|
return items;
|
|
572
861
|
}
|
|
573
862
|
|
|
863
|
+
export function* $rangeFrom(start) {
|
|
864
|
+
for (let i = start; ; i++) {
|
|
865
|
+
yield i;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
export function $rangeNext(range) {
|
|
870
|
+
return range.start < range.end ? range.start++ : undefined;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
export function $rangeNextBack(range) {
|
|
874
|
+
return range.start < range.end ? --range.end : undefined;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
export function $rangeFromNext(range) {
|
|
878
|
+
return range.start++;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
export function $asciiCase(text, upper = false) {
|
|
882
|
+
return upper
|
|
883
|
+
? text.replace(/[a-z]+/g, (letters) => letters.toUpperCase())
|
|
884
|
+
: text.replace(/[A-Z]+/g, (letters) => letters.toLowerCase());
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
export function $append(items, other) {
|
|
888
|
+
for (const item of other) {
|
|
889
|
+
items.push(item);
|
|
890
|
+
}
|
|
891
|
+
other.length = 0;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
export function $pretty(open, items, close, rest = false) {
|
|
895
|
+
if (items.length === 0) {
|
|
896
|
+
return open + (rest ? ".." : "") + close;
|
|
897
|
+
}
|
|
898
|
+
const lines = items.map((item) => " " + item.replaceAll("\n", "\n ") + ",\n").join("");
|
|
899
|
+
return open + "\n" + lines + (rest ? " ..\n" : "") + close;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
export function $charRange(start, end, inclusive = false) {
|
|
903
|
+
const items = [];
|
|
904
|
+
const last = end.codePointAt(0) - (inclusive ? 0 : 1);
|
|
905
|
+
for (let c = start.codePointAt(0); c <= last; c++) {
|
|
906
|
+
if (c < 0xd800 || c > 0xdfff) {
|
|
907
|
+
items.push(String.fromCodePoint(c));
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
return items;
|
|
911
|
+
}
|
|
912
|
+
|
|
574
913
|
export function $tryFromInt(x, lo, hi) {
|
|
575
914
|
if (x < lo) return { TAG: "Err", _0: "NegOverflow" };
|
|
576
915
|
if (x > hi) return { TAG: "Err", _0: "PosOverflow" };
|
|
@@ -634,7 +973,8 @@ export function $jsonWriter(pretty) {
|
|
|
634
973
|
null() { this.text += "null"; },
|
|
635
974
|
bool(b) { this.text += String(b); },
|
|
636
975
|
int(n) { this.text += String(n); },
|
|
637
|
-
|
|
976
|
+
// An `f32`'s, `single`, with its own digits, as serde_json writes it (ADR 0122).
|
|
977
|
+
number(x, single) { this.text += single ? $jsonF32(x) : $jsonNumber(x); },
|
|
638
978
|
char(c) { this.text += JSON.stringify(c); },
|
|
639
979
|
string(s) { this.text += JSON.stringify(s); },
|
|
640
980
|
// An externally tagged unit variant: its name.
|
|
@@ -666,7 +1006,7 @@ export function $jsonFlat(into) {
|
|
|
666
1006
|
null() { if (depth > 0) into.null(); },
|
|
667
1007
|
bool(b) { top("a boolean"); into.bool(b); },
|
|
668
1008
|
int(n) { top("an integer"); into.int(n); },
|
|
669
|
-
number(x) { top("a float"); into.number(x); },
|
|
1009
|
+
number(x, single) { top("a float"); into.number(x, single); },
|
|
670
1010
|
char(c) { top("a char"); into.char(c); },
|
|
671
1011
|
string(s) { top("a string"); into.string(s); },
|
|
672
1012
|
variant(name) {
|
|
@@ -696,6 +1036,22 @@ export function $toJson(value, write, pretty) {
|
|
|
696
1036
|
}
|
|
697
1037
|
return { TAG: "Ok", _0: json.text };
|
|
698
1038
|
}
|
|
1039
|
+
// An `f32` as serde_json writes one: its shortest digits, zmij's, fixed from
|
|
1040
|
+
// 1e-6 to 1e12, else `1.5e+16` (ADR 0122).
|
|
1041
|
+
export function $jsonF32(x) {
|
|
1042
|
+
if (!Number.isFinite(x)) return "null";
|
|
1043
|
+
if (x === 0) return Object.is(x, -0) ? "-0.0" : "0.0";
|
|
1044
|
+
const [digits, point] = $f32Digits(Math.abs(x));
|
|
1045
|
+
const sign = x < 0 ? "-" : "";
|
|
1046
|
+
const e = point - 1;
|
|
1047
|
+
if (e < -6 || e > 12) {
|
|
1048
|
+
const mantissa = digits.length > 1 ? `${digits[0]}.${digits.slice(1)}` : digits;
|
|
1049
|
+
return `${sign}${mantissa}e${e < 0 ? "-" : "+"}${Math.abs(e)}`;
|
|
1050
|
+
}
|
|
1051
|
+
if (point <= 0) return `${sign}0.${"0".repeat(-point)}${digits}`;
|
|
1052
|
+
if (point >= digits.length) return `${sign}${digits}${"0".repeat(point - digits.length)}.0`;
|
|
1053
|
+
return `${sign}${digits.slice(0, point)}.${digits.slice(point)}`;
|
|
1054
|
+
}
|
|
699
1055
|
|
|
700
1056
|
// What serde's impls and derives read, the same whatever they read from:
|
|
701
1057
|
// the text (`$JsonReader`), or a value already read (`$JsonContent`), which
|
|
@@ -717,6 +1073,12 @@ export class $JsonDecoder {
|
|
|
717
1073
|
return this.deserializeNumber("f64", (n) => Number(n.value));
|
|
718
1074
|
}
|
|
719
1075
|
|
|
1076
|
+
// serde's `f32` takes any number, `as f32`: an integer rounded once, by
|
|
1077
|
+
// `$bigToF32`, and a float, serde_json's `f64`, rounded (ADR 0122).
|
|
1078
|
+
f32() {
|
|
1079
|
+
return this.deserializeNumber("f32", (n) => (n.kind === "f" ? Math.fround(n.value) : $bigToF32(BigInt(n.value))));
|
|
1080
|
+
}
|
|
1081
|
+
|
|
720
1082
|
string() {
|
|
721
1083
|
return this.deserializeStr("a string", (s) => s);
|
|
722
1084
|
}
|
|
@@ -2513,7 +2875,7 @@ export class $JsonMissing {
|
|
|
2513
2875
|
}
|
|
2514
2876
|
}
|
|
2515
2877
|
for (const method of [
|
|
2516
|
-
"bool", "int", "f64", "string", "char", "borrowedStr", "unit", "unitStruct", "vec", "tuple", "array", "map",
|
|
2878
|
+
"bool", "int", "f64", "f32", "string", "char", "borrowedStr", "unit", "unitStruct", "vec", "tuple", "array", "map",
|
|
2517
2879
|
"struct", "untaggedStruct", "tupleStruct", "enum", "taggedUnit", "internallyTagged",
|
|
2518
2880
|
"adjacentlyTagged", "untagged",
|
|
2519
2881
|
]) {
|
|
@@ -2615,6 +2977,7 @@ export const $json = {
|
|
|
2615
2977
|
u64: (json) => json.int("u64", 0n, 18446744073709551615n),
|
|
2616
2978
|
i64: (json) => json.int("i64", -9223372036854775808n, 9223372036854775807n),
|
|
2617
2979
|
f64: (json) => json.f64(),
|
|
2980
|
+
f32: (json) => json.f32(),
|
|
2618
2981
|
string: (json) => json.string(),
|
|
2619
2982
|
str: (json) => json.borrowedStr(),
|
|
2620
2983
|
// serde_json's `Value` (ADR 0083), from whatever's there, and its `Number`.
|
|
@@ -2768,16 +3131,22 @@ export function $jsonNumberText(n) {
|
|
|
2768
3131
|
}
|
|
2769
3132
|
|
|
2770
3133
|
// serde_json's `Debug`: `Null`, `Bool(true)`, `Number(1)`, `String("a")`,
|
|
2771
|
-
// `Array [..]` and `Object {..}
|
|
2772
|
-
|
|
3134
|
+
// `Array [..]` and `Object {..}`, whose parts `{:#?}` puts on lines of their
|
|
3135
|
+
// own, as a `Vec`'s and a map's.
|
|
3136
|
+
export function $debugJsonValue(value, alternate = false) {
|
|
2773
3137
|
if (value === "Null") return "Null";
|
|
2774
3138
|
const { TAG: tag, _0: inner } = value;
|
|
2775
3139
|
if (tag === "Bool") return `Bool(${inner})`;
|
|
2776
3140
|
if (tag === "Number") return $debugJsonNumber(inner);
|
|
2777
3141
|
if (tag === "String") return `String(${$debugStr(inner)})`;
|
|
2778
|
-
if (tag === "Array")
|
|
2779
|
-
|
|
2780
|
-
|
|
3142
|
+
if (tag === "Array") {
|
|
3143
|
+
const items = inner.map((item) => $debugJsonValue(item, alternate));
|
|
3144
|
+
return alternate ? "Array " + $pretty("[", items, "]") : `Array [${items.join(", ")}]`;
|
|
3145
|
+
}
|
|
3146
|
+
const entries = $sortedEntries(inner, $cmp).map(
|
|
3147
|
+
([key, item]) => `${$debugStr(key)}: ${$debugJsonValue(item, alternate)}`,
|
|
3148
|
+
);
|
|
3149
|
+
return alternate ? "Object " + $pretty("{", entries, "}") : `Object {${entries.join(", ")}}`;
|
|
2781
3150
|
}
|
|
2782
3151
|
export function $debugJsonNumber(n) {
|
|
2783
3152
|
return `Number(${$jsonNumberText(n)})`;
|
|
@@ -2831,6 +3200,7 @@ export function $jsonValueEq(value, other, kind) {
|
|
|
2831
3200
|
if (value.TAG !== "Number") return false;
|
|
2832
3201
|
const n = value._0;
|
|
2833
3202
|
if (kind === "f64") return Number(n.value) === other;
|
|
3203
|
+
if (kind === "f32") return (n.kind === "f" ? Math.fround(n.value) : $bigToF32(BigInt(n.value))) === other;
|
|
2834
3204
|
if (kind === "i64") return $jsonNumberIsI64(n) && n.value == other;
|
|
2835
3205
|
return n.kind === "u" && n.value == other;
|
|
2836
3206
|
}
|
|
@@ -2886,6 +3256,57 @@ export function $debugJsonError(e) {
|
|
|
2886
3256
|
return `Error(${$debugStr(e.message)}, line: ${e.line}, column: ${e.column})`;
|
|
2887
3257
|
}
|
|
2888
3258
|
|
|
3259
|
+
// std's error of a message, which `Box<dyn Error>::from("..")` makes: a
|
|
3260
|
+
// `dyn Error`'s dictionary, whose `value` is the message (ADR 0141).
|
|
3261
|
+
export function $stringError() {
|
|
3262
|
+
return {
|
|
3263
|
+
Debug: () => ({ fmt: (message) => $debugStr(message) }),
|
|
3264
|
+
Display: () => ({ fmt: (message) => message }),
|
|
3265
|
+
source: () => undefined,
|
|
3266
|
+
};
|
|
3267
|
+
}
|
|
3268
|
+
|
|
3269
|
+
// A channel on one thread is a queue (ADR 0142), shared by its ends: both
|
|
3270
|
+
// are the one object, which counts its senders and knows if its receiver
|
|
3271
|
+
// is still there.
|
|
3272
|
+
export function $channel() {
|
|
3273
|
+
const channel = { queue: [], senders: 1, receiving: true };
|
|
3274
|
+
return [channel, channel];
|
|
3275
|
+
}
|
|
3276
|
+
|
|
3277
|
+
export function $send(channel, item) {
|
|
3278
|
+
if (!channel.receiving) return { TAG: "Err", _0: [item] };
|
|
3279
|
+
channel.queue.push(item);
|
|
3280
|
+
return { TAG: "Ok", _0: undefined };
|
|
3281
|
+
}
|
|
3282
|
+
|
|
3283
|
+
// Rust's `recv` waits for another thread to send; on one, none can, so an
|
|
3284
|
+
// empty channel whose senders are all there would wait forever.
|
|
3285
|
+
export function $recv(channel) {
|
|
3286
|
+
if (channel.queue.length > 0) return { TAG: "Ok", _0: channel.queue.shift() };
|
|
3287
|
+
if (channel.senders === 0) return { TAG: "Err", _0: undefined };
|
|
3288
|
+
throw new Error("`recv` of an empty channel would wait forever: no other thread can send");
|
|
3289
|
+
}
|
|
3290
|
+
|
|
3291
|
+
export function $tryRecv(channel) {
|
|
3292
|
+
if (channel.queue.length > 0) return { TAG: "Ok", _0: channel.queue.shift() };
|
|
3293
|
+
return { TAG: "Err", _0: channel.senders === 0 ? "Disconnected" : "Empty" };
|
|
3294
|
+
}
|
|
3295
|
+
|
|
3296
|
+
export function $cloneSender(channel) {
|
|
3297
|
+
channel.senders += 1;
|
|
3298
|
+
return channel;
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3301
|
+
export function $dropSender(channel) {
|
|
3302
|
+
channel.senders -= 1;
|
|
3303
|
+
}
|
|
3304
|
+
|
|
3305
|
+
export function $dropReceiver(channel) {
|
|
3306
|
+
channel.receiving = false;
|
|
3307
|
+
channel.queue.length = 0;
|
|
3308
|
+
}
|
|
3309
|
+
|
|
2889
3310
|
export function $lowerExp(x) {
|
|
2890
3311
|
if (Number.isNaN(x)) return "NaN";
|
|
2891
3312
|
if (!Number.isFinite(x)) return x > 0 ? "inf" : "-inf";
|
|
@@ -3100,6 +3521,64 @@ export function $sliceEnd(items, start, end = items.length) {
|
|
|
3100
3521
|
return end;
|
|
3101
3522
|
}
|
|
3102
3523
|
|
|
3524
|
+
export function $byteLen(s) {
|
|
3525
|
+
let bytes = 0;
|
|
3526
|
+
for (let i = 0; i < s.length; i++) {
|
|
3527
|
+
const unit = s.charCodeAt(i);
|
|
3528
|
+
if (unit < 0x80) bytes += 1;
|
|
3529
|
+
else if (unit < 0x800) bytes += 2;
|
|
3530
|
+
else if (unit >= 0xd800 && unit < 0xdc00) {
|
|
3531
|
+
bytes += 4;
|
|
3532
|
+
i++;
|
|
3533
|
+
} else bytes += 3;
|
|
3534
|
+
}
|
|
3535
|
+
return bytes;
|
|
3536
|
+
}
|
|
3537
|
+
|
|
3538
|
+
export function $strSlice(s, start, end) {
|
|
3539
|
+
const length = $byteLen(s);
|
|
3540
|
+
end ??= length;
|
|
3541
|
+
if (start > length) throw new Error(`start byte index ${start} is out of bounds for string of length ${length}`);
|
|
3542
|
+
if (end > length) throw new Error(`end byte index ${end} is out of bounds for string of length ${length}`);
|
|
3543
|
+
if (start > end) throw new Error(`byte range starts at ${start} but ends at ${end}`);
|
|
3544
|
+
return s.slice($unitAt(s, start, "start"), $unitAt(s, end, "end"));
|
|
3545
|
+
}
|
|
3546
|
+
|
|
3547
|
+
export function $unitAt(s, at, which) {
|
|
3548
|
+
let bytes = 0;
|
|
3549
|
+
let unit = 0;
|
|
3550
|
+
for (const c of s) {
|
|
3551
|
+
if (bytes === at) return unit;
|
|
3552
|
+
const next = bytes + $byteLen(c);
|
|
3553
|
+
if (next > at) {
|
|
3554
|
+
throw new Error(`${which} byte index ${at} is not a char boundary; it is inside ${$debugStr(c, "'")} (bytes ${bytes}..${next} of string)`);
|
|
3555
|
+
}
|
|
3556
|
+
bytes = next;
|
|
3557
|
+
unit += c.length;
|
|
3558
|
+
}
|
|
3559
|
+
return unit;
|
|
3560
|
+
}
|
|
3561
|
+
|
|
3562
|
+
export function $find(s, pattern) {
|
|
3563
|
+
const at = s.indexOf(pattern);
|
|
3564
|
+
return at === -1 ? undefined : $byteLen(s.slice(0, at));
|
|
3565
|
+
}
|
|
3566
|
+
|
|
3567
|
+
export function $rfind(s, pattern) {
|
|
3568
|
+
const at = s.lastIndexOf(pattern);
|
|
3569
|
+
return at === -1 ? undefined : $byteLen(s.slice(0, at));
|
|
3570
|
+
}
|
|
3571
|
+
|
|
3572
|
+
export function $charIndices(s) {
|
|
3573
|
+
const indices = [];
|
|
3574
|
+
let at = 0;
|
|
3575
|
+
for (const c of s) {
|
|
3576
|
+
indices.push([at, c]);
|
|
3577
|
+
at += $byteLen(c);
|
|
3578
|
+
}
|
|
3579
|
+
return indices;
|
|
3580
|
+
}
|
|
3581
|
+
|
|
3103
3582
|
export function $extend(v, items) {
|
|
3104
3583
|
for (const item of items) {
|
|
3105
3584
|
v.push(item);
|
|
@@ -3171,6 +3650,78 @@ export function $skipWhile(items, skip) {
|
|
|
3171
3650
|
return start < 0 ? [] : items.slice(start);
|
|
3172
3651
|
}
|
|
3173
3652
|
|
|
3653
|
+
export function* $lazyChain(a, b) {
|
|
3654
|
+
yield* a;
|
|
3655
|
+
yield* b;
|
|
3656
|
+
}
|
|
3657
|
+
|
|
3658
|
+
export function* $lazyZip(a, b) {
|
|
3659
|
+
const right = b[Symbol.iterator]();
|
|
3660
|
+
for (const item of a) {
|
|
3661
|
+
const other = right.next();
|
|
3662
|
+
if (other.done) {
|
|
3663
|
+
return;
|
|
3664
|
+
}
|
|
3665
|
+
yield [item, other.value];
|
|
3666
|
+
}
|
|
3667
|
+
}
|
|
3668
|
+
|
|
3669
|
+
export function* $lazyTakeWhile(items, keep) {
|
|
3670
|
+
for (const item of items) {
|
|
3671
|
+
if (!keep(item)) {
|
|
3672
|
+
return;
|
|
3673
|
+
}
|
|
3674
|
+
yield item;
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
|
|
3678
|
+
export function* $lazySkipWhile(items, skip) {
|
|
3679
|
+
let skipping = true;
|
|
3680
|
+
for (const item of items) {
|
|
3681
|
+
if (skipping && skip(item)) {
|
|
3682
|
+
continue;
|
|
3683
|
+
}
|
|
3684
|
+
skipping = false;
|
|
3685
|
+
yield item;
|
|
3686
|
+
}
|
|
3687
|
+
}
|
|
3688
|
+
|
|
3689
|
+
export function* $repeating(value, clone = (value) => value) {
|
|
3690
|
+
while (true) {
|
|
3691
|
+
yield clone(value);
|
|
3692
|
+
}
|
|
3693
|
+
}
|
|
3694
|
+
|
|
3695
|
+
export function* $repeatingWith(f) {
|
|
3696
|
+
while (true) {
|
|
3697
|
+
yield f();
|
|
3698
|
+
}
|
|
3699
|
+
}
|
|
3700
|
+
|
|
3701
|
+
export function* $successors(next, successor, boxed = false) {
|
|
3702
|
+
while (next != null) {
|
|
3703
|
+
const item = boxed ? $someValue(next) : next;
|
|
3704
|
+
next = successor(item);
|
|
3705
|
+
yield item;
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
|
|
3709
|
+
export function $fromFn(f, boxed = false) {
|
|
3710
|
+
return Iterator.from({
|
|
3711
|
+
next() {
|
|
3712
|
+
const item = f();
|
|
3713
|
+
if (item == null) {
|
|
3714
|
+
return { done: true, value: undefined };
|
|
3715
|
+
}
|
|
3716
|
+
return { done: false, value: boxed ? $someValue(item) : item };
|
|
3717
|
+
}
|
|
3718
|
+
});
|
|
3719
|
+
}
|
|
3720
|
+
|
|
3721
|
+
export function $unzip(pairs) {
|
|
3722
|
+
return [pairs.map(([a]) => a), pairs.map(([, b]) => b)];
|
|
3723
|
+
}
|
|
3724
|
+
|
|
3174
3725
|
export function $partition(items, keep) {
|
|
3175
3726
|
const yes = [];
|
|
3176
3727
|
const no = [];
|
|
@@ -3281,8 +3832,9 @@ export function $debugStr(s, quote = '"') {
|
|
|
3281
3832
|
return out + quote;
|
|
3282
3833
|
}
|
|
3283
3834
|
|
|
3284
|
-
export function $debugFields(name, fields, values) {
|
|
3285
|
-
|
|
3835
|
+
export function $debugFields(name, fields, values, alternate = false) {
|
|
3836
|
+
const shown = fields.map((field, i) => field + ": " + values[i]);
|
|
3837
|
+
return alternate ? $pretty(name + " {", shown, "}") : name + " { " + shown.join(", ") + " }";
|
|
3286
3838
|
}
|
|
3287
3839
|
|
|
3288
3840
|
export function $plus(text) {
|
|
@@ -3301,6 +3853,56 @@ export function $pad(text, width, align, fill = " ") {
|
|
|
3301
3853
|
return fill.repeat(before) + text + fill.repeat(room - before);
|
|
3302
3854
|
}
|
|
3303
3855
|
|
|
3856
|
+
// A `Formatter`'s options, applied to the text a number, a `bool` or a
|
|
3857
|
+
// string shows as std's `fmt`s apply them (ADR 0058): a number takes a
|
|
3858
|
+
// sign and zeros, a string is cut to its precision, and either is padded.
|
|
3859
|
+
export function $formatted(text, options, numeric = false) {
|
|
3860
|
+
if (options === undefined) return text;
|
|
3861
|
+
const { width, precision } = options;
|
|
3862
|
+
if (numeric && options.plus) text = $plus(text);
|
|
3863
|
+
if (!numeric && precision !== undefined) text = [...text].slice(0, precision).join("");
|
|
3864
|
+
if (width === undefined) return text;
|
|
3865
|
+
if (numeric && options.zero) return $zeroPad(text, width);
|
|
3866
|
+
const align = { Left: "<", Right: ">", Center: "^" }[options.align] ?? (numeric ? ">" : "<");
|
|
3867
|
+
return $pad(text, width, align, options.fill);
|
|
3868
|
+
}
|
|
3869
|
+
|
|
3870
|
+
// A float's precision is its digits; the rest is `$formatted`'s.
|
|
3871
|
+
export function $formatFloat(value, options, show) {
|
|
3872
|
+
const precision = options?.precision;
|
|
3873
|
+
return $formatted(precision === undefined ? show(value) : $toFixed(value, precision), options, true);
|
|
3874
|
+
}
|
|
3875
|
+
|
|
3876
|
+
// A JS iterator lent as a `&mut` (ADR 0071): it steps `it`, and what takes
|
|
3877
|
+
// from it and stops early, as `take` or a loop that breaks does, closes
|
|
3878
|
+
// this, not `it`, which its lender goes on stepping.
|
|
3879
|
+
export function $lent(it) {
|
|
3880
|
+
return Iterator.from({ next: () => it.next() });
|
|
3881
|
+
}
|
|
3882
|
+
|
|
3883
|
+
// `collect()` into a `Result`: the first `Err`, where it stops, as Rust's
|
|
3884
|
+
// does, or `Ok` of all the values.
|
|
3885
|
+
export function $collectResults(items) {
|
|
3886
|
+
const values = [];
|
|
3887
|
+
for (const item of items) {
|
|
3888
|
+
if (item.TAG === "Err") return item;
|
|
3889
|
+
values.push(item._0);
|
|
3890
|
+
}
|
|
3891
|
+
return { TAG: "Ok", _0: values };
|
|
3892
|
+
}
|
|
3893
|
+
|
|
3894
|
+
// `collect()` into an `Option`: `None` at the first `None`, where it stops,
|
|
3895
|
+
// as Rust's does, or all the values, each out of its box where it has one
|
|
3896
|
+
// (ADR 0051).
|
|
3897
|
+
export function $collectOptions(items, boxed = false) {
|
|
3898
|
+
const values = [];
|
|
3899
|
+
for (const item of items) {
|
|
3900
|
+
if (item == null) return undefined;
|
|
3901
|
+
values.push(boxed ? $someValue(item) : item);
|
|
3902
|
+
}
|
|
3903
|
+
return values;
|
|
3904
|
+
}
|
|
3905
|
+
|
|
3304
3906
|
export function $cmpIn(names, a, b) {
|
|
3305
3907
|
return $cmp(names.indexOf(a), names.indexOf(b));
|
|
3306
3908
|
}
|
|
@@ -3349,6 +3951,10 @@ export function $min(items) {
|
|
|
3349
3951
|
}
|
|
3350
3952
|
|
|
3351
3953
|
export function $position(items, found) {
|
|
3352
|
-
|
|
3353
|
-
|
|
3954
|
+
let i = 0;
|
|
3955
|
+
for (const item of items) {
|
|
3956
|
+
if (found(item)) return i;
|
|
3957
|
+
i++;
|
|
3958
|
+
}
|
|
3959
|
+
return undefined;
|
|
3354
3960
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rust-js/runtime",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "The helpers the JS rust-js writes imports, released with the compiler, as @rescript/runtime is with ReScript. See docs/decisions/0103-runtime-package.md.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|