@fraczak/k 6.8.0 → 6.10.1
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/CHANGELOG.md +40 -0
- package/CLAUDE.md +1 -1
- package/DOCS/BIG_PICTURE.md +3 -5
- package/DOCS/CODECS.md +80 -181
- package/DOCS/CODEC_ARCHITECTURE.md +194 -0
- package/DOCS/REPL.md +69 -51
- package/Examples/arithmetics.k +1 -0
- package/Examples/poly.k +46 -0
- package/backends/llvm/runtime/krt.c +4 -0
- package/backends/llvm/src/kvm2llvm.mjs +2 -2
- package/backends/llvm/tests/test-llvm.mjs +19 -0
- package/backends/wasm/src/kvm2wasm.mjs +4 -0
- package/backends/wasm/tests/test-wasm.mjs +4 -0
- package/browser/repl-browser.mjs +131 -779
- package/browser/shims/process.mjs +7 -0
- package/browser/tree-view.mjs +5 -2
- package/browser/vfs-data.mjs +14 -14
- package/codecs/README.md +7 -0
- package/codecs/ieee.mjs +50 -75
- package/codecs/int.mjs +83 -62
- package/codecs/json-codec.mjs +5 -77
- package/codecs/json.mjs +12 -56
- package/codecs/k-parse.mjs +3 -19
- package/codecs/runtime/codec-sdk.mjs +94 -0
- package/codecs/runtime/pattern-k.mjs +45 -0
- package/codecs/string-codec.mjs +48 -74
- package/codecs/unit.mjs +13 -61
- package/codecs/utf16.mjs +13 -56
- package/codecs/utf8.mjs +11 -55
- package/package.json +1 -1
- package/patterns/from-k.mjs +2 -22
- package/repl-codecs.mjs +69 -196
- package/repl.mjs +177 -281
- package/scripts/build-repl-html.mjs +114 -385
- package/scripts/run-tests.mjs +1 -0
- package/tests/test-codecs.mjs +186 -0
- package/tests/test-poly.mjs +44 -0
- package/tests/test-repl-html.mjs +163 -68
- package/tests/test-repl.mjs +210 -156
- package/tests/test-structural-values.mjs +17 -0
- package/tests/test-tree-view.mjs +3 -0
- package/codecs/example-pipeline.mjs +0 -163
- package/codecs/runtime/ieee-pattern.mjs +0 -30
- package/codecs/runtime/test-codec.mjs +0 -89
- package/codecs/runtime/typeFromValue.mjs +0 -34
package/DOCS/REPL.md
CHANGED
|
@@ -33,20 +33,18 @@ complete.
|
|
|
33
33
|
| Command | Meaning |
|
|
34
34
|
| --- | --- |
|
|
35
35
|
| `:help` | Show command summary |
|
|
36
|
+
| `:engine [wasm\|js]` | Display or switch evaluation engine (`wasm` or `js`) |
|
|
37
|
+
| `:wasm` / `:js` | Shortcut to switch engine |
|
|
38
|
+
| `:rel name` | Show relation definition |
|
|
36
39
|
| `:type name` | Show the canonical definition of a type |
|
|
40
|
+
| `:types` | List type aliases |
|
|
41
|
+
| `:rels` | List relation aliases |
|
|
37
42
|
| `:code name` | Alias for `:C name` |
|
|
38
|
-
| `:run expr` | Evaluate `expr` on the current value |
|
|
39
|
-
| `:eval expr` | Alias for `:run` |
|
|
40
|
-
| `:time expr` | Evaluate `expr` and report compilation and execution time |
|
|
41
|
-
| `:t name` | Show relation input/output filters |
|
|
42
|
-
| `:d name` | Show relation definition |
|
|
43
43
|
| `:C name` | Show canonical code definition |
|
|
44
|
-
| `:
|
|
45
|
-
| `:rels` | List relation aliases |
|
|
46
|
-
| `:codec load name\|file` | Load a built-in codec (`int`, `utf8`, `json`, `ieee`, `unit`) or an ES module file |
|
|
44
|
+
| `:codec load file` | Load a codec from an ES module file (e.g. `:codec load codecs/int.mjs`) |
|
|
47
45
|
| `:codec unload name` | Unload a registered codec |
|
|
48
46
|
| `:codec list` | List loaded REPL codecs (alias: `:codecs`) |
|
|
49
|
-
| `:input
|
|
47
|
+
| `:input <codec.mjs> [text]` | Read next input line or parse inline text using specified codec |
|
|
50
48
|
| `:load [--no-alias] file` | Load `.k` source or `.klib` into the current state |
|
|
51
49
|
| `:klib file` | Export the active relation closure as a `.klib` library |
|
|
52
50
|
| `:ko file expr` | Export a `.ko` executable with `expr` as main |
|
|
@@ -136,9 +134,8 @@ Tab completion covers:
|
|
|
136
134
|
|
|
137
135
|
Type aliases also complete in `$name` position inside raw k input.
|
|
138
136
|
|
|
139
|
-
For codec commands, completion covers `:codec load`, `:codec
|
|
140
|
-
after `:codec load`,
|
|
141
|
-
after the input pattern or type.
|
|
137
|
+
For codec commands, completion covers `:codec load`, `:codec unload`, `:codec list`,
|
|
138
|
+
file paths after `:codec load`, and codec names after `:input`.
|
|
142
139
|
|
|
143
140
|
## Loading
|
|
144
141
|
|
|
@@ -156,71 +153,92 @@ unless `--no-alias` is used.
|
|
|
156
153
|
|
|
157
154
|
## Codecs
|
|
158
155
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
156
|
+
A codec is a recipe for translating between external text and enveloped *k* values:
|
|
157
|
+
- `parse(text)`: parses an external string into an enveloped *k* value.
|
|
158
|
+
- `print(value)`: serializes an enveloped *k* value to external string, throwing an `Error` if the value is not representable.
|
|
159
|
+
|
|
160
|
+
Codecs can define a family of target patterns (e.g. `int` parses/prints single integers as well as lists like `[0,1,2]`).
|
|
161
|
+
|
|
162
|
+
For printing in the REPL, every value is printed with the standard *k* envelope representation, followed by lines for all loaded codecs that can format it:
|
|
163
|
+
```text
|
|
164
|
+
> 42 int
|
|
165
|
+
{}|_|0|1|0|1|0|1|+ ?<{} _, ...>
|
|
166
|
+
int: 42
|
|
167
|
+
```
|
|
163
168
|
|
|
164
169
|
For a complete guide to writing a new codec module, see
|
|
165
170
|
[`CODECS.md`](./CODECS.md).
|
|
166
171
|
|
|
167
|
-
### `:codec load
|
|
172
|
+
### `:codec load file`
|
|
168
173
|
|
|
169
|
-
Loads
|
|
170
|
-
- `int` auto-loads `Examples/arithmetics.k`
|
|
171
|
-
- `ieee` auto-loads `Examples/ieee.k`
|
|
172
|
-
- `utf8`, `json`, `unit` auto-load `Examples/core.k`
|
|
174
|
+
Loads a codec module from an ES module file (e.g. `:codec load codecs/int.mjs`). Codecs are identified by their file name, and export `parse(text)` and `print(value)` functions.
|
|
173
175
|
|
|
174
|
-
|
|
176
|
+
### `:codec unload name`
|
|
175
177
|
|
|
176
|
-
|
|
178
|
+
Unloads a previously loaded codec by name (e.g. `:codec unload int.mjs`).
|
|
177
179
|
|
|
178
|
-
|
|
179
|
-
export const name = "utf8";
|
|
180
|
-
export const codes = ["@..."];
|
|
181
|
-
export function parse(text) { /* text -> Value */ }
|
|
182
|
-
export function print(value) { /* Value -> text */ }
|
|
183
|
-
```
|
|
180
|
+
### `:codec list` (or `:codecs`)
|
|
184
181
|
|
|
185
|
-
|
|
186
|
-
property-list pattern that can be canonicalized to a code hash, and that hash is
|
|
187
|
-
recalculated by the REPL. A universal codec exports `universal = true` instead
|
|
188
|
-
of `codes` or `patterns`.
|
|
182
|
+
Lists all currently loaded codecs and their source files.
|
|
189
183
|
|
|
184
|
+
### `:input <codec.mjs> [text]`
|
|
190
185
|
|
|
191
|
-
|
|
186
|
+
Parses external input using the specified codec file.
|
|
187
|
+
|
|
188
|
+
- **Interactive mode**: `:input <codec.mjs>` switches the REPL prompt to `<codec.mjs>> `, and the next input line is parsed using that codec:
|
|
189
|
+
```text
|
|
190
|
+
> :input json.mjs
|
|
191
|
+
json.mjs> {"hello": "world"}
|
|
192
|
+
{...} ?<{...}>
|
|
193
|
+
json.mjs: {"hello":"world"}
|
|
194
|
+
```
|
|
192
195
|
|
|
193
|
-
|
|
196
|
+
- **One-line mode**: `:input <codec.mjs> <text>` immediately parses `<text>`:
|
|
197
|
+
```text
|
|
198
|
+
> :input int.mjs 42
|
|
199
|
+
{}|_|0|1|0|1|0|1|+ ?<{} _, ...>
|
|
200
|
+
int.mjs: 42
|
|
194
201
|
|
|
195
|
-
|
|
202
|
+
> :input int.mjs [0,1,2]
|
|
203
|
+
{...} ?<{...}>
|
|
204
|
+
int.mjs: [0,1,2]
|
|
205
|
+
```
|
|
196
206
|
|
|
197
|
-
|
|
207
|
+
Entering `:input` with no arguments displays usage and a list of loaded codecs.
|
|
198
208
|
|
|
199
|
-
|
|
209
|
+
## Evaluation Engines: WASM and JS
|
|
200
210
|
|
|
201
|
-
|
|
211
|
+
The REPL supports two evaluation engines:
|
|
202
212
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
213
|
+
- **`wasm`** (default): Compiles k expressions into WebAssembly bytecode via polymorphic kVM lowering. Provides near-native execution speed, tail-call optimization (TCO), and stack safety on deep recursion.
|
|
214
|
+
- **`js`**: The tree-walking JavaScript interpreter. Provides near-zero compilation latency (interprets parsed definitions directly) and convenient runtime inspection.
|
|
215
|
+
|
|
216
|
+
### Engine Commands
|
|
217
|
+
|
|
218
|
+
- `:engine` — displays active engine (`wasm` or `js`)
|
|
219
|
+
- `:engine wasm` (or `:wasm`) — switches to the WebAssembly engine
|
|
220
|
+
- `:engine js` (or `:js`) — switches to the JavaScript engine
|
|
221
|
+
|
|
222
|
+
### CLI Options
|
|
223
|
+
|
|
224
|
+
When starting `k-repl`, choose the engine with:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
k-repl --engine=js # or k-repl --js
|
|
228
|
+
k-repl --engine=wasm # or k-repl --wasm (default)
|
|
207
229
|
```
|
|
208
230
|
|
|
209
231
|
## Timing and Profiling
|
|
210
232
|
|
|
211
233
|
Timing reporting is enabled by default for all evaluations, reporting elapsed time
|
|
212
|
-
broken down into compilation
|
|
234
|
+
broken down into compilation and runtime execution, annotated with the active engine:
|
|
213
235
|
|
|
214
236
|
```text
|
|
215
237
|
> {10 int x, 5 int y} plus
|
|
216
238
|
{}|_|1|1|1|1|+ ?<{} _, ...>
|
|
217
|
-
/* comp: 12.4ms, exec: 0.8ms */
|
|
239
|
+
/* comp: 12.4ms, exec: 0.8ms (wasm) */
|
|
218
240
|
```
|
|
219
241
|
|
|
220
|
-
### `:time expr`
|
|
221
|
-
|
|
222
|
-
Evaluates `expr` with explicit timing and records performance metrics in session state.
|
|
223
|
-
|
|
224
242
|
## Web REPL (Studio)
|
|
225
243
|
|
|
226
244
|
The repository provides a standalone, single-file browser REPL: `repl.html`.
|
|
@@ -272,8 +290,8 @@ Evaluated values print in k syntax together with the inferred envelope:
|
|
|
272
290
|
> inc = | +1;
|
|
273
291
|
> {} | 0
|
|
274
292
|
{}|0 ?<{} 0, ...>
|
|
275
|
-
> :
|
|
276
|
-
inc
|
|
293
|
+
> :rel inc
|
|
294
|
+
inc = | +1; -- @...
|
|
277
295
|
> :klib nat.klib
|
|
278
296
|
saved nat.klib
|
|
279
297
|
> :ko inc.ko inc
|
package/Examples/arithmetics.k
CHANGED
package/Examples/poly.k
CHANGED
|
@@ -65,3 +65,49 @@ zip = <
|
|
|
65
65
|
{.xs cdr xs, .ys cdr ys} zip cdr
|
|
66
66
|
} cons
|
|
67
67
|
>;
|
|
68
|
+
|
|
69
|
+
-- Gen list of intgers: n -> [0, 1, ..., n]
|
|
70
|
+
gen_list = {() n, nil list} gen_list_;
|
|
71
|
+
gen_list_ = <
|
|
72
|
+
{.n neg? if, .list then} .then,
|
|
73
|
+
{.n dec n, {.n car, .list cdr} cons list} gen_list_
|
|
74
|
+
>;
|
|
75
|
+
|
|
76
|
+
-- Identity filter on {x, y} when x is greater than y: ?{X x, X y}=Y -> ?Y
|
|
77
|
+
gt? =
|
|
78
|
+
<
|
|
79
|
+
{.x/- if1, .y/+ if2, () then} .then,
|
|
80
|
+
{{.x/- y, .y/- x} GT if, () then} .then,
|
|
81
|
+
{{.x/+ x, .y/+ y} GT if, () then} .then
|
|
82
|
+
>
|
|
83
|
+
;
|
|
84
|
+
|
|
85
|
+
-- Split list into two halves by alternating elements: xs -> {first, second}
|
|
86
|
+
split = {() xs, nil first, nil second} _split;
|
|
87
|
+
_split = <
|
|
88
|
+
{.xs /nil if, {.first first, .second second} then} .then,
|
|
89
|
+
{.xs cdr /nil if, {{.xs car car, .first cdr} cons first, .second second} then} .then,
|
|
90
|
+
{.xs cdr cdr xs, {.xs car car, .first cdr} cons first, {.xs cdr car car, .second cdr} cons second} _split
|
|
91
|
+
>;
|
|
92
|
+
|
|
93
|
+
-- Merge two sorted lists: {xs, ys} -> list
|
|
94
|
+
merge = <
|
|
95
|
+
{.xs /nil if, .ys then} .then,
|
|
96
|
+
{.ys /nil if, .xs then} .then,
|
|
97
|
+
{
|
|
98
|
+
{.xs car x, .ys car y} gt? if,
|
|
99
|
+
{.ys car car, {.xs xs, .ys cdr ys} merge cdr} cons then
|
|
100
|
+
} .then,
|
|
101
|
+
{.xs car car, {.xs cdr xs, .ys ys} merge cdr} cons
|
|
102
|
+
>;
|
|
103
|
+
|
|
104
|
+
-- Sort a list in ascending order (merge sort): xs -> list
|
|
105
|
+
sort = list? <
|
|
106
|
+
/nil nil,
|
|
107
|
+
/cons <
|
|
108
|
+
{.cdr /nil if, .car singleton then} .then,
|
|
109
|
+
|cons split {.first sort xs, .second sort ys} merge
|
|
110
|
+
>
|
|
111
|
+
> list?;
|
|
112
|
+
|
|
113
|
+
|
|
@@ -563,6 +563,7 @@ k_value *k_rt_compact(k_rt *rt, k_value *root, k_rt_checkpoint *mark_ptr) {
|
|
|
563
563
|
|
|
564
564
|
if (!is_after_mark(rt, root, *mark_ptr)) {
|
|
565
565
|
rt_rewind_and_free(rt, *mark_ptr);
|
|
566
|
+
*mark_ptr = k_rt_mark(rt);
|
|
566
567
|
return root;
|
|
567
568
|
}
|
|
568
569
|
|
|
@@ -580,6 +581,7 @@ k_value *k_rt_compact(k_rt *rt, k_value *root, k_rt_checkpoint *mark_ptr) {
|
|
|
580
581
|
if (staging_used == 0) {
|
|
581
582
|
free(staging);
|
|
582
583
|
if (fixups.offsets != NULL) free(fixups.offsets);
|
|
584
|
+
*mark_ptr = k_rt_mark(rt);
|
|
583
585
|
return staged_root;
|
|
584
586
|
}
|
|
585
587
|
|
|
@@ -603,6 +605,8 @@ k_value *k_rt_compact(k_rt *rt, k_value *root, k_rt_checkpoint *mark_ptr) {
|
|
|
603
605
|
free(staging);
|
|
604
606
|
if (fixups.offsets != NULL) free(fixups.offsets);
|
|
605
607
|
|
|
608
|
+
*mark_ptr = k_rt_mark(rt);
|
|
609
|
+
|
|
606
610
|
return new_root;
|
|
607
611
|
}
|
|
608
612
|
|
|
@@ -438,7 +438,7 @@ function hasSelfTailCall(insts, funcName, isTail = true) {
|
|
|
438
438
|
for (let i = 0; i < insts.length; i++) {
|
|
439
439
|
const inst = insts[i];
|
|
440
440
|
if (isTail && isSelfTailCall(insts, i, funcName)) return true;
|
|
441
|
-
if (inst.branches) {
|
|
441
|
+
if (inst.op === "union" && inst.branches) {
|
|
442
442
|
const unionIsTail = isTail && tailValueAfter(insts, i + 1, inst.dest);
|
|
443
443
|
for (let bIdx = 0; bIdx < inst.branches.length; bIdx++) {
|
|
444
444
|
if (hasSelfTailCall(inst.branches[bIdx].body, funcName, unionIsTail)) return true;
|
|
@@ -721,7 +721,7 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
|
|
|
721
721
|
}
|
|
722
722
|
case "call": {
|
|
723
723
|
const v = getReg(inst.src);
|
|
724
|
-
if (isSelfTailCall(insts, i, tailRef)) {
|
|
724
|
+
if (isTopLevel && !returnTarget && isSelfTailCall(insts, i, tailRef)) {
|
|
725
725
|
if (ctx.catchTail) {
|
|
726
726
|
const compacted = lowerRtCompact(ctx, v, "%tail_mark_slot");
|
|
727
727
|
ctx.lines.push(` store ptr ${compacted}, ptr %tail_input_slot`);
|
|
@@ -237,4 +237,23 @@ assert.match(nestedUnionLLVM, /store ptr %union_value\d+, ptr %ptr_slot\d+/);
|
|
|
237
237
|
assert.doesNotMatch(nestedUnionLLVM, /call void @k_product_set_at\(ptr %product\d+/);
|
|
238
238
|
assert.doesNotMatch(nestedUnionLLVM, /ptr false/);
|
|
239
239
|
|
|
240
|
+
const productRecurseObject = decodeObject(compileObjectBuffer(`
|
|
241
|
+
$ list = < {} nil, { {} car, list cdr } cons >;
|
|
242
|
+
foo = $list <
|
|
243
|
+
/nil |nil,
|
|
244
|
+
/cons { .car car, .cdr foo cdr } |cons
|
|
245
|
+
> $list;
|
|
246
|
+
foo
|
|
247
|
+
`, { source: "llvm-product-recurse.k" }));
|
|
248
|
+
const { llvm: productRecurseLLVM } = compileObjectToLLVM(productRecurseObject, {
|
|
249
|
+
relation: "foo",
|
|
250
|
+
inputPattern: [
|
|
251
|
+
["closed-union", [["nil", 1], ["cons", 2]]],
|
|
252
|
+
["closed-product", []],
|
|
253
|
+
["closed-product", [["car", 1], ["cdr", 0]]]
|
|
254
|
+
]
|
|
255
|
+
});
|
|
256
|
+
// Calls inside product fields must not return tail status (i32 2)
|
|
257
|
+
assert.doesNotMatch(productRecurseLLVM, /insertvalue %k_result undef, i32 2, 0/);
|
|
258
|
+
|
|
240
259
|
console.log("OK");
|
|
@@ -397,6 +397,8 @@ export function lowerToWasm(relDef, name, options = {}) {
|
|
|
397
397
|
}
|
|
398
398
|
lines.push(` local.get $tail_loop_mark`);
|
|
399
399
|
lines.push(` call $compact_finish`);
|
|
400
|
+
lines.push(` global.get $arena_free`);
|
|
401
|
+
lines.push(` local.set $tail_loop_mark`);
|
|
400
402
|
lines.push(` end`);
|
|
401
403
|
for (let i = 0; i < N; i++) {
|
|
402
404
|
const branch = sortedBranches[i];
|
|
@@ -598,6 +600,8 @@ export function lowerToWasm(relDef, name, options = {}) {
|
|
|
598
600
|
lines.push(` local.set $${callSrc}`);
|
|
599
601
|
lines.push(` local.get $tail_loop_mark`);
|
|
600
602
|
lines.push(` call $compact_finish`);
|
|
603
|
+
lines.push(` global.get $arena_free`);
|
|
604
|
+
lines.push(` local.set $tail_loop_mark`);
|
|
601
605
|
lines.push(` end`);
|
|
602
606
|
lines.push(` local.get $${callSrc}`);
|
|
603
607
|
lines.push(` local.set $in`);
|
|
@@ -619,6 +619,10 @@ console.log("==> Running End-to-End Peano Addition & Serialization Tests");
|
|
|
619
619
|
const deepRes = exports.add(deepInputPtr);
|
|
620
620
|
assert.equal(deepRes[1], 1, "Tail-recursive Peano addition must handle deep inputs without overflowing the Wasm call stack");
|
|
621
621
|
|
|
622
|
+
const deeperInputPtr = writeAddInputPtrToArena(writeNatPtrToArena(20000), writeNatPtrToArena(0));
|
|
623
|
+
const deeperRes = exports.add(deeperInputPtr);
|
|
624
|
+
assert.equal(deeperRes[1], 1, "Tail-recursive Peano addition must handle 20000 inputs with compaction without call stack overflow");
|
|
625
|
+
|
|
622
626
|
console.log("End-to-End Peano Addition & Serialization tests passed successfully!");
|
|
623
627
|
}
|
|
624
628
|
|