@fraczak/k 7.0.1 → 7.1.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.
Files changed (89) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/DOCS/BIG_PICTURE.md +5 -5
  3. package/DOCS/FILE_FORMATS.md +241 -18
  4. package/DOCS/OBJECT_FILE_AND_PATTERN.md +9 -9
  5. package/DOCS/PATTERNS.md +3 -3
  6. package/DOCS/POLYMORPHIC_IR.md +104 -0
  7. package/DOCS/PROFILING_AND_TRACING.md +60 -72
  8. package/DOCS/REPL.md +3 -3
  9. package/DOCS/book/03-types-as-finite-automata.md +4 -4
  10. package/DOCS/book/06-values-in-memory.md +9 -9
  11. package/DOCS/book/08-operational-semantics-and-execution.md +1 -1
  12. package/DOCS/book/09-compiler-architecture.md +2 -2
  13. package/DOCS/book/10-from-ast-to-intermediate-representation.md +2 -2
  14. package/DOCS/book/14-canonical-serialization.md +1 -1
  15. package/Examples/arithmetics.k +2 -2
  16. package/Examples/bnat.k +1 -1
  17. package/Examples/karatsuba-mult.k +2 -2
  18. package/JOURNAL.md +288 -0
  19. package/README.md +13 -10
  20. package/access-table.mjs +343 -0
  21. package/backend-api.mjs +18 -0
  22. package/backends/llvm/README.md +20 -21
  23. package/backends/llvm/bin/k-llvm-build.mjs +105 -32
  24. package/backends/llvm/bin/k-llvm-compile.mjs +63 -24
  25. package/backends/llvm/bin/k-llvm.mjs +292 -0
  26. package/backends/llvm/runtime/krt.c +41 -0
  27. package/backends/llvm/runtime/krt.h +2 -0
  28. package/backends/llvm/src/cli.mjs +95 -17
  29. package/backends/llvm/src/executable.mjs +132 -15
  30. package/backends/llvm/src/kvm2llvm.mjs +50 -23
  31. package/backends/llvm/src/llvm.mjs +133 -20
  32. package/backends/llvm/tests/integration.sh +11 -11
  33. package/backends/llvm/{bin → tests}/k-llvm-run.mjs +48 -22
  34. package/backends/llvm/tests/perf-support.mjs +25 -58
  35. package/backends/llvm/tests/test-llvm.mjs +25 -4
  36. package/backends/wasm/bin/k-wasm-compile.mjs +46 -20
  37. package/backends/wasm/bin/k-wasm-run.mjs +61 -20
  38. package/backends/wasm/bin/k-wasm.mjs +47 -13
  39. package/backends/wasm/src/cli.mjs +141 -19
  40. package/backends/wasm/src/wasm.mjs +132 -24
  41. package/backends/wasm/tests/test-wasm.mjs +5 -5
  42. package/browser/repl-browser.mjs +16 -5
  43. package/browser/vfs-data.mjs +5 -5
  44. package/codecs/k-parse.mjs +29 -8
  45. package/codecs/k-print.mjs +22 -6
  46. package/codecs/runtime/codec-sdk.mjs +8 -4
  47. package/codecs/show.mjs +20 -6
  48. package/core.k +8 -8
  49. package/harness/format.mjs +2 -2
  50. package/harness/registry.mjs +0 -2
  51. package/k.mjs +56 -16
  52. package/kir.mjs +200 -75
  53. package/kvm.mjs +49 -12
  54. package/objects/compile.mjs +37 -23
  55. package/objects/decompile.mjs +40 -10
  56. package/objects/extract-aliases.mjs +44 -14
  57. package/objects/inspect.mjs +19 -14
  58. package/objects/validate.mjs +18 -11
  59. package/package.json +10 -7
  60. package/pkir.mjs +772 -0
  61. package/repl.mjs +36 -18
  62. package/scripts/benchmark-idempotency.mjs +12 -8
  63. package/scripts/benchmark.mjs +5 -9
  64. package/scripts/build-repl-html.mjs +16 -4
  65. package/scripts/perf-ieee.mjs +253 -103
  66. package/scripts/perf-int.mjs +585 -297
  67. package/scripts/perf-multiplication.mjs +66 -25
  68. package/scripts/perf-poly.mjs +292 -164
  69. package/scripts/run-tests.mjs +3 -0
  70. package/tests/code-derivation/pattern-envelope.mjs +4 -4
  71. package/tests/test-access-table.mjs +213 -0
  72. package/tests/test-kvm-polymorphic.mjs +2 -2
  73. package/tests/test-pkir.mjs +530 -0
  74. package/tests/test-polymorphic-tdd.mjs +196 -0
  75. package/tests/test-repl-html.mjs +15 -2
  76. package/tests/test-repl.mjs +30 -1
  77. package/backends/arm64/README.md +0 -103
  78. package/backends/arm64/bin/k-arm64-compile.mjs +0 -93
  79. package/backends/arm64/bin/k-arm64-run.mjs +0 -58
  80. package/backends/arm64/bin/k-arm64.mjs +0 -88
  81. package/backends/arm64/scripts/run-tests.mjs +0 -57
  82. package/backends/arm64/src/arm64.mjs +0 -229
  83. package/backends/arm64/src/cli.mjs +0 -155
  84. package/backends/arm64/src/kvm2arm64.mjs +0 -906
  85. package/backends/arm64/src/runtime.c +0 -744
  86. package/backends/arm64/tests/integration.sh +0 -112
  87. package/backends/arm64/tests/test-arm64.mjs +0 -240
  88. package/backends/llvm/bin/k-llvm-jit.mjs +0 -160
  89. package/harness/adapters/arm64.mjs +0 -105
