@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,196 @@
1
+ import assert from "node:assert/strict";
2
+ import { createState, evaluateInput } from "../backend-api.mjs";
3
+
4
+ console.log("==> Running Polymorphic TDD Suite (tests/test-polymorphic-tdd.mjs)");
5
+
6
+ const state = createState();
7
+
8
+ async function evalK(snippet) {
9
+ const res = await evaluateInput(snippet, state);
10
+ assert.ok(
11
+ res && res.length > 0 && !res[0].includes("undefined"),
12
+ `Evaluation failed for snippet:\n${snippet}`
13
+ );
14
+ const raw = res[0];
15
+ const splitIdx = raw.indexOf(" ?");
16
+ const val = splitIdx !== -1 ? raw.slice(0, splitIdx) : raw;
17
+ const type = splitIdx !== -1 ? raw.slice(splitIdx + 1) : "";
18
+ return { val, type, raw };
19
+ }
20
+
21
+ // --------------------------------------------------------------------
22
+ // Test 1: Search - Homogeneous List with Opaque Fields
23
+ // --------------------------------------------------------------------
24
+ {
25
+ console.log("--> Test 1: Search on Homogeneous List with Opaque Fields");
26
+ const script = `
27
+ search = {()list, {}|nil result} search_;
28
+ search_ = ?{X list, X result}
29
+ <
30
+ {.list/nil if, .result then}.then,
31
+ <
32
+ {.list/cons.car.flag/true if, {.list/cons.cdr list, {.list/cons.car car, .result cdr}|cons result} then} .then,
33
+ {.list/cons.cdr list, .result result}
34
+ > search_
35
+ >;
36
+
37
+ i1 = {{}|alpha id, {}|true flag, {}|payload_a payload};
38
+ i2 = {{}|beta id, {}|false flag, {}|payload_b payload};
39
+ i3 = {{}|gamma id, {}|true flag, {}|payload_c payload};
40
+
41
+ l_homo = {i1 car, {i2 car, {i3 car, {}|nil cdr}|cons cdr}|cons cdr}|cons;
42
+ l_homo search
43
+ `;
44
+
45
+ const out = await evalK(script);
46
+ // Expect i3 followed by i1 (accumulator reverses order); i2 is excluded
47
+ assert.match(out.val, /gamma/);
48
+ assert.match(out.val, /alpha/);
49
+ assert.doesNotMatch(out.val, /beta/);
50
+ // Check opaque fields are intact
51
+ assert.match(out.val, /payload_c/);
52
+ assert.match(out.val, /payload_a/);
53
+ console.log(" Passed: Homogeneous list filtered with opaque payload preserved.");
54
+ }
55
+
56
+ // --------------------------------------------------------------------
57
+ // Test 2: Search - Pathological Field Ordering & Disparate Field Schemas
58
+ // --------------------------------------------------------------------
59
+ {
60
+ console.log("--> Test 2: Search with Out-of-Order Fields & Disparate Value Types");
61
+ const script = `
62
+ p1 = {{}|large_blob data, {}|true flag, {}|first id};
63
+ p2 = {{}|false flag, {}|second id, {}|medium_blob data};
64
+ p3 = {{}|third id, {}|tiny_blob data, {}|true flag};
65
+
66
+ l_reorder = {p1 car, {p2 car, {p3 car, {}|nil cdr}|cons cdr}|cons cdr}|cons;
67
+ l_reorder search
68
+ `;
69
+
70
+ const out = await evalK(script);
71
+ // Expect p3 matching first, then p1 matching; p2 excluded
72
+ assert.match(out.val, /third/);
73
+ assert.match(out.val, /tiny_blob/);
74
+ assert.match(out.val, /first/);
75
+ assert.match(out.val, /large_blob/);
76
+ assert.doesNotMatch(out.val, /second/);
77
+ assert.doesNotMatch(out.val, /medium_blob/);
78
+ console.log(" Passed: Out-of-order fields & disparate value types preserved.");
79
+ }
80
+
81
+ // --------------------------------------------------------------------
82
+ // Test 3: Search - Boundary Cases (Empty List, All Match, None Match)
83
+ // --------------------------------------------------------------------
84
+ {
85
+ console.log("--> Test 3: Search Boundary Cases (Empty, None Match, All Match)");
86
+
87
+ // 3a: Empty list
88
+ const outEmpty = await evalK(`{}|nil search`);
89
+ assert.match(outEmpty.val, /nil/);
90
+
91
+ // 3b: None match (all flag: false)
92
+ const outNone = await evalK(`
93
+ n1 = {{}|f1 id, {}|false flag};
94
+ n2 = {{}|f2 id, {}|false flag};
95
+ l_none = {n1 car, {n2 car, {}|nil cdr}|cons cdr}|cons;
96
+ l_none search
97
+ `);
98
+ assert.match(outNone.val, /nil/);
99
+
100
+ // 3c: All match (all flag: true)
101
+ const outAll = await evalK(`
102
+ m1 = {{}|t1 id, {}|true flag};
103
+ m2 = {{}|t2 id, {}|true flag};
104
+ l_all = {m1 car, {m2 car, {}|nil cdr}|cons cdr}|cons;
105
+ l_all search
106
+ `);
107
+ assert.match(outAll.val, /t2.*t1/);
108
+ console.log(" Passed: Boundary cases handled cleanly.");
109
+ }
110
+
111
+ // --------------------------------------------------------------------
112
+ // Test 4: Multi-Variant Dispatch with Varying Offsets (extract_id)
113
+ // --------------------------------------------------------------------
114
+ {
115
+ console.log("--> Test 4: Multi-Variant Dispatch with Varying Offsets");
116
+ const script = `
117
+ extract_id = <
118
+ /click.id,
119
+ /sensor.id,
120
+ /ping.id
121
+ >;
122
+
123
+ evt_c = {{}|evt_click id, {}|active status}|click;
124
+ evt_s = {{}|battery_low status, {}|evt_sensor id, {}|reading data}|sensor;
125
+ evt_p = {{}|evt_ping id}|ping;
126
+ `;
127
+ await evalK(script);
128
+
129
+ const outC = await evalK("evt_c extract_id");
130
+ assert.match(outC.val, /evt_click/);
131
+
132
+ const outS = await evalK("evt_s extract_id");
133
+ assert.match(outS.val, /evt_sensor/);
134
+
135
+ const outP = await evalK("evt_p extract_id");
136
+ assert.match(outP.val, /evt_ping/);
137
+ console.log(" Passed: Common field extracted across variants with distinct layouts.");
138
+ }
139
+
140
+ // --------------------------------------------------------------------
141
+ // Test 5: Polymorphic Zip - Concurrency of Two Independent Types
142
+ // --------------------------------------------------------------------
143
+ {
144
+ console.log("--> Test 5: Polymorphic Zip across Independent Types");
145
+ const script = `
146
+ zip = <
147
+ {.xs /nil if, {}|nil then} .then,
148
+ {.ys /nil if, {}|nil then} .then,
149
+ {
150
+ {.xs/cons.car x, .ys/cons.car y} car,
151
+ {.xs/cons.cdr xs, .ys/cons.cdr ys} zip cdr
152
+ }|cons
153
+ >;
154
+
155
+ xs = {{}|num1 car, {{}|num2 car, {}|nil cdr}|cons cdr}|cons;
156
+ ys = {
157
+ {{}|rec1_name name, {}|flag_true flag} car,
158
+ {{{}|rec2_name name, {}|flag_false flag} car, {}|nil cdr}|cons cdr
159
+ }|cons;
160
+
161
+ {xs xs, ys ys} zip
162
+ `;
163
+
164
+ const out = await evalK(script);
165
+ assert.match(out.val, /num1.*rec1_name/);
166
+ assert.match(out.val, /num2.*rec2_name/);
167
+ console.log(" Passed: Independent polymorphic streams paired into product.");
168
+ }
169
+
170
+ // --------------------------------------------------------------------
171
+ // Test 6: Polymorphic Reverse with Opaque Records
172
+ // --------------------------------------------------------------------
173
+ {
174
+ console.log("--> Test 6: Polymorphic Reverse with Opaque Records");
175
+ const script = `
176
+ poly_rev = {()xs, {}|nil acc} _poly_rev;
177
+ _poly_rev = ?{X xs, X acc} <
178
+ {.xs/nil if, .acc then} .then,
179
+ {.xs/cons.cdr xs, {.xs/cons.car car, .acc cdr}|cons acc} _poly_rev
180
+ >;
181
+
182
+ r1 = {{}|first item, {}|opaque_1 extra};
183
+ r2 = {{}|second item, {}|opaque_2 extra};
184
+ r3 = {{}|third item, {}|opaque_3 extra};
185
+ list_to_rev = {r1 car, {r2 car, {r3 car, {}|nil cdr}|cons cdr}|cons cdr}|cons;
186
+
187
+ list_to_rev poly_rev
188
+ `;
189
+
190
+ const out = await evalK(script);
191
+ assert.match(out.val, /third.*second.*first/);
192
+ assert.match(out.val, /opaque_3.*opaque_2.*opaque_1/);
193
+ console.log(" Passed: Polymorphic list reversed with opaque record payloads intact.");
194
+ }
195
+
196
+ console.log("\n==> All Polymorphic TDD Tests Passed Successfully!");
@@ -622,9 +622,12 @@ if (chromiumBin) {
622
622
  assert.strictEqual(narrowNavbarCheck.hasTiming, false, "Help modal should not contain :timing");
623
623
  assert.strictEqual(narrowNavbarCheck.hasCodecDefine, false, "Help modal should not contain :codec define");
624
624
 
625
- // 6. Test dual engine switching in browser (wasm <-> js)
625
+ // 6. Test 3-way engine switching in browser (wasm -> pkir -> js)
626
626
  const engineCheck = await evaluateAsync(`(async () => {
627
627
  const initialEngine = window.kRepl.getEngine();
628
+ await window.kRepl.executeCommand(":engine pkir");
629
+ const pkirEngine = window.kRepl.getEngine();
630
+ const badgePkirText = document.getElementById("engine-badge")?.textContent;
628
631
  await window.kRepl.executeCommand(":engine js");
629
632
  const jsEngine = window.kRepl.getEngine();
630
633
  const badgeJsText = document.getElementById("engine-badge")?.textContent;
@@ -632,9 +635,19 @@ if (chromiumBin) {
632
635
  await window.kRepl.executeCommand(":engine wasm");
633
636
  const wasmEngine = window.kRepl.getEngine();
634
637
  const badgeWasmText = document.getElementById("engine-badge")?.textContent;
635
- return { initialEngine, jsEngine, wasmEngine, badgeJsText, badgeWasmText };
638
+ return {
639
+ initialEngine,
640
+ pkirEngine,
641
+ jsEngine,
642
+ wasmEngine,
643
+ badgePkirText,
644
+ badgeJsText,
645
+ badgeWasmText
646
+ };
636
647
  })()`);
637
648
  assert.strictEqual(engineCheck.initialEngine, "wasm");
649
+ assert.strictEqual(engineCheck.pkirEngine, "pkir");
650
+ assert.strictEqual(engineCheck.badgePkirText, "pkir-wasm");
638
651
  assert.strictEqual(engineCheck.jsEngine, "js");
639
652
  assert.strictEqual(engineCheck.badgeJsText, "js-in-process");
640
653
  assert.strictEqual(engineCheck.wasmEngine, "wasm");
@@ -471,6 +471,10 @@ output = await evaluateInput(":wasm", timingState);
471
471
  assert.equal(output[0], "engine set to wasm");
472
472
  assert.equal(timingState.engine, "wasm");
473
473
 
474
+ output = await evaluateInput(":pkir", timingState);
475
+ assert.equal(output[0], "engine set to pkir");
476
+ assert.equal(timingState.engine, "pkir");
477
+
474
478
  output = await evaluateInput(":js", timingState);
475
479
  assert.equal(output[0], "engine set to js");
476
480
  assert.equal(timingState.engine, "js");
@@ -483,12 +487,13 @@ assert.equal(timingState.engine, "js");
483
487
  // Verify invalid engine error
484
488
  await assert.rejects(
485
489
  () => evaluateInput(":engine invalid", timingState),
486
- /Invalid engine 'invalid'\. Valid engines: wasm, js/
490
+ /Invalid engine 'invalid'\. Valid engines: wasm, pkir, js/
487
491
  );
488
492
 
489
493
  // Verify autocompletion for :engine
490
494
  completions = completeInput(":engine ", timingState)[0];
491
495
  assert(completions.includes(":engine wasm"));
496
+ assert(completions.includes(":engine pkir"));
492
497
  assert(completions.includes(":engine js"));
493
498
 
494
499
  // Verify evaluating expressions in JS engine
@@ -509,6 +514,30 @@ output = await evaluateInput("{ 10 int x, 5 int y } plus", jsState);
509
514
  assert.match(output[0], /^\{\}\|_\|1\|1\|1\|1\|\+/);
510
515
  assert.match(output[1], /\(js\)/);
511
516
 
517
+ // Verify evaluating expressions in PKIR-powered Wasm engine
518
+ const pkirState = createState({ engine: "pkir" });
519
+ assert.equal(pkirState.engine, "pkir");
520
+
521
+ output = await evaluateInput(
522
+ "succ = | succ;\n{} | zero succ succ",
523
+ pkirState
524
+ );
525
+ assert.match(output[0], /^\{\}\|zero\|succ\|succ/);
526
+ assert.match(output[1], /\(pkir\)/);
527
+
528
+ output = await evaluateInput(
529
+ "not = </true | false, /false | true >;\n{} | true not",
530
+ pkirState
531
+ );
532
+ assert.match(output[0], /^\{\}\|false/);
533
+ assert.match(output[1], /\(pkir\)/);
534
+
535
+ output = await evaluateInput(":load Examples/arithmetics.k", pkirState);
536
+ assert.equal(output[0], "loaded Examples/arithmetics.k");
537
+ output = await evaluateInput("{ 10 int x, 5 int y } plus", pkirState);
538
+ assert.match(output[0], /^\{\}\|_\|1\|1\|1\|1\|\+/);
539
+ assert.match(output[1], /\(pkir\)/);
540
+
512
541
  // Verify open product projection in wasm engine
513
542
  const wasmListState = createState({ engine: "wasm" });
514
543
  await evaluateInput(":load Examples/arithmetics.k", wasmListState);
@@ -1,103 +0,0 @@
1
- # k Linux ARM64 Backend
2
-
3
- `@fraczak/k-arm64` is the native Linux AArch64 (ARM64) backend for
4
- [`@fraczak/k`](https://github.com/fraczak/k). It lowers typed k programs directly from
5
- the kVM representation into GNU AArch64 assembly text (`.s`), compiles native ELF
6
- executables, and provides CLI tools and runners for binary `pattern + value` streams
7
- and `--json` input/output.
8
-
9
- ## Features
10
-
11
- - **Direct kVM to ARM64 Lowering**: Generates pure GNU AArch64 assembly text without requiring LLVM or external code generators.
12
- - **Calling Convention & Registers (AAPCS64)**:
13
- - `x0`: Input value pointer on entry; Return status (`0` for success, non-zero for failure) on exit.
14
- - `x1`: Output value pointer on exit.
15
- - `x19`: Callee-saved Arena bump pointer preserved and updated across relation calls.
16
- - Standard 16-byte stack frame alignment with FP (`x29`) and LR (`x30`) chaining.
17
- - **Flat Arena Memory Representation**:
18
- - Word 0 (bytes 0–1): Total block size in bytes (16-byte aligned).
19
- - Word 1 (bytes 2–3): Value kind (`1` = Product, `2` = Variant).
20
- - Word 2 (bytes 4–7): Product field count OR Variant 32-bit tag ID.
21
- - Payloads (bytes 8+): 64-bit pointers to child values in the arena.
22
- - **Self Tail-Call Optimization (TCO)**:
23
- - Recursion where the tail call passes new product fields directly updates cached field slots and jumps back to `.Ltail_loop` with zero frame overhead, enabling deep recursion without stack overflow.
24
- - **Union Backtracking & Memory Rollback**:
25
- - Union branches save the arena bump pointer (`x19`) before evaluation. If a branch fails, `x19` is rewound to cleanly roll back all allocations made in the failed speculative branch before attempting subsequent branches.
26
- - **Standalone Linux Binaries**:
27
- - Links with a lightweight C driver (`backends/arm64/src/runtime.c`) and `backends/llvm/runtime/krt.c` to produce independent native ELF executables that execute directly on Linux without Node.js.
28
- - Supports `--json` for direct text output, stdin/stdout piping, and binary envelope streams.
29
-
30
- ## Quick Start
31
-
32
- Compile an expression to a standalone native Linux ARM64 executable:
33
-
34
- ```bash
35
- node ./backends/arm64/bin/k-arm64-compile.mjs -o /tmp/ok '|ok'
36
- ```
37
-
38
- Execute it over unit input and decode output:
39
-
40
- ```bash
41
- node ./codecs/unit.mjs --parse | /tmp/ok --json
42
- ```
43
-
44
- Output:
45
- ```json
46
- "ok"
47
- ```
48
-
49
- Compile with library dependencies (`--lib` and `--export`):
50
-
51
- ```bash
52
- node ./backends/arm64/bin/k-arm64-compile.mjs \
53
- --lib Examples/arithmetics.k \
54
- --export plus:+ --export succ --export int \
55
- '{succ int x,int y}+' \
56
- -o /tmp/add-one
57
-
58
- node ./codecs/k-parse.mjs <<< '{"x":{"succ":"zero"},"y":{"succ":{"succ":"zero"}}}' |
59
- /tmp/add-one --json
60
- ```
61
-
62
- Output:
63
- ```json
64
- {"+":{"1":"_"}}
65
- ```
66
-
67
- ## CLI Commands
68
-
69
- | Command | Purpose |
70
- | --- | --- |
71
- | `k-arm64` | Compile k source, `.ko`, or `.kvm` in memory and run immediately |
72
- | `k-arm64-compile` | Compile k source, `.ko`, or `.kvm` to standalone ELF executable or assembly (`-S`) |
73
- | `k-arm64-run` | Run a compiled ELF executable over binary wire streams |
74
-
75
- ### Options
76
-
77
- All tools accept `--help`.
78
-
79
- - `-S, --assembly`: Emit GNU ARM64 assembly text (`.s`) instead of an ELF binary.
80
- - `-o, --output <file>`: Specify the output file path.
81
- - `--lib <file>`: Load one `.klib` or `.k` library dependency.
82
- - `--export <spec>`: Export library symbols into source scope (`name` or `libname:localname`).
83
- - `--json`: Format output as JSON instead of binary wire format.
84
-
85
- ## Running Tests
86
-
87
- Run unit tests:
88
-
89
- ```bash
90
- node ./backends/arm64/tests/test-arm64.mjs
91
- ```
92
-
93
- Run integration tests:
94
-
95
- ```bash
96
- ./backends/arm64/tests/integration.sh
97
- ```
98
-
99
- Run the complete ARM64 backend test suite:
100
-
101
- ```bash
102
- npm run test:arm64
103
- ```
@@ -1,93 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from "node:fs";
4
- import { argv, exit, stdin, stdout } from "node:process";
5
-
6
- import {
7
- compileProgramInput,
8
- parseCompileOptions,
9
- resolveProgramInput
10
- } from "../src/cli.mjs";
11
-
12
- function usage(stream = console.error) {
13
- const prog = argv[1] || "k-arm64-compile.mjs";
14
- stream(`Usage: node ${prog} [options] [source-snippet | input-file [output-file]]`);
15
- stream("Compile k source, .ko, or .kvm input into a standalone Linux ARM64 executable or assembly.");
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(" output-file Output file path. Writes to stdout when omitted and --assembly is set.");
21
- stream("");
22
- stream("Options:");
23
- stream(" -S, --assembly Emit GNU ARM64 assembly (.s) instead of an executable binary.");
24
- stream(" -o, --output Specify output file path explicitly.");
25
- stream(" --lib file Load one .klib dependency before compiling.");
26
- stream(" --export spec Export a library alias into source scope. May be repeated.");
27
- stream(" spec is 'name' or 'libname:localname'.");
28
- stream(" -h, --help Show this help.");
29
- }
30
-
31
- try {
32
- const args = argv.slice(2);
33
- if (args.includes("-h") || args.includes("--help")) {
34
- usage(console.log);
35
- exit(0);
36
- }
37
-
38
- let emitAssembly = false;
39
- let explicitOutput = null;
40
- // Extract ARM64-specific flags
41
- const remainingArgs = [];
42
- while (args.length > 0) {
43
- const arg = args[0];
44
- if (arg === "-S" || arg === "--assembly") {
45
- emitAssembly = true;
46
- args.shift();
47
- } else if (arg === "-o" || arg === "--output") {
48
- args.shift();
49
- explicitOutput = args.shift();
50
- if (!explicitOutput) throw new Error("-o/--output requires a file argument");
51
- } else {
52
- remainingArgs.push(args.shift());
53
- }
54
- }
55
-
56
- const { libraries, exportSpecs } = parseCompileOptions(remainingArgs);
57
- const input = resolveProgramInput(remainingArgs, { allowStdinSource: true });
58
- const positionalOutput = remainingArgs.shift();
59
- if (remainingArgs.length > 0) throw new Error("Too many arguments");
60
-
61
- const outputPath = explicitOutput || positionalOutput || null;
62
-
63
- const result = await compileProgramInput(input, {
64
- libraries,
65
- exportSpecs,
66
- stdin,
67
- emit: emitAssembly ? "assembly" : null,
68
- outputPath: (!emitAssembly && outputPath) ? outputPath : null
69
- });
70
-
71
- if (emitAssembly) {
72
- const asmText = typeof result === "string" ? result : result.assembly;
73
- if (outputPath == null) {
74
- stdout.write(asmText);
75
- } else {
76
- fs.writeFileSync(outputPath, asmText, "utf8");
77
- }
78
- } else {
79
- if (outputPath == null) {
80
- if (Buffer.isBuffer(result)) {
81
- stdout.write(result);
82
- } else if (typeof result === "string") {
83
- stdout.write(fs.readFileSync(result));
84
- }
85
- } else if (Buffer.isBuffer(result)) {
86
- fs.writeFileSync(outputPath, result, { mode: 0o755 });
87
- }
88
- }
89
- } catch (error) {
90
- console.error(error.stack || error.message || String(error));
91
- usage();
92
- exit(1);
93
- }
@@ -1,58 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from "node:fs";
4
- import { argv, exit, stdin, stdout } from "node:process";
5
-
6
- import { readAll } from "../src/cli.mjs";
7
- import { runARM64Artifact } from "../src/arm64.mjs";
8
-
9
- function usage(stream = console.error) {
10
- const prog = argv[1] || "k-arm64-run.mjs";
11
- stream(`Usage: node ${prog} [options] executable-file [input-file]`);
12
- stream("Run a standalone k Linux ARM64 executable over a binary wire stream.");
13
- stream("");
14
- stream("Arguments:");
15
- stream(" executable-file Input compiled ELF binary produced by k-arm64-compile.");
16
- stream(" input-file Optional binary input wire file. Reads stdin when omitted.");
17
- stream("");
18
- stream("Options:");
19
- stream(" --json Format output as JSON instead of binary wire format.");
20
- stream(" -h, --help Show this help.");
21
- }
22
-
23
- async function main() {
24
- const args = argv.slice(2);
25
- if (args.includes("-h") || args.includes("--help")) {
26
- usage(console.log);
27
- return;
28
- }
29
-
30
- let json = false;
31
- const filteredArgs = [];
32
- for (const arg of args) {
33
- if (arg === "--json") {
34
- json = true;
35
- } else {
36
- filteredArgs.push(arg);
37
- }
38
- }
39
-
40
- const execPath = filteredArgs.shift();
41
- if (!execPath) throw new Error("Missing executable artifact path");
42
- const inputPath = filteredArgs.shift();
43
- if (filteredArgs.length > 0) throw new Error("Too many arguments");
44
-
45
- const input = await readAll(inputPath == null ? stdin : fs.createReadStream(inputPath));
46
- const result = runARM64Artifact(execPath, input, { json });
47
- if (json) {
48
- stdout.write(result);
49
- } else {
50
- stdout.write(result);
51
- }
52
- }
53
-
54
- main().catch((error) => {
55
- console.error(error.stack || error.message || String(error));
56
- usage();
57
- exit(1);
58
- });
@@ -1,88 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from "node:fs";
4
- import { argv, exit, stdin, stdout } from "node:process";
5
- import { decodeWire } from "../../../backend-api.mjs";
6
-
7
- import {
8
- compileProgramInput,
9
- parseCompileOptions,
10
- readAll,
11
- resolveProgramInput
12
- } from "../src/cli.mjs";
13
- import { runARM64Artifact } from "../src/arm64.mjs";
14
-
15
- function usage(stream = console.error) {
16
- const prog = argv[1] || "k-arm64.mjs";
17
- stream(`Usage: node ${prog} [options] ( source-snippet | input-file ) [ input-file ]`);
18
- stream("Compile k source, .ko, or .kvm input to ARM64 in memory and run it over a binary pattern+value stream.");
19
- stream("");
20
- stream("Options:");
21
- stream(" --json Format output as JSON instead of binary wire format.");
22
- stream(" --entry name Specify entry relation name.");
23
- stream(" --lib file Load one .klib dependency before compiling.");
24
- stream(" --export spec Export a library alias into source scope. May be repeated.");
25
- stream(" spec is 'name' or 'libname:localname'.");
26
- stream(" -h, --help Show this help.");
27
- }
28
-
29
- async function main() {
30
- const args = argv.slice(2);
31
- if (args.includes("-h") || args.includes("--help")) {
32
- usage(console.log);
33
- return;
34
- }
35
-
36
- let json = false;
37
- let entry = null;
38
- const remainingArgs = [];
39
- while (args.length > 0) {
40
- const arg = args[0];
41
- if (arg === "--json") {
42
- json = true;
43
- args.shift();
44
- } else if (arg === "--entry") {
45
- args.shift();
46
- entry = args.shift();
47
- if (!entry) throw new Error("--entry requires a name argument");
48
- } else {
49
- remainingArgs.push(args.shift());
50
- }
51
- }
52
-
53
- const { libraries, exportSpecs } = parseCompileOptions(remainingArgs);
54
- const programInput = resolveProgramInput(remainingArgs);
55
- const inputPath = remainingArgs.shift();
56
- if (remainingArgs.length > 0) throw new Error("Too many arguments");
57
-
58
- const inputBuffer = await readAll(inputPath == null ? stdin : fs.createReadStream(inputPath));
59
- let inputEnvelopePattern = null;
60
- if (inputBuffer.length > 0) {
61
- try {
62
- const decoded = decodeWire(inputBuffer);
63
- inputEnvelopePattern = decoded.pattern;
64
- } catch {
65
- // Not a valid wire buffer or empty
66
- }
67
- }
68
-
69
- const artifact = await compileProgramInput(programInput, {
70
- libraries,
71
- exportSpecs,
72
- inputEnvelopePattern,
73
- entry
74
- });
75
-
76
- const output = runARM64Artifact(artifact, inputBuffer, { json });
77
- if (json) {
78
- stdout.write(output);
79
- } else {
80
- stdout.write(output);
81
- }
82
- }
83
-
84
- main().catch((error) => {
85
- console.error(error.stack || error.message || String(error));
86
- usage();
87
- exit(1);
88
- });
@@ -1,57 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import path from "node:path";
4
- import { spawnSync } from "node:child_process";
5
- import { fileURLToPath } from "node:url";
6
-
7
- const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
8
- const node = process.execPath;
9
-
10
- function printHelp() {
11
- console.log(`Usage: node backends/arm64/scripts/run-tests.mjs [options]
12
-
13
- Runs ARM64 backend test suite and integration tests.
14
-
15
- Options:
16
- -h, --help
17
- Show this help message and exit.`);
18
- }
19
-
20
- if (process.argv.includes("-h") || process.argv.includes("--help")) {
21
- printHelp();
22
- process.exit(0);
23
- }
24
-
25
- const tests = [
26
- ["tests/test-arm64.mjs", node, ["tests/test-arm64.mjs"]],
27
- ["tests/integration.sh", path.join(root, "tests/integration.sh"), []]
28
- ];
29
-
30
- function formatDuration(milliseconds) {
31
- if (milliseconds < 1000) return `${milliseconds.toFixed(0)} ms`;
32
- return `${(milliseconds / 1000).toFixed(2)} s`;
33
- }
34
-
35
- const suiteStarted = performance.now();
36
- for (const [name, command, args] of tests) {
37
- console.log(`\n==> ${name}`);
38
- const started = performance.now();
39
- const result = spawnSync(command, args, {
40
- cwd: root,
41
- stdio: "inherit"
42
- });
43
- const duration = formatDuration(performance.now() - started);
44
-
45
- if (result.error) {
46
- console.error(`<== ${name} FAILED (${duration})`);
47
- console.error(result.error.message);
48
- process.exit(1);
49
- }
50
- if (result.status !== 0) {
51
- console.error(`<== ${name} FAILED (${duration})`);
52
- process.exit(result.status ?? 1);
53
- }
54
- console.log(`<== ${name} passed (${duration})`);
55
- }
56
-
57
- console.log(`\nAll ARM64 tests passed (${formatDuration(performance.now() - suiteStarted)})`);