@amritk/nish 0.11.0 → 0.13.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.
@@ -28,7 +28,7 @@
28
28
  *
29
29
  * **The compiler resolves the link itself as of 0.6.0** -- the real path of
30
30
  * whatever `argv[0]` named is a candidate for the package root
31
- * ([wp19 §5a](../docs/wp19-stage0-retirement.md) item 4, in `self/`) -- so this
31
+ * ([wp19 §5a](../docs/wp19-stage0-retirement.md) item 4, in `src/`) -- so this
32
32
  * `exec` is no longer what makes a copy work, and the reason it is still here is
33
33
  * the 91 ms above rather than the defect. It also still carries whoever never
34
34
  * ran this script: a shim npm linked and nothing swapped now finds its own
@@ -45,44 +45,54 @@
45
45
  * postinstall that can break `npm ci` is a worse bug than the startup cost it
46
46
  * exists to remove.
47
47
  */
48
- import fs from "node:fs";
49
- import path from "node:path";
50
- import { createRequire } from "node:module";
51
- import { pathToFileURL } from "node:url";
48
+ import fs from "node:fs"
49
+ import path from "node:path"
50
+ import { createRequire } from "node:module"
51
+ import { pathToFileURL } from "node:url"
52
52
 
53
- const root = path.resolve(import.meta.dirname, "..");
53
+ const root = path.resolve(import.meta.dirname, "..")
54
54
 
55
55
  const swap = async () => {
56
56
  // A checkout, not an install. The repository is its own package, so `npm ci`
57
57
  // here installs this package's own optionalDependencies -- and without this
58
58
  // guard, the first `npm ci` after they are published would overwrite the
59
59
  // tracked `bin/nish` with a binary and leave the working tree dirty. The
60
- // landmark is `self/compile.ts`, which is the check `scripts/bootstrap.sh`
61
- // makes and is not in `files`; it used to be `src/launcher.ts`, which stopped
60
+ // landmark is `src/compile.ts`, which is the check `scripts/bootstrap.sh`
61
+ // makes and is not in `files`; it used to be stage0's `src/launcher.ts`, which stopped
62
62
  // being a landmark when the launcher moved into `bin/` and ships.
63
- if (fs.existsSync(path.join(root, "self", "compile.ts"))) return "a checkout, so the shim stays";
63
+ if (fs.existsSync(path.join(root, "src", "compile.ts"))) {
64
+ return "a checkout, so the shim stays"
65
+ }
64
66
 
65
- const shim = path.join(root, "bin", "nish");
66
- if (!fs.existsSync(shim)) return "no bin/nish to replace";
67
+ const shim = path.join(root, "bin", "nish")
68
+ if (!fs.existsSync(shim)) {
69
+ return "no bin/nish to replace"
70
+ }
67
71
 
68
- const packaging = path.join(root, "bin", "packaging.js");
69
- if (!fs.existsSync(packaging)) return "bin/packaging.js is missing";
70
- const { assetFor, platformPackageName } = await import(pathToFileURL(packaging).href);
71
- const asset = assetFor(process.platform, process.arch);
72
- if (asset === null) return `no prebuilt binary for ${process.platform}/${process.arch}`;
72
+ const packaging = path.join(root, "bin", "packaging.js")
73
+ if (!fs.existsSync(packaging)) {
74
+ return "bin/packaging.js is missing"
75
+ }
76
+ const { assetFor, platformPackageName } = await import(pathToFileURL(packaging).href)
77
+ const asset = assetFor(process.platform, process.arch)
78
+ if (asset === null) {
79
+ return `no prebuilt binary for ${process.platform}/${process.arch}`
80
+ }
73
81
 
74
82
  const name = platformPackageName(
75
83
  JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8")).name,
76
84
  asset
77
- );
78
- let binary;
85
+ )
86
+ let binary
79
87
  try {
80
- const manifest = createRequire(import.meta.url).resolve(`${name}/package.json`);
81
- binary = path.join(path.dirname(manifest), "bin", "nish");
88
+ const manifest = createRequire(import.meta.url).resolve(`${name}/package.json`)
89
+ binary = path.join(path.dirname(manifest), "bin", "nish")
82
90
  } catch {
83
- return `${name} is not installed`;
91
+ return `${name} is not installed`
92
+ }
93
+ if (!fs.existsSync(binary)) {
94
+ return `${name} carries no bin/nish`
84
95
  }
85
- if (!fs.existsSync(binary)) return `${name} carries no bin/nish`;
86
96
 
87
97
  // The absolute path is baked in at install time, which is the one thing this
88
98
  // gives up: a `node_modules` copied to a different path without a reinstall