@@ -0,0 +1,292 @@
1
+ #!/usr/bin/env node
2
+
3
+ import crypto from "node:crypto";
4
+ import fs from "node:fs";
5
+ import os from "node:os";
6
+ import path from "node:path";
7
+ import { spawn } from "node:child_process";
8
+ import { argv, exit, stdin, stdout } from "node:process";
9
+ import { fileURLToPath } from "node:url";
10
+
11
+ import { decodeWire } from "@fraczak/k/backend-api.mjs";
12
+ import {
13
+ compileProgramInputToObject,
14
+ parseCompileOptions,
15
+ readAll,
16
+ resolveProgramInput
17
+ } from "../src/cli.mjs";
18
+ import {
19
+ compileKVMToExecutable,
20
+ compileObjectToExecutable,
21
+ compilePKIRToExecutable
22
+ } from "../src/executable.mjs";
23
+
24
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
25
+
26
+ function helpText() {
27
+ const prog = argv[1] || "k-llvm.mjs";
28
+ const defaultCache = path.join(os.tmpdir(), "k-llvm-cache");
29
+ return [
30
+ "Compile k source, .ko, .kvm, or .pkir to native machine code via",
31
+ "LLVM and execute it over a binary pattern+value wire stream.",
32
+ "",
33
+ `Usage: ${prog} [options]`,
34
+ " ( source-snippet | input-file ) [ input-wire-file ]",
35
+ "",
36
+ "Arguments:",
37
+ " source-snippet Inline k source snippet (in the style of k.mjs).",
38
+ " input-file Source .k, .ko, .klib, .kvm, or .pkir file.",
39
+ " input-wire-file Optional binary wire input (default: stdin).",
40
+ "",
41
+ "Options:",
42
+ " --lib file Load one .klib or .k library before compiling.",
43
+ " --export spec Export library alias into scope. Repeatable.",
44
+ " spec is 'name' or 'libname:localname'.",
45
+ " --pkir Lower via Polymorphic K IR (PKIR).",
46
+ " --retype rel Relation to specialize (default: object main).",
47
+ " --cache-dir path Directory to store compiled native executables.",
48
+ " --no-cache Compile in a temporary directory and discard.",
49
+ " --opt flag Clang optimization level (-O0, -O1, -O2, -O3).",
50
+ " -h, --help Show this help.",
51
+ "",
52
+ "Environment Variables:",
53
+ ` K_LLVM_CACHE_DIR Cache directory (default: ${defaultCache}).`,
54
+ " K_LLVM_CLANG_OPT Default Clang optimization flag (default: -O1).",
55
+ " CLANG, CC Path to Clang compiler binary (default: clang).",
56
+ " K_TRACE, TRACE When '1', output phase tracing to stderr.",
57
+ "",
58
+ "Examples:",
59
+ ` echo '"abc"' | k-parse | ${prog} '{.1 0}' | k-print`,
60
+ ` echo '{"x":1,"y":2}' | k-json --parse | \\`,
61
+ ` ${prog} --lib Examples/poly.k --export concat \\`,
62
+ ` 's={()xs,()ys}concat; s' | k-json --print`
63
+ ].join("\n");
64
+ }
65
+
66
+ function usage(stream = console.error) {
67
+ stream(helpText());
68
+ }
69
+
70
+ let cachedFingerprint = null;
71
+ function getBackendFingerprint() {
72
+ if (cachedFingerprint != null) return cachedFingerprint;
73
+ const hash = crypto.createHash("sha256");
74
+ for (const relPath of [
75
+ "src/llvm.mjs",
76
+ "src/kvm2llvm.mjs",
77
+ "src/executable.mjs",
78
+ "runtime/krt.c",
79
+ "runtime/krt.h"
80
+ ]) {
81
+ const full = path.join(root, relPath);
82
+ if (fs.existsSync(full)) {
83
+ hash.update(relPath);
84
+ hash.update("\0");
85
+ hash.update(fs.readFileSync(full));
86
+ hash.update("\0");
87
+ }
88
+ }
89
+ cachedFingerprint = hash.digest("hex").slice(0, 16);
90
+ return cachedFingerprint;
91
+ }
92
+
93
+ function computeCacheKey({
94
+ fingerprint,
95
+ inputKey,
96
+ relation,
97
+ inputPattern,
98
+ usePKIR,
99
+ clangOpt
100
+ }) {
101
+ const hash = crypto.createHash("sha256");
102
+ hash.update("k-llvm-runner-v1\0");
103
+ hash.update(fingerprint);
104
+ hash.update("\0");
105
+ hash.update(String(relation || ""));
106
+ hash.update("\0");
107
+ hash.update(JSON.stringify(inputPattern || null));
108
+ hash.update("\0");
109
+ hash.update(usePKIR ? "pkir\0" : "monomorphic\0");
110
+ hash.update(String(clangOpt || ""));
111
+ hash.update("\0");
112
+ hash.update(inputKey);
113
+ return hash.digest("hex");
114
+ }
115
+
116
+ function runNativeExecutable(exePath, inputBuffer) {
117
+ return new Promise((resolve, reject) => {
118
+ const child = spawn(exePath, [], {
119
+ stdio: ["pipe", "pipe", "inherit"]
120
+ });
121
+ const chunks = [];
122
+ child.stdout.on("data", (chunk) => chunks.push(chunk));
123
+ child.on("error", reject);
124
+ child.on("close", (status) => {
125
+ if (status !== 0) {
126
+ reject(new Error(`${exePath} exited with status ${status}`));
127
+ return;
128
+ }
129
+ resolve(Buffer.concat(chunks));
130
+ });
131
+ child.stdin.end(inputBuffer);
132
+ });
133
+ }
134
+
135
+ async function main() {
136
+ const rawArgs = argv.slice(2);
137
+ if (rawArgs.includes("-h") || rawArgs.includes("--help")) {
138
+ usage(console.log);
139
+ return;
140
+ }
141
+
142
+ let cacheDir = process.env.K_LLVM_CACHE_DIR ||
143
+ path.join(os.tmpdir(), "k-llvm-cache");
144
+ let useCache = true;
145
+ let relation = null;
146
+ let clangOpt = process.env.K_LLVM_CLANG_OPT || "-O1";
147
+
148
+ const remainingArgs = [];
149
+ while (rawArgs.length > 0) {
150
+ const arg = rawArgs[0];
151
+ if (arg === "--cache-dir") {
152
+ rawArgs.shift();
153
+ cacheDir = rawArgs.shift();
154
+ if (!cacheDir) {
155
+ throw new Error("--cache-dir requires a directory path");
156
+ }
157
+ } else if (arg === "--no-cache") {
158
+ rawArgs.shift();
159
+ useCache = false;
160
+ } else if (arg === "--retype") {
161
+ rawArgs.shift();
162
+ relation = rawArgs.shift();
163
+ if (!relation) {
164
+ throw new Error("--retype requires a relation name");
165
+ }
166
+ } else if (arg === "--opt") {
167
+ rawArgs.shift();
168
+ clangOpt = rawArgs.shift();
169
+ if (!clangOpt) {
170
+ throw new Error("--opt requires a flag (e.g. -O1)");
171
+ }
172
+ } else {
173
+ remainingArgs.push(rawArgs.shift());
174
+ }
175
+ }
176
+
177
+ const { libraries, exportSpecs, usePKIR } =
178
+ parseCompileOptions(remainingArgs);
179
+ const programInput = resolveProgramInput(remainingArgs);
180
+ const inputWirePath = remainingArgs.shift();
181
+ if (remainingArgs.length > 0) {
182
+ throw new Error(`Unexpected argument: ${remainingArgs[0]}`);
183
+ }
184
+
185
+ const inputBuffer = await readAll(
186
+ inputWirePath == null ? stdin : fs.createReadStream(inputWirePath)
187
+ );
188
+ if (inputBuffer.length === 0) {
189
+ throw new Error("No input wire data received on stdin");
190
+ }
191
+
192
+ const { pattern: inputEnvelopePattern } = decodeWire(inputBuffer);
193
+
194
+ let exePath;
195
+ let tempExeDir = null;
196
+
197
+ if (useCache) {
198
+ fs.mkdirSync(cacheDir, { recursive: true });
199
+ const fingerprint = getBackendFingerprint();
200
+ const inputKey = JSON.stringify({
201
+ programInput,
202
+ libraries: libraries.map((lib) => Object.keys(lib.rels || {})),
203
+ exportSpecs
204
+ });
205
+ const key = computeCacheKey({
206
+ fingerprint,
207
+ inputKey,
208
+ relation,
209
+ inputPattern: inputEnvelopePattern,
210
+ usePKIR,
211
+ clangOpt
212
+ });
213
+ exePath = path.join(cacheDir, `${key}.exe`);
214
+ if (!fs.existsSync(exePath)) {
215
+ const tmpPath = path.join(cacheDir, `${key}.${process.pid}.tmp`);
216
+ if (programInput.kind === "pkir") {
217
+ const pkir = JSON.parse(
218
+ fs.readFileSync(programInput.path, "utf8")
219
+ );
220
+ compilePKIRToExecutable(pkir, tmpPath, {
221
+ inputPattern: inputEnvelopePattern,
222
+ clangOpt
223
+ });
224
+ } else if (programInput.kind === "kvm") {
225
+ const kvm = JSON.parse(
226
+ fs.readFileSync(programInput.path, "utf8")
227
+ );
228
+ compileKVMToExecutable(kvm, tmpPath, {
229
+ inputPattern: inputEnvelopePattern,
230
+ clangOpt,
231
+ usePKIR
232
+ });
233
+ } else {
234
+ const object = await compileProgramInputToObject(programInput, {
235
+ libraries,
236
+ exportSpecs,
237
+ stdin
238
+ });
239
+ compileObjectToExecutable(object, tmpPath, {
240
+ relation: relation || object.main,
241
+ inputPattern: inputEnvelopePattern,
242
+ clangOpt,
243
+ usePKIR
244
+ });
245
+ }
246
+ fs.renameSync(tmpPath, exePath);
247
+ }
248
+ } else {
249
+ tempExeDir = fs.mkdtempSync(path.join(os.tmpdir(), "k-llvm-run-"));
250
+ exePath = path.join(tempExeDir, "prog.exe");
251
+ if (programInput.kind === "pkir") {
252
+ const pkir = JSON.parse(fs.readFileSync(programInput.path, "utf8"));
253
+ compilePKIRToExecutable(pkir, exePath, {
254
+ inputPattern: inputEnvelopePattern,
255
+ clangOpt
256
+ });
257
+ } else if (programInput.kind === "kvm") {
258
+ const kvm = JSON.parse(fs.readFileSync(programInput.path, "utf8"));
259
+ compileKVMToExecutable(kvm, exePath, {
260
+ inputPattern: inputEnvelopePattern,
261
+ clangOpt,
262
+ usePKIR
263
+ });
264
+ } else {
265
+ const object = await compileProgramInputToObject(programInput, {
266
+ libraries,
267
+ exportSpecs,
268
+ stdin
269
+ });
270
+ compileObjectToExecutable(object, exePath, {
271
+ relation: relation || object.main,
272
+ inputPattern: inputEnvelopePattern,
273
+ clangOpt,
274
+ usePKIR
275
+ });
276
+ }
277
+ }
278
+
279
+ try {
280
+ const outputBuffer = await runNativeExecutable(exePath, inputBuffer);
281
+ stdout.write(outputBuffer);
282
+ } finally {
283
+ if (tempExeDir) {
284
+ fs.rmSync(tempExeDir, { recursive: true, force: true });
285
+ }
286
+ }
287
+ }
288
+
289
+ main().catch((error) => {
290
+ console.error(error.stack || error.message || String(error));
291
+ exit(1);
292
+ });
@@ -1489,6 +1489,39 @@ k_value *k_read_wire(FILE *in, k_rt *rt) {
1489
1489
  return value;
1490
1490
  }
