@fraczak/k 6.3.3 → 6.4.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.
- package/DOCS/CODECS.md +258 -0
- package/DOCS/DICTIONARY.md +1172 -0
- package/DOCS/KIR_SPECIALIZATION_DESIGN.md +51 -46
- package/DOCS/KIR_V1.md +221 -0
- package/DOCS/KVM_EXECUTION_MODEL.md +56 -19
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +2 -1
- package/DOCS/PROFILING_AND_TRACING.md +196 -0
- package/DOCS/REPL.md +61 -4
- package/DOCS/WEB_REPL_REQUIREMENTS.md +135 -0
- package/DOCS/dependency-graph.md +144 -0
- package/Examples/arithmetics.k +51 -48
- package/Examples/bnat.k +1 -1
- package/Examples/buda.k +44 -0
- package/Examples/ieee.k +300 -8671
- package/Examples/poly.k +67 -0
- package/README.md +68 -19
- package/augmentation.mjs +9 -7
- package/backend-api.mjs +11 -2
- package/backends/arm64/README.md +104 -0
- package/backends/arm64/bin/k-arm64-compile.mjs +101 -0
- package/backends/arm64/bin/k-arm64-run.mjs +58 -0
- package/backends/arm64/bin/k-arm64.mjs +88 -0
- package/backends/arm64/scripts/run-tests.mjs +41 -0
- package/backends/arm64/src/arm64.mjs +229 -0
- package/backends/arm64/src/cli.mjs +155 -0
- package/backends/arm64/src/kvm2arm64.mjs +906 -0
- package/backends/arm64/src/runtime.c +728 -0
- package/backends/arm64/tests/integration.sh +112 -0
- package/backends/arm64/tests/test-arm64.mjs +240 -0
- package/backends/llvm/README.md +135 -0
- package/backends/llvm/bin/k-llvm-build.mjs +71 -0
- package/backends/llvm/bin/k-llvm-compile.mjs +68 -0
- package/backends/llvm/bin/k-llvm-jit.mjs +160 -0
- package/backends/llvm/bin/k-llvm-run.mjs +90 -0
- package/backends/llvm/runtime/krt.c +1508 -0
- package/backends/llvm/runtime/krt.h +109 -0
- package/backends/llvm/scripts/conformance.mjs +76 -0
- package/backends/llvm/scripts/run-tests.mjs +28 -0
- package/backends/llvm/src/executable.mjs +562 -0
- package/backends/llvm/src/kvm2llvm.mjs +998 -0
- package/backends/llvm/src/llvm.mjs +1092 -0
- package/backends/llvm/tests/composition-driver.c +22 -0
- package/backends/llvm/tests/identity-driver.c +27 -0
- package/backends/llvm/tests/integration.sh +153 -0
- package/backends/llvm/tests/perf-support.mjs +735 -0
- package/backends/llvm/tests/product-driver.c +23 -0
- package/backends/llvm/tests/projection-driver.c +19 -0
- package/backends/llvm/tests/relation-driver.c +29 -0
- package/backends/llvm/tests/test-llvm.mjs +240 -0
- package/backends/llvm/tests/union-driver.c +31 -0
- package/backends/llvm/tests/variant-driver.c +19 -0
- package/backends/llvm/tests/variant-projection-driver.c +21 -0
- package/backends/wasm/DOCS/USE-WEB-ASSEMBLY.md +233 -0
- package/backends/wasm/README.md +97 -0
- package/backends/wasm/TECHNICAL-DEBT.md +60 -0
- package/backends/wasm/bin/k-wasm-compile.mjs +50 -0
- package/backends/wasm/bin/k-wasm-run.mjs +140 -0
- package/backends/wasm/bin/k-wasm.mjs +49 -0
- package/backends/wasm/demo_ieee.mjs +319 -0
- package/backends/wasm/runtime.wat +102 -0
- package/backends/wasm/scripts/run-tests.mjs +41 -0
- package/backends/wasm/src/cli.mjs +133 -0
- package/backends/wasm/src/kvm2wasm.mjs +661 -0
- package/backends/wasm/src/wasm.mjs +1010 -0
- package/backends/wasm/tests/integration.sh +182 -0
- package/backends/wasm/tests/perf-support.mjs +336 -0
- package/backends/wasm/tests/test-wasm.mjs +617 -0
- package/browser/repl-browser.mjs +1584 -0
- package/browser/shims/assert.mjs +19 -0
- package/browser/shims/buffer.mjs +224 -0
- package/browser/shims/crypto.mjs +103 -0
- package/browser/shims/fs.mjs +159 -0
- package/browser/shims/os.mjs +17 -0
- package/browser/shims/path.mjs +54 -0
- package/browser/shims/process.mjs +44 -0
- package/browser/shims/readline.mjs +11 -0
- package/browser/shims/url.mjs +12 -0
- package/browser/vfs-data.mjs +19 -0
- package/codecs/README.md +3 -0
- package/codecs/runtime/show-value.mjs +2 -2
- package/conformance/helper-reuse/case.json +8 -0
- package/conformance/helper-reuse/expected.kv +1 -0
- package/conformance/helper-reuse/input.kv +1 -0
- package/conformance/helper-reuse/program.k +2 -0
- package/conformance/identity/case.json +8 -0
- package/conformance/identity/expected.kv +1 -0
- package/conformance/identity/input.kv +1 -0
- package/conformance/identity/program.k +1 -0
- package/conformance/product-projection/case.json +8 -0
- package/conformance/product-projection/expected.kv +1 -0
- package/conformance/product-projection/input.kv +1 -0
- package/conformance/product-projection/program.k +1 -0
- package/conformance/run.mjs +149 -0
- package/conformance/variant-construction/case.json +8 -0
- package/conformance/variant-construction/expected.kv +1 -0
- package/conformance/variant-construction/input.kv +1 -0
- package/conformance/variant-construction/program.k +1 -0
- package/harness/adapters/arm64.mjs +105 -0
- package/harness/adapters/kvm.mjs +144 -0
- package/harness/adapters/llvm.mjs +118 -0
- package/harness/adapters/native.mjs +77 -0
- package/harness/adapters/wasm.mjs +169 -0
- package/harness/backend.mjs +95 -0
- package/harness/cli.mjs +130 -0
- package/harness/format.mjs +95 -0
- package/harness/index.mjs +25 -0
- package/harness/persistent.mjs +121 -0
- package/harness/registry.mjs +44 -0
- package/harness/suite.mjs +257 -0
- package/harness/suites/poly.mjs +156 -0
- package/harness/trace.mjs +50 -0
- package/harness/util.mjs +92 -0
- package/intrinsics.mjs +41 -0
- package/kir.mjs +399 -0
- package/kvm.mjs +585 -81
- package/objects/README.md +37 -3
- package/objects/compile.mjs +15 -22
- package/objects/inspect.mjs +96 -0
- package/objects/validate.mjs +415 -0
- package/package.json +51 -19
- package/repl-codecs.mjs +105 -6
- package/repl.mjs +335 -49
- package/run.mjs +2 -7
- package/scripts/benchmark.mjs +406 -22
- package/scripts/build-repl-html.mjs +1784 -0
- package/scripts/perf-ieee.mjs +586 -0
- package/scripts/perf-int.mjs +531 -0
- package/scripts/perf-poly.mjs +945 -0
- package/scripts/render-benchmark.mjs +12 -8
- package/scripts/run-tests.mjs +6 -0
- package/tests/integration.sh +19 -2
- package/tests/test-arithmetics.mjs +54 -0
- package/tests/test-ieee-arithmetic.mjs +4 -0
- package/tests/test-kir.mjs +85 -0
- package/tests/test-kvm-polymorphic.mjs +278 -0
- package/tests/test-kvm.mjs +25 -6
- package/tests/test-poly.mjs +129 -0
- package/tests/test-repl-html.mjs +303 -0
- package/tests/test-repl.mjs +144 -1
- package/tests/test-validate-object.mjs +40 -0
- package/a.klib +0 -1
- package/a.kvm +0 -142676
- package/a.wasm +0 -0
- package/benchmark-results.html +0 -532
- package/benchmark-results.json +0 -480
- package/k-vscode/.vscode/launch.json +0 -14
- package/k-vscode/README.md +0 -75
- package/k-vscode/extension.js +0 -273
- package/k-vscode/language-configuration.json +0 -42
- package/k-vscode/package.json +0 -79
- package/k-vscode/snippets/k.json +0 -92
- package/k-vscode/syntaxes/k.tmLanguage.json +0 -599
- package/k-vscode/test/background-ranges.test.js +0 -50
- package/k-vscode/themes/k-contrast-color-theme.json +0 -104
- package/scripts/git-hooks/pre-commit +0 -10
package/Examples/poly.k
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
-- Examples/poly.k: Polymorphic list functions in k
|
|
2
|
+
-- Uses list constructors/destructors from Examples/list.k
|
|
3
|
+
-- and arbitrary-precision integer representations from Examples/arithmetics.k.
|
|
4
|
+
|
|
5
|
+
-- Decrement an integer and normalize its representation (stripping leading zeros)
|
|
6
|
+
pred_int = dec /'+' nat | '+';
|
|
7
|
+
|
|
8
|
+
-- Concatenate two lists: {xs, ys} -> list
|
|
9
|
+
concat = {.xs reverse xs, .ys acc} _reverse;
|
|
10
|
+
|
|
11
|
+
-- Reverse a list: xs -> list
|
|
12
|
+
reverse = {() xs, nil acc} _reverse;
|
|
13
|
+
_reverse = <
|
|
14
|
+
{.xs cdr xs, {.xs car car, .acc cdr} cons acc} _reverse,
|
|
15
|
+
.acc
|
|
16
|
+
>;
|
|
17
|
+
|
|
18
|
+
-- Length of a list: xs -> int
|
|
19
|
+
length = {() xs, 0 int acc} _length;
|
|
20
|
+
_length = <
|
|
21
|
+
{.xs /nil if, .acc then} .then,
|
|
22
|
+
{.xs cdr xs, .acc inc acc} _length
|
|
23
|
+
>;
|
|
24
|
+
|
|
25
|
+
-- Get N-th element of a list (0-indexed): {n, xs} -> element
|
|
26
|
+
get_nth = <
|
|
27
|
+
{.n zero_int? if, .xs car then} .then,
|
|
28
|
+
{.n pred_int n, .xs cdr xs} get_nth
|
|
29
|
+
>;
|
|
30
|
+
|
|
31
|
+
-- Split list at index N: {n, xs} -> {first, second}
|
|
32
|
+
split_by = {.n n, .xs xs, nil acc} _split_by;
|
|
33
|
+
_split_by = <
|
|
34
|
+
{.n zero_int? if, {.acc reverse first, .xs second} then} .then,
|
|
35
|
+
{.xs /nil if, {.acc reverse first, nil second} then} .then,
|
|
36
|
+
{.n pred_int n, .xs cdr xs, {.xs car car, .acc cdr} cons acc} _split_by
|
|
37
|
+
>;
|
|
38
|
+
|
|
39
|
+
-- Take the first N elements of a list: {n, xs} -> list
|
|
40
|
+
take = split_by .first;
|
|
41
|
+
|
|
42
|
+
-- Drop the first N elements of a list: {n, xs} -> list
|
|
43
|
+
drop = <
|
|
44
|
+
{.n zero_int? if, .xs then} .then,
|
|
45
|
+
{.xs /nil if, nil then} .then,
|
|
46
|
+
{.n pred_int n, .xs cdr xs} drop
|
|
47
|
+
>;
|
|
48
|
+
|
|
49
|
+
-- Repeat element X, N times: {n, x} -> list
|
|
50
|
+
repeat = {.n n, .x x, nil acc} _repeat;
|
|
51
|
+
_repeat = <
|
|
52
|
+
{.n zero_int? if, .acc then} .then,
|
|
53
|
+
{.n pred_int n, .x x, {.x car, .acc cdr} cons acc} _repeat
|
|
54
|
+
>;
|
|
55
|
+
|
|
56
|
+
-- Append an element to the end of a list: {x, xs} -> list
|
|
57
|
+
append = {.xs xs, {.x car, nil cdr} cons ys} concat;
|
|
58
|
+
|
|
59
|
+
-- Zip two lists into a list of {x, y} pairs: {xs, ys} -> list
|
|
60
|
+
zip = <
|
|
61
|
+
{.xs /nil if, nil then} .then,
|
|
62
|
+
{.ys /nil if, nil then} .then,
|
|
63
|
+
{
|
|
64
|
+
{.xs car x, .ys car y} car,
|
|
65
|
+
{.xs cdr xs, .ys cdr ys} zip cdr
|
|
66
|
+
} cons
|
|
67
|
+
>;
|
package/README.md
CHANGED
|
@@ -64,6 +64,15 @@ one successor. The REPL also prints the inferred value envelope.
|
|
|
64
64
|
|
|
65
65
|
## Try It
|
|
66
66
|
|
|
67
|
+
### In the Browser (Zero Install)
|
|
68
|
+
|
|
69
|
+
Try k directly in your browser without installing anything:
|
|
70
|
+
**[https://fraczak.github.io/k/](https://fraczak.github.io/k/)**
|
|
71
|
+
|
|
72
|
+
The Web REPL is self-contained, powered by an in-process WebAssembly compiler and execution engine with built-in codecs, autocompletion, interactive input, and standard libraries.
|
|
73
|
+
|
|
74
|
+
### From Source
|
|
75
|
+
|
|
67
76
|
From a checkout:
|
|
68
77
|
|
|
69
78
|
```bash
|
|
@@ -91,6 +100,13 @@ Start the interactive interpreter:
|
|
|
91
100
|
k-repl
|
|
92
101
|
```
|
|
93
102
|
|
|
103
|
+
Build the standalone single-file Web REPL locally:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm run build:repl-html
|
|
107
|
+
# generates repl.html (open in any web browser)
|
|
108
|
+
```
|
|
109
|
+
|
|
94
110
|
Every installed command supports `-h` and `--help`.
|
|
95
111
|
|
|
96
112
|
## What k Gives You
|
|
@@ -149,13 +165,13 @@ k-print # binary pattern+value stream -> textual value
|
|
|
149
165
|
|
|
150
166
|
k programs can be compiled into:
|
|
151
167
|
|
|
152
|
-
- `.klib`:
|
|
168
|
+
- `.klib`: library objects
|
|
153
169
|
- `.ko`: executable binary object containers
|
|
154
|
-
- `.kvm`:
|
|
170
|
+
- `.kvm`: specialized kVM backend artifacts
|
|
155
171
|
|
|
156
172
|
Object and library files keep canonical code/relation definitions, aliases,
|
|
157
|
-
metadata, and type-derivation status. The `.kvm` output is a lowered
|
|
158
|
-
|
|
173
|
+
metadata, and type-derivation status. The `.kvm` output is a lowered backend
|
|
174
|
+
artifact for inspection and execution experiments.
|
|
159
175
|
|
|
160
176
|
## CLI Tour
|
|
161
177
|
|
|
@@ -191,17 +207,30 @@ program for backend inspection and execution experiments.
|
|
|
191
207
|
| `k-compile` | Compile `.k` source to `.ko`, `.klib`, or `.kvm` output |
|
|
192
208
|
| `k-decompile` | Decompile `.ko` or `.klib` back to k source |
|
|
193
209
|
| `k-extract-aliases` | Recover metadata aliases as k source |
|
|
210
|
+
| `k-inspect-object` | Inspect object sections or print the KIR-P backend export |
|
|
211
|
+
| `k-validate-object` | Validate `.ko`, `.klib`, or exported KIR-P artifacts |
|
|
212
|
+
| `k-kir` | Export KIR-P from `.ko` or `.klib` |
|
|
213
|
+
| `k-vm` | Execute or inspect lowered kVM bytecode artifacts (`.kvm`) |
|
|
214
|
+
| `k-wasm` / `k-wasm-compile` / `k-wasm-run` | Compile and run via WebAssembly backend |
|
|
215
|
+
| `k-llvm-build` / `k-llvm-compile` / `k-llvm-run` | Compile and run via LLVM backend |
|
|
216
|
+
| `k-arm64` / `k-arm64-compile` / `k-arm64-run` | Compile and run via Linux ARM64 native backend |
|
|
194
217
|
|
|
195
218
|
Installed binary names are `k-` plus the source basename without `.mjs`, except
|
|
196
219
|
for `k.mjs` itself. Source names that already include `k-`, such as
|
|
197
220
|
`codecs/k-parse.mjs`, keep that name.
|
|
198
221
|
|
|
199
|
-
##
|
|
222
|
+
## Backends
|
|
223
|
+
|
|
224
|
+
Backends live under [`backends/`](backends/) as npm workspaces:
|
|
225
|
+
|
|
226
|
+
- [`backends/wasm`](backends/wasm/) lowers typed k programs through kVM into
|
|
227
|
+
WebAssembly artifacts. Powers both the CLI REPL (`wasm-in-process`) and the standalone Web REPL.
|
|
228
|
+
- [`backends/llvm`](backends/llvm/) lowers polymorphic kVM programs into LLVM IR and
|
|
229
|
+
compiled native test executables.
|
|
230
|
+
- [`backends/arm64`](backends/arm64/) lowers polymorphic kVM programs directly into
|
|
231
|
+
standalone Linux ARM64 native executables without external compiler dependencies.
|
|
200
232
|
|
|
201
|
-
|
|
202
|
-
[`fraczak/k-wasm`](https://github.com/fraczak/k-wasm) repository. Keeping the
|
|
203
|
-
backend separate avoids adding its WABT dependency to the core runtime. External
|
|
204
|
-
backends integrate with the compiler and binary codecs through
|
|
233
|
+
All backends integrate with the compiler and binary codecs through
|
|
205
234
|
[`backend-api.mjs`](backend-api.mjs).
|
|
206
235
|
|
|
207
236
|
## Node.js API
|
|
@@ -265,28 +294,41 @@ serialization, or teaching tools, there is room to shape the project.
|
|
|
265
294
|
|
|
266
295
|
## Examples
|
|
267
296
|
|
|
268
|
-
The [`Examples/`](Examples/) directory contains
|
|
297
|
+
The [`Examples/`](Examples/) directory contains language demonstrations and standard libraries:
|
|
269
298
|
|
|
270
299
|
| File | Contents |
|
|
271
300
|
| --- | --- |
|
|
301
|
+
| `core.k` | Standard library: booleans, units, strings, optionals, and fundamental helpers |
|
|
302
|
+
| `arithmetics.k` | Integer and rational arithmetic built from bit-level relations from scratch |
|
|
303
|
+
| `ieee.k` | IEEE 754 binary64 floating-point arithmetic (`add`, `sub`, `mul`, `div`) |
|
|
304
|
+
| `poly.k` | Polymorphic list operations (`concat`, `reverse`, `length`, `get_nth`, `split_by`, `zip`) |
|
|
272
305
|
| `nat.k` | Peano natural numbers |
|
|
273
306
|
| `byte.k` | Byte type |
|
|
274
|
-
| `ieee.k` | IEEE 754 floating-point layout |
|
|
275
307
|
| `bnat.k` | Binary natural numbers |
|
|
276
|
-
| `
|
|
308
|
+
| `buda.k` | Compact relational data transformations |
|
|
277
309
|
|
|
278
310
|
`list.k` is also useful as a focused demonstration of filters and patterns.
|
|
279
311
|
|
|
280
|
-
`ieee.k` is
|
|
281
|
-
|
|
282
|
-
`mul`, and `div` relations. Those public aliases
|
|
283
|
-
compose with `.result` when only the floating-point
|
|
312
|
+
`ieee.k` is a complete IEEE-754 binary64 model built from bit-level types
|
|
313
|
+
upward with hierarchical significand adders, including comparison and
|
|
314
|
+
floating-point `add`, `sub`, `mul`, and `div` relations. Those public aliases
|
|
315
|
+
return `{ result, flags }`; compose with `.result` when only the floating-point
|
|
316
|
+
value is needed.
|
|
284
317
|
|
|
285
318
|
## Development
|
|
286
319
|
|
|
287
320
|
```bash
|
|
288
|
-
npm run prepare
|
|
289
|
-
npm test
|
|
321
|
+
npm run prepare # regenerate parsers from .jison grammars
|
|
322
|
+
npm test # run the fail-fast full suite with per-test timings
|
|
323
|
+
npm run test:wasm # run the WebAssembly backend tests
|
|
324
|
+
npm run test:llvm # run the LLVM backend tests
|
|
325
|
+
npm run test:arm64 # run the Linux ARM64 backend tests
|
|
326
|
+
npm run build:repl-html # build the standalone single-file Web REPL (repl.html)
|
|
327
|
+
npm run compare # run multi-backend benchmark and conformance harness
|
|
328
|
+
npm run perf:poly # run polymorphic benchmark across backends
|
|
329
|
+
npm run perf:poly:trace # run execution phase tracing & call profiling
|
|
330
|
+
npm run perf:int # run integer arithmetic benchmark across backends
|
|
331
|
+
npm run perf:ieee # run IEEE-754 arithmetic benchmark across backends
|
|
290
332
|
```
|
|
291
333
|
|
|
292
334
|
The test runner prints each test before execution and reports its elapsed time
|
|
@@ -297,14 +339,21 @@ afterward. It stops immediately when a test fails. The suite covers:
|
|
|
297
339
|
- hash/fingerprint stability
|
|
298
340
|
- object file round-trips
|
|
299
341
|
- REPL scripted interaction
|
|
342
|
+
- Web REPL headless browser verification
|
|
343
|
+
- polymorphic kVM and specialization
|
|
300
344
|
- shell integration tests
|
|
301
345
|
|
|
302
346
|
## Further Reading
|
|
303
347
|
|
|
304
|
-
- [DOCS/
|
|
348
|
+
- [DOCS/DICTIONARY.md](DOCS/DICTIONARY.md) - concept names and terminology
|
|
349
|
+
- [DOCS/REPL.md](DOCS/REPL.md) - interactive interpreter and codec details
|
|
350
|
+
- [DOCS/WEB_REPL_REQUIREMENTS.md](DOCS/WEB_REPL_REQUIREMENTS.md) - Web REPL architecture and requirements
|
|
351
|
+
- [DOCS/KVM_EXECUTION_MODEL.md](DOCS/KVM_EXECUTION_MODEL.md) - kVM execution model and bytecode design
|
|
305
352
|
- [DOCS/TEXTUAL_VALUES.md](DOCS/TEXTUAL_VALUES.md) - textual boundary notation
|
|
306
353
|
- [DOCS/PATTERNS.md](DOCS/PATTERNS.md) - pattern representation
|
|
307
354
|
- [DOCS/OBJECT_FILE_AND_PATTERN.md](DOCS/OBJECT_FILE_AND_PATTERN.md) - object format
|
|
355
|
+
- [DOCS/PROFILING_AND_TRACING.md](DOCS/PROFILING_AND_TRACING.md) - execution phase tracing and function call profiling across backends
|
|
356
|
+
- [DOCS/CODECS.md](DOCS/CODECS.md) - writing external codecs
|
|
308
357
|
- [codecs/README.md](codecs/README.md) - binary codec internals
|
|
309
358
|
- [objects/README.md](objects/README.md) - object/library tools
|
|
310
359
|
- [DOCS/book.md](DOCS/book.md) - longer language reference
|
package/augmentation.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import codes from "./codes.mjs";
|
|
2
|
+
import { getIntrinsic } from "./intrinsics.mjs";
|
|
2
3
|
|
|
3
4
|
const unitCode = codes.unitCode;
|
|
4
5
|
|
|
@@ -187,12 +188,13 @@ function augmentRef(rel,rootDef,rels) {
|
|
|
187
188
|
});
|
|
188
189
|
return;
|
|
189
190
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
191
|
+
const intrinsic = getIntrinsic(rel.ref);
|
|
192
|
+
if (intrinsic) {
|
|
193
|
+
if (intrinsic.type !== "identity") {
|
|
194
|
+
throw new Error(`No type rule found for intrinsic '${rel.ref}'`);
|
|
195
|
+
}
|
|
196
|
+
rel.patterns[0] = rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
|
|
197
|
+
return;
|
|
197
198
|
}
|
|
199
|
+
throw new Error(`No definition found for '${rel.ref}'`);
|
|
198
200
|
}
|
package/backend-api.mjs
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
export { annotate } from "./index.mjs";
|
|
2
|
-
export { lowerToKVM, executeKVM } from "./kvm.mjs";
|
|
2
|
+
export { KVM_FORMAT, KVM_VERSION, lowerKIRRelationToKVM, lowerKIRToKVM, lowerToKVM, executeKVM, objectToKVMArtifact, specializeKVM } from "./kvm.mjs";
|
|
3
3
|
export { decodeWire, encodeToWire } from "./codecs/runtime/prefix-codec.mjs";
|
|
4
4
|
export { exportPatternGraph, NODE_KIND } from "./codecs/runtime/codec.mjs";
|
|
5
5
|
export { patternToPropertyList, propertyListToPattern } from "./codecs/runtime/pattern-json.mjs";
|
|
6
6
|
export { Value, fromObject, isProduct, isVariant, isValue } from "./Value.mjs";
|
|
7
|
-
export {
|
|
7
|
+
export { KIR_FORMAT, KIR_VERSION, objectToKIRP, retypeObjectRelation, retypeObjectRelationForBackend } from "./kir.mjs";
|
|
8
|
+
export { compileLibrary, decodeObject, loadLibrary } from "./object.mjs";
|
|
8
9
|
export { createState, evaluateInput } from "./repl.mjs";
|
|
9
10
|
export { parse as parseFloat64, print as printFloat64 } from "./codecs/ieee.mjs";
|
|
10
11
|
export { valueForCode } from "./repl-codecs.mjs";
|
|
11
12
|
export { default as codes } from "./codes.mjs";
|
|
12
13
|
export { default as run, run_converged } from "./run.mjs";
|
|
14
|
+
export {
|
|
15
|
+
intrinsicDefinitions,
|
|
16
|
+
jsIntrinsicFunctions,
|
|
17
|
+
getIntrinsic,
|
|
18
|
+
isIntrinsic,
|
|
19
|
+
isIdentityIntrinsic,
|
|
20
|
+
unsupportedIntrinsic
|
|
21
|
+
} from "./intrinsics.mjs";
|
|
@@ -0,0 +1,104 @@
|
|
|
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
|
+
- `--entry <name>`: Specify the entry relation (defaults to `__main__`).
|
|
82
|
+
- `--lib <file>`: Load one `.klib` or `.k` library dependency.
|
|
83
|
+
- `--export <spec>`: Export library symbols into source scope (`name` or `libname:localname`).
|
|
84
|
+
- `--json`: Format output as JSON instead of binary wire format.
|
|
85
|
+
|
|
86
|
+
## Running Tests
|
|
87
|
+
|
|
88
|
+
Run unit tests:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
node ./backends/arm64/tests/test-arm64.mjs
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Run integration tests:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
./backends/arm64/tests/integration.sh
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Run the complete ARM64 backend test suite:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
npm run test:arm64
|
|
104
|
+
```
|
|
@@ -0,0 +1,101 @@
|
|
|
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(" --entry name Specify entry relation name.");
|
|
26
|
+
stream(" --lib file Load one .klib dependency before compiling.");
|
|
27
|
+
stream(" --export spec Export a library alias into source scope. May be repeated.");
|
|
28
|
+
stream(" spec is 'name' or 'libname:localname'.");
|
|
29
|
+
stream(" -h, --help Show this help.");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const args = argv.slice(2);
|
|
34
|
+
if (args.includes("-h") || args.includes("--help")) {
|
|
35
|
+
usage(console.log);
|
|
36
|
+
exit(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let emitAssembly = false;
|
|
40
|
+
let explicitOutput = null;
|
|
41
|
+
let entry = null;
|
|
42
|
+
|
|
43
|
+
// Extract ARM64-specific flags
|
|
44
|
+
const remainingArgs = [];
|
|
45
|
+
while (args.length > 0) {
|
|
46
|
+
const arg = args[0];
|
|
47
|
+
if (arg === "-S" || arg === "--assembly") {
|
|
48
|
+
emitAssembly = true;
|
|
49
|
+
args.shift();
|
|
50
|
+
} else if (arg === "-o" || arg === "--output") {
|
|
51
|
+
args.shift();
|
|
52
|
+
explicitOutput = args.shift();
|
|
53
|
+
if (!explicitOutput) throw new Error("-o/--output requires a file argument");
|
|
54
|
+
} else if (arg === "--entry") {
|
|
55
|
+
args.shift();
|
|
56
|
+
entry = args.shift();
|
|
57
|
+
if (!entry) throw new Error("--entry requires a name argument");
|
|
58
|
+
} else {
|
|
59
|
+
remainingArgs.push(args.shift());
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const { libraries, exportSpecs } = parseCompileOptions(remainingArgs);
|
|
64
|
+
const input = resolveProgramInput(remainingArgs, { allowStdinSource: true });
|
|
65
|
+
const positionalOutput = remainingArgs.shift();
|
|
66
|
+
if (remainingArgs.length > 0) throw new Error("Too many arguments");
|
|
67
|
+
|
|
68
|
+
const outputPath = explicitOutput || positionalOutput || null;
|
|
69
|
+
|
|
70
|
+
const result = await compileProgramInput(input, {
|
|
71
|
+
libraries,
|
|
72
|
+
exportSpecs,
|
|
73
|
+
stdin,
|
|
74
|
+
entry,
|
|
75
|
+
emit: emitAssembly ? "assembly" : null,
|
|
76
|
+
outputPath: (!emitAssembly && outputPath) ? outputPath : null
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (emitAssembly) {
|
|
80
|
+
const asmText = typeof result === "string" ? result : result.assembly;
|
|
81
|
+
if (outputPath == null) {
|
|
82
|
+
stdout.write(asmText);
|
|
83
|
+
} else {
|
|
84
|
+
fs.writeFileSync(outputPath, asmText, "utf8");
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
if (outputPath == null) {
|
|
88
|
+
if (Buffer.isBuffer(result)) {
|
|
89
|
+
stdout.write(result);
|
|
90
|
+
} else if (typeof result === "string") {
|
|
91
|
+
stdout.write(fs.readFileSync(result));
|
|
92
|
+
}
|
|
93
|
+
} else if (Buffer.isBuffer(result)) {
|
|
94
|
+
fs.writeFileSync(outputPath, result, { mode: 0o755 });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error(error.stack || error.message || String(error));
|
|
99
|
+
usage();
|
|
100
|
+
exit(1);
|
|
101
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
const tests = [
|
|
10
|
+
["tests/test-arm64.mjs", node, ["tests/test-arm64.mjs"]],
|
|
11
|
+
["tests/integration.sh", path.join(root, "tests/integration.sh"), []]
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
function formatDuration(milliseconds) {
|
|
15
|
+
if (milliseconds < 1000) return `${milliseconds.toFixed(0)} ms`;
|
|
16
|
+
return `${(milliseconds / 1000).toFixed(2)} s`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const suiteStarted = performance.now();
|
|
20
|
+
for (const [name, command, args] of tests) {
|
|
21
|
+
console.log(`\n==> ${name}`);
|
|
22
|
+
const started = performance.now();
|
|
23
|
+
const result = spawnSync(command, args, {
|
|
24
|
+
cwd: root,
|
|
25
|
+
stdio: "inherit"
|
|
26
|
+
});
|
|
27
|
+
const duration = formatDuration(performance.now() - started);
|
|
28
|
+
|
|
29
|
+
if (result.error) {
|
|
30
|
+
console.error(`<== ${name} FAILED (${duration})`);
|
|
31
|
+
console.error(result.error.message);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
if (result.status !== 0) {
|
|
35
|
+
console.error(`<== ${name} FAILED (${duration})`);
|
|
36
|
+
process.exit(result.status ?? 1);
|
|
37
|
+
}
|
|
38
|
+
console.log(`<== ${name} passed (${duration})`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log(`\nAll ARM64 tests passed (${formatDuration(performance.now() - suiteStarted)})`);
|