@amritk/nish-aarch64-linux 0.11.0 → 0.12.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/INSTALL.md +16 -0
- package/bin/nish +0 -0
- package/package.json +1 -1
- package/runtime/nish.d.ts +2 -0
- package/runtime/nish.mjs +13 -0
- package/std/README.md +14 -9
- package/std/collections.ts +39 -2
- package/std/map.ts +37 -0
package/INSTALL.md
CHANGED
|
@@ -446,6 +446,21 @@ Other build profiles: `--profile size` (smallest binary), `--profile debug`
|
|
|
446
446
|
(no optimisation, symbols kept). Run `nish --help` for every flag, and
|
|
447
447
|
see the [README](../README.md) for the language subset.
|
|
448
448
|
|
|
449
|
+
To use a program as a script rather than keep the binary, run it:
|
|
450
|
+
|
|
451
|
+
```bash
|
|
452
|
+
nish run hello.ts
|
|
453
|
+
# hello from Nish
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
`nish run [options] <file.ts> [args ...]` compiles the file, links it into
|
|
457
|
+
`$XDG_CACHE_HOME/nish/run` (or `~/.cache/nish/run`) when that program has not
|
|
458
|
+
been linked before, and starts it with the arguments after the file. The first
|
|
459
|
+
run of a new edit pays for one link, and later runs start the cached binary.
|
|
460
|
+
It needs clang only for that link. It needs `HOME` or `XDG_CACHE_HOME` set,
|
|
461
|
+
and refuses the run without them rather than keep a binary in a shared
|
|
462
|
+
directory.
|
|
463
|
+
|
|
449
464
|
## 4. Exit codes
|
|
450
465
|
|
|
451
466
|
| Code | Meaning |
|
|
@@ -454,6 +469,7 @@ see the [README](../README.md) for the language subset.
|
|
|
454
469
|
| 1 | the program was rejected: compile error (`file:line:col: error: ...`), missing input file, or an `-o` layout that does not fit the module count |
|
|
455
470
|
| 2 | usage error: unknown flag, missing argument, no input files |
|
|
456
471
|
| 3 | toolchain error: `--link` found no `clang` (`CC` overrides), or `scripts/build.sh` failed (its output is shown; the `.ll` files are still written) — or the `nish` command found no prebuilt compiler for this platform, or one that would not start. Under `--json` all of these are one `NL0002` object |
|
|
472
|
+
| | `nish run` answers these codes until the program starts, and the program's own exit status (`128 + n` for a signal) after that. With neither `HOME` nor `XDG_CACHE_HOME` set it answers 3 |
|
|
457
473
|
| 70 | internal compiler error: an unexpected exception. Please report it at <https://github.com/amritk/nish/issues> with the input and command line; `NISH_DEBUG=1` prints the stack trace |
|
|
458
474
|
|
|
459
475
|
## Troubleshooting
|
package/bin/nish
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/runtime/nish.d.ts
CHANGED
|
@@ -34,6 +34,8 @@
|
|
|
34
34
|
* only as the iterable of a `for...of`, and `get`'s `V | undefined` only as a
|
|
35
35
|
* `const`'s initialiser, the left of `??` or an operand of `=== undefined`,
|
|
36
36
|
* none of which `tsc` can know (docs/LANGUAGE.md -> `Map` and `Set`).
|
|
37
|
+
* `nish/map`'s `reserve` and `getOrInsert` are not declared here either: they
|
|
38
|
+
* are ordinary source, `std/map.ts`, which `tsconfig.json` maps `nish/*` to.
|
|
37
39
|
*/
|
|
38
40
|
|
|
39
41
|
// ---- Numeric widths (docs/LANGUAGE.md -> Types) ------------------------------
|
package/runtime/nish.mjs
CHANGED
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
* harness already uses, so the two cannot drift apart: a semantic fixed there
|
|
55
55
|
* is fixed here in the same commit.
|
|
56
56
|
*/
|
|
57
|
+
import { registerHooks } from "node:module";
|
|
57
58
|
import * as shim from "./shim.mjs";
|
|
58
59
|
|
|
59
60
|
/** Install `value` as a global unless the program declared its own. */
|
|
@@ -141,3 +142,15 @@ provide("Arena", {
|
|
|
141
142
|
reset: shim.arenaReset,
|
|
142
143
|
used: shim.arenaUsed,
|
|
143
144
|
});
|
|
145
|
+
|
|
146
|
+
// The standard library. A program imports it as `nish/<module>`, which the
|
|
147
|
+
// compiler resolves to `std/<module>.ts` beside itself; this does the same for
|
|
148
|
+
// Node, relative to this file rather than to the program, so the same
|
|
149
|
+
// specifier works from any directory. Only the `nish/` package is answered
|
|
150
|
+
// here, and every other specifier goes to Node as it was written.
|
|
151
|
+
registerHooks({
|
|
152
|
+
resolve: (specifier, context, next) =>
|
|
153
|
+
specifier.startsWith("nish/")
|
|
154
|
+
? next(new URL(`../std/${specifier.slice("nish/".length)}.ts`, import.meta.url).href, context)
|
|
155
|
+
: next(specifier, context),
|
|
156
|
+
});
|
package/std/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# `std/` — the standard library
|
|
2
2
|
|
|
3
3
|
Nish modules written in Nish, for Nish programs to import. There is no magic
|
|
4
|
-
here and — with
|
|
5
|
-
the compiler knows about: a module in this directory is an ordinary Nish source file, compiled as part of
|
|
4
|
+
here and — with three exceptions, `threads.ts`, `collections.ts` and `map.ts` —
|
|
5
|
+
nothing the compiler knows about: a module in this directory is an ordinary Nish source file, compiled as part of
|
|
6
6
|
whatever program imports it, and subject to the same rules as `examples/` or
|
|
7
7
|
`self/` ([`docs/LANGUAGE.md`](../docs/LANGUAGE.md) is the style guide).
|
|
8
8
|
|
|
@@ -12,7 +12,8 @@ whatever program imports it, and subject to the same rules as `examples/` or
|
|
|
12
12
|
| [`text.ts`](./text.ts) | the string operations a program would otherwise write inline: `splitLines`, `splitWhitespace`, `trim` and its halves, `contains`, `replaceAll`, and `firstDifference` over two arrays of lines |
|
|
13
13
|
| [`json.ts`](./json.ts) | `jsonField(object, name)`: the value of one field of one flat JSON object, which is the shape the compiler's own `--json` diagnostics have. A reader and not a parser — it answers text, answers `null` for a field that is not there, and does not validate |
|
|
14
14
|
| [`pair.ts`](./pair.ts) | `Pair<A, B>`: an interface with `first` and `second`, for a function that answers two values from one call. A type and nothing else — the caller writes an object literal at the return — and for returning two values rather than storing them side by side |
|
|
15
|
-
| [`collections.ts`](./collections.ts) | the global `Map<K, V>` and `Set<T>`: insertion-ordered tables whose buckets carry a hash fingerprint beside the entry index and whose entries keep their full hash, so every `get`, `set`, `add`, `has` and `delete` is one probe. `get` is not a method here: its `V | undefined` never crosses a call, so the compiler lowers it to `probe` and, where the key was found, `valueAt`. Nor are `keys()` and `values()`: an iterator is not a value, so a `for...of` over one is lowered to `walkOpen`, `walkNext`, `keyAt` or `valueAt`, and `walkClose`, and a count of live walks defers compaction until no loop is walking the table. A program never imports it: naming `Map` or `Set` loads it, and the compiler emits what a module uses of it into that module ([`docs/wp32-map.md`](../docs/wp32-map.md), [`docs/LANGUAGE.md`](../docs/LANGUAGE.md#map-and-set)). Its `hashKey` and `
|
|
15
|
+
| [`collections.ts`](./collections.ts) | the global `Map<K, V>` and `Set<T>`: insertion-ordered tables whose buckets carry a hash fingerprint beside the entry index and whose entries keep their full hash, so every `get`, `set`, `add`, `has` and `delete` is one probe. `get` is not a method here: its `V | undefined` never crosses a call, so the compiler lowers it to `probe` and, where the key was found, `valueAt`. Nor are `keys()` and `values()`: an iterator is not a value, so a `for...of` over one is lowered to `walkOpen`, `walkNext`, `keyAt` or `valueAt`, and `walkClose`, and a count of live walks defers compaction until no loop is walking the table. A program never imports it: naming `Map` or `Set` loads it, and the compiler emits what a module uses of it into that module ([`docs/wp32-map.md`](../docs/wp32-map.md), [`docs/LANGUAGE.md`](../docs/LANGUAGE.md#map-and-set)). Its `hashKey`, `sameKey` and `storedKey` are lowered by the compiler per key type |
|
|
16
|
+
| [`map.ts`](./map.ts) | `reserve(m, n)` and `getOrInsert(m, k, v)` for the global `Map`. Their bodies are the meaning, and what runs under Node: `reserve` does nothing, and `getOrInsert` is a `get`, and a `set` of `v` when the key was missing. Natively the compiler lowers every call in place — `reserve` to the table's `reserveSlots`, which grows the buckets once so that `n` entries fit without a rebuild, and `getOrInsert` to one `probe` and a `valueAt` or an `insertAt` through its answer — so, like `collections.ts`, it writes no `.ll` of its own ([`docs/wp32-map.md`](../docs/wp32-map.md) §9.2, [`docs/LANGUAGE.md`](../docs/LANGUAGE.md#map-and-set)) |
|
|
16
17
|
| [`threads.ts`](./threads.ts) | `parallelMapInto(src, dst, f)` and `parallelReduce(src, f, identity)`: a function over every element of an array, on as many threads as the length is worth. Its bodies are the sequential meaning, which is what runs under Node; the compiler recognises the two templates by module and name, lowers the one loop in each onto `nish_parallel_range`, holds the function to the rules that make that safe, and compiles an importing program with `--threads` ([`docs/LANGUAGE.md`](../docs/LANGUAGE.md#data-parallelism-nishthreads)). `tests/link/par_*` are its programs |
|
|
17
18
|
|
|
18
19
|
## How a program imports it
|
|
@@ -64,12 +65,16 @@ reasons that are only true of it:
|
|
|
64
65
|
package and is versioned with it, so "the `std/` beside this binary" is not a
|
|
65
66
|
guess a resolver makes — it is the only `std/` that can be correct for the
|
|
66
67
|
compiler reading it. No version can be skewed against it.
|
|
67
|
-
- It **is** what Node
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
`
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
- It **is** what Node and `tsc` resolve. The package is published as
|
|
69
|
+
`@amritk/nish`, so a bare `nish/text` is not a package self-reference on its
|
|
70
|
+
own; `runtime/nish.mjs`, the prelude a program runs under Node with
|
|
71
|
+
(`node --experimental-strip-types --import ./runtime/nish.mjs`), resolves
|
|
72
|
+
`nish/<module>` to `std/<module>.ts` beside itself, and the repository's
|
|
73
|
+
`tsconfig.json` maps `nish/*` to `./std/*`, which is what gives `tsc` and an
|
|
74
|
+
editor go-to-definition into the real source. `package.json` still declares
|
|
75
|
+
`"./*": "./std/*.ts"` in `exports`, so `@amritk/nish/text` reaches the same
|
|
76
|
+
file. The compiler short-circuits to that answer rather than walking
|
|
77
|
+
`node_modules` to reach it.
|
|
73
78
|
|
|
74
79
|
So this is WP21's first slice rather than a detour around it: the spelling is
|
|
75
80
|
the one WP21 specifies, and what is still missing is resolution for specifiers
|
package/std/collections.ts
CHANGED
|
@@ -394,7 +394,7 @@ export class Map<K, V> {
|
|
|
394
394
|
this.rebuild();
|
|
395
395
|
bucket = -1;
|
|
396
396
|
}
|
|
397
|
-
this.entryKeys.push(key);
|
|
397
|
+
this.entryKeys.push(storedKey(key));
|
|
398
398
|
this.entryValues.push(value);
|
|
399
399
|
this.entryHashes.push(h);
|
|
400
400
|
this.live = this.live + 1;
|
|
@@ -427,6 +427,33 @@ export class Map<K, V> {
|
|
|
427
427
|
this.mask = toI32(slots.length) - 1;
|
|
428
428
|
refile(slots, this.entryHashes);
|
|
429
429
|
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* `reserve(m, n)` from `nish/map` (docs/wp32-map.md §9.2): grow the bucket
|
|
433
|
+
* table until `n` entries, dead ones included, fit under the load bound, so
|
|
434
|
+
* that the inserts up to `n` rebuild nothing. It only ever grows the buckets
|
|
435
|
+
* and re-files them from the stored hashes, so no entry moves and it is as
|
|
436
|
+
* safe under a walk as the doubling a walk already allows (§6.2). A count
|
|
437
|
+
* that is not positive, or not a number, does nothing, as `reserve` does
|
|
438
|
+
* under Node; one past the entry cap is the cap.
|
|
439
|
+
*/
|
|
440
|
+
reserveSlots(n: number): void {
|
|
441
|
+
if (!(n > 0)) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
const want: i32 = n >= 16777215 ? INDEX_CAP : toI32(n);
|
|
445
|
+
const have = toI32(this.slots.length);
|
|
446
|
+
let size = have;
|
|
447
|
+
while (want * 4 > size * 3) {
|
|
448
|
+
size = size * 2;
|
|
449
|
+
}
|
|
450
|
+
if (size > have) {
|
|
451
|
+
const slots = new Array<u32>(size);
|
|
452
|
+
this.slots = slots;
|
|
453
|
+
this.mask = size - 1;
|
|
454
|
+
refile(slots, this.entryHashes);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
430
457
|
}
|
|
431
458
|
|
|
432
459
|
/** A set of keys in insertion order: `Map`'s table with no values, and the same probe. */
|
|
@@ -519,7 +546,7 @@ export class Set<T> {
|
|
|
519
546
|
this.rebuild();
|
|
520
547
|
bucket = -1;
|
|
521
548
|
}
|
|
522
|
-
this.entryKeys.push(key);
|
|
549
|
+
this.entryKeys.push(storedKey(key));
|
|
523
550
|
this.entryHashes.push(h);
|
|
524
551
|
this.live = this.live + 1;
|
|
525
552
|
this.size = this.size + 1;
|
|
@@ -546,3 +573,13 @@ export class Set<T> {
|
|
|
546
573
|
refile(slots, this.entryHashes);
|
|
547
574
|
}
|
|
548
575
|
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* The key as an entry stores it: a float's -0 becomes +0, as ECMA-262's
|
|
579
|
+
* `Map.prototype.set` and `Set.prototype.add` store it, so a walk over the
|
|
580
|
+
* keys yields +0 whichever zero was inserted. Lowered in place, like `hashKey`
|
|
581
|
+
* and `sameKey`: `fadd` of +0 for a float, and nothing at all for every other
|
|
582
|
+
* key. It sits last in the file so that adding it moved no line a `-g` build
|
|
583
|
+
* of an earlier function records.
|
|
584
|
+
*/
|
|
585
|
+
const storedKey = <K>(key: K): K => key;
|
package/std/map.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nish/map` — two operations on the global `Map` that JavaScript's does not
|
|
3
|
+
* have, written so that a program using them still runs unmodified under Node
|
|
4
|
+
* (docs/wp32-map.md §9.2).
|
|
5
|
+
*
|
|
6
|
+
* The bodies below are the meaning, and they are what runs under Node:
|
|
7
|
+
* `reserve` does nothing, and `getOrInsert` is a `get`, and a `set` when the
|
|
8
|
+
* key was missing. Natively neither body is ever called. The compiler lowers
|
|
9
|
+
* every call in place, as it lowers `hashKey` and `sameKey`: `reserve` is the
|
|
10
|
+
* table's `reserveSlots`, which grows the buckets so that `n` entries fit
|
|
11
|
+
* without a rebuild, and `getOrInsert` is one `probe` of the key, then the
|
|
12
|
+
* value it found or an insert through the empty bucket the probe stopped at,
|
|
13
|
+
* with the hash it already computed. So this module writes no `.ll` of its
|
|
14
|
+
* own, and a one-file program that imports it is still one module.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Make room in `m` for `n` entries in all, so that inserting up to `n` never
|
|
19
|
+
* grows the table. Only the speed of a program depends on it: natively it
|
|
20
|
+
* presizes the bucket table, and under Node, which has no such call, it does
|
|
21
|
+
* nothing. A count that is not positive does nothing either way.
|
|
22
|
+
*/
|
|
23
|
+
export const reserve = <K, V>(m: Map<K, V>, n: number): void => {};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The value of `key` in `m`; or, when `key` is missing, `value`, after
|
|
27
|
+
* setting `key` to it. One hash and one probe natively, whichever it was.
|
|
28
|
+
* `value` is evaluated either way, as every argument is.
|
|
29
|
+
*/
|
|
30
|
+
export const getOrInsert = <K, V>(m: Map<K, V>, key: K, value: V): V => {
|
|
31
|
+
const found = m.get(key);
|
|
32
|
+
if (found !== undefined) {
|
|
33
|
+
return found;
|
|
34
|
+
}
|
|
35
|
+
m.set(key, value);
|
|
36
|
+
return value;
|
|
37
|
+
};
|