@amritk/nish 0.12.0 → 0.14.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/README.md +27 -23
- package/bin/launcher.js +45 -41
- package/bin/packaging.js +39 -33
- package/docs/AI.md +157 -35
- package/docs/INSTALL.md +13 -13
- package/llms.txt +1 -1
- package/package.json +9 -6
- package/runtime/nish.d.ts +85 -1
- package/runtime/nish.h +62 -11
- package/runtime/nish.mjs +38 -3
- package/runtime/runtime-host.c +178 -0
- package/runtime/{runtime_os.c → runtime-os.c} +16 -1
- package/runtime/{runtime_parallel.c → runtime-parallel.c} +112 -4
- package/runtime/{runtime_wasm.c → runtime-wasm.c} +6 -1
- package/runtime/runtime.c +5 -5
- package/runtime/shim.mjs +128 -6
- package/scripts/bootstrap.sh +29 -29
- package/scripts/build.sh +19 -13
- package/scripts/changelog-gen.mjs +260 -192
- package/scripts/ci-profile.mjs +80 -68
- package/scripts/codes-registry.js +25 -13
- package/scripts/gen-diagnostic-codes.mjs +124 -85
- package/scripts/nish-compiler.sh +2 -0
- package/scripts/platform-package.mjs +26 -26
- package/scripts/postinstall.mjs +46 -36
- package/scripts/size-report.sh +3 -3
- package/scripts/smoke.sh +1 -1
- package/std/README.md +42 -2
- package/std/collections.ts +194 -188
- package/std/crypto/base64url.ts +145 -0
- package/std/crypto/ct.ts +64 -0
- package/std/crypto/hkdf.ts +118 -0
- package/std/crypto/hmac.ts +155 -0
- package/std/crypto/sha256.ts +444 -0
- package/std/crypto/sha512.ts +510 -0
- package/std/crypto/x25519.ts +494 -0
- package/std/json.ts +136 -136
- package/std/map.ts +9 -7
- package/std/pair.ts +2 -2
- package/std/testing.ts +67 -67
- package/std/text.ts +54 -54
- package/std/threads.ts +126 -38
package/docs/AI.md
CHANGED
|
@@ -46,16 +46,18 @@ stdout, nothing on stderr, every field 1-based with `endLine`/`endColumn`
|
|
|
46
46
|
exclusive:
|
|
47
47
|
|
|
48
48
|
```json
|
|
49
|
-
{"file":"p.ts","line":3,"column":17,"endLine":3,"endColumn":20,"severity":"error","code":"NL2249","message":"Unknown method `map` on i32[] (supported: push, pop, indexOf, join)"}
|
|
49
|
+
{"file":"p.ts","line":3,"column":17,"endLine":3,"endColumn":20,"severity":"error","code":"NL2249","message":"Unknown method `map` on i32[] (supported: push, pop, indexOf, join, set, fill)"}
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
- **Key on `code`, never on `message`.** A code is a promise: `NL2249` means
|
|
53
53
|
the same rule next release. The prose may improve; the code may not.
|
|
54
|
-
- **`severity`** is `"error"` or `"
|
|
55
|
-
changes the exit code — it is advice, not a rejection.
|
|
54
|
+
- **`severity`** is `"error"`, `"performance"` or `"portability"`. A warning
|
|
55
|
+
of either kind never changes the exit code — it is advice, not a rejection.
|
|
56
|
+
Portability warnings, the sites where the program's TypeScript reading
|
|
57
|
+
answers differently, print only under `--warn-portability`.
|
|
56
58
|
- **Bands**: `NL1xxx` the Phase 0 forbidden-syntax sweep, `NL2xxx` the checker,
|
|
57
|
-
`NL3xxx` the driver and modules, `NL4xxx` the interop sidecars, `
|
|
58
|
-
performance, `NL0001` syntax, `NL0002` toolchain, `NL0003` internal,
|
|
59
|
+
`NL3xxx` the driver and modules, `NL4xxx` the interop sidecars, `NL8xxx`
|
|
60
|
+
portability, `NL9xxx` performance, `NL0001` syntax, `NL0002` toolchain, `NL0003` internal,
|
|
59
61
|
`NL0000` a diagnostic with no rule yet.
|
|
60
62
|
- **Exit codes**: `0` ok, `1` the program was rejected, `2` usage, `3` the C
|
|
61
63
|
toolchain is unusable, `70` an internal compiler error — that last one is a
|
|
@@ -114,31 +116,39 @@ rejects. This table is the highest-value part of the page.
|
|
|
114
116
|
| `x == y` | `Loose equality is forbidden` | `x === y` |
|
|
115
117
|
| `throw new Error(m)` | `` `throw` is forbidden `` | `return Err(m)`, or `panic(m)` to end the process |
|
|
116
118
|
| `try { … } catch { … }` | `` `try`/`catch`/`finally` is forbidden `` | `Result<T, E>` and `isErr()` |
|
|
117
|
-
| `xs.map(f)`, `.filter`, `.reduce`, `.forEach`, `.slice`, `.sort`, `.shift` | `` Unknown method `map` on i32[] (supported: push, pop, indexOf, join) `` | a `for` loop, or a top-level `map(xs, f)` with a [function parameter](#function-parameters) |
|
|
119
|
+
| `xs.map(f)`, `.filter`, `.reduce`, `.forEach`, `.slice`, `.sort`, `.shift` | `` Unknown method `map` on i32[] (supported: push, pop, indexOf, join, set, fill) `` | a `for` loop, or a top-level `map(xs, f)` with a [function parameter](#function-parameters) |
|
|
118
120
|
| `s.toUpperCase()`, `s.split()`, `s.trim()`, `s.replace()` | `` Unknown method … on string `` | index bytes with `charCodeAt` / `substring` |
|
|
119
121
|
| `a?.b`, `a ?? b` | forbidden, except `m.get(k) ?? d` | `if (a !== null)` first |
|
|
120
|
-
| `x as T`, `<T>x`, `x
|
|
122
|
+
| `x as T`, `<T>x`, `x satisfies T` | `Unsupported expression in Phase 1: AsExpression` (to `any` or `unknown`: `` Type assertion to `any` is forbidden ``); `x!` is a syntax error | there are no casts; `implements` is the only widening |
|
|
123
|
+
| `typeof x`, `x instanceof C`, `"k" in o`, `delete o.k`, `void 0`, `a, b` | each refused by name (`` `typeof` is forbidden ``, …) | there is no runtime type information: a value's type is its declared one |
|
|
124
|
+
| `a ** b`, `[...xs]`, `[1, , 2]` | `` Unsupported binary operator `**` `` / `Spread in array literals is not supported` / `Holes in array literals are not supported` | `Math.pow` on `f64`, a loop, every element written out |
|
|
121
125
|
| `any`, `unknown` | forbidden | name the real type |
|
|
122
126
|
| `undefined` | forbidden, except `x === undefined` on a `Map.get` result | `null`, with a `T \| null` type |
|
|
123
127
|
| `let total = 0` at the top level | `` Top-level `let` is not supported `` | a module `const`, or a local |
|
|
128
|
+
| `for (const k in o)` | `` `for...in` is forbidden `` | `for (const x of xs)` over an array, or `for (const k of m.keys())` over a `Map` |
|
|
124
129
|
| a callback stored or returned: `const cb = (n: i32) => n`, a field `cb: (n: i32) => i32` | `An arrow may only be written as the argument for a function-typed parameter` / `` … is a function type, which may only annotate a parameter of a top-level function `` | there are **no function values**; a callback the call names is a [function parameter](#function-parameters) |
|
|
125
|
-
| `type Pair<T>` (a generic alias) |
|
|
130
|
+
| `type Pair<T>` (a generic alias) | `Generic type parameters are forbidden on a type alias` | a generic **function**, **class**, **interface** and **method** all work — see below; an alias renames a type that already exists, so it has nothing to specialise |
|
|
126
131
|
| `constructor<T>(x: T)` | a syntax error: `a constructor cannot have type parameters` | put the parameter on the class (`class Box<T>`), or on a method |
|
|
127
132
|
| `h.get<i32>(7)` (type argument at a method call) | `Type arguments are not written at a call site in Nish` | `h.get(7)` — a generic method infers like a generic function |
|
|
128
133
|
| `<T>(p: T) => p.x` (a member of a type parameter) | `` Cannot read `x` of `T`: an unconstrained type parameter has no members `` | `<T extends Point>(p: T) => p.x`, where `Point` is a class or interface that declares `x` |
|
|
129
134
|
| `identity<i32>(7)` (type argument at a call) | `Type arguments are not written at a call site in Nish` | `identity(7)` — `T` is inferred from the arguments |
|
|
130
135
|
| `async` / `await` / `Promise` | forbidden (no event loop) | the I/O builtins are synchronous |
|
|
136
|
+
| `namespace N { }`, `declare global { }`, `@decorator` | forbidden | one module per file; a plain function in place of a decorator |
|
|
137
|
+
| `keyof T`, `<T = i32>` (a default type argument) | `` Unsupported type `keyof T` `` / `a default type argument … is not supported` | name the type; a type argument is always inferred |
|
|
131
138
|
| `class B extends A` | `` `extends` is not supported: Nish has no inheritance `` | repeat the fields and `implements` an interface |
|
|
132
|
-
| `static` members,
|
|
139
|
+
| `static` members, `get x()` / `set x(v)` | `` … `static` members are not supported `` / `` Getters and setters are not supported in class `C` (use a method) `` | module `const`s and plain methods |
|
|
140
|
+
| `const [a, b] = xs`, `({ x }: Point) =>`, `(n = 1)`, `(n?: i32)`, `(...ns: i32[])` | `Destructuring is not supported` / `Destructured parameters are not supported` / `Optional/default parameters are not supported` / `Rest parameters are not supported` | one name per binding, every parameter passed; an array for a variable count |
|
|
141
|
+
| `function f(): void;` (an overload), `export default function ()` | `Functions must have a body` / `Functions must be named` | one named function, with its body |
|
|
142
|
+
| a method in an `interface` | `` Interface `I` cannot declare methods (interfaces describe layout only) `` | fields only; a top-level function over the interface |
|
|
143
|
+
| `abstract class`, `declare class`, `constructor(public x: i32)`, `interface B extends A`, `[k: string]: T` | `Abstract classes are not supported` / `` `declare class` is not supported `` / `Parameter properties … are not supported; declare the field and assign it` / ``Interface inheritance (`extends`) is not supported; list every field`` / `Index signatures are not supported …` | a plain class with its fields declared and assigned in the constructor; every field listed; a `Map` for a keyed table |
|
|
144
|
+
| `{ m() { … } }`, `{ "a": 1 }`, `const C = class { }` | ``Unsupported object literal member: MethodDeclaration (only `key: value`)`` / `Object literal keys must be plain identifiers` / `Classes must be named` | fields with identifier keys, a top-level function over the interface, a named top-level class |
|
|
133
145
|
| `type Pair = { a: i32 }` (inline object type) | `` Unsupported type `{ a: i32 }` `` | declare an `interface` |
|
|
134
146
|
| `A \| B` unions | `` Union types other than `T \| null` are forbidden `` | one type, or an `interface` prefix |
|
|
135
147
|
| `String(n)`, `n.toString()` | `` Unknown function `String` `` / `` Unknown method `toString` on i32 `` | `` `${n}` `` |
|
|
136
148
|
| `xs.length = 0` | `` Cannot assign to `length` of i32[] (array length is read-only; use `push`) `` | build a new array |
|
|
137
|
-
| `for (const k in o)` | `Unsupported statement in Phase 1: ForInStatement` | `for (const x of xs)` over an array |
|
|
138
149
|
| `import { readFileSync } from "fs"` | `` Cannot find package `fs` `` — a bare specifier is a **package name**, so it is looked for in `node_modules`; one that is installed but has no `nish` condition is `` Package `fs` has no Nish entry point `` | `readFileSync` is a global; no import needed (or `import { readFileSync } from "nish:fs"`) |
|
|
139
150
|
| `export default f` | `` `export default` / `export =` are not supported `` | `export const f = …` |
|
|
140
|
-
| `
|
|
141
|
-
| `new Date()`, `Date.now()` | `` Unknown builtin `Date.now` `` | `monotonicNanos()` for elapsed time; there is no wall clock and no calendar |
|
|
151
|
+
| `new Date()` | `` `new Date()` is refused: Nish has no `Date` object; the one `Date` member is `Date.now()`, the wall clock in milliseconds `` | `Date.now()` for the wall clock, `monotonicNanos()` for elapsed time; there is no calendar |
|
|
142
152
|
| `JSON.parse`, `RegExp`, `Promise` | unknown / forbidden | none of these exist; write them or restructure |
|
|
143
153
|
| `for (const [k, v] of m)`, `m.forEach(...)`, `new Map(entries)` | each refused by name | `Map` and `Set` exist, with `size`, `get`, `set` / `add`, `has`, `delete`, `clear`, and `keys()` / `values()` in a `for...of` — see [Map and Set](#map-and-set) |
|
|
144
154
|
| `let v = m.get(k)`, `f(m.get(k))`, `m.get(k) + 1` | `` `m.get(k)` is `i32 \| undefined` and cannot be held in a `let` `` (and one message per place) | `m.get(k) ?? 0`, or `const v = m.get(k); if (v !== undefined) { … }` |
|
|
@@ -198,16 +208,42 @@ const good = (a: i32, b: i64): i64 => {
|
|
|
198
208
|
};
|
|
199
209
|
```
|
|
200
210
|
|
|
201
|
-
**`integer
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
211
|
+
**`integer<Lo, Hi>` is an `i32` that stays in `[Lo, Hi]`.** The bounds are two
|
|
212
|
+
integer literals (a sign allowed) inside `i32`, and it is an `i32` everywhere
|
|
213
|
+
the machine can see. Putting a value *into* one is checked: an `i32` or
|
|
214
|
+
another range is compared once and the program panics (exit 1) outside the
|
|
215
|
+
range, a literal outside it is a compile error, and a literal inside it or a
|
|
216
|
+
narrower range costs nothing. So does a value a loop condition or a guard
|
|
217
|
+
already bounds (`b` below), and `toI32` of a `u8`; a check left inside a loop
|
|
218
|
+
is a performance warning (NL9013) naming the guard that removes it. A range
|
|
219
|
+
bounds an index too: `integer<0, 255>`, like `u8`, needs only a length guard of
|
|
220
|
+
256. Taking one *out* is free: every operator reads
|
|
221
|
+
it as `i32`, a `const` keeps the range and a `let` widens to `i32`. A `u8` is
|
|
222
|
+
not an `integer<0, 255>` (convert with `toI32`), and a `declare function` may
|
|
223
|
+
not mention a range. Put the range on the value you use, not on a loop
|
|
224
|
+
counter, which has to leave the range to end the loop.
|
|
206
225
|
|
|
207
|
-
```ts nish:
|
|
208
|
-
const
|
|
226
|
+
```ts nish:ok
|
|
227
|
+
const getByte = (buf: u8[], i: integer<0, 255>): u8 => buf[i];
|
|
228
|
+
|
|
229
|
+
const sumAll = (buf: u8[]): i32 => {
|
|
230
|
+
let sum = 0;
|
|
231
|
+
for (let i = 0; i < 256 && i < buf.length; i++) {
|
|
232
|
+
const b: integer<0, 255> = i;
|
|
233
|
+
sum = sum + toI32(getByte(buf, b));
|
|
234
|
+
}
|
|
235
|
+
return sum;
|
|
236
|
+
};
|
|
209
237
|
```
|
|
210
238
|
|
|
239
|
+
```ts nish:err-body NL2384
|
|
240
|
+
const digit: integer<0, 9> = 12;
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Declaring a type alias, enum, class, interface or function named `integer` is
|
|
244
|
+
refused, because the name is the type. A value named `integer` (a local or a
|
|
245
|
+
module constant) is fine.
|
|
246
|
+
|
|
211
247
|
```ts nish:err NL2332
|
|
212
248
|
class integer {
|
|
213
249
|
value: i32 = 0;
|
|
@@ -317,6 +353,8 @@ Narrowing follows the same engine as `T | null` below: it applies to a
|
|
|
317
353
|
**variable**, never a property path; it ends at any assignment to that
|
|
318
354
|
variable; and it is dropped before a loop that assigns it. `if (r.isOk()) A
|
|
319
355
|
else B` narrows in `A`, and after the `if` when `B` cannot fall through.
|
|
356
|
+
The proof stays with `r`: `const y = r`, `c ? r : q` and `[r]` are plain
|
|
357
|
+
`Result`s, so test `y` itself before `y.value`.
|
|
320
358
|
|
|
321
359
|
`panic(message)` is the other ending: message to stderr, exit 1. It is for an
|
|
322
360
|
invariant that cannot hold, not for a failure a caller should handle. It
|
|
@@ -360,8 +398,9 @@ export const main = (): i32 => {
|
|
|
360
398
|
`Map.get` (see [Map and Set](#map-and-set)).
|
|
361
399
|
- Narrowing applies to a **local or parameter**, never a property path. `if
|
|
362
400
|
(n.next !== null) n.next.v` is rejected — copy into a local first.
|
|
363
|
-
- A narrowing ends at any assignment to the variable,
|
|
364
|
-
|
|
401
|
+
- A narrowing ends at any assignment to the variable, including one in an
|
|
402
|
+
earlier operand of the same `&&` / `||` chain, and is dropped before a loop
|
|
403
|
+
whose body, condition or update assigns it.
|
|
365
404
|
- Two nullables cannot be compared with each other; compare each with `null`.
|
|
366
405
|
- `new Array<T | null>(n)` is allowed: the zero fill *is* `null`.
|
|
367
406
|
|
|
@@ -723,6 +762,62 @@ import { parallelReduce } from "nish/threads";
|
|
|
723
762
|
export const main = (): i32 => parallelReduce([1, 2, 3], (a, b) => a + b, 1); // `+` folds from 0
|
|
724
763
|
```
|
|
725
764
|
|
|
765
|
+
### Scoped tasks: `using s = scope()`
|
|
766
|
+
|
|
767
|
+
Different functions at once, one thread each: open a scope with `using`, give
|
|
768
|
+
it tasks with `spawn(entry, arg, dst, at)`, and every task has run and stored
|
|
769
|
+
`entry(arg)` into `dst[at]` when the block ends. (A plain block, for the same
|
|
770
|
+
reason as the one above: `tests/link/thread_scope_basic` compiles it.)
|
|
771
|
+
|
|
772
|
+
```ts
|
|
773
|
+
import { scope } from "nish/threads";
|
|
774
|
+
|
|
775
|
+
const sumOf = (xs: f64[]): f64 => xs[0] + xs[1];
|
|
776
|
+
const square = (n: i32): i32 => n * n;
|
|
777
|
+
|
|
778
|
+
export const main = (): i32 => {
|
|
779
|
+
const xs: f64[] = [1.5, 2.5];
|
|
780
|
+
const sums: f64[] = [0.0];
|
|
781
|
+
const squares: i32[] = [0];
|
|
782
|
+
{
|
|
783
|
+
using s = scope();
|
|
784
|
+
s.spawn(sumOf, xs, sums, 0);
|
|
785
|
+
s.spawn(square, 12, squares, 0);
|
|
786
|
+
} // joined here, on every exit
|
|
787
|
+
console.log(`${sums[0]} ${squares[0]}`); // 4 144
|
|
788
|
+
return 0;
|
|
789
|
+
};
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
- **The tasks run when the block ends**, together, and each answer is stored
|
|
793
|
+
after the last one finishes. So read a destination after the block. A
|
|
794
|
+
destination is a `const` bound to a fresh array (`[0, 0]`, `new Array`) that
|
|
795
|
+
you only index; never pass it on. Between the first `spawn` and the block's
|
|
796
|
+
end, don't read a destination, and don't write memory a task could read (a
|
|
797
|
+
store into an argument, a call that writes through its argument).
|
|
798
|
+
- **`using` takes only `scope()`**, and `scope()` only comes from `using`.
|
|
799
|
+
The scope is only ever the receiver of a `spawn` statement: never pass it,
|
|
800
|
+
store it or return it.
|
|
801
|
+
- **The task is a named top-level function**, not an arrow, and it follows the
|
|
802
|
+
data-parallel rules: it writes nothing anybody else can see, answers a number,
|
|
803
|
+
a `boolean` or an enum, and leaves `Arena` alone. A task cannot open a scope
|
|
804
|
+
of its own or call `parallelMapInto`.
|
|
805
|
+
- **Any argument will do** — an array, an object — because nothing writes
|
|
806
|
+
memory while the tasks run. There is no thread count: one task, one thread.
|
|
807
|
+
|
|
808
|
+
```ts nish:err NL2388
|
|
809
|
+
import { scope } from "nish/threads";
|
|
810
|
+
|
|
811
|
+
const one = (n: i32): i32 => n + 1;
|
|
812
|
+
|
|
813
|
+
export const main = (): i32 => {
|
|
814
|
+
const out: i32[] = [0];
|
|
815
|
+
const s = scope(); // a scope must be introduced by `using`
|
|
816
|
+
s.spawn(one, 1, out, 0);
|
|
817
|
+
return out[0];
|
|
818
|
+
};
|
|
819
|
+
```
|
|
820
|
+
|
|
726
821
|
### Calling C
|
|
727
822
|
|
|
728
823
|
`declare function name(params): T;` declares a C function this program calls but
|
|
@@ -911,15 +1006,21 @@ export const main = (): i32 => {
|
|
|
911
1006
|
point: `Kind` and `i32` never convert in either direction. Members must be
|
|
912
1007
|
numeric literals. `===` and `!==` are the **only** operators — no arithmetic,
|
|
913
1008
|
no bitwise, so **bit flags stay `i32` module constants**.
|
|
914
|
-
- **
|
|
915
|
-
|
|
916
|
-
|
|
1009
|
+
- **Both can be exported.** `export enum Kind` is imported like a function,
|
|
1010
|
+
`import { Kind } from "./kinds"` (renamed with `as` if you like), and in the
|
|
1011
|
+
importer it *is* the exporter's `Kind`: the members fold to the exporter's
|
|
1012
|
+
integers and a `Kind` value crosses a call as itself. A `Kind` a second
|
|
1013
|
+
module declares for itself is a different type, however alike the two are.
|
|
1014
|
+
`export type Conn = Socket | null` is imported the same way and is, in the
|
|
1015
|
+
importer, the type it names; its right-hand side is resolved in the module
|
|
1016
|
+
that wrote it, so the importer need not import `Socket`. A module does not
|
|
1017
|
+
re-export an enum or alias it imported.
|
|
917
1018
|
- Both are top-level only.
|
|
918
1019
|
|
|
919
1020
|
### Modules
|
|
920
1021
|
|
|
921
|
-
- `export` goes on `const` (function or constant), `class`,
|
|
922
|
-
declarations. No `export default`, no `export { … }`, no `export *`.
|
|
1022
|
+
- `export` goes on `const` (function or constant), `class`, `interface`,
|
|
1023
|
+
`enum` and `type` declarations. No `export default`, no `export { … }`, no `export *`.
|
|
923
1024
|
- The only import form is a **named import**:
|
|
924
1025
|
`import { square, cube as pow3 } from "./math"`. `.ts` is optional. Default
|
|
925
1026
|
imports, namespace imports, side-effect imports and type-only imports are all
|
|
@@ -940,8 +1041,8 @@ export const main = (): i32 => {
|
|
|
940
1041
|
`"engines": {"nish": ">=X.Y.Z"}` is above this compiler `` needs a newer
|
|
941
1042
|
compiler ``, and a `package.json` that is not JSON names its line and
|
|
942
1043
|
column — each with its own `--json` code (`NL3017`–`NL3021`).
|
|
943
|
-
- Functions may be renamed on import; classes and interfaces may
|
|
944
|
-
name is part of the ABI.
|
|
1044
|
+
- Functions and enums may be renamed on import; classes and interfaces may
|
|
1045
|
+
not — the type name is part of the ABI.
|
|
945
1046
|
- Import cycles are allowed; a shared dependency is compiled once.
|
|
946
1047
|
- **A function name is unique across the whole program**, exported or not.
|
|
947
1048
|
- An `export`ed function is an external C-ABI symbol; every other function gets
|
|
@@ -963,7 +1064,8 @@ export const main = (): i32 => {
|
|
|
963
1064
|
- `for (init; cond; update)` with every clause optional, and
|
|
964
1065
|
`for (const x of xs)` over an **array** only — `x` gets the element type and
|
|
965
1066
|
must not be annotated. The array's `length` is re-read each iteration, so a
|
|
966
|
-
`push` inside the body extends the loop.
|
|
1067
|
+
`push` inside the body extends the loop. `for...in`, `for await` and
|
|
1068
|
+
`for (x of xs)` over an existing `x` are each refused by name.
|
|
967
1069
|
- `switch` takes an **integer or enum** discriminant; every `case` label is an
|
|
968
1070
|
integer constant expression of that type. **There is no implicit
|
|
969
1071
|
fallthrough** — a clause with statements ends in `break`, `return`,
|
|
@@ -1048,9 +1150,19 @@ constants `Math.PI` / `Math.E`. The f64-only ones reject an `i32`: write
|
|
|
1048
1150
|
`Number(x)`, `parseInt(s)` (base 10, `i32`, no `NaN` — no digits give `0`),
|
|
1049
1151
|
`parseFloat(s)`.
|
|
1050
1152
|
|
|
1153
|
+
**Constant time.** `ctSelect(mask, a, b)` is `(a & mask) | (b & ~mask)` and
|
|
1154
|
+
`ctEq(a, b)` is all-ones when equal and zero otherwise, both over `u32` or
|
|
1155
|
+
`u64` only, every operand one type. The mask sits behind an optimisation
|
|
1156
|
+
barrier, so neither ever becomes a branch; select and compare on a secret with
|
|
1157
|
+
these, never with `if`, `?:`, `===` or `table[secret]`, which they cannot fix.
|
|
1158
|
+
|
|
1051
1159
|
**Arrays.** `a.length` (read-only), `a.push(v)`, `a.pop()` (panics when empty —
|
|
1052
1160
|
there is no `undefined` to return), `a.indexOf(v)`, `a.join(sep)` — **`join` is
|
|
1053
|
-
`string[]` only
|
|
1161
|
+
`string[]` only** — and, on an array of numbers only, `dst.set(src[, offset])`
|
|
1162
|
+
(one `memmove`; a range past the end panics) and `a.fill(v[, start[, end]])`
|
|
1163
|
+
(ends clamped and counted back from the end when negative, as in JavaScript;
|
|
1164
|
+
never panics). Both are statements. That is every array method there is. A
|
|
1165
|
+
window into a buffer is `(buf, offset, length)`; there is no view type.
|
|
1054
1166
|
|
|
1055
1167
|
**Strings.** `s.length` (**bytes**), `s.charCodeAt(i)` (the byte, bounds-checked),
|
|
1056
1168
|
`s.substring(start[, end])`, `s.slice(start[, end])`, `s.indexOf(sub)`,
|
|
@@ -1066,15 +1178,24 @@ in half is possible.
|
|
|
1066
1178
|
`process.platform`, `process.arch`.
|
|
1067
1179
|
|
|
1068
1180
|
**Files and the system.** `readFileSync(path)` (exits 1 on failure),
|
|
1069
|
-
`readFileSyncOrNull(path)` (`string | null`), `
|
|
1181
|
+
`readFileSyncOrNull(path)` (`string | null`), `readFileBytesSync(path)`
|
|
1182
|
+
(`u8[] | null`, the bytes as they are on disk), `writeFileSync(path, data)`,
|
|
1070
1183
|
`appendFileSync(path, data)`, `mkdirSync(path)` (one level, `boolean`),
|
|
1071
1184
|
`isDirectorySync(path)`, `readdirSync(path)` (`string[] | null`, sorted by
|
|
1072
1185
|
bytes, no `.`/`..`), `spawnSync(argv)`, `spawnSyncTo(argv, outPath, errPath)`,
|
|
1073
1186
|
`getenv(name)` (`string | null` — unset and empty are different answers),
|
|
1074
1187
|
`realpathSync(path)` (`string | null`; symbolic links resolved, absolute, and
|
|
1075
1188
|
`null` when it does not resolve),
|
|
1076
|
-
`monotonicNanos()` (`i64`; elapsed time only
|
|
1077
|
-
|
|
1189
|
+
`monotonicNanos()` (`i64`; elapsed time only).
|
|
1190
|
+
|
|
1191
|
+
**The host.** `Date.now()` (an `f64` of whole milliseconds, the wall clock —
|
|
1192
|
+
**the only `Date` there is**: `new Date()`, `Date.parse` and the rest are
|
|
1193
|
+
refused), `crypto.getRandomValues(bytes)` (a statement over a `u8[]` only, at
|
|
1194
|
+
most 65,536 bytes a call, from the kernel's CSPRNG; more panics),
|
|
1195
|
+
`statMtimeSync(path)` (an `f64` of milliseconds, **NaN** — not `null` — when the
|
|
1196
|
+
path cannot be stat'd, so test `m !== m`), and `signalFd()` / `readSignal(fd)`:
|
|
1197
|
+
call `signalFd()` once at the top of `main`, then `readSignal(fd)` blocks until
|
|
1198
|
+
SIGTERM or SIGINT and answers 15 or 2. None of them exists on a wasm target.
|
|
1078
1199
|
|
|
1079
1200
|
**Arena.** `Arena.mark()`, `Arena.release(m)`, `Arena.reset()`, `Arena.used()`
|
|
1080
1201
|
— see below.
|
|
@@ -1291,8 +1412,9 @@ export const main = (): i32 => {
|
|
|
1291
1412
|
**Optional parameters** — there are none. Write two functions with different
|
|
1292
1413
|
names, or take the value and document the sentinel.
|
|
1293
1414
|
|
|
1294
|
-
**A
|
|
1295
|
-
|
|
1415
|
+
**A calendar, regex, JSON, threads of your own** — not in the language (the
|
|
1416
|
+
wall clock is `Date.now()`, and that is all of `Date`). Say so rather than
|
|
1417
|
+
emitting code that cannot compile.
|
|
1296
1418
|
|
|
1297
1419
|
## Before you say it compiles
|
|
1298
1420
|
|
package/docs/INSTALL.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`nish` compiles a static subset of TypeScript to LLVM IR and, with
|
|
4
4
|
`--link`, to a native binary. The compiler is itself a native binary, written
|
|
5
|
-
in Nish (`
|
|
5
|
+
in Nish (`src/`), and needs nothing to run; the `--link` step (and anything
|
|
6
6
|
else that turns `.ll` into machine code) needs an LLVM toolchain.
|
|
7
7
|
|
|
8
8
|
## 1. Prerequisites
|
|
@@ -142,7 +142,7 @@ The npm route installs the **native** compiler, and it is a download rather than
|
|
|
142
142
|
build: nothing is compiled on your machine. The package declares one
|
|
143
143
|
`nish-<os>-<arch>` package per supported platform as an `optionalDependencies`
|
|
144
144
|
entry with `os` and `cpu` set, so npm fetches exactly the one that matches and
|
|
145
|
-
skips the rest. Each of those carries the self-hosted compiler — `
|
|
145
|
+
skips the rest. Each of those carries the self-hosted compiler — `src/`
|
|
146
146
|
compiled by itself — already built, `--verify`d and smoke-tested on a machine
|
|
147
147
|
of its own architecture by the release workflow.
|
|
148
148
|
|
|
@@ -198,7 +198,7 @@ nish: no prebuilt compiler for freebsd/x64
|
|
|
198
198
|
|
|
199
199
|
Until 0.6.0 it ran the TypeScript compiler that shipped in the same package
|
|
200
200
|
instead — the same compiler by every test here, about eight times slower, and
|
|
201
|
-
no C toolchain needed. That compiler was
|
|
201
|
+
no C toolchain needed. That compiler was stage0, which was deleted in R6
|
|
202
202
|
([wp19](wp19-stage0-retirement.md)), so there is nothing left in the package to
|
|
203
203
|
fall back to. The cost is stated where the rest of that deletion's costs are,
|
|
204
204
|
in [wp19 §6](wp19-stage0-retirement.md#6-what-retirement-costs-stated-plainly),
|
|
@@ -218,7 +218,7 @@ routes are
|
|
|
218
218
|
# in the glibc container, where build/nish runs
|
|
219
219
|
build/nish app.ts -o app.ll
|
|
220
220
|
# on the musl host, with its own clang and the runtime from this repository
|
|
221
|
-
clang app.ll runtime/runtime.c runtime/
|
|
221
|
+
clang app.ll runtime/runtime.c runtime/runtime-os.c -lm -o app
|
|
222
222
|
```
|
|
223
223
|
|
|
224
224
|
`--link`ing inside the container is the mistake to avoid: it shells out to the
|
|
@@ -268,7 +268,7 @@ npm install -g ./amritk-nish-0.4.0.tgz ./amritk-nish-x86_64-linux-0.4.0.tgz
|
|
|
268
268
|
```
|
|
269
269
|
|
|
270
270
|
As a native compiler, which needs no Node at all. A release also attaches the
|
|
271
|
-
self-hosted compiler — the binary `
|
|
271
|
+
self-hosted compiler — the binary `src/` produces by compiling itself — one
|
|
272
272
|
per supported platform, from the version named in the last column:
|
|
273
273
|
|
|
274
274
|
| Asset | For | Attached from |
|
|
@@ -326,7 +326,7 @@ release exists, take its version from
|
|
|
326
326
|
Unpack it and run `bin/nish` from wherever you like; put that on `PATH` if you
|
|
327
327
|
want it there. Keep the directory intact rather than moving the binary out of
|
|
328
328
|
it: `--link` runs `scripts/build.sh` and compiles the C runtime
|
|
329
|
-
(`runtime/runtime.c` and `runtime/
|
|
329
|
+
(`runtime/runtime.c` and `runtime/runtime-os.c`, the system-call half), and the
|
|
330
330
|
compiler finds all of them relative to its own location — `bin/nish` alone in a
|
|
331
331
|
directory can still emit IR with `-o`, but `--link` will tell you it cannot
|
|
332
332
|
find `scripts/build.sh`.
|
|
@@ -367,23 +367,23 @@ Building the IR yourself rather than through `--link` means naming the runtime
|
|
|
367
367
|
on the `clang` line, and it is two files:
|
|
368
368
|
|
|
369
369
|
```bash
|
|
370
|
-
clang app.ll runtime/runtime.c runtime/
|
|
370
|
+
clang app.ll runtime/runtime.c runtime/runtime-os.c -lm -o app
|
|
371
371
|
```
|
|
372
372
|
|
|
373
373
|
`runtime.c` is the half every program touches — the arena, strings, arrays,
|
|
374
|
-
number formatting, the panics — and `
|
|
374
|
+
number formatting, the panics — and `runtime-os.c` is the half that wraps the
|
|
375
375
|
system calls: files, directories, subprocesses, `getenv`, the monotonic clock.
|
|
376
376
|
They are separate so that each carries its own measured size ceiling
|
|
377
377
|
([docs/wp7-runtime.md](wp7-runtime.md)); nothing in the core calls into the
|
|
378
378
|
system-call half, so an older line that names `runtime.c` alone still links a
|
|
379
379
|
program that reads no files and spawns nothing. `scripts/build.sh` compiles
|
|
380
|
-
`
|
|
380
|
+
`runtime-os.c` beside any `runtime.c` it is handed, so a build that goes
|
|
381
381
|
through it — every `--link`, and every `--profile` recipe in these documents —
|
|
382
382
|
needs to name only the one.
|
|
383
383
|
|
|
384
384
|
## 2a. What `npm run build` does
|
|
385
385
|
|
|
386
|
-
`
|
|
386
|
+
`src/` is the compiler, written in Nish, and it compiles itself
|
|
387
387
|
([docs/wp14-selfhost.md](wp14-selfhost.md)). From a checkout, with clang on
|
|
388
388
|
`PATH`, `npm run build` runs `scripts/bootstrap.sh` with the seed from §2:
|
|
389
389
|
|
|
@@ -406,11 +406,11 @@ binary itself. It looks for `scripts/build.sh` and the two `runtime/*.c` files
|
|
|
406
406
|
one level up from wherever it was invoked, then in the working directory, so
|
|
407
407
|
it wants a checkout or an installed package around it the way `nish` does.
|
|
408
408
|
|
|
409
|
-
Because the seed is the last release, `
|
|
409
|
+
Because the seed is the last release, `src/` may only *use* in its own
|
|
410
410
|
source the constructs that release compiles. A new construct is implemented
|
|
411
|
-
in `
|
|
411
|
+
in `src/` and becomes usable inside `src/` from the next release on; CI's
|
|
412
412
|
`bootstrap` job is what checks that the released seed still builds stage1.
|
|
413
|
-
Until R6 the seed was a TypeScript compiler
|
|
413
|
+
Until R6 the seed was a TypeScript compiler, stage0, run under Node; it was
|
|
414
414
|
deleted once the native one answered every flag it did
|
|
415
415
|
([wp19](wp19-stage0-retirement.md)).
|
|
416
416
|
|
package/llms.txt
CHANGED
|
@@ -26,7 +26,7 @@ the compiler.
|
|
|
26
26
|
- [Rules card](https://raw.githubusercontent.com/amritk/nish/main/docs/AI.md): the whole language as rules, sized for one read — the traps first, then types, `Result`, nullables, declarations, statements, the complete builtin inventory, and recipes for what is missing. Ships in the npm package as `docs/AI.md`.
|
|
27
27
|
- [Language reference](https://raw.githubusercontent.com/amritk/nish/main/docs/LANGUAGE.md): normative and exhaustive. Every rule cites the test that pins it and quotes the exact rejection message. Settles any disagreement with the rules card.
|
|
28
28
|
- [Ambient declarations](https://raw.githubusercontent.com/amritk/nish/main/runtime/nish.d.ts): reference this from `tsconfig.json` and `tsc --strict`, your editor and your language server accept `Result<T, E>`, `i32` and the rest. `nish` is still the authority — `tsc` cannot see the flow rules.
|
|
29
|
-
- [Standard library](https://raw.githubusercontent.com/amritk/nish/main/std/README.md): `std/testing`, a suite a program drives to check itself and answer an exit code; `std/text`, the `split` / `trim` / `replace` the language does not have; `std/json`, the value of one field of one flat JSON object — enough to read this compiler's own `--json` output. Source rather than a built library, imported
|
|
29
|
+
- [Standard library](https://raw.githubusercontent.com/amritk/nish/main/std/README.md): `std/testing`, a suite a program drives to check itself and answer an exit code; `std/text`, the `split` / `trim` / `replace` the language does not have; `std/json`, the value of one field of one flat JSON object — enough to read this compiler's own `--json` output; `nish/crypto`, SHA-256, SHA-384 and SHA-512, HMAC, HKDF, a constant-time compare, base64url and X25519, one module each (`nish/crypto/sha256` and so on). Source rather than a built library, imported as `nish/<module>`, and shipped in the npm package under `std/`.
|
|
30
30
|
- [FAQ](https://raw.githubusercontent.com/amritk/nish/main/docs/FAQ.md): why no `any`, why `i32`, how to get JavaScript numbers, why no GC, overflow and `--wrapping`, what errors look like.
|
|
31
31
|
- [Install](https://raw.githubusercontent.com/amritk/nish/main/docs/INSTALL.md): prerequisites per OS, hello world, exit codes, troubleshooting.
|
|
32
32
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amritk/nish",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Nish: an ahead-of-time compiler from a static subset of TypeScript to LLVM IR",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -46,7 +46,9 @@
|
|
|
46
46
|
"test:nish": "build/nish tests/nish/run.ts -o build/nish-runner.ir/ --link build/nish-runner && build/nish-runner",
|
|
47
47
|
"test:cli": "build/nish tests/nish/cli.ts -o build/nish-cli.ir/ --link build/nish-cli && build/nish-cli",
|
|
48
48
|
"test:node": "node tests/differential/unmodified.js",
|
|
49
|
-
"lint": "node scripts/check-filenames.mjs
|
|
49
|
+
"lint": "node scripts/check-filenames.mjs && biome check .",
|
|
50
|
+
"lint:fix": "biome check --write .",
|
|
51
|
+
"lint:dead": "knip --no-progress",
|
|
50
52
|
"format": "biome format --write .",
|
|
51
53
|
"prepublishOnly": "npm run check && npm test",
|
|
52
54
|
"changelog": "node scripts/changelog-gen.mjs"
|
|
@@ -75,14 +77,15 @@
|
|
|
75
77
|
"nish"
|
|
76
78
|
],
|
|
77
79
|
"optionalDependencies": {
|
|
78
|
-
"@amritk/nish-x86_64-linux": "0.
|
|
79
|
-
"@amritk/nish-aarch64-linux": "0.
|
|
80
|
-
"@amritk/nish-aarch64-darwin": "0.
|
|
81
|
-
"@amritk/nish-x86_64-darwin": "0.
|
|
80
|
+
"@amritk/nish-x86_64-linux": "0.14.0",
|
|
81
|
+
"@amritk/nish-aarch64-linux": "0.14.0",
|
|
82
|
+
"@amritk/nish-aarch64-darwin": "0.14.0",
|
|
83
|
+
"@amritk/nish-x86_64-darwin": "0.14.0"
|
|
82
84
|
},
|
|
83
85
|
"devDependencies": {
|
|
84
86
|
"@biomejs/biome": "2.5.12",
|
|
85
87
|
"@types/node": "^22.0.0",
|
|
88
|
+
"knip": "6.38.0",
|
|
86
89
|
"typescript": "^5.6.0"
|
|
87
90
|
}
|
|
88
91
|
}
|
package/runtime/nish.d.ts
CHANGED
|
@@ -54,6 +54,12 @@ type u64 = number;
|
|
|
54
54
|
type f32 = number;
|
|
55
55
|
type f64 = number;
|
|
56
56
|
|
|
57
|
+
// ---- Ranged integers (docs/wp31-ranged-integers.md) --------------------------
|
|
58
|
+
//
|
|
59
|
+
// An `i32` the compiler knows lies in `[Lo, Hi]`. The bounds are numeric
|
|
60
|
+
// literal types, and `tsc` checks only that; the range itself is `nish`'s.
|
|
61
|
+
type integer<Lo extends number, Hi extends number> = number;
|
|
62
|
+
|
|
57
63
|
// ---- Result (docs/LANGUAGE.md -> Result and error handling) ------------------
|
|
58
64
|
//
|
|
59
65
|
// Modelled as the tagged union TypeScript would use anyway, intersected with
|
|
@@ -104,6 +110,19 @@ declare function Err<T, E>(error: E): Result<T, E>;
|
|
|
104
110
|
// `Process` is not that lucky, so a project that needs `@types/node` for other
|
|
105
111
|
// reasons should drop this file's `process` rather than fight it.
|
|
106
112
|
|
|
113
|
+
/**
|
|
114
|
+
* The disposable protocol `using` reads (WP29 P2, docs/wp29-thread-surface.md
|
|
115
|
+
* §5): declared here so that a program using `nish/threads`'s scope needs no
|
|
116
|
+
* `"ESNext.Disposable"` in its `lib`. `nish` itself takes `using` only for a
|
|
117
|
+
* `scope()`, and `[Symbol.dispose]` only in `nish/threads`.
|
|
118
|
+
*/
|
|
119
|
+
interface SymbolConstructor {
|
|
120
|
+
readonly dispose: unique symbol;
|
|
121
|
+
}
|
|
122
|
+
interface Disposable {
|
|
123
|
+
[Symbol.dispose](): void;
|
|
124
|
+
}
|
|
125
|
+
|
|
107
126
|
interface Console {
|
|
108
127
|
/** `x` and a newline to stdout. Statement position; exactly one argument. */
|
|
109
128
|
log(x: string | number | boolean): void;
|
|
@@ -142,6 +161,18 @@ declare function toF64(x: number | boolean): f64;
|
|
|
142
161
|
declare function f64ToBits(x: f64): i64;
|
|
143
162
|
declare function bitsToF64(bits: i64): f64;
|
|
144
163
|
|
|
164
|
+
// ---- Constant time (WP34 N6) ------------------------------------------------
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* `(a & mask) | (b & ~mask)` with the mask hidden from the optimiser, so it is
|
|
168
|
+
* never turned into a branch: `a` for an all-ones mask, `b` for zero. Every
|
|
169
|
+
* operand is one type, `u32` or `u64`. Both are `number` here, so one generic
|
|
170
|
+
* declaration stands for the two.
|
|
171
|
+
*/
|
|
172
|
+
declare function ctSelect<T extends u32 | u64>(mask: T, a: T, b: T): T;
|
|
173
|
+
/** All-ones of the operands' type when `a === b`, zero otherwise, without a branch. */
|
|
174
|
+
declare function ctEq<T extends u32 | u64>(a: T, b: T): T;
|
|
175
|
+
|
|
145
176
|
// ---- Streams and files (globals: Nish has no package resolution) ---------
|
|
146
177
|
|
|
147
178
|
/** `s` to stdout with no trailing newline and no conversion. */
|
|
@@ -152,7 +183,7 @@ declare function writeError(s: string): void;
|
|
|
152
183
|
* `message` and a newline to stderr, then exit 1. Terminates control flow, so
|
|
153
184
|
* it is `never`: that is what lets `tsc` agree that a function ending in a
|
|
154
185
|
* `panic` returns, and that `x` is not null after `if (x === null) { panic(...); }`
|
|
155
|
-
* — the guard-then-panic shape `
|
|
186
|
+
* — the guard-then-panic shape `src/` uses everywhere in place of an assert.
|
|
156
187
|
*/
|
|
157
188
|
declare function panic(message: string): never;
|
|
158
189
|
/**
|
|
@@ -163,6 +194,12 @@ declare function panic(message: string): never;
|
|
|
163
194
|
declare function readFileSync(path: string): string;
|
|
164
195
|
/** The same read, answering `null` for every path the other exits over. */
|
|
165
196
|
declare function readFileSyncOrNull(path: string): string | null;
|
|
197
|
+
/**
|
|
198
|
+
* The file's bytes as they are on disk — no UTF-8 assumed, so a zero byte and
|
|
199
|
+
* a byte of 0x80 or above survive — or `null` for every path
|
|
200
|
+
* `readFileSyncOrNull` answers `null` for.
|
|
201
|
+
*/
|
|
202
|
+
declare function readFileBytesSync(path: string): u8[] | null;
|
|
166
203
|
declare function writeFileSync(path: string, data: string): void;
|
|
167
204
|
declare function appendFileSync(path: string, data: string): void;
|
|
168
205
|
/** One directory, not recursive; whether a directory is there afterwards. */
|
|
@@ -194,6 +231,34 @@ declare function getenv(name: string): string | null;
|
|
|
194
231
|
* origin is arbitrary, so only the difference between two reads is meaningful.
|
|
195
232
|
*/
|
|
196
233
|
declare function monotonicNanos(): i64;
|
|
234
|
+
/**
|
|
235
|
+
* The modification time of `path` in milliseconds since the epoch, with the
|
|
236
|
+
* sub-millisecond fraction the file system keeps (Node's `mtimeMs`), or NaN
|
|
237
|
+
* when it cannot be stat'd. Follows a symbolic link; a directory has one too.
|
|
238
|
+
*/
|
|
239
|
+
declare function statMtimeSync(path: string): f64;
|
|
240
|
+
/**
|
|
241
|
+
* A descriptor that becomes readable when SIGTERM or SIGINT arrives, whichever
|
|
242
|
+
* thread the signal lands on, made once (every call answers the same one), or -1.
|
|
243
|
+
*/
|
|
244
|
+
declare function signalFd(): i32;
|
|
245
|
+
/**
|
|
246
|
+
* Block until SIGTERM or SIGINT arrives and answer its number, 15 or 2; -1 for
|
|
247
|
+
* any `fd` that is not `signalFd()`'s. No reading under Node, which throws.
|
|
248
|
+
*/
|
|
249
|
+
declare function readSignal(fd: i32): i32;
|
|
250
|
+
|
|
251
|
+
// ---- `Date` and `crypto` (WP34 N3) -----------------------------------------------
|
|
252
|
+
//
|
|
253
|
+
// `lib.es2022` already declares `Date`, whose `now()` is Nish's one member of it,
|
|
254
|
+
// so nothing is added: `tsc` accepting `new Date()` is `tsc` not being Nish's
|
|
255
|
+
// checker. `crypto` is a Web API that `lib.es2022` leaves out, so it is
|
|
256
|
+
// declared with its one member, typed as Nish types it.
|
|
257
|
+
|
|
258
|
+
declare var crypto: {
|
|
259
|
+
/** Every byte of `bytes` from the system's CSPRNG; at most 65,536 bytes a call. */
|
|
260
|
+
getRandomValues(bytes: u8[]): void;
|
|
261
|
+
};
|
|
197
262
|
|
|
198
263
|
// ---- The builtin modules (`nish:`) ---------------------------------------------
|
|
199
264
|
//
|
|
@@ -210,6 +275,7 @@ declare function monotonicNanos(): i64;
|
|
|
210
275
|
declare module "nish:fs" {
|
|
211
276
|
export function readFileSync(path: string): string;
|
|
212
277
|
export function readFileSyncOrNull(path: string): string | null;
|
|
278
|
+
export function readFileBytesSync(path: string): u8[] | null;
|
|
213
279
|
export function writeFileSync(path: string, data: string): void;
|
|
214
280
|
export function appendFileSync(path: string, data: string): void;
|
|
215
281
|
/** `true` when the directory was created, `false` when it already existed. */
|
|
@@ -221,6 +287,8 @@ declare module "nish:fs" {
|
|
|
221
287
|
*/
|
|
222
288
|
export function readdirSync(path: string): string[] | null;
|
|
223
289
|
export function realpathSync(path: string): string | null;
|
|
290
|
+
/** Node's `mtimeMs` for the path, or NaN when it cannot be stat'd. */
|
|
291
|
+
export function statMtimeSync(path: string): f64;
|
|
224
292
|
}
|
|
225
293
|
|
|
226
294
|
declare module "nish:process" {
|
|
@@ -238,6 +306,10 @@ declare module "nish:process" {
|
|
|
238
306
|
* difference between two reads is meaningful.
|
|
239
307
|
*/
|
|
240
308
|
export function monotonicNanos(): i64;
|
|
309
|
+
/** A descriptor readable when SIGTERM or SIGINT arrives; the same one every call, or -1. */
|
|
310
|
+
export function signalFd(): i32;
|
|
311
|
+
/** Block until SIGTERM or SIGINT arrives: 15 or 2, or -1 for a descriptor that is not `signalFd()`'s. */
|
|
312
|
+
export function readSignal(fd: i32): i32;
|
|
241
313
|
/** The command line; `argv[0]` is the program path, as in C. Read-only. */
|
|
242
314
|
export const argv: readonly string[];
|
|
243
315
|
/** The operating system the program runs on: `"linux"`, `"darwin"`, or `"unknown"`. */
|
|
@@ -284,6 +356,18 @@ declare interface CPtr {
|
|
|
284
356
|
readonly __nishForeignPointer: unique symbol;
|
|
285
357
|
}
|
|
286
358
|
|
|
359
|
+
// ---- `set` on an array ----------------------------------------------------------
|
|
360
|
+
//
|
|
361
|
+
// `dst.set(src, offset)` copies all of `src` into `dst` from `offset` on, with
|
|
362
|
+
// `TypedArray.prototype.set`'s meaning (WP34 N2). A `u8[]` is an `Array` to
|
|
363
|
+
// `tsc`, and `lib.es5.d.ts` gives `Array` a `fill` of the same meaning but no
|
|
364
|
+
// `set`, so this is the one method added to it; `runtime/nish.mjs` installs it
|
|
365
|
+
// under Node. Nish admits it on an array of numbers only.
|
|
366
|
+
|
|
367
|
+
interface Array<T> {
|
|
368
|
+
set(source: readonly T[], offset?: number): void;
|
|
369
|
+
}
|
|
370
|
+
|
|
287
371
|
// ---- What this file cannot say ----------------------------------------------
|
|
288
372
|
//
|
|
289
373
|
// The typed-array aliases (`Int32Array`, `Float32Array`, `Float64Array`,
|