@fraczak/k 7.0.2 → 7.2.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 (71) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/DOCS/CODECS.md +37 -2
  3. package/DOCS/CODEC_ARCHITECTURE.md +12 -5
  4. package/DOCS/FILE_FORMATS.md +238 -15
  5. package/DOCS/POLYMORPHIC_IR.md +104 -0
  6. package/DOCS/REPL.md +11 -3
  7. package/DOCS/WEB_REPL_REQUIREMENTS.md +1 -1
  8. package/JOURNAL.md +288 -0
  9. package/README.md +12 -3
  10. package/Value.mjs +28 -3
  11. package/access-table.mjs +343 -0
  12. package/backend-api.mjs +23 -1
  13. package/backends/llvm/README.md +20 -21
  14. package/backends/llvm/bin/k-llvm-build.mjs +105 -32
  15. package/backends/llvm/bin/k-llvm-compile.mjs +63 -24
  16. package/backends/llvm/bin/k-llvm.mjs +292 -0
  17. package/backends/llvm/runtime/krt.c +8 -0
  18. package/backends/llvm/runtime/krt.h +1 -0
  19. package/backends/llvm/src/cli.mjs +95 -17
  20. package/backends/llvm/src/executable.mjs +128 -11
  21. package/backends/llvm/src/kvm2llvm.mjs +10 -5
  22. package/backends/llvm/src/llvm.mjs +121 -9
  23. package/backends/llvm/tests/integration.sh +11 -11
  24. package/backends/llvm/{bin → tests}/k-llvm-run.mjs +48 -22
  25. package/backends/llvm/tests/perf-support.mjs +24 -3
  26. package/backends/llvm/tests/test-llvm.mjs +22 -1
  27. package/backends/wasm/bin/k-wasm-compile.mjs +46 -20
  28. package/backends/wasm/bin/k-wasm-run.mjs +61 -20
  29. package/backends/wasm/bin/k-wasm.mjs +47 -13
  30. package/backends/wasm/src/cli.mjs +141 -19
  31. package/backends/wasm/src/wasm.mjs +130 -23
  32. package/browser/repl-browser.mjs +16 -5
  33. package/browser/tree-view.mjs +47 -8
  34. package/browser/vfs-data.mjs +6 -5
  35. package/codecs/README.md +1 -0
  36. package/codecs/djson.mjs +270 -0
  37. package/codecs/int.mjs +2 -1
  38. package/codecs/json.mjs +7 -2
  39. package/codecs/k-parse.mjs +36 -9
  40. package/codecs/k-print.mjs +26 -6
  41. package/codecs/runtime/codec-sdk.mjs +24 -6
  42. package/codecs/runtime/pattern-json.mjs +80 -1
  43. package/codecs/runtime/prefix-codec.mjs +629 -67
  44. package/codecs/runtime/show-value.mjs +93 -51
  45. package/codecs/runtime/unicode-string.mjs +29 -6
  46. package/codecs/show.mjs +24 -6
  47. package/codecs/utf16.mjs +2 -1
  48. package/k.mjs +56 -16
  49. package/kir.mjs +58 -19
  50. package/kvm.mjs +49 -12
  51. package/objects/compile.mjs +37 -23
  52. package/objects/decompile.mjs +40 -10
  53. package/objects/extract-aliases.mjs +44 -14
  54. package/objects/inspect.mjs +19 -14
  55. package/objects/validate.mjs +18 -11
  56. package/package.json +11 -3
  57. package/pkir.mjs +772 -0
  58. package/repl.mjs +92 -23
  59. package/scripts/build-repl-html.mjs +22 -3
  60. package/scripts/perf-ieee.mjs +264 -13
  61. package/scripts/perf-int.mjs +315 -22
  62. package/scripts/perf-multiplication.mjs +58 -7
  63. package/scripts/perf-poly.mjs +306 -24
  64. package/scripts/run-tests.mjs +3 -0
  65. package/tests/test-access-table.mjs +213 -0
  66. package/tests/test-codecs.mjs +168 -0
  67. package/tests/test-pkir.mjs +530 -0
  68. package/tests/test-polymorphic-tdd.mjs +196 -0
  69. package/tests/test-repl-html.mjs +16 -2
  70. package/tests/test-repl.mjs +63 -1
  71. package/backends/llvm/bin/k-llvm-jit.mjs +0 -160
