@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
@@ -0,0 +1,343 @@
1
+ // access-table.mjs: State-Indexed Access Table for Polymorphic Execution
2
+ // Translates an input schema S into a compact, indexable navigation table
3
+ // allowing function instructions to access payload fields and variants
4
+ // without runtime string comparisons or dynamic dictionary lookups.
5
+
6
+ import { patternToPropertyList } from "./codecs/runtime/pattern-json.mjs";
7
+
8
+ /**
9
+ * Node descriptor for a single state in schema S.
10
+ */
11
+ export class NodeDescriptor {
12
+ constructor(state, kind) {
13
+ this.state = state;
14
+ this.kind = kind; // 'product', 'union', or 'any'
15
+ }
16
+ }
17
+
18
+ /**
19
+ * AccessTable encapsulates the complete navigation structure for a
20
+ * schema S with respect to a function's structural access profile P.
21
+ */
22
+ export class AccessTable {
23
+ constructor(descriptors, rootState = 0, wordSize = 8) {
24
+ this.descriptors = descriptors;
25
+ this.rootState = rootState;
26
+ this.wordSize = wordSize;
27
+ this.stateCount = descriptors.length;
28
+ }
29
+
30
+ getDescriptor(stateId) {
31
+ const desc = this.descriptors[stateId];
32
+ if (!desc) {
33
+ throw new Error(`AccessTable has no descriptor for state ${stateId}`);
34
+ }
35
+ return desc;
36
+ }
37
+
38
+ /**
39
+ * Resolves physical byte offset and child state for a logical
40
+ * field access.
41
+ */
42
+ resolveField(stateId, fieldNameOrSlot) {
43
+ const desc = this.getDescriptor(stateId);
44
+ if (desc.kind !== "product") {
45
+ throw new Error(`State ${stateId} is not a product (is ${desc.kind})`);
46
+ }
47
+
48
+ if (typeof fieldNameOrSlot === "string") {
49
+ const fieldMeta = desc.fields[fieldNameOrSlot];
50
+ if (!fieldMeta) {
51
+ throw new Error(
52
+ `Field '${fieldNameOrSlot}' not found in state ${stateId}`
53
+ );
54
+ }
55
+ return fieldMeta;
56
+ }
57
+
58
+ const slot = fieldNameOrSlot;
59
+ return {
60
+ byteOffset: desc.slotToByteOffset[slot],
61
+ childState: desc.slotToChildState[slot],
62
+ skipCount: desc.slotToSkipCount[slot]
63
+ };
64
+ }
65
+
66
+ /**
67
+ * Resolves branch index and child state for a variant discriminator.
68
+ */
69
+ resolveVariant(stateId, tagOrId) {
70
+ const desc = this.getDescriptor(stateId);
71
+ if (desc.kind !== "union") {
72
+ throw new Error(`State ${stateId} is not a union (is ${desc.kind})`);
73
+ }
74
+
75
+ if (typeof tagOrId === "string") {
76
+ const tagMeta = desc.tags[tagOrId];
77
+ if (!tagMeta) {
78
+ throw new Error(`Variant '${tagOrId}' not found in state ${stateId}`);
79
+ }
80
+ return tagMeta;
81
+ }
82
+
83
+ const tagId = tagOrId;
84
+ return {
85
+ branchId: desc.branchMap[tagId],
86
+ childState: desc.childStateMap[tagId]
87
+ };
88
+ }
89
+
90
+ toJSON() {
91
+ return {
92
+ rootState: this.rootState,
93
+ wordSize: this.wordSize,
94
+ stateCount: this.stateCount,
95
+ descriptors: this.descriptors
96
+ };
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Normalizes a pattern, pattern graph, or property list into
102
+ * a canonical property list format.
103
+ */
104
+ function toPropertyList(patternOrGraph) {
105
+ if (Array.isArray(patternOrGraph)) {
106
+ return patternOrGraph;
107
+ }
108
+ if (patternOrGraph && Array.isArray(patternOrGraph.nodes)) {
109
+ return patternToPropertyList(patternOrGraph);
110
+ }
111
+ if (patternOrGraph?.patterns?.nodes) {
112
+ // TypePatternGraph from KIR / KVM
113
+ return patternOrGraph.patterns.nodes.map((node, nodeIdx) => {
114
+ let kind = "any";
115
+ if (node.pattern === "{}") kind = "closed-product";
116
+ else if (node.pattern === "{...}") kind = "open-product";
117
+ else if (node.pattern === "<>") kind = "closed-union";
118
+ else if (node.pattern === "<...>") kind = "open-union";
119
+ const edgeMap = patternOrGraph.edges?.[nodeIdx] || {};
120
+ const edges = (node.fields || []).map((f) => {
121
+ const dest = edgeMap[f];
122
+ const target = Array.isArray(dest)
123
+ ? dest[0]
124
+ : typeof dest === "object" && dest !== null
125
+ ? Object.values(dest)[0]
126
+ : (dest ?? 0);
127
+ return [f, target];
128
+ });
129
+ return [kind, edges];
130
+ });
131
+ }
132
+ throw new Error("Invalid pattern representation for access table generation");
133
+ }
134
+
135
+ /**
136
+ * Builds an Access Table bridging an Input Schema S to Function
137
+ * Pattern P.
138
+ *
139
+ * @param {Array|Object} schema - Concrete input value schema S
140
+ * @param {Array|Object} pattern - Function's input pattern graph P
141
+ * @param {Object} [options]
142
+ * @param {number} [options.wordSize=8] - Word size in bytes (default: 8)
143
+ * @returns {AccessTable}
144
+ */
145
+ export function buildAccessTable(schema, pattern, options = {}) {
146
+ const wordSize = options.wordSize ?? 8;
147
+ const sList = toPropertyList(schema);
148
+ const pList = toPropertyList(pattern);
149
+
150
+ const stateCount = sList.length;
151
+ const descriptors = new Array(stateCount);
152
+
153
+ const schemaRoot = options.schemaRoot ?? 0;
154
+ const patternRoot = options.patternRoot ?? 0;
155
+
156
+ // Simulation worklist: pairs of [stateInS, stateInP]
157
+ const visited = new Set();
158
+ const worklist = [[schemaRoot, patternRoot]];
159
+
160
+ while (worklist.length > 0) {
161
+ const [sId, pId] = worklist.shift();
162
+ const key = `${sId}:${pId}`;
163
+ if (visited.has(key)) continue;
164
+ visited.add(key);
165
+
166
+ const sNode = sList[sId];
167
+ const pNode = pList[pId];
168
+ if (!sNode || !pNode) continue;
169
+
170
+ const [sKind, sEdges] = sNode;
171
+ const [pKind, pEdges] = pNode;
172
+
173
+ const sIsUnion = sKind.includes("union");
174
+ const pIsUnion = pKind.includes("union");
175
+
176
+ if (sIsUnion !== pIsUnion && sKind !== "any" && pKind !== "any") {
177
+ throw new Error(
178
+ `Structural mismatch at S[${sId}] (${sKind}) vs P[${pId}] (${pKind})`
179
+ );
180
+ }
181
+
182
+ if (sIsUnion) {
183
+ // Sort variant tags in S alphabetically to define physical tag IDs
184
+ const sortedSEdges = [...sEdges].sort((a, b) =>
185
+ a[0].localeCompare(b[0])
186
+ );
187
+ const pEdgeMap = new Map(
188
+ pEdges.map(([label, target], idx) => [
189
+ label,
190
+ { target, branchIdx: idx }
191
+ ])
192
+ );
193
+
194
+ const tags = {};
195
+ const branchMap = [];
196
+ const childStateMap = [];
197
+
198
+ sortedSEdges.forEach(([tag, sTarget], tagId) => {
199
+ const pMatch = pEdgeMap.get(tag);
200
+ const branchId = pMatch ? pMatch.branchIdx : -1;
201
+ const pTarget = pMatch ? pMatch.target : null;
202
+
203
+ tags[tag] = {
204
+ tagId,
205
+ branchId,
206
+ childState: sTarget,
207
+ childPatternState: pTarget
208
+ };
209
+ branchMap[tagId] = branchId;
210
+ childStateMap[tagId] = sTarget;
211
+
212
+ if (pTarget != null && pTarget < pList.length) {
213
+ worklist.push([sTarget, pTarget]);
214
+ }
215
+ });
216
+
217
+ descriptors[sId] = {
218
+ state: sId,
219
+ kind: "union",
220
+ isOpen: sKind.startsWith("open"),
221
+ variantCount: sortedSEdges.length,
222
+ tags,
223
+ branchMap,
224
+ childStateMap
225
+ };
226
+ } else if (sKind.includes("product")) {
227
+ // Physical layout of fields in S: sorted by label
228
+ const sortedSEdges = [...sEdges].sort((a, b) =>
229
+ a[0].localeCompare(b[0])
230
+ );
231
+ const sFieldMap = new Map(
232
+ sortedSEdges.map(([label, target], idx) => [
233
+ label,
234
+ { target, physicalSlot: idx }
235
+ ])
236
+ );
237
+
238
+ const fields = {};
239
+ const slotToByteOffset = [];
240
+ const slotToChildState = [];
241
+ const slotToSkipCount = [];
242
+
243
+ pEdges.forEach(([field, pTarget], logicalSlot) => {
244
+ const sMatch = sFieldMap.get(field);
245
+ if (!sMatch) {
246
+ if (!pKind.startsWith("open")) {
247
+ throw new Error(`Required field '${field}' missing in S[${sId}]`);
248
+ }
249
+ return;
250
+ }
251
+
252
+ const physicalSlot = sMatch.physicalSlot;
253
+ const sTarget = sMatch.target;
254
+ const byteOffset = physicalSlot * wordSize;
255
+ const skipCount = physicalSlot;
256
+
257
+ fields[field] = {
258
+ logicalSlot,
259
+ physicalSlot,
260
+ byteOffset,
261
+ skipCount,
262
+ childState: sTarget,
263
+ childPatternState: pTarget
264
+ };
265
+
266
+ slotToByteOffset[logicalSlot] = byteOffset;
267
+ slotToChildState[logicalSlot] = sTarget;
268
+ slotToSkipCount[logicalSlot] = skipCount;
269
+
270
+ if (pTarget < pList.length) {
271
+ worklist.push([sTarget, pTarget]);
272
+ }
273
+ });
274
+
275
+ descriptors[sId] = {
276
+ state: sId,
277
+ kind: "product",
278
+ isOpen: sKind.startsWith("open"),
279
+ fieldCount: sortedSEdges.length,
280
+ fields,
281
+ slotToByteOffset,
282
+ slotToChildState,
283
+ slotToSkipCount
284
+ };
285
+ } else {
286
+ descriptors[sId] = {
287
+ state: sId,
288
+ kind: "any"
289
+ };
290
+ }
291
+ }
292
+
293
+ // Populate any unreached states in S as self-contained descriptors
294
+ for (let sId = 0; sId < stateCount; sId++) {
295
+ if (!descriptors[sId]) {
296
+ const [sKind, sEdges] = sList[sId];
297
+ if (sKind.includes("union")) {
298
+ const sorted = [...sEdges].sort((a, b) => a[0].localeCompare(b[0]));
299
+ descriptors[sId] = {
300
+ state: sId,
301
+ kind: "union",
302
+ isOpen: sKind.startsWith("open"),
303
+ variantCount: sorted.length,
304
+ tags: Object.fromEntries(
305
+ sorted.map(([t, target], i) => [
306
+ t,
307
+ { tagId: i, branchId: i, childState: target }
308
+ ])
309
+ ),
310
+ branchMap: sorted.map((_, i) => i),
311
+ childStateMap: sorted.map(([_, target]) => target)
312
+ };
313
+ } else if (sKind.includes("product")) {
314
+ const sorted = [...sEdges].sort((a, b) => a[0].localeCompare(b[0]));
315
+ descriptors[sId] = {
316
+ state: sId,
317
+ kind: "product",
318
+ isOpen: sKind.startsWith("open"),
319
+ fieldCount: sorted.length,
320
+ fields: Object.fromEntries(
321
+ sorted.map(([f, target], i) => [
322
+ f,
323
+ {
324
+ logicalSlot: i,
325
+ physicalSlot: i,
326
+ byteOffset: i * wordSize,
327
+ skipCount: i,
328
+ childState: target
329
+ }
330
+ ])
331
+ ),
332
+ slotToByteOffset: sorted.map((_, i) => i * wordSize),
333
+ slotToChildState: sorted.map(([_, target]) => target),
334
+ slotToSkipCount: sorted.map((_, i) => i)
335
+ };
336
+ } else {
337
+ descriptors[sId] = { state: sId, kind: "any" };
338
+ }
339
+ }
340
+ }
341
+
342
+ return new AccessTable(descriptors, schemaRoot, wordSize);
343
+ }
package/backend-api.mjs CHANGED
@@ -1,6 +1,10 @@
1
1
  export { annotate } from "./index.mjs";
2
2
  export { KVM_FORMAT, KVM_VERSION, lowerKIRRelationToKVM, lowerKIRToKVM, lowerToKVM, executeKVM, objectToKVMArtifact, specializeKVM } from "./kvm.mjs";
3
- export { decodeWire, encodeToWire } from "./codecs/runtime/prefix-codec.mjs";
3
+ export {
4
+ decodeWire,
5
+ decodeWireLazy,
6
+ encodeToWire
7
+ } from "./codecs/runtime/prefix-codec.mjs";
4
8
  export { exportPatternGraph, NODE_KIND } from "./codecs/runtime/codec.mjs";
5
9
  export { patternToPropertyList, propertyListToPattern } from "./codecs/runtime/pattern-json.mjs";
6
10
  export { Value, fromObject, isProduct, isVariant, isValue } from "./Value.mjs";
@@ -27,3 +31,21 @@ export {
27
31
  isIdentityIntrinsic,
28
32
  unsupportedIntrinsic
29
33
  } from "./intrinsics.mjs";
34
+ export { buildAccessTable, AccessTable } from "./access-table.mjs";
35
+ export {
36
+ PKIR_FORMAT,
37
+ PKIR_VERSION,
38
+ compileRelationToPKIR,
39
+ executePKIR,
40
+ specializePKIR,
41
+ valueToKVal,
42
+ kvalToValue,
43
+ lowerPKIRToKVM
44
+ } from "./pkir.mjs";
45
+ export {
46
+ compileWasmArtifactFromPKIR
47
+ } from "./backends/wasm/src/wasm.mjs";
48
+ export {
49
+ compileLLVMArtifactFromPKIR
50
+ } from "./backends/llvm/src/llvm.mjs";
51
+
@@ -46,34 +46,33 @@ status in `k_result`. These operations can compose through KIR `comp`.
46
46
  ```sh
47
47
  k-llvm-build [options] [source-snippet | input-file [output-exe]]
48
48
  k-llvm-compile [options] [source-snippet | input-file [output.ll]]
49
- k-llvm-jit [options] object.ko
50
- k-llvm-run [options] object.ko [input.kv]
49
+ k-llvm [options] ( source-snippet | input-file ) [ input-wire-file ]
51
50
  ```
52
51
 
53
52
  Options:
54
53
 
55
54
  - `-o, --output path`: specify output file path explicitly.
56
55
  - `--lib file`: load one `.klib` or `.k` dependency before compiling.
57
- - `--export spec`: export a library alias into source scope (`name` or `libname:localname`). May be repeated.
58
- - `--input-pattern json-or-file`: for `k-llvm-run`, KIR property-list input pattern.
59
- - `--retype rel`: for `k-llvm-run`, relation to specialize (defaults to object main).
60
- - `--expect value-or-file`: for `k-llvm-run`, compare the output value against expected value text.
61
- - `-h`, `--help`: show usage.
62
-
63
- The compile commands follow the unified k CLI input convention: pass inline
64
- source, an existing `.k` source file, or a `.ko`/`.klib` object. Reads UTF-8
65
- source from stdin when input is omitted. Output paths can be specified as a
66
- positional argument or explicitly with `-o, --output`.
67
- Without `--expect`, `k-llvm-run` prints the result value as compact JSON.
56
+ - `--export spec`: export a library alias into scope. Repeatable.
57
+ - `--pkir`: lower via Polymorphic K IR (PKIR).
58
+ - `--retype rel`: relation to specialize (defaults to object main).
59
+ - `--opt flag`: Clang optimization flag (-O0, -O1, -O2, -O3).
60
+ - `-h, --help`: show usage.
61
+
62
+ The compile commands follow the unified k CLI input convention: pass
63
+ inline source, an existing `.k` source file, or a `.ko`/`.klib` object.
64
+ Reads UTF-8 source from stdin when input is omitted. Output paths can be
65
+ specified as a positional argument or explicitly with `-o, --output`.
68
66
  `k-llvm-build` emits a native executable that reads and writes the binary
69
- k pattern+value envelope used by `codecs/k-parse.mjs` and `codecs/k-print.mjs`.
70
- The executable checks the stdin envelope pattern against the compiled input
71
- pattern and encodes stdout with the compiled output pattern.
72
- `k-llvm-jit` reads the stdin binary envelope first, specializes the object with
73
- that input pattern, caches the native executable, and writes the compiled
74
- output envelope to stdout.
75
- The native envelope codec supports the k Unicode string encoding used for
76
- labels and tags.
67
+ k pattern+value envelope used by `codecs/k-parse.mjs` and
68
+ `codecs/k-print.mjs`. The executable checks the stdin envelope pattern
69
+ against the compiled input pattern and encodes stdout with the compiled
70
+ output pattern.
71
+ `k-llvm` reads the binary wire envelope, specializes the relation to
72
+ that input pattern, caches the compiled native executable, and writes
73
+ the compiled output envelope to stdout.
74
+ The native envelope codec supports the k Unicode string encoding used
75
+ for labels and tags.
77
76
 
78
77
  ## Tests
79
78
 
@@ -9,35 +9,61 @@ import {
9
9
  } from "../src/cli.mjs";
10
10
  import {
11
11
  compileKVMToExecutable,
12
- compileObjectToExecutable
12
+ compileObjectToExecutable,
13
+ compilePKIRToExecutable
13
14
  } from "../src/executable.mjs";
14
15
 
15
- function usage(stream = console.error) {
16
+ function helpText() {
16
17
  const prog = argv[1] || "k-llvm-build.mjs";
17
- stream(`Usage: node ${prog} [options] [source-snippet | input-file [output-exe]]`);
18
- stream("Compile k source, .ko, .klib, or .kvm input into a native executable.");
19
- stream("");
20
- stream("Arguments:");
21
- stream(" source-snippet Inline k source, in the same style as k.mjs.");
22
- stream(" input-file Source .k, .ko, .klib, or .kvm file. Reads UTF-8 source from stdin when omitted.");
23
- stream(" output-exe Output executable path.");
24
- stream("");
25
- stream("The executable reads a binary k pattern+value envelope from stdin and");
26
- stream("writes a binary k pattern+value envelope to stdout.");
27
- stream("The stdin envelope pattern must match the compiled input pattern;");
28
- stream("stdout is encoded with the compiled output pattern.");
29
- stream("");
30
- stream("Options:");
31
- stream(" -o, --output path Specify output file path explicitly.");
32
- stream(" --input-pattern pattern Input pattern JSON or file path for specialization.");
33
- stream(" --lib file Load one .klib dependency before compiling.");
34
- stream(" --export spec Export a library alias into source scope. May be repeated.");
35
- stream(" spec is 'name' or 'libname:localname'.");
36
- stream(" -h, --help Show this help.");
18
+ return [
19
+ "Compile k source, .ko, .klib, .kvm, or .pkir into a native",
20
+ "standalone executable.",
21
+ "",
22
+ `Usage: ${prog} [options]`,
23
+ " [source-snippet | input-file [output-exe]]",
24
+ "",
25
+ "Arguments:",
26
+ " source-snippet Inline k source snippet (in the style of k.mjs).",
27
+ " input-file Source .k, .ko, .klib, .kvm, or .pkir file.",
28
+ " Reads UTF-8 source from stdin when omitted.",
29
+ " output-exe Output executable path.",
30
+ "",
31
+ "The executable reads a binary k pattern+value envelope from stdin",
32
+ "and writes a binary k pattern+value envelope to stdout.",
33
+ "The stdin envelope pattern must match the compiled input pattern;",
34
+ "stdout is encoded with the compiled output pattern.",
35
+ "",
36
+ "Options:",
37
+ " -o, --output path Specify output file path explicitly.",
38
+ " --pkir Lower via Polymorphic K IR (PKIR).",
39
+ " --input-pattern pattern Input pattern JSON or file for spec.",
40
+ " --retype rel Relation to specialize (default: main).",
41
+ " --lib file Load .klib or .k library before build.",
42
+ " --export spec Export library alias into scope. Repeatable.",
43
+ " spec is 'name' or 'libname:localname'.",
44
+ " --opt flag Clang optimization flag (default: -O3).",
45
+ " -h, --help Show this help.",
46
+ "",
47
+ "Environment Variables:",
48
+ " CLANG, CC Path to Clang compiler (default: clang).",
49
+ " K_LLVM_CLANG_OPT Clang optimization flag (default: -O3).",
50
+ " K_LLVM_RUNTIME_MODE Runtime mode: fast, compact (default: fast).",
51
+ "",
52
+ "Examples:",
53
+ ` ${prog} 'x = |x; x x' -o program.exe`,
54
+ ` ${prog} program.k program.exe`,
55
+ ` ${prog} --lib Examples/poly.k --export concat 's' -o s.exe`
56
+ ].join("\n");
57
+ }
58
+
59
+ function usage(stream = console.error) {
60
+ stream(helpText());
37
61
  }
38
62
 
39
63
  function readMaybeFile(textOrPath) {
40
- return fs.existsSync(textOrPath) ? fs.readFileSync(textOrPath, "utf8") : textOrPath;
64
+ return fs.existsSync(textOrPath)
65
+ ? fs.readFileSync(textOrPath, "utf8")
66
+ : textOrPath;
41
67
  }
42
68
 
43
69
  function readPattern(inputPattern) {
@@ -54,17 +80,38 @@ try {
54
80
 
55
81
  let explicitOutput = null;
56
82
  let rawInputPattern = null;
83
+ let relation = null;
84
+ let clangOpt = process.env.K_LLVM_CLANG_OPT || "-O3";
85
+ let runtimeMode = process.env.K_LLVM_RUNTIME_MODE || "fast";
57
86
  const remainingArgs = [];
58
87
  while (args.length > 0) {
59
88
  const arg = args[0];
60
89
  if (arg === "-o" || arg === "--output") {
61
90
  args.shift();
62
91
  explicitOutput = args.shift();
63
- if (!explicitOutput) throw new Error("-o/--output requires a file argument");
92
+ if (!explicitOutput) {
93
+ throw new Error("-o/--output requires a file argument");
94
+ }
64
95
  } else if (arg === "--input-pattern") {
65
96
  args.shift();
66
97
  rawInputPattern = args.shift();
67
- if (!rawInputPattern) throw new Error("--input-pattern requires a JSON or file argument");
98
+ if (!rawInputPattern) {
99
+ throw new Error(
100
+ "--input-pattern requires a JSON or file argument"
101
+ );
102
+ }
103
+ } else if (arg === "--retype") {
104
+ args.shift();
105
+ relation = args.shift();
106
+ if (!relation) {
107
+ throw new Error("--retype requires a relation name");
108
+ }
109
+ } else if (arg === "--opt") {
110
+ args.shift();
111
+ clangOpt = args.shift();
112
+ if (!clangOpt) {
113
+ throw new Error("--opt requires a flag (e.g. -O3)");
114
+ }
68
115
  } else {
69
116
  remainingArgs.push(args.shift());
70
117
  }
@@ -72,17 +119,38 @@ try {
72
119
 
73
120
  const inputPattern = readPattern(rawInputPattern);
74
121
 
75
- const { libraries, exportSpecs } = parseCompileOptions(remainingArgs);
76
- const input = resolveProgramInput(remainingArgs, { allowStdinSource: true });
122
+ const { libraries, exportSpecs, usePKIR } =
123
+ parseCompileOptions(remainingArgs);
124
+ const input = resolveProgramInput(remainingArgs, {
125
+ allowStdinSource: true
126
+ });
77
127
  const positionalOutput = remainingArgs.shift();
78
128
  if (remainingArgs.length > 0) throw new Error("Too many arguments");
79
129
 
80
130
  const outputPath = explicitOutput || positionalOutput || null;
81
- if (outputPath == null) throw new Error("Output executable path is required (pass output-exe or -o/--output)");
131
+ if (outputPath == null) {
132
+ throw new Error(
133
+ "Output executable path required (pass output-exe or -o/--output)"
134
+ );
135
+ }
82
136
 
83
- if (input.kind === "kvm") {
137
+ if (input.kind === "pkir") {
138
+ const pkir = JSON.parse(fs.readFileSync(input.path, "utf8"));
139
+ compilePKIRToExecutable(pkir, outputPath, {
140
+ entry: relation,
141
+ inputPattern,
142
+ clangOpt,
143
+ runtimeMode
144
+ });
145
+ } else if (input.kind === "kvm") {
84
146
  const kvm = JSON.parse(fs.readFileSync(input.path, "utf8"));
85
- compileKVMToExecutable(kvm, outputPath, { inputPattern });
147
+ compileKVMToExecutable(kvm, outputPath, {
148
+ entry: relation,
149
+ inputPattern,
150
+ clangOpt,
151
+ runtimeMode,
152
+ usePKIR
153
+ });
86
154
  } else {
87
155
  const object = await compileProgramInputToObject(input, {
88
156
  libraries,
@@ -90,10 +158,15 @@ try {
90
158
  stdin
91
159
  });
92
160
 
93
- compileObjectToExecutable(object, outputPath, { inputPattern });
161
+ compileObjectToExecutable(object, outputPath, {
162
+ relation: relation || object.main,
163
+ inputPattern,
164
+ clangOpt,
165
+ runtimeMode,
166
+ usePKIR
167
+ });
94
168
  }
95
169
  } catch (error) {
96
170
  console.error(error.stack || error.message || String(error));
97
- usage();
98
171
  exit(1);
99
172
  }