@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amritk/nish-aarch64-linux",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "The @amritk/nish native compiler for Linux on ARM64",
5
5
  "license": "MIT",
6
6
  "os": [
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 two exceptions, `threads.ts` and `collections.ts` — nothing
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 `sameKey` are lowered by the compiler per key type |
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 resolves. `package.json` declares
68
- `"./*": "./std/*.ts"` in `exports`, so `nish/text` is a package
69
- self-reference: `import.meta.resolve("nish/text")` answers `std/text.ts`, and
70
- `tsc` under `moduleResolution: node16` resolves it to the same file, which is
71
- what gives an editor go-to-definition into the real source. The compiler
72
- short-circuits to that answer rather than walking `node_modules` to reach it.
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
@@ -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
+ };