@amritk/nish-x86_64-linux 0.20.0 → 0.22.1
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/INSTALL.md +52 -10
- package/bin/nish +0 -0
- package/docs/AI.md +2777 -0
- package/package.json +2 -1
- package/runtime/browser.d.ts +6 -0
- package/runtime/browser.mjs +77 -0
- package/runtime/core/arrays.d.ts +36 -0
- package/runtime/core/arrays.mjs +153 -0
- package/runtime/core/checks.mjs +20 -0
- package/runtime/core/host.d.ts +80 -0
- package/runtime/core/host.mjs +325 -0
- package/runtime/core/numbers.d.ts +66 -0
- package/runtime/core/numbers.mjs +343 -0
- package/runtime/core/result.d.ts +45 -0
- package/runtime/core/result.mjs +140 -0
- package/runtime/core/strings.d.ts +14 -0
- package/runtime/core/strings.mjs +169 -0
- package/runtime/core.d.ts +9 -0
- package/runtime/core.mjs +55 -0
- package/runtime/host.d.ts +6 -0
- package/runtime/nish.d.ts +46 -2
- package/runtime/nish.h +75 -2
- package/runtime/nish.mjs +23 -3
- package/runtime/node.mjs +386 -0
- package/runtime/runtime-bytes.c +401 -0
- package/runtime/runtime-cross.c +1 -1
- package/runtime/runtime-host.c +1 -1
- package/runtime/runtime-net.c +12 -1
- package/runtime/runtime-os.c +1 -1
- package/runtime/runtime-parallel.c +1 -1
- package/runtime/runtime-simd.c +231 -7
- package/runtime/runtime-term.c +504 -0
- package/runtime/runtime-wasm.c +0 -11
- package/runtime/runtime.c +190 -6
- package/runtime/shim.mjs +6 -985
- package/runtime/sqlite/LICENSE.md +85 -0
- package/runtime/sqlite/README.md +45 -0
- package/runtime/sqlite/nish-sqlite.c +359 -0
- package/runtime/sqlite/sqlite3.c +271131 -0
- package/runtime/sqlite/sqlite3.h +14447 -0
- package/scripts/build.sh +311 -59
- package/std/LICENSE-fast-float +23 -0
- package/std/LICENSE-fdlibm +8 -0
- package/std/README.md +32 -12
- package/std/base64.ts +116 -0
- package/std/bytes.ts +256 -0
- package/std/cli.ts +344 -0
- package/std/collections.ts +6 -5
- package/std/crypto/LICENSE-crypt_blowfish +26 -0
- package/std/crypto/base64url.ts +125 -0
- package/std/crypto/bcrypt.ts +1557 -0
- package/std/crypto/hmac.ts +138 -2
- package/std/crypto/x509.ts +11 -122
- package/std/json-tokenizer.ts +2998 -57
- package/std/json-value.ts +22 -3
- package/std/json.ts +87 -8
- package/std/math.ts +608 -0
- package/std/net/http1-server.ts +282 -40
- package/std/net/http1.ts +40 -21
- package/std/net/http3-server.ts +50 -12
- package/std/net/postgres.ts +2294 -0
- package/std/net/prefork.ts +23 -3
- package/std/net/quic-listener.ts +20 -4
- package/std/random.ts +66 -4
- package/std/regex.ts +4274 -0
- package/std/sqlite.ts +391 -0
- package/std/term-io.ts +978 -0
- package/std/term-pixels.ts +874 -0
- package/std/term-probe.ts +459 -0
- package/std/term-rate.ts +993 -0
- package/std/term.ts +2453 -0
- package/std/text.ts +90 -7
- package/std/yaml-lines.ts +141 -0
- package/std/yaml.ts +4675 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The numeric half of the core (docs/RUN_UNDER_NODE.md): the integer widths,
|
|
3
|
+
* their wrapping arithmetic and conversions, and the text-to-number readers.
|
|
4
|
+
*/
|
|
5
|
+
import { panic } from "./host.mjs";
|
|
6
|
+
|
|
7
|
+
const I32_MIN = -2147483648;
|
|
8
|
+
const I32_MAX = 2147483647;
|
|
9
|
+
const I64_MIN = -(1n << 63n);
|
|
10
|
+
const I64_MAX = (1n << 63n) - 1n;
|
|
11
|
+
|
|
12
|
+
/** `trunc i64 -> i32` (wrap) for BigInt, `llvm.fptosi.sat.i32.f64` for numbers. */
|
|
13
|
+
export function toI32(x) {
|
|
14
|
+
if (typeof x === "bigint") return Number(BigInt.asIntN(32, x));
|
|
15
|
+
if (Number.isNaN(x)) return 0;
|
|
16
|
+
if (x >= I32_MAX) return I32_MAX;
|
|
17
|
+
if (x <= I32_MIN) return I32_MIN;
|
|
18
|
+
return Math.trunc(x) | 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** `sext i32 -> i64` for int32 numbers, `llvm.fptosi.sat.i64.f64` for doubles. */
|
|
22
|
+
export function toI64(x) {
|
|
23
|
+
if (typeof x === "bigint") return x;
|
|
24
|
+
if (Number.isNaN(x)) return 0n;
|
|
25
|
+
if (x >= 9223372036854775808) return I64_MAX;
|
|
26
|
+
if (x <= -9223372036854775808) return I64_MIN;
|
|
27
|
+
return BigInt(Math.trunc(x));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** `sitofp` for both integer widths; a double is returned unchanged. */
|
|
31
|
+
export function toF64(x) {
|
|
32
|
+
return typeof x === "bigint" ? Number(x) : x;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* `f64ToBits` / `bitsToF64`: reinterpret the 64 bits, never convert the value.
|
|
37
|
+
* The compiler lowers each to one `bitcast`; here it is a one-element
|
|
38
|
+
* DataView, which is the only way JavaScript lets you see a double's bits.
|
|
39
|
+
* The result is signed, matching the i64 the compiler produces.
|
|
40
|
+
*/
|
|
41
|
+
const BITS = new DataView(new ArrayBuffer(8));
|
|
42
|
+
|
|
43
|
+
export function f64ToBits(x) {
|
|
44
|
+
BITS.setFloat64(0, x);
|
|
45
|
+
return BigInt.asIntN(64, BITS.getBigUint64(0));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** `f32ToBits`: the 32 bits of an `f32`, signed like the compiler's i32. */
|
|
49
|
+
export function f32ToBits(x) {
|
|
50
|
+
BITS.setFloat32(0, x);
|
|
51
|
+
return BITS.getInt32(0);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function bitsToF64(b) {
|
|
55
|
+
BITS.setBigInt64(0, BigInt.asIntN(64, b));
|
|
56
|
+
return BITS.getFloat64(0);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* `ctSelect` / `ctEq` (WP34 N6). A `u32` is a `number` here and a `u64` a
|
|
61
|
+
* BigInt, so the operands' kind picks the width, and a mix of the two — which
|
|
62
|
+
* the native checker refuses, and which a `u64` written as a bare literal is
|
|
63
|
+
* under an unrewritten run — throws the `TypeError` BigInt arithmetic throws
|
|
64
|
+
* rather than comparing a number with a BigInt and answering zero. JavaScript's
|
|
65
|
+
* `&` reads a `number` as a signed 32-bit integer, so each answer is put back in
|
|
66
|
+
* range with `>>> 0` or `asUintN(64, ...)`. These branch: only the native
|
|
67
|
+
* lowering promises constant time.
|
|
68
|
+
*/
|
|
69
|
+
function ctWide(name, first, second, third) {
|
|
70
|
+
const wide = typeof first === "bigint";
|
|
71
|
+
if ((typeof second === "bigint") !== wide || (typeof third === "bigint") !== wide) {
|
|
72
|
+
throw new TypeError(`${name}: cannot mix a u64 (BigInt) with a u32 (number)`);
|
|
73
|
+
}
|
|
74
|
+
return wide;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const U64_ONES = (1n << 64n) - 1n;
|
|
78
|
+
|
|
79
|
+
export function ctSelect(mask, a, b) {
|
|
80
|
+
if (ctWide("ctSelect", mask, a, b)) return wrapU64((a & mask) | (b & ~mask));
|
|
81
|
+
return ((a & mask) | (b & ~mask)) >>> 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function ctEq(a, b) {
|
|
85
|
+
// `ctEq` has two operands, so the second one stands in for the third.
|
|
86
|
+
if (ctWide("ctEq", a, b, b)) return wrapU64(a ^ b) === 0n ? U64_ONES : 0n;
|
|
87
|
+
return (a ^ b) === 0 ? 0xffffffff : 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ---- Checked arithmetic (wp33 R4) ----------------------------------------------------
|
|
91
|
+
//
|
|
92
|
+
// What `--emit-ts` writes for a signed `+ - *` and unary minus the compiler
|
|
93
|
+
// neither proved in range nor was told to wrap (`--wrapping`): the exact
|
|
94
|
+
// answer, or the panic `nish_panic_overflow` prints, in its words. An i32 is
|
|
95
|
+
// exact in a double until it leaves the range, and `Math.imul` is the
|
|
96
|
+
// wrapped product, so a product that differs from the double one overflowed.
|
|
97
|
+
// A `bigint` operand is an i64.
|
|
98
|
+
|
|
99
|
+
export function checkedAdd(a, b) {
|
|
100
|
+
return typeof a === "bigint" ? checkI64(a + b, "add") : checkI32(a + b, "add");
|
|
101
|
+
}
|
|
102
|
+
export function checkedSub(a, b) {
|
|
103
|
+
return typeof a === "bigint" ? checkI64(a - b, "subtract") : checkI32(a - b, "subtract");
|
|
104
|
+
}
|
|
105
|
+
export function checkedMul(a, b) {
|
|
106
|
+
if (typeof a === "bigint") return checkI64(a * b, "multiply");
|
|
107
|
+
const r = Math.imul(a, b);
|
|
108
|
+
if (r !== a * b) panic("attempt to multiply with overflow");
|
|
109
|
+
return r;
|
|
110
|
+
}
|
|
111
|
+
export function checkedNeg(a) {
|
|
112
|
+
return typeof a === "bigint" ? checkI64(-a, "negate") : checkI32(0 - a, "negate");
|
|
113
|
+
}
|
|
114
|
+
function checkI32(r, op) {
|
|
115
|
+
if (r > I32_MAX || r < I32_MIN) panic(`attempt to ${op} with overflow`);
|
|
116
|
+
return r;
|
|
117
|
+
}
|
|
118
|
+
function checkI64(r, op) {
|
|
119
|
+
if (r > I64_MAX || r < I64_MIN) panic(`attempt to ${op} with overflow`);
|
|
120
|
+
return r;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// `wrappingAdd`, `wrappingSub` and `wrappingMul` from `nish:unsafe`: the
|
|
124
|
+
// two's-complement answer at the operands' width, i32 or i64, whatever
|
|
125
|
+
// `--wrapping` says.
|
|
126
|
+
export function wrappingAdd(a, b) {
|
|
127
|
+
return typeof a === "bigint" ? BigInt.asIntN(64, a + b) : (a + b) | 0;
|
|
128
|
+
}
|
|
129
|
+
export function wrappingSub(a, b) {
|
|
130
|
+
return typeof a === "bigint" ? BigInt.asIntN(64, a - b) : (a - b) | 0;
|
|
131
|
+
}
|
|
132
|
+
export function wrappingMul(a, b) {
|
|
133
|
+
return typeof a === "bigint" ? BigInt.asIntN(64, a * b) : Math.imul(a, b);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Integer `/` and `%` at every width: `sdiv`/`udiv` and `srem`/`urem`, which
|
|
138
|
+
* truncate, and the two panics the compiler guards them with. Only a signed
|
|
139
|
+
* operand can be the minimum, so the overflow test needs no width. A double
|
|
140
|
+
* quotient of two 32-bit integers truncates exactly, and `+ 0` turns the `-0`
|
|
141
|
+
* of `-1 / 2` or `-4 % 2` into the integer zero.
|
|
142
|
+
*/
|
|
143
|
+
export function idiv(a, b) {
|
|
144
|
+
divCheck(a, b);
|
|
145
|
+
return typeof a === "bigint" ? a / b : Math.trunc(a / b) + 0;
|
|
146
|
+
}
|
|
147
|
+
export function irem(a, b) {
|
|
148
|
+
divCheck(a, b);
|
|
149
|
+
return typeof a === "bigint" ? a % b : (a % b) + 0;
|
|
150
|
+
}
|
|
151
|
+
function divCheck(a, b) {
|
|
152
|
+
if (b === 0 || b === 0n) panic("attempt to divide by zero");
|
|
153
|
+
if ((a === I32_MIN && b === -1) || (a === I64_MIN && b === -1n)) panic("attempt to divide with overflow");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Wrap a BigInt to the i64 range: every i64 `+ - * /` and unary minus goes through here. */
|
|
157
|
+
export function wrapI64(x) {
|
|
158
|
+
return BigInt.asIntN(64, x);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The i64 shifts. BigInt has no `>>>` and neither of its shifts masks the
|
|
163
|
+
* count, so all three do here what `codegen/emit/bitwise.ts` emits: mask the
|
|
164
|
+
* count to 6 bits, then shift. `lshrI64` reads the operand as unsigned before
|
|
165
|
+
* shifting and hands back the signed reading of the result, which is `lshr`.
|
|
166
|
+
*/
|
|
167
|
+
export function shlI64(a, b) {
|
|
168
|
+
return BigInt.asIntN(64, a << (b & 63n));
|
|
169
|
+
}
|
|
170
|
+
export function ashrI64(a, b) {
|
|
171
|
+
return a >> (b & 63n);
|
|
172
|
+
}
|
|
173
|
+
export function lshrI64(a, b) {
|
|
174
|
+
return BigInt.asIntN(64, BigInt.asUintN(64, a) >> (b & 63n));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** `llvm.abs.i64(x, false)`: the minimum value wraps to itself. */
|
|
178
|
+
export function absI64(x) {
|
|
179
|
+
return BigInt.asIntN(64, x < 0n ? -x : x);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** `Math.sign` on an i64 or u64, which Math rejects as a BigInt. */
|
|
183
|
+
export function signI64(x) {
|
|
184
|
+
return x > 0n ? 1n : x < 0n ? -1n : 0n;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** `llvm.smin.i64` / `llvm.smax.i64` (Math.min/max reject BigInt). */
|
|
188
|
+
export function minI64(a, b) {
|
|
189
|
+
return a < b ? a : b;
|
|
190
|
+
}
|
|
191
|
+
export function maxI64(a, b) {
|
|
192
|
+
return a > b ? a : b;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ---- Unsigned integers (WP15) -------------------------------------------------------
|
|
196
|
+
//
|
|
197
|
+
// JavaScript has no unsigned integer type, so every unsigned result is masked
|
|
198
|
+
// back into its width right where the compiled code would have relied on the
|
|
199
|
+
// LLVM type. u8/u16/u32 fit a `number` exactly (2^32 - 1 < 2^53); u64 is a
|
|
200
|
+
// BigInt, so it goes through `wrapU64` the way i64 goes through `wrapI64`.
|
|
201
|
+
|
|
202
|
+
/** Wrap into the u64 range: every u64 `+ - * /`, unary minus and shift ends here. */
|
|
203
|
+
export function wrapU64(x) {
|
|
204
|
+
return BigInt.asUintN(64, x);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* The u64 shifts. A u64 is already held as a non-negative BigInt, so its `>>`
|
|
209
|
+
* *is* the logical shift; what these add over the operator is the count mask
|
|
210
|
+
* (BigInt does not mask) and the wrap back into the unsigned range.
|
|
211
|
+
*/
|
|
212
|
+
export function shlU64(a, b) {
|
|
213
|
+
return BigInt.asUintN(64, a << (b & 63n));
|
|
214
|
+
}
|
|
215
|
+
export function lshrU64(a, b) {
|
|
216
|
+
return BigInt.asUintN(64, a) >> (b & 63n);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* The widths, as the rewriter names them. `bits` drives every mask and
|
|
221
|
+
* `BigInt.asIntN`/`asUintN` call; `big` says whether the value is a BigInt
|
|
222
|
+
* (i64/u64) or a `number` in JavaScript.
|
|
223
|
+
*/
|
|
224
|
+
const INT_KINDS = {
|
|
225
|
+
i32: { bits: 32, signed: true, big: false },
|
|
226
|
+
i64: { bits: 64, signed: true, big: true },
|
|
227
|
+
u8: { bits: 8, signed: false, big: false },
|
|
228
|
+
u16: { bits: 16, signed: false, big: false },
|
|
229
|
+
u32: { bits: 32, signed: false, big: false },
|
|
230
|
+
u64: { bits: 64, signed: false, big: true },
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
/** `llvm.umin`/`umax` on u64 (`Math.min`/`max` reject BigInt); the u32-and-below widths use Math. */
|
|
234
|
+
export function minU64(a, b) {
|
|
235
|
+
return a < b ? a : b;
|
|
236
|
+
}
|
|
237
|
+
export function maxU64(a, b) {
|
|
238
|
+
return a > b ? a : b;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Every numeric conversion whose source or target is unsigned (`toU8`,
|
|
243
|
+
* `toU32(i)`, `toI32(u)`, ...), as one function taking the kind names the
|
|
244
|
+
* checker recorded. Doing it through BigInt is what makes the whole matrix
|
|
245
|
+
* exact in one place: an integer source is turned into its true mathematical
|
|
246
|
+
* value, then `asIntN`/`asUintN` performs the sign-extend, zero-extend or
|
|
247
|
+
* truncate that the target's width and signedness call for — which is exactly
|
|
248
|
+
* what `sext`/`zext`/`trunc` do natively.
|
|
249
|
+
*/
|
|
250
|
+
export function convert(x, from, to) {
|
|
251
|
+
// The hot paths of u64 arithmetic written over i64 (`toU64(a) * toU64(b)`)
|
|
252
|
+
// stay in a function small enough for V8 to inline at the call: the two
|
|
253
|
+
// 64-bit kinds reinterpret each other's bits, and a zext of an unsigned
|
|
254
|
+
// 32-bit-or-narrower value to u64 keeps the value as it is.
|
|
255
|
+
if (typeof x === "bigint") {
|
|
256
|
+
if (to === "u64") return BigInt.asUintN(64, x);
|
|
257
|
+
if (to === "i64") return BigInt.asIntN(64, x);
|
|
258
|
+
} else if (to === "u64" && (from === "u32" || from === "u16" || from === "u8")) {
|
|
259
|
+
return BigInt(x);
|
|
260
|
+
}
|
|
261
|
+
return convertOther(x, from, to);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Every conversion `convert` does not answer inline. */
|
|
265
|
+
function convertOther(x, from, to) {
|
|
266
|
+
if (typeof x === "bigint") {
|
|
267
|
+
// A BigInt source narrowed to 32 bits or less skips the `number` round trip.
|
|
268
|
+
const narrow = INT_KINDS[to];
|
|
269
|
+
if (narrow !== undefined) {
|
|
270
|
+
return Number(narrow.signed ? BigInt.asIntN(narrow.bits, x) : BigInt.asUintN(narrow.bits, x));
|
|
271
|
+
}
|
|
272
|
+
} else if (from === "i32" || from === "u32" || from === "u16" || from === "u8") {
|
|
273
|
+
// An integer `number` source narrowed to a `number` kind: the 32-bit wrap
|
|
274
|
+
// `|0` / `>>>0` and a mask do what trunc/sext/zext do, without a BigInt.
|
|
275
|
+
if (to === "i32") return x | 0;
|
|
276
|
+
if (to === "u32") return x >>> 0;
|
|
277
|
+
if (to === "u16") return x & 0xffff;
|
|
278
|
+
if (to === "u8") return x & 0xff;
|
|
279
|
+
if (to === "i64") return BigInt(x);
|
|
280
|
+
}
|
|
281
|
+
const num = typeof x === "bigint" ? Number(x) : x;
|
|
282
|
+
if (to === "f64") return num; // sitofp / uitofp / fpext
|
|
283
|
+
if (to === "f32") return Math.fround(num); // ... / fptrunc, then rounded to a float
|
|
284
|
+
const target = INT_KINDS[to];
|
|
285
|
+
let exact;
|
|
286
|
+
if (INT_KINDS[from] === undefined) {
|
|
287
|
+
// A float source (f32 or f64) goes through the saturating intrinsics: NaN
|
|
288
|
+
// is 0 and out-of-range values clamp. The bounds are BigInt because
|
|
289
|
+
// 2^64 - 1 has no exact `number`, and are built only here, where they are read.
|
|
290
|
+
const lo = target.signed ? -(1n << BigInt(target.bits - 1)) : 0n;
|
|
291
|
+
const hi = target.signed ? (1n << BigInt(target.bits - 1)) - 1n : (1n << BigInt(target.bits)) - 1n;
|
|
292
|
+
if (Number.isNaN(x)) exact = 0n;
|
|
293
|
+
else if (x <= Number(lo)) exact = lo;
|
|
294
|
+
else if (x >= Number(hi)) exact = hi;
|
|
295
|
+
else exact = BigInt(Math.trunc(x));
|
|
296
|
+
} else {
|
|
297
|
+
exact = BigInt(x); // sext/zext/trunc, below
|
|
298
|
+
}
|
|
299
|
+
const wrapped = target.signed ? BigInt.asIntN(target.bits, exact) : BigInt.asUintN(target.bits, exact);
|
|
300
|
+
return target.big ? wrapped : Number(wrapped);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const SPACES = "[ \\t\\n\\v\\f\\r]*";
|
|
304
|
+
/** What `nish_parse_number` recognises: strtod's decimal and hex-integer forms, or an exact `Infinity`. */
|
|
305
|
+
const LITERAL = new RegExp(`^${SPACES}([+-]?)(Infinity|0[xX][0-9a-fA-F]+|(?:\\d+\\.?\\d*|\\.\\d+)(?:[eE][+-]?\\d+)?)`);
|
|
306
|
+
const BLANK = new RegExp(`^${SPACES}$`);
|
|
307
|
+
const WHOLE = new RegExp(`${LITERAL.source}${SPACES}$`);
|
|
308
|
+
|
|
309
|
+
/** The value of a `LITERAL` match: hex goes through parseInt(16), and `-` applies afterwards as in strtod. */
|
|
310
|
+
function literalValue(m) {
|
|
311
|
+
const magnitude = /^0[xX]/.test(m[2]) ? Number.parseInt(m[2], 16) : Number(m[2]);
|
|
312
|
+
return m[1] === "-" ? -magnitude : magnitude;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** `parseInt(s)`: strtoll base 10 (ASCII whitespace, sign, digits; 0 without digits) saturated into i32. */
|
|
316
|
+
export function parseInt(s) {
|
|
317
|
+
const m = new RegExp(`^${SPACES}([+-]?\\d+)`).exec(s);
|
|
318
|
+
return m ? toI32(Number(m[1])) : 0;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** `parseFloat(s)`: the longest literal after ASCII whitespace, else NaN (`0x1A` is 26, as strtod reads it). */
|
|
322
|
+
export function parseFloat(s) {
|
|
323
|
+
const m = LITERAL.exec(s);
|
|
324
|
+
return m ? literalValue(m) : NaN;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** `Number(x)`: a string must be one literal bar surrounding ASCII whitespace (blank is 0); others convert numerically. */
|
|
328
|
+
export function number(x) {
|
|
329
|
+
if (typeof x === "bigint") return Number(x);
|
|
330
|
+
if (typeof x === "boolean") return x ? 1 : 0;
|
|
331
|
+
if (typeof x !== "string") return x;
|
|
332
|
+
const m = WHOLE.exec(x);
|
|
333
|
+
if (m) return literalValue(m);
|
|
334
|
+
return BLANK.test(x) ? 0 : NaN;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** A method's arguments with each `bigint` from the `first` on made a number: the offsets and indices `--emit-ts` cannot wrap one by one. */
|
|
338
|
+
export function offsets(first, args) {
|
|
339
|
+
for (let i = first; i < args.length; i++) {
|
|
340
|
+
if (typeof args[i] === "bigint") args[i] = Number(args[i]);
|
|
341
|
+
}
|
|
342
|
+
return args;
|
|
343
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** `Result` (docs/LANGUAGE.md -> Result and error handling) and `nish:secret`, as `runtime/nish.d.ts` declares them. */
|
|
2
|
+
import type { u8 } from "./numbers.js";
|
|
3
|
+
|
|
4
|
+
/** The success arm of a `Result`, as the discriminant proves it. */
|
|
5
|
+
export type ResultOk<T> = { readonly ok: true; readonly value: T };
|
|
6
|
+
/** The failure arm. */
|
|
7
|
+
export type ResultErr<E> = { readonly ok: false; readonly error: E };
|
|
8
|
+
|
|
9
|
+
export interface ResultMethods<T, E> {
|
|
10
|
+
isOk(): this is ResultOk<T> & ResultMethods<T, E>;
|
|
11
|
+
isErr(): this is ResultErr<E> & ResultMethods<T, E>;
|
|
12
|
+
/** The success payload, or a throw that `caught` turns into the enclosing function's `Err`. */
|
|
13
|
+
orReturn(): T;
|
|
14
|
+
unwrapOr(fallback: T): T;
|
|
15
|
+
/** The success payload, or `message` through `panic`. */
|
|
16
|
+
expect(message: string): T;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** The outcome of an operation that can fail. */
|
|
20
|
+
export type Result<T, E> = (ResultOk<T> | ResultErr<E>) & ResultMethods<T, E>;
|
|
21
|
+
|
|
22
|
+
/** The class every `Result` is an instance of; `--emit-ts` imports it under the type's name. */
|
|
23
|
+
export declare const Result: { prototype: ResultMethods<unknown, unknown> };
|
|
24
|
+
|
|
25
|
+
export declare function Ok<T, E>(value: T): Result<T, E>;
|
|
26
|
+
export declare function Ok<E>(): Result<void, E>;
|
|
27
|
+
export declare function Err<T, E>(error: E): Result<T, E>;
|
|
28
|
+
/** `r.expect(message())`, with `message` called on the `Err` path only. */
|
|
29
|
+
export declare function expectLazy<T, E>(r: Result<T, E>, message: () => string): T;
|
|
30
|
+
/** `r.unwrapOr(fallback())`, with `fallback` called on the `Err` path only. */
|
|
31
|
+
export declare function unwrapOrLazy<T, E>(r: Result<T, E>, fallback: () => T): T;
|
|
32
|
+
/** The `catch` of a body that calls `orReturn`: the `Err` to return, or `thrown` thrown again. */
|
|
33
|
+
export declare function caught<T, E>(thrown: unknown): Result<T, E>;
|
|
34
|
+
|
|
35
|
+
/** Key material, held as a plain wrapper: under JavaScript a wipe promises nothing about copies. */
|
|
36
|
+
export declare class Secret<T> {
|
|
37
|
+
constructor(value: T);
|
|
38
|
+
readonly value: T;
|
|
39
|
+
}
|
|
40
|
+
export declare function secret<T>(value: T): Secret<T>;
|
|
41
|
+
export declare function expose<T, R>(s: Secret<T>, f: (value: T) => R): R;
|
|
42
|
+
export declare function exposeWith<T, A, R>(s: Secret<T>, arg: A, f: (value: T, arg: A) => R): R;
|
|
43
|
+
export declare function wipe<T>(target: Secret<T>): void;
|
|
44
|
+
export declare function wipe(target: object): void;
|
|
45
|
+
export declare function secureZero(bytes: u8[]): void;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { panic } from "./host.mjs";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `nish:secret` (docs/LANGUAGE.md, "Secrets"). Natively the compiler holds a
|
|
5
|
+
* `Secret` to its rules and `wipe` is a volatile store of zeros; here the rules
|
|
6
|
+
* have already been checked by the compiler or by nobody, so a `Secret` is a
|
|
7
|
+
* plain wrapper and `wipe` zero-fills what it holds. `runtime/nish.mjs`
|
|
8
|
+
* resolves the `nish:secret` specifier to `runtime/shim.mjs`, which re-exports
|
|
9
|
+
* this module, so these are its exports by name. A zero-fill under Node promises nothing about copies the
|
|
10
|
+
* engine made, which is the native wipe's whole point and is not claimed here.
|
|
11
|
+
*/
|
|
12
|
+
export class Secret {
|
|
13
|
+
constructor(value) {
|
|
14
|
+
this.value = value;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function secret(value) {
|
|
19
|
+
return new Secret(value);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function expose(s, f) {
|
|
23
|
+
return f(s.value);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function exposeWith(s, arg, f) {
|
|
27
|
+
return f(s.value, arg);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* `r.expect(message)` and `r.unwrapOr(fallback)` as `--emit-ts` writes them
|
|
32
|
+
* when the argument is more than a name or a literal: natively it is
|
|
33
|
+
* evaluated on the `Err` path only (#182), so here it is a function of none.
|
|
34
|
+
*/
|
|
35
|
+
export function expectLazy(r, message) {
|
|
36
|
+
return r.ok ? r.value : r.expect(message());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function unwrapOrLazy(r, fallback) {
|
|
40
|
+
return r.ok ? r.value : fallback();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Zero an array of integers, or every integer field of a record: `0` for a number, `0n` for an `i64` or `u64`. */
|
|
44
|
+
function zeroFill(target) {
|
|
45
|
+
if (Array.isArray(target) || ArrayBuffer.isView(target)) {
|
|
46
|
+
for (let i = 0; i < target.length; i++) {
|
|
47
|
+
target[i] = typeof target[i] === "bigint" ? 0n : 0;
|
|
48
|
+
}
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
for (const key of Object.keys(target)) {
|
|
52
|
+
const field = target[key];
|
|
53
|
+
if (typeof field === "bigint") target[key] = 0n;
|
|
54
|
+
else if (typeof field === "number") target[key] = 0;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function wipe(target) {
|
|
59
|
+
zeroFill(target instanceof Secret ? target.value : target);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* `secureZero(bytes)` (#385): every element set to zero, which is what the
|
|
64
|
+
* native `nish_wipe` leaves. Nothing here can drop a store to an array the
|
|
65
|
+
* caller still holds, so a loop is the whole of it. What it cannot promise is
|
|
66
|
+
* what the native build does: a collector that moved the array may have left a
|
|
67
|
+
* copy of the old bytes behind, and JavaScript has no way to reach it.
|
|
68
|
+
*/
|
|
69
|
+
export function secureZero(bytes) {
|
|
70
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
71
|
+
bytes[i] = 0;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ---- Result (WP16) ---------------------------------------------------------
|
|
76
|
+
//
|
|
77
|
+
// Natively a `Result` is a two-arm struct in the arena; here it is an ordinary
|
|
78
|
+
// object with the same three names, so `r.ok`, `r.value` and `r.error` in the
|
|
79
|
+
// rewritten program mean what they mean in the compiled one. Only `orReturn`
|
|
80
|
+
// needs help: it returns from the *enclosing* function, which no expression in
|
|
81
|
+
// JavaScript can do, so it throws a sentinel that `rewrite.js` catches in a
|
|
82
|
+
// wrapper around every body that contains one.
|
|
83
|
+
|
|
84
|
+
class Propagate {
|
|
85
|
+
constructor(error) {
|
|
86
|
+
this.error = error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
class NishResult {
|
|
91
|
+
constructor(ok, value, error) {
|
|
92
|
+
this.ok = ok;
|
|
93
|
+
if (ok) this.value = value;
|
|
94
|
+
else this.error = error;
|
|
95
|
+
}
|
|
96
|
+
isOk() {
|
|
97
|
+
return this.ok;
|
|
98
|
+
}
|
|
99
|
+
isErr() {
|
|
100
|
+
return !this.ok;
|
|
101
|
+
}
|
|
102
|
+
unwrapOr(fallback) {
|
|
103
|
+
return this.ok ? this.value : fallback;
|
|
104
|
+
}
|
|
105
|
+
expect(message) {
|
|
106
|
+
if (this.ok) return this.value;
|
|
107
|
+
panic(message);
|
|
108
|
+
}
|
|
109
|
+
orReturn() {
|
|
110
|
+
if (this.ok) return this.value;
|
|
111
|
+
throw new Propagate(this.error);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The class behind every `Result`, under the type's name, so a module
|
|
117
|
+
* `--emit-ts` writes can import `Result` for its annotations from here.
|
|
118
|
+
*/
|
|
119
|
+
export { NishResult as Result };
|
|
120
|
+
|
|
121
|
+
/** `Ok(v)`; `Ok()` on a `Result<void, E>` carries nothing. */
|
|
122
|
+
export function Ok(value) {
|
|
123
|
+
return new NishResult(true, value, undefined);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** `Err(e)`. */
|
|
127
|
+
export function Err(error) {
|
|
128
|
+
return new NishResult(false, undefined, error);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The `catch` half of `orReturn`: re-raise anything that is not a propagation,
|
|
133
|
+
* and answer the `Err` the enclosing function should return otherwise. The
|
|
134
|
+
* rewriter emits `return __nish.caught(e)` and nothing else, so a genuine
|
|
135
|
+
* runtime error still reaches Node unchanged.
|
|
136
|
+
*/
|
|
137
|
+
export function caught(thrown) {
|
|
138
|
+
if (thrown instanceof Propagate) return new NishResult(false, undefined, thrown.error);
|
|
139
|
+
throw thrown;
|
|
140
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** The string half of the core: every length and offset counts UTF-8 bytes. */
|
|
2
|
+
import type { i32, u8 } from "./numbers.js";
|
|
3
|
+
|
|
4
|
+
export declare function strLen(x: string | readonly unknown[]): i32;
|
|
5
|
+
export declare function strLenOpt(x: string | null | undefined): i32 | undefined;
|
|
6
|
+
export declare function charCodeAt(s: string, i: number | bigint): u8;
|
|
7
|
+
export declare function substring(s: string, a: number | bigint, b?: number | bigint): string;
|
|
8
|
+
export declare function slice(s: string, a: number | bigint, b?: number | bigint): string;
|
|
9
|
+
export declare function indexOf(s: string, sub: string, from?: number | bigint): i32;
|
|
10
|
+
export declare function startsWith(s: string, sub: string): boolean;
|
|
11
|
+
export declare function endsWith(s: string, sub: string): boolean;
|
|
12
|
+
export declare function fromCharCode(code: number | bigint): string;
|
|
13
|
+
export declare function fromBytes(bytes: readonly u8[], start: number | bigint, end: number | bigint): string;
|
|
14
|
+
export declare function utf8FromBytes(bytes: readonly u8[], start: number | bigint, end: number | bigint): string;
|