@@ -99,35 +109,35 @@ const swap = async () => {
99
109
  // have ended up in, the shell would expand them. A home directory with a `$`
100
110
  // in it is unusual and entirely legal. In POSIX sh nothing is special inside
101
111
  // single quotes, so the only thing to handle is a single quote itself.
102
- const shellQuote = (value) => `'${value.split("'").join(`'\\''`)}'`;
103
- const target = shellQuote(binary);
112
+ const shellQuote = (value) => `'${value.split("'").join(`'\\''`)}'`
113
+ const target = shellQuote(binary)
104
114
  const script =
105
115
  "#!/bin/sh\n" +
106
116
  "# Written by scripts/postinstall.mjs. `npm rebuild` regenerates it.\n" +
107
117
  `if [ -x ${target} ]; then exec ${target} "$@"; fi\n` +
108
118
  // Exit 3 is this project's toolchain code: the compiler could not be run,
109
119
  // which is exactly what has happened (docs/wp12-release.md, "Exit codes").
110
- 'echo "nish: the prebuilt compiler is not where it was installed; run \'npm rebuild\' to repoint this" >&2\n' +
111
- "exit 3\n";
112
- const temp = `${shim}.${process.pid}.tmp`;
120
+ "echo \"nish: the prebuilt compiler is not where it was installed; run 'npm rebuild' to repoint this\" >&2\n" +
121
+ "exit 3\n"
122
+ const temp = `${shim}.${process.pid}.tmp`
113
123
  try {
114
- fs.writeFileSync(temp, script);
115
- fs.chmodSync(temp, 0o755);
116
- fs.renameSync(temp, shim);
124
+ fs.writeFileSync(temp, script)
125
+ fs.chmodSync(temp, 0o755)
126
+ fs.renameSync(temp, shim)
117
127
  } catch (err) {
118
128
  try {
119
- fs.rmSync(temp, { force: true });
129
+ fs.rmSync(temp, { force: true })
120
130
  } catch {
121
131
  // Nothing to do about it, and it is not worth a second message.
122
132
  }
123
- throw err;
133
+ throw err
124
134
  }
125
- return `bin/nish now execs the native compiler in ${name}`;
126
- };
135
+ return `bin/nish now execs the native compiler in ${name}`
136
+ }
127
137
 
128
138
  try {
129
- console.log(`nish: ${await swap()}`);
139
+ console.log(`nish: ${await swap()}`)
130
140
  } catch (err) {
131
141
  // Deliberately not an error: the shim is still there and still works.
132
- console.log(`nish: keeping the node launcher (${err instanceof Error ? err.message : String(err)})`);
142
+ console.log(`nish: keeping the node launcher (${err instanceof Error ? err.message : String(err)})`)
133
143
  }
@@ -51,13 +51,13 @@ text_sum() { # text_sum <object>: every .text* secti
51
51
  }
52
52
  sect() { size -A "$2" | awk -v s="$1" '$1 == s { print $2 }'; }
53
53
  "${CC:-clang}" -Oz -c runtime/runtime.c -o build/size/runtime.o
54
- "${CC:-clang}" -Oz -c runtime/runtime_os.c -o build/size/runtime_os.o
54
+ "${CC:-clang}" -Oz -c runtime/runtime-os.c -o build/size/runtime_os.o
55
55
  row runtime "$(text_sum build/size/runtime.o)" \
56
56
  "clang -Oz -c runtime/runtime.c && size -A (.text*; budget 3584)"
57
57
  row runtime_os "$(text_sum build/size/runtime_os.o)" \
58
- "clang -Oz -c runtime/runtime_os.c && size -A (.text*; budget 1280)"
58
+ "clang -Oz -c runtime/runtime-os.c && size -A (.text*; budget 1280)"
59
59
  row rodata "$(sect .rodata build/size/runtime.o)" "the core object's .rodata (no budget; the Ryu tables live here)"
60
- # `scripts/build.sh` compiles runtime_os.c beside any runtime.c it is handed, so naming the
60
+ # `scripts/build.sh` compiles runtime-os.c beside any runtime.c it is handed, so naming the
61
61
  # core here builds the whole runtime -- and `--gc-sections` then drops whatever this module
62
62
  # never calls, which for `examples/add.ts` is all of it.
63
63
  for p in debug speed size; do
package/scripts/smoke.sh CHANGED
@@ -17,7 +17,7 @@
17
17
  # link, or run with the expected status. Binaries and IR go to build/smoke/.
18
18
  # Needs clang on PATH.
19
19
  set -uo pipefail
20
- cd "$(dirname "$0")/.."
20
+ cd "$(dirname "$0")/.." || exit
21
21
 
22
22
  examples=${1:-examples}
23
23
  out=build/smoke
package/std/README.md CHANGED
@@ -1,10 +1,10 @@
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
- `self/` ([`docs/LANGUAGE.md`](../docs/LANGUAGE.md) is the style guide).
7
+ `src/` ([`docs/LANGUAGE.md`](../docs/LANGUAGE.md) is the style guide).
8
8
 
9
9
  | Module | What it is |
10
10
  | --- | --- |
@@ -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
@@ -125,7 +130,7 @@ that are *not* this package.
125
130
  stdout. `tests/link/std_text_f64` is the same corpus under
126
131
  `--number-mode f64`, which is where a module that spelled its widths and forgot
127
132
  a `toI32` is caught.
128
- - **`std/` is not on the compiler's dependency list.** Nothing in `self/`
133
+ - **`std/` is not on the compiler's dependency list.** Nothing in `src/`
129
134
  imports it, and nothing should: the compiler is the thing that has to
130
135
  build before the library means anything.
131
136