1491
1491
 
1492
+ static int is_union_kind(int kind) {
1493
+ return kind == KP_OPEN_UNION || kind == KP_CLOSED_UNION;
1494
+ }
1495
+
1496
+ static int is_product_kind(int kind) {
1497
+ return kind == KP_OPEN_PRODUCT || kind == KP_CLOSED_PRODUCT;
1498
+ }
1499
+
1500
+ int k_pattern_compatible(k_pattern *a, k_pattern *b) {
1501
+ if (a == b) return 1;
1502
+ if (a == NULL || b == NULL) return 0;
1503
+ if (a->node_count != b->node_count) return 0;
1504
+ for (size_t i = 0; i < a->node_count; i++) {
1505
+ k_pattern_node *left = &a->nodes[i];
1506
+ k_pattern_node *right = &b->nodes[i];
1507
+ if (left->kind != right->kind) {
1508
+ if (!(is_union_kind(left->kind) && is_union_kind(right->kind)) &&
1509
+ !(is_product_kind(left->kind) && is_product_kind(right->kind))) {
1510
+ return 0;
1511
+ }
1512
+ }
1513
+ if (left->edge_count != right->edge_count) return 0;
1514
+ for (size_t j = 0; j < left->edge_count; j++) {
1515
+ if (left->edges[j].target != right->edges[j].target) return 0;
1516
+ if (!bytes_equal(left->edges[j].label, left->edges[j].label_length,
1517
+ right->edges[j].label, right->edges[j].label_length)) {
1518
+ return 0;
1519
+ }
1520
+ }
1521
+ }
1522
+ return 1;
1523
+ }
1524
+
1492
1525
  int k_pattern_equal(k_pattern *a, k_pattern *b) {
1493
1526
  if (a == b) return 1;
1494
1527
  if (a == NULL || b == NULL) return 0;
@@ -1881,6 +1914,14 @@ unsigned char *k_encode_wire_as(k_pattern *pattern, k_value *value, size_t *out_
1881
1914
  return bytes;
1882
1915
  }
1883
1916
 
1917
+ unsigned char *k_encode_wire(k_value *value, size_t *out_length) {
1918
+ k_pattern *pattern = derive_pattern(value);
1919
+ if (pattern == NULL) return NULL;
1920
+ unsigned char *bytes = k_encode_wire_as(pattern, value, out_length);
1921
+ free_pattern(pattern);
1922
+ return bytes;
1923
+ }
1924
+
1884
1925
  int k_write_wire_as(FILE *out, k_pattern *pattern, k_value *value) {
1885
1926
  size_t length = 0;
1886
1927
  unsigned char *bytes = k_encode_wire_as(pattern, value, &length);
@@ -104,11 +104,13 @@ k_wire_input k_read_wire_envelope(FILE *in, k_rt *rt);
104
104
  k_wire_input k_decode_wire_envelope(const unsigned char *bytes, size_t length, k_rt *rt);
105
105
  void k_wire_input_free(k_wire_input input);
106
106
  int k_pattern_equal(k_pattern *a, k_pattern *b);
107
+ int k_pattern_compatible(k_pattern *a, k_pattern *b);
107
108
  k_wire_prefix k_wire_prefix_for_pattern(k_pattern *pattern);
108
109
  void k_wire_prefix_free(k_wire_prefix prefix);
109
110
  k_value *k_decode_wire_value_with_prefix(const unsigned char *bytes, size_t length, k_wire_prefix *prefix, k_pattern *pattern, k_rt *rt);
110
111
  unsigned char *k_encode_wire_as_with_prefix(k_wire_prefix *prefix, k_pattern *pattern, k_value *value, size_t *out_length);
111
112
  unsigned char *k_encode_wire_as(k_pattern *pattern, k_value *value, size_t *out_length);
113
+ unsigned char *k_encode_wire(k_value *value, size_t *out_length);
112
114
  int k_write_wire(FILE *out, k_value *value);
113
115
  int k_write_wire_as(FILE *out, k_pattern *pattern, k_value *value);
114
116
 
@@ -1,20 +1,73 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { compileLibrary, compileObjectBuffer, decodeObject, loadLibrary } from "@fraczak/k/object.mjs";
3
+ import {
4
+ compileLibrary,
5
+ compileObjectBuffer,
6
+ decodeObject,
7
+ loadLibrary
8
+ } from "@fraczak/k/object.mjs";
4
9
 
5
10
  export function readAll(stream) {
6
11
  return new Promise((resolve, reject) => {
7
12
  const chunks = [];
8
- stream.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
13
+ stream.on("data", (chunk) => {
14
+ chunks.push(
15
+ Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
16
+ );
17
+ });
9
18
  stream.on("end", () => resolve(Buffer.concat(chunks)));
10
19
  stream.on("error", reject);
11
20
  });
12
21
  }
13
22
 
23
+ export function loadLibraryFromPath(libPath) {
24
+ const ext = path.extname(libPath).toLowerCase();
25
+ if (ext === ".klib" || ext === ".ko") {
26
+ return loadLibrary(decodeObject(fs.readFileSync(libPath)));
27
+ }
28
+ if (ext === ".k") {
29
+ let depLibs = [];
30
+ const baseName = path.basename(libPath);
31
+ if (baseName === "poly.k") {
32
+ const dir = path.dirname(libPath);
33
+ const arithPath = path.join(dir, "arithmetics.k");
34
+ if (fs.existsSync(arithPath)) {
35
+ const arithLib = loadLibrary(
36
+ compileLibrary(fs.readFileSync(arithPath, "utf8"), {
37
+ source: arithPath
38
+ })
39
+ );
40
+ depLibs.push(arithLib);
41
+ }
42
+ }
43
+ const rawSource = fs.readFileSync(libPath, "utf8");
44
+ if (depLibs.length > 0) {
45
+ const neededExports = [
46
+ "0", "int", "inc", "dec", "nat", "zero_int?", "neg?", "GT"
47
+ ];
48
+ const preamble = buildExportPreamble(neededExports, depLibs);
49
+ const compiled = compileLibrary(preamble + rawSource, {
50
+ source: libPath,
51
+ libraries: depLibs
52
+ });
53
+ const mergedLib = loadLibrary(compiled);
54
+ for (const dep of depLibs) {
55
+ for (const [k, v] of Object.entries(dep.relAlias || {})) {
56
+ if (!mergedLib.relAlias[k]) mergedLib.relAlias[k] = v;
57
+ }
58
+ }
59
+ return mergedLib;
60
+ }
61
+ return loadLibrary(compileLibrary(rawSource, { source: libPath }));
62
+ }
63
+ throw new Error(`Unsupported library extension: ${ext}`);
64
+ }
65
+
14
66
  export function parseCompileOptions(args) {
15
67
  const libraries = [];
16
68
  const exportSpecs = [];
17
69
  let loadedLibrary = false;
70
+ let usePKIR = false;
18
71
 
19
72
  while (args.length > 0) {
20
73
  if (args[0] === "--") {
@@ -22,22 +75,26 @@ export function parseCompileOptions(args) {
22
75
  break;
23
76
  }
24
77
  if (args[0] === "--lib") {
25
- if (loadedLibrary) throw new Error("--lib may only be specified once");
78
+ if (loadedLibrary) {
79
+ throw new Error("--lib may only be specified once");
80
+ }
26
81
  args.shift();
27
82
  const libPath = args.shift();
28
83
  if (!libPath) throw new Error("--lib requires a file argument");
29
- const ext = path.extname(libPath).toLowerCase();
30
- if (ext === ".k") {
31
- libraries.push(loadLibrary(compileLibrary(fs.readFileSync(libPath, "utf8"), { source: libPath })));
32
- } else {
33
- libraries.push(loadLibrary(decodeObject(fs.readFileSync(libPath))));
34
- }
84
+ libraries.push(loadLibraryFromPath(libPath));
35
85
  loadedLibrary = true;
36
86
  } else if (args[0] === "--export") {
37
87
  args.shift();
38
88
  const spec = args.shift();
39
- if (!spec) throw new Error("--export requires a spec argument (name or libname:localname)");
89
+ if (!spec) {
90
+ throw new Error(
91
+ "--export requires a spec argument (name or libname:localname)"
92
+ );
93
+ }
40
94
  exportSpecs.push(spec);
95
+ } else if (args[0] === "--pkir") {
96
+ args.shift();
97
+ usePKIR = true;
41
98
  } else if (args[0].startsWith("--")) {
42
99
  break;
43
100
  } else {
@@ -45,7 +102,7 @@ export function parseCompileOptions(args) {
45
102
  }
46
103
  }
47
104
 
48
- return { libraries, exportSpecs };
105
+ return { libraries, exportSpecs, usePKIR };
49
106
  }
50
107
 
51
108
  export function fileInputType(filePath) {
@@ -53,6 +110,7 @@ export function fileInputType(filePath) {
53
110
  if (ext === ".ko") return "ko";
54
111
  if (ext === ".kvm") return "kvm";
55
112
  if (ext === ".klib") return "klib";
113
+ if (ext === ".pkir") return "pkir";
56
114
  return "source-file";
57
115
  }
58
116
 
@@ -69,11 +127,15 @@ export function resolveProgramInput(args, { allowStdinSource = false } = {}) {
69
127
  return { kind, path: inputArg, sourceName: inputArg };
70
128
  }
71
129
 
72
- if ([".k", ".ko", ".kvm", ".klib"].includes(ext)) {
130
+ if ([".k", ".ko", ".kvm", ".klib", ".pkir"].includes(ext)) {
73
131
  throw new Error(`Input file not found: ${inputArg}`);
74
132
  }
75
133
 
76
- return { kind: "inline-source", source: inputArg, sourceName: "<inline>" };
134
+ return {
135
+ kind: "inline-source",
136
+ source: inputArg,
137
+ sourceName: "<inline>"
138
+ };
77
139
  }
78
140
 
79
141
  export function buildExportPreamble(exports = [], libraries = []) {
@@ -94,9 +156,15 @@ export function buildExportPreamble(exports = [], libraries = []) {
94
156
  }
95
157
  const lines = [];
96
158
  for (const spec of exports) {
97
- const [libName, localName] = spec.includes(":") ? spec.split(":", 2) : [spec, spec];
159
+ const [libName, localName] = spec.includes(":")
160
+ ? spec.split(":", 2)
161
+ : [spec, spec];
98
162
  const hash = aliasMap[libName];
99
- if (!hash) throw new Error(`--export: '${libName}' not found in loaded libraries`);
163
+ if (!hash) {
164
+ throw new Error(
165
+ `--export: '${libName}' not found in loaded libraries`
166
+ );
167
+ }
100
168
  const body = hash.startsWith("@") ? hash.slice(1) : hash;
101
169
  lines.push(`${localName} = @${body};`);
102
170
  }
@@ -116,7 +184,9 @@ export async function compileProgramInputToObject(
116
184
  });
117
185
  object = decodeObject(objBuffer);
118
186
  } else if (input.kind === "stdin-source") {
119
- if (!stdin) throw new Error("stdin source requested without a stdin stream");
187
+ if (!stdin) {
188
+ throw new Error("stdin source requested without a stdin stream");
189
+ }
120
190
  const source = (await readAll(stdin)).toString("utf8");
121
191
  const preamble = buildExportPreamble(exportSpecs, libraries);
122
192
  const objBuffer = compileObjectBuffer(preamble + source, {
@@ -135,7 +205,15 @@ export async function compileProgramInputToObject(
135
205
  } else if (input.kind === "ko" || input.kind === "klib") {
136
206
  object = decodeObject(fs.readFileSync(input.path));
137
207
  } else if (input.kind === "kvm") {
138
- throw new Error("Input is already lowered to .kvm bytecode; use compileLLVMArtifactFromKVM or compileKVMToExecutable.");
208
+ throw new Error(
209
+ "Input is already lowered to .kvm bytecode; use " +
210
+ "compileLLVMArtifactFromKVM or compileKVMToExecutable."
211
+ );
212
+ } else if (input.kind === "pkir") {
213
+ throw new Error(
214
+ "Input is already lowered to .pkir IR; use " +
215
+ "compileLLVMArtifactFromPKIR or compilePKIRToExecutable."
216
+ );
139
217
  } else {
140
218
  throw new Error(`Unsupported input kind: ${input.kind}`);
141
219
  }