@@ -1,7 +1,14 @@
1
1
  import assert from "node:assert";
2
2
  import { compileObjectBuffer, decodeObject } from "@fraczak/k/object.mjs";
3
- import { compileObjectToLLVM, compileLLVMArtifactFromKVM, emitLLVMModule, llvmIdentifier } from "../src/llvm.mjs";
3
+ import {
4
+ compileObjectToLLVM,
5
+ compileLLVMArtifactFromKVM,
6
+ compileLLVMArtifactFromPKIR,
7
+ emitLLVMModule,
8
+ llvmIdentifier
9
+ } from "../src/llvm.mjs";
4
10
  import { objectToKVMArtifact } from "@fraczak/k/kvm.mjs";
11
+ import { compileRelationToPKIR } from "@fraczak/k/backend-api.mjs";
5
12
 
6
13
  function kirP(body, { patternGraph = null } = {}) {
7
14
  return {
@@ -270,4 +277,18 @@ assert.match(kvmLLVM, /source_filename = "k-llvm:__main__"/);
270
277
  assert.match(kvmLLVM, /define %k_result @k_main/);
271
278
  assert.match(kvmLLVM, /store ptr %product_fields\d+, ptr %ptr_slot\d+/);
272
279
 
280
+ // Test PKIR lowering via compileObjectToLLVM with usePKIR: true
281
+ const pkirObjectLLVM = compileObjectToLLVM(productObject, {
282
+ usePKIR: true
283
+ });
284
+ assert.match(pkirObjectLLVM.llvm, /source_filename = "k-llvm:__main__"/);
285
+ assert.match(pkirObjectLLVM.llvm, /define %k_result @k_main/);
286
+
287
+ // Test compileLLVMArtifactFromPKIR directly
288
+ const mainRel = productObject.rels[productObject.main || "__main__"];
289
+ const pkirFromRel = compileRelationToPKIR(mainRel, "__main__");
290
+ const pkirLLVM = compileLLVMArtifactFromPKIR(pkirFromRel);
291
+ assert.match(pkirLLVM, /source_filename = "k-llvm:__main__"/);
292
+ assert.match(pkirLLVM, /define %k_result @k_main/);
293
+
273
294
  console.log("OK");
@@ -9,22 +9,39 @@ import {
9
9
  resolveProgramInput
10
10
  } from "../src/cli.mjs";
11
11
 
12
- function usage(stream = console.error) {
12
+ function helpText() {
13
13
  const prog = argv[1] || "k-wasm-compile.mjs";
14
- stream(`Usage: node ${prog} [options] [source-snippet | input-file [wasm-file]]`);
15
- stream("Compile k source, .ko, or .kvm input into a standalone WebAssembly artifact.");
16
- stream("");
17
- stream("Arguments:");
18
- stream(" source-snippet Inline k source, in the same style as k.mjs.");
19
- stream(" input-file Source .k, .ko, or .kvm file. Reads UTF-8 source from stdin when omitted.");
20
- stream(" wasm-file Output .wasm path. Writes the binary artifact to stdout when omitted.");
21
- stream("");
22
- stream("Options:");
23
- stream(" -o, --output Specify output file path explicitly.");
24
- stream(" --lib file Load one .klib dependency before compiling.");
25
- stream(" --export spec Export a library alias into source scope. May be repeated.");
26
- stream(" spec is 'name' or 'libname:localname'.");
27
- stream(" -h, --help Show this help.");
14
+ return [
15
+ "Compile k source, .ko, .kvm, or .pkir into a WebAssembly (.wasm)",
16
+ "artifact.",
17
+ "",
18
+ `Usage: ${prog} [options]`,
19
+ " [source-snippet | input-file [output-wasm]]",
20
+ "",
21
+ "Arguments:",
22
+ " source-snippet Inline k source snippet (in the style of k.mjs).",
23
+ " input-file Source .k, .ko, .kvm, or .pkir file.",
24
+ " Reads UTF-8 source from stdin when omitted.",
25
+ " output-wasm Output .wasm path. Writes to stdout if omitted.",
26
+ "",
27
+ "Options:",
28
+ " -o, --output path Specify output file path explicitly.",
29
+ " --pkir Lower via Polymorphic K IR (PKIR).",
30
+ " --lib file Load one .klib or .k library before compiling.",
31
+ " --export spec Export library alias into scope. Repeatable.",
32
+ " spec is 'name' or 'libname:localname'.",
33
+ " -h, --help Show this help.",
34
+ "",
35
+ "Examples:",
36
+ ` ${prog} 'x = |x; x x' program.wasm`,
37
+ ` ${prog} program.k -o program.wasm`,
38
+ ` ${prog} --pkir program.k -o program.wasm`,
39
+ ` ${prog} --lib core.klib --export add program.k program.wasm`
40
+ ].join("\n");
41
+ }
42
+
43
+ function usage(stream = console.error) {
44
+ stream(helpText());
28
45
  }
29
46
 
30
47
  try {
@@ -41,18 +58,28 @@ try {
41
58
  if (arg === "-o" || arg === "--output") {
42
59
  args.shift();
43
60
  explicitOutput = args.shift();
44
- if (!explicitOutput) throw new Error("-o/--output requires a file argument");
61
+ if (!explicitOutput) {
62
+ throw new Error("-o/--output requires a file argument");
63
+ }
45
64
  } else {
46
65
  remainingArgs.push(args.shift());
47
66
  }
48
67
  }
49
68
 
50
- const { libraries, exportSpecs } = parseCompileOptions(remainingArgs);
51
- const input = resolveProgramInput(remainingArgs, { allowStdinSource: true });
69
+ const { libraries, exportSpecs, usePKIR } =
70
+ parseCompileOptions(remainingArgs);
71
+ const input = resolveProgramInput(remainingArgs, {
72
+ allowStdinSource: true
73
+ });
52
74
  const positionalOutput = remainingArgs.shift();
53
75
  if (remainingArgs.length > 0) throw new Error("Too many arguments");
54
76
  const outputPath = explicitOutput || positionalOutput || null;
55
- const artifact = await compileProgramInput(input, { libraries, exportSpecs, stdin });
77
+ const artifact = await compileProgramInput(input, {
78
+ libraries,
79
+ exportSpecs,
80
+ stdin,
81
+ usePKIR
82
+ });
56
83
  if (outputPath == null) {
57
84
  stdout.write(artifact);
58
85
  } else {
@@ -60,6 +87,5 @@ try {
60
87
  }
61
88
  } catch (error) {
62
89
  console.error(error.stack || error.message || String(error));
63
- usage();
64
90
  exit(1);
65
91
  }
@@ -5,31 +5,54 @@ import { argv, exit, stdin, stdout, stderr } from "node:process";
5
5
 
6
6
  import { instantiateWasmArtifact } from "../src/wasm.mjs";
7
7
 
8
- function usage(stream = console.error) {
8
+ function helpText() {
9
9
  const prog = argv[1] || "k-wasm-run.mjs";
10
- stream(`Usage: node ${prog} [ --server ] wasm-file [ input-file ]`);
11
- stream("Run a standalone k WebAssembly artifact over a binary pattern+value stream.");
12
- stream("");
13
- stream("Arguments:");
14
- stream(" wasm-file Input .wasm artifact produced by k-wasm-compile.");
15
- stream(" input-file Optional binary input stream. Reads stdin when omitted.");
16
- stream("");
17
- stream("Options:");
18
- stream(" --server Run as a persistent server reading framed requests from stdin.");
19
- stream(" -h, --help Show this help.");
10
+ return [
11
+ "Run a standalone WebAssembly (.wasm) artifact over a binary",
12
+ "pattern+value wire stream.",
13
+ "",
14
+ `Usage: ${prog} [options] wasm-file [ input-wire-file ]`,
15
+ "",
16
+ "Arguments:",
17
+ " wasm-file Input .wasm artifact produced by k-wasm-compile.",
18
+ " input-wire-file Optional binary wire input (default: stdin).",
19
+ "",
20
+ "Options:",
21
+ " --server Run as persistent server reading framed requests from",
22
+ " stdin.",
23
+ " -h, --help Show this help.",
24
+ "",
25
+ "Environment Variables:",
26
+ " K_TRACE, TRACE When '1', output phase tracing metrics to stderr.",
27
+ "",
28
+ "Examples:",
29
+ ` echo '"hello"' | k-json --parse | \\`,
30
+ ` ${prog} prog.wasm | k-json --print`,
31
+ ` ${prog} prog.wasm input.wire > output.wire`,
32
+ ` ${prog} --server prog.wasm`
33
+ ].join("\n");
34
+ }
35
+
36
+ function usage(stream = console.error) {
37
+ stream(helpText());
20
38
  }
21
39
 
22
40
  function readAll(stream) {
23
41
  return new Promise((resolve, reject) => {
24
42
  const chunks = [];
25
- stream.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
43
+ stream.on("data", (chunk) => {
44
+ chunks.push(
45
+ Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
46
+ );
47
+ });
26
48
  stream.on("end", () => resolve(Buffer.concat(chunks)));
27
49
  stream.on("error", reject);
28
50
  });
29
51
  }
30
52
 
31
53
  async function runServer(runner) {
32
- const tracing = process.env.K_TRACE === "1" || process.env.TRACE === "1";
54
+ const tracing =
55
+ process.env.K_TRACE === "1" || process.env.TRACE === "1";
33
56
  let inBuffer = Buffer.alloc(0);
34
57
  let readStartNs = 0n;
35
58
 
@@ -82,10 +105,17 @@ async function runServer(runner) {
82
105
  const encodeNs = trace.encodeNs;
83
106
  const ipcWriteNs = Number(tEnd - writeStart);
84
107
  const totalNs = Number(tEnd - (readStartNs > 0n ? readStartNs : t1));
85
- stderr.write(`K_TRACE_PHASES backend=wasm ipc_read_ns=${ipcReadNs} decode_ns=${decodeNs} flat_in_ns=${flatInNs} eval_ns=${evalNs} flat_out_ns=${flatOutNs} encode_ns=${encodeNs} ipc_write_ns=${ipcWriteNs} total_ns=${totalNs}\n`);
108
+ stderr.write(
109
+ `K_TRACE_PHASES backend=wasm ipc_read_ns=${ipcReadNs} ` +
110
+ `decode_ns=${decodeNs} flat_in_ns=${flatInNs} ` +
111
+ `eval_ns=${evalNs} flat_out_ns=${flatOutNs} ` +
112
+ `encode_ns=${encodeNs} ipc_write_ns=${ipcWriteNs} ` +
113
+ `total_ns=${totalNs}\n`
114
+ );
86
115
  }
87
116
  } catch (err) {
88
- stderr.write(`Wasm server error: ${err.stack || err.message || String(err)}\n`);
117
+ const msg = err.stack || err.message || String(err);
118
+ stderr.write(`Wasm server error: ${msg}\n`);
89
119
  exit(1);
90
120
  }
91
121
 
@@ -102,7 +132,7 @@ async function main() {
102
132
  }
103
133
 
104
134
  const isServer = rawArgs.includes("--server");
105
- const args = rawArgs.filter(a => a !== "--server");
135
+ const args = rawArgs.filter((a) => a !== "--server");
106
136
 
107
137
  const wasmPath = args.shift();
108
138
  if (!wasmPath) throw new Error("Missing .wasm artifact path");
@@ -111,7 +141,9 @@ async function main() {
111
141
  const runner = await instantiateWasmArtifact(artifact);
112
142
 
113
143
  if (isServer) {
114
- if (args.length > 0) throw new Error("Too many arguments for --server mode");
144
+ if (args.length > 0) {
145
+ throw new Error("Too many arguments for --server mode");
146
+ }
115
147
  await runServer(runner);
116
148
  return;
117
149
  }
@@ -119,14 +151,23 @@ async function main() {
119
151
  const inputPath = args.shift();
120
152
  if (args.length > 0) throw new Error("Too many arguments");
121
153
 
122
- const input = await readAll(inputPath == null ? stdin : fs.createReadStream(inputPath));
123
- const tracing = process.env.K_TRACE === "1" || process.env.TRACE === "1";
154
+ const inputStream =
155
+ inputPath == null ? stdin : fs.createReadStream(inputPath);
156
+ const input = await readAll(inputStream);
157
+ const tracing =
158
+ process.env.K_TRACE === "1" || process.env.TRACE === "1";
124
159
  if (tracing) {
125
160
  const res = runner.execute(input, { trace: true });
126
161
  stdout.write(res.outputWire);
127
162
  if (res.trace) {
128
163
  const trace = res.trace;
129
- stderr.write(`K_TRACE_PHASES backend=wasm ipc_read_ns=0 decode_ns=${trace.decodeNs} flat_in_ns=${trace.flatInNs} eval_ns=${trace.evalNs} flat_out_ns=${trace.flatOutNs} encode_ns=${trace.encodeNs} ipc_write_ns=0 total_ns=${trace.evalNs}\n`);
164
+ stderr.write(
165
+ `K_TRACE_PHASES backend=wasm ipc_read_ns=0 ` +
166
+ `decode_ns=${trace.decodeNs} flat_in_ns=${trace.flatInNs} ` +
167
+ `eval_ns=${trace.evalNs} flat_out_ns=${trace.flatOutNs} ` +
168
+ `encode_ns=${trace.encodeNs} ipc_write_ns=0 ` +
169
+ `total_ns=${trace.evalNs}\n`
170
+ );
130
171
  }
131
172
  } else {
132
173
  stdout.write(runner.execute(input));
@@ -12,16 +12,40 @@ import {
12
12
  } from "../src/cli.mjs";
13
13
  import { runWasmArtifact } from "../src/wasm.mjs";
14
14
 
15
- function usage(stream = console.error) {
15
+ function helpText() {
16
16
  const prog = argv[1] || "k-wasm.mjs";
17
- stream(`Usage: node ${prog} [options] ( source-snippet | input-file ) [ input-file ]`);
18
- stream("Compile k source, .ko, or .kvm input to WebAssembly in memory and run it over a binary pattern+value stream.");
19
- stream("");
20
- stream("Options:");
21
- stream(" --lib file Load one .klib dependency before compiling.");
22
- stream(" --export spec Export a library alias into source scope. May be repeated.");
23
- stream(" spec is 'name' or 'libname:localname'.");
24
- stream(" -h, --help Show this help.");
17
+ return [
18
+ "Compile k source, .ko, .kvm, or .pkir to WebAssembly in memory",
19
+ "and run it over a binary pattern+value wire stream.",
20
+ "",
21
+ `Usage: ${prog} [options]`,
22
+ " ( source-snippet | input-file ) [ input-wire-file ]",
23
+ "",
24
+ "Arguments:",
25
+ " source-snippet Inline k source snippet (in the style of k.mjs).",
26
+ " input-file Source .k, .ko, .kvm, or .pkir file.",
27
+ " input-wire-file Optional binary wire input (default: stdin).",
28
+ "",
29
+ "Options:",
30
+ " --lib file Load one .klib or .k library before compiling.",
31
+ " --export spec Export library alias into scope. Repeatable.",
32
+ " spec is 'name' or 'libname:localname'.",
33
+ " --pkir Lower via Polymorphic K IR (PKIR).",
34
+ " -h, --help Show this help.",
35
+ "",
36
+ "Environment Variables:",
37
+ " K_TRACE, TRACE When '1', output phase tracing to stderr.",
38
+ "",
39
+ "Examples:",
40
+ ` echo '"abc"' | k-parse | ${prog} '{.1 0}' | k-print`,
41
+ ` echo '{"x":1,"y":2}' | k-json --parse | \\`,
42
+ ` ${prog} --lib Examples/poly.k --export concat \\`,
43
+ ` 's={()xs,()ys}concat; s' | k-json --print`
44
+ ].join("\n");
45
+ }
46
+
47
+ function usage(stream = console.error) {
48
+ stream(helpText());
25
49
  }
26
50
 
27
51
  async function main() {
@@ -31,19 +55,29 @@ async function main() {
31
55
  return;
32
56
  }
33
57
 
34
- const { libraries, exportSpecs } = parseCompileOptions(args);
58
+ const { libraries, exportSpecs, usePKIR } = parseCompileOptions(args);
35
59
  const programInput = resolveProgramInput(args);
36
60
  const inputPath = args.shift();
37
61
  if (args.length > 0) throw new Error("Too many arguments");
38
62
 
39
- const inputBuffer = await readAll(inputPath == null ? stdin : fs.createReadStream(inputPath));
63
+ const inputBuffer = await readAll(
64
+ inputPath == null ? stdin : fs.createReadStream(inputPath)
65
+ );
66
+ if (inputBuffer.length === 0) {
67
+ throw new Error("No input wire data received on stdin");
68
+ }
69
+
40
70
  const { pattern: inputEnvelopePattern } = decodeWire(inputBuffer);
41
- const artifact = await compileProgramInput(programInput, { libraries, exportSpecs, inputEnvelopePattern });
71
+ const artifact = await compileProgramInput(programInput, {
72
+ libraries,
73
+ exportSpecs,
74
+ inputEnvelopePattern,
75
+ usePKIR
76
+ });
42
77
  stdout.write(await runWasmArtifact(artifact, inputBuffer));
43
78
  }
44
79
 
45
80
  main().catch((error) => {
46
81
  console.error(error.stack || error.message || String(error));
47
- usage();
48
82
  exit(1);
49
83
  });
@@ -1,26 +1,112 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
 
4
- import { decodeObject, loadLibrary } from "@fraczak/k/backend-api.mjs";
4
+ import {
5
+ compileLibrary,
6
+ decodeObject,
7
+ loadLibrary
8
+ } from "@fraczak/k/backend-api.mjs";
5
9
  import {
6
10
  compileWasmArtifact,
7
11
  compileWasmArtifactFromKVM,
8
- compileWasmArtifactFromObject
12
+ compileWasmArtifactFromObject,
13
+ compileWasmArtifactFromPKIR
9
14
  } from "./wasm.mjs";
10
15
 
11
16
  function readAll(stream) {
12
17
  return new Promise((resolve, reject) => {
13
18
  const chunks = [];
14
- stream.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
19
+ stream.on("data", (chunk) => {
20
+ chunks.push(
21
+ Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
22
+ );
23
+ });
15
24
  stream.on("end", () => resolve(Buffer.concat(chunks)));
16
25
  stream.on("error", reject);
17
26
  });
18
27
  }
19
28
 
29
+ export function buildExportPreamble(exports = [], libraries = []) {
30
+ if (exports.length === 0) return "";
31
+ const aliasMap = {};
32
+ for (const lib of libraries) {
33
+ for (const [name, hash] of Object.entries(lib.relAlias || {})) {
34
+ if (name !== "__main__") aliasMap[name] = hash;
35
+ }
36
+ for (const [hash, entry] of Object.entries(lib.meta || {})) {
37
+ if (entry?.type !== "rel") continue;
38
+ for (const origin of entry?.origins || []) {
39
+ if (origin?.name && origin.name !== "__main__") {
40
+ aliasMap[origin.name] = hash;
41
+ }
42
+ }
43
+ }
44
+ }
45
+ const lines = [];
46
+ for (const spec of exports) {
47
+ const [libName, localName] = spec.includes(":")
48
+ ? spec.split(":", 2)
49
+ : [spec, spec];
50
+ const hash = aliasMap[libName];
51
+ if (!hash) {
52
+ throw new Error(
53
+ `--export: '${libName}' not found in loaded libraries`
54
+ );
55
+ }
56
+ const body = hash.startsWith("@") ? hash.slice(1) : hash;
57
+ lines.push(`${localName} = @${body};`);
58
+ }
59
+ return lines.join("\n") + "\n";
60
+ }
61
+
62
+ export function loadLibraryFromPath(libPath) {
63
+ const ext = path.extname(libPath).toLowerCase();
64
+ if (ext === ".klib" || ext === ".ko") {
65
+ return loadLibrary(decodeObject(fs.readFileSync(libPath)));
66
+ }
67
+ if (ext === ".k") {
68
+ let depLibs = [];
69
+ const baseName = path.basename(libPath);
70
+ if (baseName === "poly.k") {
71
+ const dir = path.dirname(libPath);
72
+ const arithPath = path.join(dir, "arithmetics.k");
73
+ if (fs.existsSync(arithPath)) {
74
+ const arithLib = loadLibrary(
75
+ compileLibrary(fs.readFileSync(arithPath, "utf8"), {
76
+ source: arithPath
77
+ })
78
+ );
79
+ depLibs.push(arithLib);
80
+ }
81
+ }
82
+ const rawSource = fs.readFileSync(libPath, "utf8");
83
+ if (depLibs.length > 0) {
84
+ const neededExports = [
85
+ "0", "int", "inc", "dec", "nat", "zero_int?", "neg?", "GT"
86
+ ];
87
+ const preamble = buildExportPreamble(neededExports, depLibs);
88
+ const compiled = compileLibrary(preamble + rawSource, {
89
+ source: libPath,
90
+ libraries: depLibs
91
+ });
92
+ const mergedLib = loadLibrary(compiled);
93
+ for (const dep of depLibs) {
94
+ for (const [k, v] of Object.entries(dep.relAlias || {})) {
95
+ if (!mergedLib.relAlias[k]) mergedLib.relAlias[k] = v;
96
+ }
97
+ }
98
+ return mergedLib;
99
+ }
100
+ return loadLibrary(compileLibrary(rawSource, { source: libPath }));
101
+ }
102
+ throw new Error(`Unsupported library extension: ${ext}`);
103
+ }
104
+
20
105
  function parseCompileOptions(args) {
21
106
  const libraries = [];
22
107
  const exportSpecs = [];
23
108
  let loadedLibrary = false;
109
+ let usePKIR = false;
24
110
 
25
111
  while (args.length > 0) {
26
112
  if (args[0] === "--") {
@@ -28,25 +114,34 @@ function parseCompileOptions(args) {
28
114
  break;
29
115
  }
30
116
  if (args[0] === "--lib") {
31
- if (loadedLibrary) throw new Error("--lib may only be specified once");
117
+ if (loadedLibrary) {
118
+ throw new Error("--lib may only be specified once");
119
+ }
32
120
  args.shift();
33
121
  const libPath = args.shift();
34
122
  if (!libPath) throw new Error("--lib requires a file argument");
35
- libraries.push(loadLibrary(decodeObject(fs.readFileSync(libPath))));
123
+ libraries.push(loadLibraryFromPath(libPath));
36
124
  loadedLibrary = true;
37
125
  } else if (args[0] === "--export") {
38
126
  args.shift();
39
127
  const spec = args.shift();
40
- if (!spec) throw new Error("--export requires a spec argument (name or libname:localname)");
128
+ if (!spec) {
129
+ throw new Error(
130
+ "--export requires a spec argument (name or libname:localname)"
131
+ );
132
+ }
41
133
  exportSpecs.push(spec);
134
+ } else if (args[0] === "--pkir") {
135
+ args.shift();
136
+ usePKIR = true;
42
137
  } else if (args[0].startsWith("--")) {
43
- throw new Error(`Unknown option: ${args[0]}`);
138
+ break;
44
139
  } else {
45
140
  break;
46
141
  }
47
142
  }
48
143
 
49
- return { libraries, exportSpecs };
144
+ return { libraries, exportSpecs, usePKIR };
50
145
  }
51
146
 
52
147
  function fileInputType(filePath) {
@@ -54,6 +149,7 @@ function fileInputType(filePath) {
54
149
  if (ext === ".ko") return "ko";
55
150
  if (ext === ".kvm") return "kvm";
56
151
  if (ext === ".klib") return "klib";
152
+ if (ext === ".pkir") return "pkir";
57
153
  return "source-file";
58
154
  }
59
155
 
@@ -68,38 +164,55 @@ function resolveProgramInput(args, { allowStdinSource = false } = {}) {
68
164
  if (fs.existsSync(inputArg)) {
69
165
  const kind = fileInputType(inputArg);
70
166
  if (kind === "klib") {
71
- throw new Error("Cannot compile a .klib library to WebAssembly without a main relation; load it with --lib.");
167
+ throw new Error(
168
+ "Cannot compile a .klib library to WebAssembly without a main " +
169
+ "relation; load it with --lib."
170
+ );
72
171
  }
73
172
  return { kind, path: inputArg, sourceName: inputArg };
74
173
  }
75
174
 
76
- if ([".k", ".ko", ".kvm", ".klib", ".wasm"].includes(ext)) {
175
+ if ([".k", ".ko", ".kvm", ".klib", ".wasm", ".pkir"].includes(ext)) {
77
176
  throw new Error(`Input file not found: ${inputArg}`);
78
177
  }
79
178
 
80
- return { kind: "inline-source", source: inputArg, sourceName: "<inline>" };
179
+ return {
180
+ kind: "inline-source",
181
+ source: inputArg,
182
+ sourceName: "<inline>"
183
+ };
81
184
  }
82
185
 
83
186
  async function compileProgramInput(
84
187
  input,
85
- { libraries = [], exportSpecs = [], stdin = null, inputEnvelopePattern = null } = {}
188
+ {
189
+ libraries = [],
190
+ exportSpecs = [],
191
+ stdin = null,
192
+ inputEnvelopePattern = null,
193
+ usePKIR = false
194
+ } = {}
86
195
  ) {
87
196
  if (input.kind === "inline-source") {
88
197
  return compileWasmArtifact(input.source, {
89
198
  libraries,
90
199
  exports: exportSpecs,
91
200
  source: input.sourceName,
92
- inputEnvelopePattern
201
+ inputEnvelopePattern,
202
+ usePKIR
93
203
  });
94
204
  }
95
205
 
96
206
  if (input.kind === "stdin-source") {
97
- if (!stdin) throw new Error("stdin source requested without a stdin stream");
207
+ if (!stdin) {
208
+ throw new Error("stdin source requested without a stdin stream");
209
+ }
98
210
  return compileWasmArtifact((await readAll(stdin)).toString("utf8"), {
99
211
  libraries,
100
212
  exports: exportSpecs,
101
213
  source: input.sourceName,
102
- inputEnvelopePattern
214
+ inputEnvelopePattern,
215
+ usePKIR
103
216
  });
104
217
  }
105
218
 
@@ -108,18 +221,27 @@ async function compileProgramInput(
108
221
  libraries,
109
222
  exports: exportSpecs,
110
223
  source: input.sourceName,
111
- inputEnvelopePattern
224
+ inputEnvelopePattern,
225
+ usePKIR
112
226
  });
113
227
  }
114
228
 
115
229
  if (input.kind === "ko") {
116
- return compileWasmArtifactFromObject(decodeObject(fs.readFileSync(input.path)), {
117
- inputEnvelopePattern
230
+ const obj = decodeObject(fs.readFileSync(input.path));
231
+ return compileWasmArtifactFromObject(obj, {
232
+ inputEnvelopePattern,
233
+ usePKIR
118
234
  });
119
235
  }
120
236
 
237
+ if (input.kind === "pkir") {
238
+ const pkir = JSON.parse(fs.readFileSync(input.path, "utf8"));
239
+ return compileWasmArtifactFromPKIR(pkir);
240
+ }
241
+
121
242
  if (input.kind === "kvm") {
122
- return compileWasmArtifactFromKVM(JSON.parse(fs.readFileSync(input.path, "utf8")));
243
+ const kvm = JSON.parse(fs.readFileSync(input.path, "utf8"));
244
+ return compileWasmArtifactFromKVM(kvm);
123
245
  }
124
246
 
125
247
  throw new Error(`Unsupported input kind: ${input.kind}`);