@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
@@ -6,22 +6,35 @@ import { decodeObject } from "@fraczak/k/object.mjs";
6
6
  import { parseValue } from "@fraczak/k/valueIO.mjs";
7
7
  import { compileObjectAndRun } from "../src/executable.mjs";
8
8
 
9
- function usage(stream = console.error) {
9
+ function helpText() {
10
10
  const prog = argv[1] || "k-llvm-run.mjs";
11
- stream(`Usage: node ${prog} [options] object-file [input.kv]`);
12
- stream("Compile and execute a k .ko/.klib object through LLVM.");
13
- stream("");
14
- stream("Arguments:");
15
- stream(" object-file Input .ko or .klib file.");
16
- stream(" input.kv Input value. Reads text from stdin when omitted.");
17
- stream("");
18
- stream("Options:");
19
- stream(" --retype rel Relation to specialize. Defaults to object main.");
20
- stream(" --input-pattern value Input pattern property-list JSON, or a file containing it.");
21
- stream(" --expect value Expected output value JSON/k-value text, or a file containing it.");
22
- stream(" -h, --help Show this help.");
23
- stream("");
24
- stream("Without --expect, the runner prints the result value as compact JSON.");
11
+ return [
12
+ "Compile and execute a k .ko/.klib object through LLVM.",
13
+ "",
14
+ `Usage: ${prog} [options] object-file [input.kv]`,
15
+ "",
16
+ "Arguments:",
17
+ " object-file Input .ko or .klib file.",
18
+ " input.kv Input value. Reads text from stdin when omitted.",
19
+ "",
20
+ "Options:",
21
+ " --retype rel Relation to specialize (default: main).",
22
+ " --input-pattern value Input pattern property-list JSON or file.",
23
+ " --expect value Expected output value JSON/text or file.",
24
+ " --pkir Lower via Polymorphic K IR (PKIR).",
25
+ " -h, --help Show this help.",
26
+ "",
27
+ "Without --expect, the runner prints the result as compact JSON.",
28
+ "",
29
+ "Examples:",
30
+ ` ${prog} program.ko input.kv`,
31
+ ` ${prog} --expect expected.kv program.ko input.kv`,
32
+ ` echo '{"x":"left"}' | ${prog} program.ko`
33
+ ].join("\n");
34
+ }
35
+
36
+ function usage(stream = console.error) {
37
+ stream(helpText());
25
38
  }
26
39
 
27
40
  async function readStdinText() {
@@ -33,7 +46,9 @@ async function readStdinText() {
33
46
  }
34
47
 
35
48
  function readMaybeFile(textOrPath) {
36
- return fs.existsSync(textOrPath) ? fs.readFileSync(textOrPath, "utf8") : textOrPath;
49
+ return fs.existsSync(textOrPath)
50
+ ? fs.readFileSync(textOrPath, "utf8")
51
+ : textOrPath;
37
52
  }
38
53
 
39
54
  function readPattern(inputPattern) {
@@ -51,6 +66,7 @@ try {
51
66
  let relation = null;
52
67
  let inputPattern = null;
53
68
  let expectedText = null;
69
+ let usePKIR = false;
54
70
  while (args.length > 0 && args[0].startsWith("--")) {
55
71
  const option = args.shift();
56
72
  if (option === "--retype") {
@@ -58,10 +74,16 @@ try {
58
74
  if (!relation) throw new Error("--retype requires a relation name");
59
75
  } else if (option === "--input-pattern") {
60
76
  inputPattern = args.shift();
61
- if (!inputPattern) throw new Error("--input-pattern requires JSON or a file path");
77
+ if (!inputPattern) {
78
+ throw new Error("--input-pattern requires JSON or a file path");
79
+ }
62
80
  } else if (option === "--expect") {
63
81
  expectedText = args.shift();
64
- if (!expectedText) throw new Error("--expect requires a value or file path");
82
+ if (!expectedText) {
83
+ throw new Error("--expect requires a value or file path");
84
+ }
85
+ } else if (option === "--pkir") {
86
+ usePKIR = true;
65
87
  } else {
66
88
  throw new Error(`Unknown option: ${option}`);
67
89
  }
@@ -73,18 +95,22 @@ try {
73
95
  if (args.length > 0) throw new Error(`Unexpected argument: ${args[0]}`);
74
96
 
75
97
  const object = decodeObject(fs.readFileSync(objectPath));
76
- const inputText = inputPath == null ? await readStdinText() : fs.readFileSync(inputPath, "utf8");
98
+ const inputText = inputPath == null
99
+ ? await readStdinText()
100
+ : fs.readFileSync(inputPath, "utf8");
77
101
  const input = parseValue(inputText);
78
- const expected = expectedText == null ? null : parseValue(readMaybeFile(expectedText));
102
+ const expected = expectedText == null
103
+ ? null
104
+ : parseValue(readMaybeFile(expectedText));
79
105
  const result = compileObjectAndRun(object, {
80
106
  relation: relation || object.main,
81
107
  input,
82
108
  expected,
83
- inputPattern: readPattern(inputPattern)
109
+ inputPattern: readPattern(inputPattern),
110
+ usePKIR
84
111
  });
85
112
  stdout.write(expected == null ? result.stdout : "OK\n");
86
113
  } catch (error) {
87
114
  console.error(error.stack || error.message || String(error));
88
- usage();
89
115
  exit(1);
90
116
  }
@@ -148,7 +148,17 @@ export function wireInput(value, pattern = value.pattern) {
148
148
  };
149
149
  }
150
150
 
151
- export function compileCaseExecutable({ object, relationName, relHash, inputPattern, cacheDir, sourceLabel, runtimeMode = "fast", clangOpt = "-O3" }) {
151
+ export function compileCaseExecutable({
152
+ object,
153
+ relationName,
154
+ relHash,
155
+ inputPattern,
156
+ cacheDir,
157
+ sourceLabel,
158
+ runtimeMode = "fast",
159
+ clangOpt = "-O3",
160
+ usePKIR = false
161
+ }) {
152
162
  const key = sha256([
153
163
  "k-llvm-perf-v1",
154
164
  backendFingerprint(),
@@ -157,12 +167,23 @@ export function compileCaseExecutable({ object, relationName, relHash, inputPatt
157
167
  sourceLabel,
158
168
  relationName,
159
169
  relHash,
160
- JSON.stringify(inputPattern)
170
+ JSON.stringify(inputPattern),
171
+ usePKIR ? "pkir" : "kir"
161
172
  ].join("\0"));
162
173
  const exePath = path.join(cacheDir, `${key}.exe`);
163
174
  if (!fs.existsSync(exePath)) {
164
175
  const tmpPath = path.join(cacheDir, `${key}.${process.pid}.tmp`);
165
- compileObjectToExecutable(cloneObjectPayload(object), tmpPath, { relation: relationName, inputPattern, runtimeMode, clangOpt });
176
+ compileObjectToExecutable(
177
+ cloneObjectPayload(object),
178
+ tmpPath,
179
+ {
180
+ relation: relationName,
181
+ inputPattern,
182
+ runtimeMode,
183
+ clangOpt,
184
+ usePKIR
185
+ }
186
+ );
166
187
  fs.renameSync(tmpPath, exePath);
167
188
  }
168
189
  return exePath;
@@ -216,7 +237,7 @@ export function runExecutableMainBench(exePath, inputWire, calls) {
216
237
  reject(new Error(`${exePath} --bench-main failed with status ${status}\n${text}`.trim()));
217
238
  return;
218
239
  }
219
- const match = text.match(/K_(?:LLVM|ARM64)_BENCH_MAIN calls=(\d+) total_ns=(\d+) per_call_ns=([0-9.]+)/);
240
+ const match = text.match(/K_LLVM_BENCH_MAIN calls=(\d+) total_ns=(\d+) per_call_ns=([0-9.]+)/);
220
241
  if (!match) {
221
242
  reject(new Error(`${exePath} --bench-main did not report timing\n${text}`.trim()));
222
243
  return;
@@ -599,61 +620,7 @@ export function llvmLaneName() {
599
620
  : "LLVM Executable (persistent)";
600
621
  }
601
622
 
602
- export function arm64LaneName(optLevel = "-O2") {
603
- const mode = process.env.ARM64_SPAWN_PER_CALL === "1"
604
- ? "spawn/call"
605
- : (process.env.ARM64_PIPELINE === "1" ? "persistent, pipelined" : "persistent");
606
- return `Linux ARM64 ${optLevel} (${mode})`;
607
- }
608
-
609
- export function createARM64Runner(testSuite) {
610
- const arm64Cases = testSuite.filter(tc => tc.arm64?.status === "ok");
611
- if (process.env.ARM64_SPAWN_PER_CALL === "1") {
612
- return {
613
- run(iterations) {
614
- if (arm64Cases.length === 0) return null;
615
- return runTimedIterationsAsync(iterations, async () => {
616
- for (const tc of arm64Cases) {
617
- await runExecutable(tc.arm64.exePath, tc.inputWire);
618
- }
619
- });
620
- },
621
- close() {}
622
- };
623
- }
624
-
625
- const servers = new Map();
626
- function serverFor(exePath) {
627
- let server = servers.get(exePath);
628
- if (server == null) {
629
- server = new PersistentExecutable(exePath);
630
- servers.set(exePath, server);
631
- }
632
- return server;
633
- }
634
623
 
635
- return {
636
- async run(iterations) {
637
- if (arm64Cases.length === 0) return null;
638
- return runTimedIterationsAsync(iterations, async () => {
639
- if (process.env.ARM64_PIPELINE === "1") {
640
- const pending = [];
641
- for (const tc of arm64Cases) {
642
- pending.push(serverFor(tc.arm64.exePath).request(tc.inputWire));
643
- }
644
- await Promise.all(pending);
645
- return;
646
- }
647
- for (const tc of arm64Cases) {
648
- await serverFor(tc.arm64.exePath).request(tc.inputWire);
649
- }
650
- });
651
- },
652
- close() {
653
- for (const server of servers.values()) server.close();
654
- }
655
- };
656
- }
657
624
 
658
625
  export function average(times) {
659
626
  return times.reduce((sum, time) => sum + time, 0) / times.length;
@@ -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 {
@@ -239,11 +246,11 @@ assert.doesNotMatch(nestedUnionLLVM, /call void @k_product_set_at\(ptr %product\
239
246
  assert.doesNotMatch(nestedUnionLLVM, /ptr false/);
240
247
 
241
248
  const productRecurseObject = decodeObject(compileObjectBuffer(`
242
- list = ?< {} nil, { {} car, list cdr } cons >;
243
- foo = list <
249
+ list? = ?< {} nil, { {} car, list? cdr } cons >;
250
+ foo = list? <
244
251
  /nil |nil,
245
252
  /cons { .car car, .cdr foo cdr } |cons
246
- > list;
253
+ > list?;
247
254
  foo
248
255
  `, { source: "llvm-product-recurse.k" }));
249
256
  const { llvm: productRecurseLLVM } = compileObjectToLLVM(productRecurseObject, {
@@ -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
  });