@fraczak/k 7.0.0 → 7.0.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 +14 -0
- package/backend-api.mjs +9 -1
- package/backends/llvm/scripts/run-tests.mjs +5 -0
- package/backends/llvm/src/kvm2llvm.mjs +2 -1
- package/backends/llvm/src/llvm.mjs +18 -25
- package/backends/llvm/tests/integration.sh +2 -2
- package/backends/llvm/tests/test-llvm.mjs +3 -3
- package/backends/llvm/tests/test-open-product-misalignment.mjs +140 -0
- package/backends/wasm/demo_ieee.mjs +2 -3
- package/backends/wasm/runtime.wat +157 -5
- package/backends/wasm/src/kvm2wasm.mjs +57 -34
- package/backends/wasm/src/wasm.mjs +38 -17
- package/backends/wasm/tests/perf-support.mjs +4 -4
- package/backends/wasm/tests/test-wasm.mjs +13 -14
- package/browser/vfs-data.mjs +1 -1
- package/kir.mjs +213 -17
- package/package.json +1 -1
- package/tests/test-repl-html.mjs +15 -0
- package/tests/test-repl.mjs +10 -0
|
@@ -13,7 +13,8 @@ import {
|
|
|
13
13
|
patternToPropertyList,
|
|
14
14
|
propertyListToPattern,
|
|
15
15
|
Value,
|
|
16
|
-
specializeKVM
|
|
16
|
+
specializeKVM,
|
|
17
|
+
specializeObjectRelations
|
|
17
18
|
} from "@fraczak/k/backend-api.mjs";
|
|
18
19
|
import { intersectPropertyListPatterns } from "@fraczak/k/codecs/runtime/codec.mjs";
|
|
19
20
|
import { propertyListToFilter } from "@fraczak/k/codecs/runtime/show-value.mjs";
|
|
@@ -304,7 +305,8 @@ function compileModule(mainRelName, defs) {
|
|
|
304
305
|
|
|
305
306
|
kvmFunc.name = nameMap.get(name) || cleanName(name);
|
|
306
307
|
cleanCallNames(kvmFunc.body, nameMap);
|
|
307
|
-
|
|
308
|
+
const wasmText = lowerToWasm(kvmFunc, kvmFunc.name);
|
|
309
|
+
wats.push(wasmText);
|
|
308
310
|
}
|
|
309
311
|
|
|
310
312
|
return {
|
|
@@ -611,8 +613,10 @@ async function compileWasmArtifactFromObject(object, { entry = null, inputEnvelo
|
|
|
611
613
|
if (!mainRel) throw new Error(`No main relation (${mainRelName}) defined in object`);
|
|
612
614
|
const inputPattern = getPatternPropertyList(mainRel.typePatternGraph, mainRel.def.patterns[0]);
|
|
613
615
|
const outputPattern = getPatternPropertyList(mainRel.typePatternGraph, mainRel.def.patterns[1]);
|
|
614
|
-
|
|
615
|
-
|
|
616
|
+
const typing = classifyTyping(inputPattern, outputPattern);
|
|
617
|
+
if (typing.program === "polymorphic") {
|
|
618
|
+
const specialized = specializeObjectRelations(object, mainRelName, inputEnvelopePattern);
|
|
619
|
+
defs = { rels: specialized.rels, relAlias: specialized.relAlias, compileStats: specialized.compileStats };
|
|
616
620
|
typingMode = "specialized";
|
|
617
621
|
}
|
|
618
622
|
}
|
|
@@ -635,7 +639,9 @@ async function compileWasmArtifact(
|
|
|
635
639
|
const inputPattern = getPatternPropertyList(mainRel.typePatternGraph, mainRel.def.patterns[0]);
|
|
636
640
|
const outputPattern = getPatternPropertyList(mainRel.typePatternGraph, mainRel.def.patterns[1]);
|
|
637
641
|
if (classifyTyping(inputPattern, outputPattern).program === "polymorphic") {
|
|
638
|
-
|
|
642
|
+
const obj = { format: "k-object", rels: defs.rels, relAlias: defs.relAlias, compileStats: defs.compileStats, main: "__main__" };
|
|
643
|
+
const specialized = specializeObjectRelations(obj, "__main__", inputEnvelopePattern);
|
|
644
|
+
defs = { rels: specialized.rels, relAlias: specialized.relAlias, compileStats: specialized.compileStats };
|
|
639
645
|
typingMode = "specialized";
|
|
640
646
|
}
|
|
641
647
|
}
|
|
@@ -723,20 +729,18 @@ function readArenaValue(exports, ptr, pattern, patternNodeId, patternPropertyLis
|
|
|
723
729
|
|
|
724
730
|
if (patternNode.kind === NODE_KIND.OPEN_PRODUCT || patternNode.kind === NODE_KIND.CLOSED_PRODUCT) {
|
|
725
731
|
const N = view.getUint32(frame.ptr + 4, true);
|
|
726
|
-
if (N !== patternNode.edges.length) {
|
|
727
|
-
throw new Error(`Cannot decode product pointer ${frame.ptr}: arena field count ${N} does not match output pattern`);
|
|
728
|
-
}
|
|
729
732
|
const product = {};
|
|
730
733
|
frame.assign(Value.product(product, patternForNode(patternPropertyList, frame.patternNodeId)));
|
|
731
734
|
for (let i = N - 1; i >= 0; i--) {
|
|
732
|
-
const
|
|
733
|
-
const
|
|
734
|
-
const
|
|
735
|
+
const tagId = view.getUint32(frame.ptr + 8 + 4 * i, true);
|
|
736
|
+
const label = tags.getTag(tagId) || patternNode.edges[i]?.label;
|
|
737
|
+
const edge = patternNode.edges.find((candidate) => candidate.label === label);
|
|
738
|
+
const childPtr = view.getUint32(frame.ptr + 8 + 4 * N + 4 * i, true);
|
|
735
739
|
stack.push({
|
|
736
740
|
ptr: wasmPtr(childPtr),
|
|
737
|
-
patternNodeId: edge
|
|
741
|
+
patternNodeId: edge?.target ?? null,
|
|
738
742
|
assign(value) {
|
|
739
|
-
product[
|
|
743
|
+
product[label] = value;
|
|
740
744
|
}
|
|
741
745
|
});
|
|
742
746
|
}
|
|
@@ -787,8 +791,9 @@ function writeValueToArena(exports, value, pattern, patternNodeId, arenaValues,
|
|
|
787
791
|
view.setUint32(frame.ptr, 8 + 8 * N, true);
|
|
788
792
|
view.setUint32(frame.ptr + 4, N, true);
|
|
789
793
|
for (let i = 0; i < N; i++) {
|
|
794
|
+
const tagId = tags.getId(frame.fields[i].label);
|
|
790
795
|
const offset = 8 + 4 * N + 4 * i;
|
|
791
|
-
view.setUint32(frame.ptr + 8 + 4 * i,
|
|
796
|
+
view.setUint32(frame.ptr + 8 + 4 * i, tagId, true);
|
|
792
797
|
view.setUint32(frame.ptr + offset, frame.childPtrs[i], true);
|
|
793
798
|
}
|
|
794
799
|
arenaValues.set(frame.ptr, valueWithPatternNode(frame.value, patternPropertyList, frame.patternNodeId));
|
|
@@ -820,6 +825,13 @@ function writeValueToArena(exports, value, pattern, patternNodeId, arenaValues,
|
|
|
820
825
|
? Object.keys(frame.value.product)
|
|
821
826
|
.sort()
|
|
822
827
|
.map((label) => ({ label, patternNodeId: frame.patternNodeId }))
|
|
828
|
+
: isOpenProduct
|
|
829
|
+
? Object.keys(frame.value.product)
|
|
830
|
+
.sort()
|
|
831
|
+
.map((label) => {
|
|
832
|
+
const edge = patternNode.edges.find((e) => e.label === label);
|
|
833
|
+
return { label, patternNodeId: edge ? edge.target : null };
|
|
834
|
+
})
|
|
823
835
|
: patternNode.edges.map((edge) => ({ label: edge.label, patternNodeId: edge.target }));
|
|
824
836
|
const fieldLabels = new Set(fields.map(({ label }) => label));
|
|
825
837
|
if (isClosedProduct) {
|
|
@@ -829,9 +841,17 @@ function writeValueToArena(exports, value, pattern, patternNodeId, arenaValues,
|
|
|
829
841
|
}
|
|
830
842
|
}
|
|
831
843
|
}
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
844
|
+
if (isOpenProduct) {
|
|
845
|
+
for (const edge of patternNode.edges) {
|
|
846
|
+
if (!Object.hasOwn(frame.value.product, edge.label)) {
|
|
847
|
+
throw new Error(`Product field '${edge.label}' is required by input pattern node ${frame.patternNodeId}`);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
} else {
|
|
851
|
+
for (const { label } of fields) {
|
|
852
|
+
if (!Object.hasOwn(frame.value.product, label)) {
|
|
853
|
+
throw new Error(`Product field '${label}' is required by input pattern node ${frame.patternNodeId}`);
|
|
854
|
+
}
|
|
835
855
|
}
|
|
836
856
|
}
|
|
837
857
|
|
|
@@ -841,6 +861,7 @@ function writeValueToArena(exports, value, pattern, patternNodeId, arenaValues,
|
|
|
841
861
|
finishProduct: true,
|
|
842
862
|
value: frame.value,
|
|
843
863
|
patternNodeId: frame.patternNodeId,
|
|
864
|
+
fields,
|
|
844
865
|
ptr,
|
|
845
866
|
childPtrs,
|
|
846
867
|
assign: frame.assign
|
|
@@ -200,8 +200,7 @@ export function readArenaValue(exports, rootPtr, pattern, rootPatternNodeId, pat
|
|
|
200
200
|
|
|
201
201
|
for (let i = N - 1; i >= 0; i--) {
|
|
202
202
|
const edge = patternNode.edges[i];
|
|
203
|
-
const
|
|
204
|
-
const childPtr = view.getUint32(ptr + offsetVal, true);
|
|
203
|
+
const childPtr = view.getUint32(ptr + 8 + 4 * N + 4 * i, true);
|
|
205
204
|
const label = edge.label;
|
|
206
205
|
stack.push({
|
|
207
206
|
ptr: childPtr,
|
|
@@ -253,14 +252,14 @@ export function writeValueToArena(exports, rootValue, pattern, rootPatternNodeId
|
|
|
253
252
|
const frame = stack.pop();
|
|
254
253
|
|
|
255
254
|
if (frame.finishProduct) {
|
|
256
|
-
const { ptr, totalSize, N, childPtrs, assign } = frame;
|
|
255
|
+
const { ptr, totalSize, N, keys, childPtrs, assign } = frame;
|
|
257
256
|
const view = new DataView(exports.memory.buffer);
|
|
258
257
|
view.setUint32(ptr, totalSize, true);
|
|
259
258
|
view.setUint32(ptr + 4, N, true);
|
|
260
259
|
|
|
261
260
|
for (let i = 0; i < N; i++) {
|
|
262
261
|
const offsetVal = 8 + 4 * N + 4 * i;
|
|
263
|
-
view.setUint32(ptr + 8 + 4 * i,
|
|
262
|
+
view.setUint32(ptr + 8 + 4 * i, getTagId(keys[i]), true);
|
|
264
263
|
view.setUint32(ptr + offsetVal, childPtrs[i], true);
|
|
265
264
|
}
|
|
266
265
|
assign(ptr);
|
|
@@ -294,6 +293,7 @@ export function writeValueToArena(exports, rootValue, pattern, rootPatternNodeId
|
|
|
294
293
|
ptr,
|
|
295
294
|
totalSize,
|
|
296
295
|
N,
|
|
296
|
+
keys,
|
|
297
297
|
childPtrs,
|
|
298
298
|
assign
|
|
299
299
|
});
|
|
@@ -194,7 +194,7 @@ console.log("==> Running Product Operations Integration Tests");
|
|
|
194
194
|
for (let i = 0; i < N; i++) {
|
|
195
195
|
const offsetVal = 8 + 4 * N + 4 * i;
|
|
196
196
|
const val = fieldsMap[keys[i]];
|
|
197
|
-
view.setUint32(ptr + 8 + 4 * i,
|
|
197
|
+
view.setUint32(ptr + 8 + 4 * i, getTagId(keys[i]), true);
|
|
198
198
|
view.setUint32(ptr + offsetVal, val, true);
|
|
199
199
|
}
|
|
200
200
|
return ptr;
|
|
@@ -215,8 +215,8 @@ console.log("==> Running Product Operations Integration Tests");
|
|
|
215
215
|
const view = new DataView(exports.memory.buffer);
|
|
216
216
|
assert.equal(view.getUint32(ptrOut, true), 24, "Product total size should be 24");
|
|
217
217
|
assert.equal(view.getUint32(ptrOut + 4, true), 2, "Product field count should be 2");
|
|
218
|
-
assert.equal(view.getUint32(ptrOut + 8, true),
|
|
219
|
-
assert.equal(view.getUint32(ptrOut + 12, true),
|
|
218
|
+
assert.equal(view.getUint32(ptrOut + 8, true), getTagId("x"), "TagId of first field (x)");
|
|
219
|
+
assert.equal(view.getUint32(ptrOut + 12, true), getTagId("y"), "TagId of second field (y)");
|
|
220
220
|
|
|
221
221
|
// The first field value should be the projection of x from ptrIn (101)
|
|
222
222
|
const valXPtr = view.getUint32(ptrOut + 16, true);
|
|
@@ -443,12 +443,12 @@ console.log("==> Running End-to-End Peano Addition & Serialization Tests");
|
|
|
443
443
|
const runtimeWat = fs.readFileSync("runtime.wat", "utf8");
|
|
444
444
|
|
|
445
445
|
const script = `
|
|
446
|
-
|
|
447
|
-
0 = {} | 0
|
|
448
|
-
inc = | +1
|
|
449
|
-
dec =
|
|
450
|
-
add =
|
|
451
|
-
{ . x dec x, . y inc y } add,
|
|
446
|
+
nat = ?< {} 0, nat +1 >;
|
|
447
|
+
0 = {} | 0 nat;
|
|
448
|
+
inc = | +1 nat;
|
|
449
|
+
dec = nat / +1;
|
|
450
|
+
add = ?{ nat x, nat y } = X <
|
|
451
|
+
{ . x dec x, . y inc y } ?X add,
|
|
452
452
|
. y
|
|
453
453
|
>;
|
|
454
454
|
()
|
|
@@ -483,8 +483,7 @@ console.log("==> Running End-to-End Peano Addition & Serialization Tests");
|
|
|
483
483
|
|
|
484
484
|
for (let i = 0; i < N; i++) {
|
|
485
485
|
const edge = patternNode.edges[i];
|
|
486
|
-
const
|
|
487
|
-
const childPtr = view.getUint32(ptr + offsetVal, true);
|
|
486
|
+
const childPtr = view.getUint32(ptr + 8 + 4 * N + 4 * i, true);
|
|
488
487
|
productObj[edge.label] = readArenaValue(childPtr, pattern, edge.target);
|
|
489
488
|
}
|
|
490
489
|
return Value.product(productObj, pattern);
|
|
@@ -530,7 +529,7 @@ console.log("==> Running End-to-End Peano Addition & Serialization Tests");
|
|
|
530
529
|
|
|
531
530
|
for (let i = 0; i < N; i++) {
|
|
532
531
|
const offsetVal = 8 + 4 * N + 4 * i;
|
|
533
|
-
view.setUint32(ptr + 8 + 4 * i,
|
|
532
|
+
view.setUint32(ptr + 8 + 4 * i, getTagId(keys[i]), true);
|
|
534
533
|
view.setUint32(ptr + offsetVal, childPtrs[i], true);
|
|
535
534
|
}
|
|
536
535
|
return ptr;
|
|
@@ -580,8 +579,8 @@ console.log("==> Running End-to-End Peano Addition & Serialization Tests");
|
|
|
580
579
|
const view = new DataView(exports.memory.buffer);
|
|
581
580
|
view.setUint32(ptr, 24, true);
|
|
582
581
|
view.setUint32(ptr + 4, 2, true);
|
|
583
|
-
view.setUint32(ptr + 8,
|
|
584
|
-
view.setUint32(ptr + 12,
|
|
582
|
+
view.setUint32(ptr + 8, getTagId("x"), true);
|
|
583
|
+
view.setUint32(ptr + 12, getTagId("y"), true);
|
|
585
584
|
view.setUint32(ptr + 16, xPtr, true);
|
|
586
585
|
view.setUint32(ptr + 20, yPtr, true);
|
|
587
586
|
return ptr;
|
package/browser/vfs-data.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Auto-generated by scripts/build-repl-html.mjs - DO NOT EDIT MANUALLY
|
|
2
2
|
export const vfsData = {
|
|
3
|
-
"backends/wasm/runtime.wat": "(module\n (memory (export \"memory\") 1)\n\n ;; Start allocation at byte offset 1024 to leave a safety margin and avoid returning 0 (nullptr)\n (global $arena_free (export \"arena_free\") (mut i32) (i32.const 1024))\n ;; Maximum address of the currently allocated pages (1 page = 64KB)\n (global $arena_max (export \"arena_max\") (mut i32) (i32.const 65536))\n ;; Out-of-memory status flags\n (global $oom_flag (export \"oom_flag\") (mut i32) (i32.const 0))\n (global $oom_size (export \"oom_size\") (mut i32) (i32.const 0))\n\n ;; Bump allocator function: aligns allocation size to 8 bytes, grows memory if needed, and returns the pointer\n (func $alloc (export \"alloc\") (param $size i32) (result i32)\n (local $old_free i32)\n (local $new_free i32)\n (local $pages_needed i32)\n (local $grow_res i32)\n\n ;; Check if size exceeds maximum allocatable size\n local.get $size\n i32.const -8\n i32.gt_u\n if\n i32.const 1\n global.set $oom_flag\n local.get $size\n global.set $oom_size\n unreachable\n end\n\n ;; 1. Align size to 8 bytes: size = (size + 7) & ~8\n local.get $size\n i32.const 7\n i32.add\n i32.const -8\n i32.and\n local.set $size\n\n ;; Get current free pointer\n global.get $arena_free\n local.set $old_free\n\n ;; Calculate proposed new free pointer\n local.get $old_free\n local.get $size\n i32.add\n local.set $new_free\n\n ;; Check for 32-bit unsigned overflow\n local.get $new_free\n local.get $old_free\n i32.lt_u\n if\n i32.const 1\n global.set $oom_flag\n local.get $size\n global.set $oom_size\n unreachable\n end\n\n ;; 2. Check if we exceed current memory size\n local.get $new_free\n global.get $arena_max\n i32.gt_u\n if\n ;; Calculate how many additional pages we need to grow\n local.get $new_free\n global.get $arena_max\n i32.sub\n i32.const 65535\n i32.add\n i32.const 65536\n i32.div_u\n local.set $pages_needed\n\n ;; Try to grow memory by $pages_needed\n local.get $pages_needed\n memory.grow\n local.set $grow_res\n\n ;; If grow returns -1, we are out of memory\n local.get $grow_res\n i32.const -1\n i32.eq\n if\n i32.const 1\n global.set $oom_flag\n local.get $size\n global.set $oom_size\n unreachable\n end\n\n ;; Update arena_max: arena_max = arena_max + pages_needed * 65536\n global.get $arena_max\n local.get $pages_needed\n i32.const 65536\n i32.mul\n i32.add\n global.set $arena_max\n end\n\n ;; 3. Perform the allocation\n local.get $new_free\n global.set $arena_free\n\n local.get $old_free\n )\n\n ;; Return a checkpoint that can be restored after a temporary result is consumed.\n (func $arena_mark (export \"arena_mark\") (result i32)\n global.get $arena_free\n )\n\n ;; Bulk-free allocations made after a checkpoint. Memory pages remain available for reuse.\n (func $arena_reset (export \"arena_reset\") (param $mark i32)\n local.get $mark\n i32.const 1024\n i32.lt_u\n if\n unreachable\n end\n\n local.get $mark\n global.get $arena_free\n i32.gt_u\n if\n unreachable\n end\n\n local.get $mark\n global.set $arena_free\n )\n\n ;; Compaction staging globals\n (global $compact_buf_start (mut i32) (i32.const 0))\n (global $compact_buf_free (mut i32) (i32.const 0))\n\n ;; Ensure memory capacity in staging buffer\n (func $ensure_buf_capacity (param $size i32)\n (local $needed i32)\n (local $pages i32)\n (local $grow_res i32)\n\n global.get $compact_buf_free\n local.get $size\n i32.add\n local.set $needed\n\n ;; Check for 32-bit overflow\n local.get $needed\n global.get $compact_buf_free\n i32.lt_u\n if\n i32.const 1\n global.set $oom_flag\n local.get $size\n global.set $oom_size\n unreachable\n end\n\n local.get $needed\n global.get $arena_max\n i32.gt_u\n if\n local.get $needed\n global.get $arena_max\n i32.sub\n i32.const 65535\n i32.add\n i32.const 65536\n i32.div_u\n local.set $pages\n\n local.get $pages\n memory.grow\n local.set $grow_res\n\n local.get $grow_res\n i32.const -1\n i32.eq\n if\n i32.const 1\n global.set $oom_flag\n local.get $size\n global.set $oom_size\n unreachable\n end\n\n global.get $arena_max\n local.get $pages\n i32.const 65536\n i32.mul\n i32.add\n global.set $arena_max\n end\n )\n\n ;; Initialize compaction staging buffer at current arena top\n (func $compact_start (export \"compact_start\")\n global.get $arena_free\n global.set $compact_buf_start\n global.get $arena_free\n global.set $compact_buf_free\n )\n\n ;; Compact an object graph reachable from $root down toward $mark\n (func $compact_obj (export \"compact_obj\") (param $root i32) (param $mark i32) (result i32)\n (local $curr i32)\n (local $first_final i32)\n (local $prev_dest i32)\n (local $dest i32)\n (local $final_ptr i32)\n (local $tag i32)\n (local $payload i32)\n (local $size i32)\n (local $alignedSize i32)\n (local $N i32)\n (local $i i32)\n (local $offsetVal i32)\n (local $fieldPtr i32)\n (local $child_final i32)\n\n local.get $root\n local.set $curr\n i32.const 0\n local.set $prev_dest\n\n (block $break_chain\n (loop $chain_loop\n ;; 1. Check null / empty\n local.get $curr\n i32.eqz\n if\n local.get $prev_dest\n if\n local.get $prev_dest\n i32.const 0\n i32.store offset=8\n end\n br $break_chain\n end\n\n ;; 2. Check if < mark\n local.get $curr\n local.get $mark\n i32.lt_u\n if\n local.get $prev_dest\n if\n local.get $prev_dest\n local.get $curr\n i32.store offset=8\n end\n local.get $first_final\n i32.eqz\n if\n local.get $curr\n local.set $first_final\n end\n br $break_chain\n end\n\n ;; 3. Check if forwarded\n local.get $curr\n i32.load offset=0\n local.set $size\n\n local.get $size\n i32.const -1\n i32.eq\n if\n local.get $curr\n i32.load offset=4\n local.set $final_ptr\n local.get $prev_dest\n if\n local.get $prev_dest\n local.get $final_ptr\n i32.store offset=8\n end\n local.get $first_final\n i32.eqz\n if\n local.get $final_ptr\n local.set $first_final\n end\n br $break_chain\n end\n\n ;; 4. Compute final_ptr = mark + (compact_buf_free - compact_buf_start)\n local.get $mark\n global.get $compact_buf_free\n global.get $compact_buf_start\n i32.sub\n i32.add\n local.set $final_ptr\n\n local.get $first_final\n i32.eqz\n if\n local.get $final_ptr\n local.set $first_final\n end\n\n ;; If prev_dest != 0, patch its payload to final_ptr\n local.get $prev_dest\n if\n local.get $prev_dest\n local.get $final_ptr\n i32.store offset=8\n end\n\n ;; 5. Check if Variant (size == 12)\n local.get $size\n i32.const 12\n i32.eq\n if\n i32.const 16\n call $ensure_buf_capacity\n\n local.get $curr\n i32.load offset=4\n local.set $tag\n local.get $curr\n i32.load offset=8\n local.set $payload\n\n local.get $curr\n i32.const -1\n i32.store offset=0\n local.get $curr\n local.get $final_ptr\n i32.store offset=4\n\n global.get $compact_buf_free\n local.set $dest\n global.get $compact_buf_free\n i32.const 16\n i32.add\n global.set $compact_buf_free\n\n local.get $dest\n i32.const 12\n i32.store offset=0\n local.get $dest\n local.get $tag\n i32.store offset=4\n\n local.get $dest\n local.set $prev_dest\n local.get $payload\n local.set $curr\n br $chain_loop\n end\n\n ;; 6. Product handling (size != 12)\n local.get $size\n i32.const 7\n i32.add\n i32.const -8\n i32.and\n local.set $alignedSize\n\n local.get $alignedSize\n call $ensure_buf_capacity\n\n local.get $curr\n i32.load offset=4\n local.set $N\n\n local.get $curr\n i32.const -1\n i32.store offset=0\n local.get $curr\n local.get $final_ptr\n i32.store offset=4\n\n global.get $compact_buf_free\n local.set $dest\n global.get $compact_buf_free\n local.get $alignedSize\n i32.add\n global.set $compact_buf_free\n\n local.get $dest\n local.get $size\n i32.store offset=0\n local.get $dest\n local.get $N\n i32.store offset=4\n\n i32.const 0\n local.set $i\n (block $break_prod\n (loop $loop_prod\n local.get $i\n local.get $N\n i32.ge_u\n br_if $break_prod\n\n local.get $curr\n i32.const 8\n local.get $i\n i32.const 4\n i32.mul\n i32.add\n i32.add\n i32.load\n local.set $offsetVal\n\n local.get $dest\n i32.const 8\n local.get $i\n i32.const 4\n i32.mul\n i32.add\n i32.add\n local.get $offsetVal\n i32.store\n\n local.get $curr\n local.get $offsetVal\n i32.add\n i32.load\n local.set $fieldPtr\n\n local.get $fieldPtr\n local.get $mark\n call $compact_obj\n local.set $child_final\n\n local.get $dest\n local.get $offsetVal\n i32.add\n local.get $child_final\n i32.store\n\n local.get $i\n i32.const 1\n i32.add\n local.set $i\n br $loop_prod\n )\n )\n br $break_chain\n )\n )\n\n local.get $first_final\n )\n\n ;; Finalize compaction by copying staging buffer into mark and resetting arena_free\n (func $compact_finish (export \"compact_finish\") (param $mark i32)\n (local $total_size i32)\n global.get $compact_buf_free\n global.get $compact_buf_start\n i32.sub\n local.set $total_size\n\n local.get $total_size\n i32.const 0\n i32.gt_u\n if\n local.get $mark\n global.get $compact_buf_start\n local.get $total_size\n memory.copy\n end\n\n local.get $mark\n local.get $total_size\n i32.add\n global.set $arena_free\n )\n)\n",
|
|
3
|
+
"backends/wasm/runtime.wat": "(module\n (memory (export \"memory\") 1)\n\n ;; Start allocation at byte offset 1024 to leave a safety margin and avoid returning 0 (nullptr)\n (global $arena_free (export \"arena_free\") (mut i32) (i32.const 1024))\n ;; Maximum address of the currently allocated pages (1 page = 64KB)\n (global $arena_max (export \"arena_max\") (mut i32) (i32.const 65536))\n ;; Out-of-memory status flags\n (global $oom_flag (export \"oom_flag\") (mut i32) (i32.const 0))\n (global $oom_size (export \"oom_size\") (mut i32) (i32.const 0))\n\n ;; Bump allocator function: aligns allocation size to 8 bytes, grows memory if needed, and returns the pointer\n (func $alloc (export \"alloc\") (param $size i32) (result i32)\n (local $old_free i32)\n (local $new_free i32)\n (local $pages_needed i32)\n (local $grow_res i32)\n\n ;; Check if size exceeds maximum allocatable size\n local.get $size\n i32.const -8\n i32.gt_u\n if\n i32.const 1\n global.set $oom_flag\n local.get $size\n global.set $oom_size\n unreachable\n end\n\n ;; 1. Align size to 8 bytes: size = (size + 7) & ~8\n local.get $size\n i32.const 7\n i32.add\n i32.const -8\n i32.and\n local.set $size\n\n ;; Get current free pointer\n global.get $arena_free\n local.set $old_free\n\n ;; Calculate proposed new free pointer\n local.get $old_free\n local.get $size\n i32.add\n local.set $new_free\n\n ;; Check for 32-bit unsigned overflow\n local.get $new_free\n local.get $old_free\n i32.lt_u\n if\n i32.const 1\n global.set $oom_flag\n local.get $size\n global.set $oom_size\n unreachable\n end\n\n ;; 2. Check if we exceed current memory size\n local.get $new_free\n global.get $arena_max\n i32.gt_u\n if\n ;; Calculate how many additional pages we need to grow\n local.get $new_free\n global.get $arena_max\n i32.sub\n i32.const 65535\n i32.add\n i32.const 65536\n i32.div_u\n local.set $pages_needed\n\n ;; Try to grow memory by $pages_needed\n local.get $pages_needed\n memory.grow\n local.set $grow_res\n\n ;; If grow returns -1, we are out of memory\n local.get $grow_res\n i32.const -1\n i32.eq\n if\n i32.const 1\n global.set $oom_flag\n local.get $size\n global.set $oom_size\n unreachable\n end\n\n ;; Update arena_max: arena_max = arena_max + pages_needed * 65536\n global.get $arena_max\n local.get $pages_needed\n i32.const 65536\n i32.mul\n i32.add\n global.set $arena_max\n end\n\n ;; 3. Perform the allocation\n local.get $new_free\n global.set $arena_free\n\n local.get $old_free\n )\n\n ;; Return a checkpoint that can be restored after a temporary result is consumed.\n (func $arena_mark (export \"arena_mark\") (result i32)\n global.get $arena_free\n )\n\n ;; Bulk-free allocations made after a checkpoint. Memory pages remain available for reuse.\n (func $arena_reset (export \"arena_reset\") (param $mark i32)\n local.get $mark\n i32.const 1024\n i32.lt_u\n if\n unreachable\n end\n\n local.get $mark\n global.get $arena_free\n i32.gt_u\n if\n unreachable\n end\n\n local.get $mark\n global.set $arena_free\n )\n\n ;; Compaction staging globals\n (global $compact_buf_start (mut i32) (i32.const 0))\n (global $compact_buf_free (mut i32) (i32.const 0))\n\n ;; Ensure memory capacity in staging buffer\n (func $ensure_buf_capacity (param $size i32)\n (local $needed i32)\n (local $pages i32)\n (local $grow_res i32)\n\n global.get $compact_buf_free\n local.get $size\n i32.add\n local.set $needed\n\n ;; Check for 32-bit overflow\n local.get $needed\n global.get $compact_buf_free\n i32.lt_u\n if\n i32.const 1\n global.set $oom_flag\n local.get $size\n global.set $oom_size\n unreachable\n end\n\n local.get $needed\n global.get $arena_max\n i32.gt_u\n if\n local.get $needed\n global.get $arena_max\n i32.sub\n i32.const 65535\n i32.add\n i32.const 65536\n i32.div_u\n local.set $pages\n\n local.get $pages\n memory.grow\n local.set $grow_res\n\n local.get $grow_res\n i32.const -1\n i32.eq\n if\n i32.const 1\n global.set $oom_flag\n local.get $size\n global.set $oom_size\n unreachable\n end\n\n global.get $arena_max\n local.get $pages\n i32.const 65536\n i32.mul\n i32.add\n global.set $arena_max\n end\n )\n\n ;; Initialize compaction staging buffer at current arena top\n (func $compact_start (export \"compact_start\")\n global.get $arena_free\n global.set $compact_buf_start\n global.get $arena_free\n global.set $compact_buf_free\n )\n\n ;; Compact an object graph reachable from $root down toward $mark\n (func $compact_obj (export \"compact_obj\") (param $root i32) (param $mark i32) (result i32)\n (local $curr i32)\n (local $first_final i32)\n (local $prev_dest i32)\n (local $dest i32)\n (local $final_ptr i32)\n (local $tag i32)\n (local $payload i32)\n (local $size i32)\n (local $alignedSize i32)\n (local $N i32)\n (local $i i32)\n (local $offsetVal i32)\n (local $fieldPtr i32)\n (local $child_final i32)\n\n local.get $root\n local.set $curr\n i32.const 0\n local.set $prev_dest\n\n (block $break_chain\n (loop $chain_loop\n ;; 1. Check null / empty\n local.get $curr\n i32.eqz\n if\n local.get $prev_dest\n if\n local.get $prev_dest\n i32.const 0\n i32.store offset=8\n end\n br $break_chain\n end\n\n ;; 2. Check if < mark\n local.get $curr\n local.get $mark\n i32.lt_u\n if\n local.get $prev_dest\n if\n local.get $prev_dest\n local.get $curr\n i32.store offset=8\n end\n local.get $first_final\n i32.eqz\n if\n local.get $curr\n local.set $first_final\n end\n br $break_chain\n end\n\n ;; 3. Check if forwarded\n local.get $curr\n i32.load offset=0\n local.set $size\n\n local.get $size\n i32.const -1\n i32.eq\n if\n local.get $curr\n i32.load offset=4\n local.set $final_ptr\n local.get $prev_dest\n if\n local.get $prev_dest\n local.get $final_ptr\n i32.store offset=8\n end\n local.get $first_final\n i32.eqz\n if\n local.get $final_ptr\n local.set $first_final\n end\n br $break_chain\n end\n\n ;; 4. Compute final_ptr = mark + (compact_buf_free - compact_buf_start)\n local.get $mark\n global.get $compact_buf_free\n global.get $compact_buf_start\n i32.sub\n i32.add\n local.set $final_ptr\n\n local.get $first_final\n i32.eqz\n if\n local.get $final_ptr\n local.set $first_final\n end\n\n ;; If prev_dest != 0, patch its payload to final_ptr\n local.get $prev_dest\n if\n local.get $prev_dest\n local.get $final_ptr\n i32.store offset=8\n end\n\n ;; 5. Check if Variant (size == 12)\n local.get $size\n i32.const 12\n i32.eq\n if\n i32.const 16\n call $ensure_buf_capacity\n\n local.get $curr\n i32.load offset=4\n local.set $tag\n local.get $curr\n i32.load offset=8\n local.set $payload\n\n local.get $curr\n i32.const -1\n i32.store offset=0\n local.get $curr\n local.get $final_ptr\n i32.store offset=4\n\n global.get $compact_buf_free\n local.set $dest\n global.get $compact_buf_free\n i32.const 16\n i32.add\n global.set $compact_buf_free\n\n local.get $dest\n i32.const 12\n i32.store offset=0\n local.get $dest\n local.get $tag\n i32.store offset=4\n\n local.get $dest\n local.set $prev_dest\n local.get $payload\n local.set $curr\n br $chain_loop\n end\n\n ;; 6. Product handling (size != 12)\n local.get $size\n i32.const 7\n i32.add\n i32.const -8\n i32.and\n local.set $alignedSize\n\n local.get $alignedSize\n call $ensure_buf_capacity\n\n local.get $curr\n i32.load offset=4\n local.set $N\n\n local.get $curr\n i32.const -1\n i32.store offset=0\n local.get $curr\n local.get $final_ptr\n i32.store offset=4\n\n global.get $compact_buf_free\n local.set $dest\n global.get $compact_buf_free\n local.get $alignedSize\n i32.add\n global.set $compact_buf_free\n\n local.get $dest\n local.get $size\n i32.store offset=0\n local.get $dest\n local.get $N\n i32.store offset=4\n\n i32.const 0\n local.set $i\n (block $break_prod\n (loop $loop_prod\n local.get $i\n local.get $N\n i32.ge_u\n br_if $break_prod\n\n ;; Copy field tagId at offset 8 + 4*i\n local.get $dest\n i32.const 8\n local.get $i\n i32.const 4\n i32.mul\n i32.add\n i32.add\n local.get $curr\n i32.const 8\n local.get $i\n i32.const 4\n i32.mul\n i32.add\n i32.add\n i32.load\n i32.store\n\n ;; Value slot offset is 8 + 4*N + 4*i\n i32.const 8\n local.get $N\n i32.const 4\n i32.mul\n i32.add\n local.get $i\n i32.const 4\n i32.mul\n i32.add\n local.set $offsetVal\n\n local.get $curr\n local.get $offsetVal\n i32.add\n i32.load\n local.set $fieldPtr\n\n local.get $fieldPtr\n local.get $mark\n call $compact_obj\n local.set $child_final\n\n local.get $dest\n local.get $offsetVal\n i32.add\n local.get $child_final\n i32.store\n\n local.get $i\n i32.const 1\n i32.add\n local.set $i\n br $loop_prod\n )\n )\n br $break_chain\n )\n )\n\n local.get $first_final\n )\n\n ;; Finalize compaction by copying staging buffer into mark and resetting arena_free\n (func $compact_finish (export \"compact_finish\") (param $mark i32)\n (local $total_size i32)\n global.get $compact_buf_free\n global.get $compact_buf_start\n i32.sub\n local.set $total_size\n\n local.get $total_size\n i32.const 0\n i32.gt_u\n if\n local.get $mark\n global.get $compact_buf_start\n local.get $total_size\n memory.copy\n end\n\n local.get $mark\n local.get $total_size\n i32.add\n global.set $arena_free\n )\n\n ;; Lookup product field by tagId with predictedIndex hint\n (func $product_get (export \"product_get\") (param $prod i32) (param $targetTagId i32) (param $predictedIndex i32) (result i32)\n (local $size i32)\n (local $N i32)\n (local $i i32)\n\n ;; 1. Null check\n local.get $prod\n i32.eqz\n if\n i32.const 0\n return\n end\n\n ;; 2. Read totalSize at prod + 0\n local.get $prod\n i32.load offset=0\n local.set $size\n\n ;; 3. Validate product header (not a variant and size >= 8)\n local.get $size\n i32.const 12\n i32.eq\n if\n i32.const 0\n return\n end\n local.get $size\n i32.const 8\n i32.lt_u\n if\n i32.const 0\n return\n end\n\n ;; 4. Read field count N at prod + 4\n local.get $prod\n i32.load offset=4\n local.set $N\n\n ;; 5. Validate size == 8 + 8 * N\n local.get $size\n i32.const 8\n local.get $N\n i32.const 8\n i32.mul\n i32.add\n i32.ne\n if\n i32.const 0\n return\n end\n\n ;; 6. Fast path: check predictedIndex if in range [0, N)\n local.get $predictedIndex\n i32.const 0\n i32.ge_s\n if\n local.get $predictedIndex\n local.get $N\n i32.lt_u\n if\n ;; Load tagId at prod + 8 + 4 * predictedIndex\n local.get $prod\n i32.const 8\n local.get $predictedIndex\n i32.const 4\n i32.mul\n i32.add\n i32.add\n i32.load\n local.get $targetTagId\n i32.eq\n if\n ;; Match! Return value at prod + 8 + 4*N + 4*predictedIndex\n local.get $prod\n i32.const 8\n local.get $N\n i32.const 4\n i32.mul\n i32.add\n local.get $predictedIndex\n i32.const 4\n i32.mul\n i32.add\n i32.add\n i32.load\n return\n end\n end\n end\n\n ;; 7. Scan path: check i = 0 .. N-1\n i32.const 0\n local.set $i\n (block $break_scan\n (loop $loop_scan\n local.get $i\n local.get $N\n i32.ge_u\n br_if $break_scan\n\n ;; Load tagId at prod + 8 + 4 * i\n local.get $prod\n i32.const 8\n local.get $i\n i32.const 4\n i32.mul\n i32.add\n i32.add\n i32.load\n local.get $targetTagId\n i32.eq\n if\n ;; Match! Return value at prod + 8 + 4*N + 4*i\n local.get $prod\n i32.const 8\n local.get $N\n i32.const 4\n i32.mul\n i32.add\n local.get $i\n i32.const 4\n i32.mul\n i32.add\n i32.add\n i32.load\n return\n end\n\n local.get $i\n i32.const 1\n i32.add\n local.set $i\n br $loop_scan\n )\n )\n\n ;; 8. Not found: return 0\n i32.const 0\n )\n)\n",
|
|
4
4
|
"core.k": "-- core.k — the k standard core library\n--\n-- Defines:\n-- 1. bits — canonical binary number / bit-path relation\n-- 2. string — unicode strings relation (used for labels)\n-- 3. pattern — pattern graph relation\n\n---------------------------------------------------------------\n-- §1. Bits\n---------------------------------------------------------------\n\nbit = ?< {} 0, {} 1 >;\n\nbits = ?< {} _, bits 0, bits 1 > = bits;\n\n_ = {}|_;\n\n0 = _ |0;\n1 = _ |1;\n\n---------------------------------------------------------------\n-- §2. Unicode and String\n---------------------------------------------------------------\n\nstring = ? <\n {} nil,\n {\n <\n {\n < {} 0, {} 1 >=Bit 0, Bit 1, Bit 2, Bit 3, Bit 4, Bit 5, Bit 6\n }=Bits7 ascii,\n {\n <\n { Bit 0, Bit 1, Bit 2 }=Bits3 h08_0F,\n Bits7 h10_7F,\n Bits7 h80_CF,\n Bits3 hD0_D7,\n {} hF9,\n { Bit 0 }=Bits1 hFA_FB,\n Bits1 hFC_FD,\n Bits1 hFE_FF\n > hi,\n { Bit 0, Bit 1, Bit 2, Bit 3, Bit 4, Bit 5, Bit 6, Bit 7 }=Bits8 lo\n } bmp_common,\n {\n <\n { Bit 0, Bit 1, Bit 2, Bit 3 } hE0_EF,\n Bits3 hF0_F7,\n {} hF8\n > hi,\n Bits8 lo\n } bmp_private_use,\n {\n Bit 0, Bit 1, Bit 2, Bit 3, Bit 4, Bit 5, Bit 6, Bit 7, Bit 8, Bit 9, Bit 10\n } plane0,\n { Bits8 mid, Bits8 lo } supplementary_plane1,\n {\n <\n Bits1 p02_03,\n { Bit 0, Bit 1 } p04_07,\n Bits3 p08_0F,\n {} p10\n > plane,\n Bits8 mid,\n Bits8 lo\n } supplementary_planes2_16\n > car,\n String cdr\n } cons\n> = String;\n\n---------------------------------------------------------------\n-- §3. Pattern\n---------------------------------------------------------------\n\n-- Pattern: a cons-list of pattern nodes.\n-- Node 0 (first element) is the root.\n-- Edges reference nodes by their index in this list, encoded as bits.\n--\n-- The singleton pattern of pattern itself is a fixed constant.\n-- That constant is the implicit framing for all k wire formats:\n-- to decode any value, first decode a pattern under that constant,\n-- then decode the value under the pattern just obtained.\n\npattern = ? <\n {} nil,\n {\n <\n {} any,\n <\n {} nil,\n {\n {\n <\n {} nil,\n {\n <\n {\n < {} 0, {} 1 >=Bit 0, Bit 1, Bit 2, Bit 3, Bit 4, Bit 5, Bit 6\n }=Bits7 ascii,\n {\n <\n { Bit 0, Bit 1, Bit 2 }=Bits3 h08_0F,\n Bits7 h10_7F,\n Bits7 h80_CF,\n Bits3 hD0_D7,\n {} hF9,\n { Bit 0 }=Bits1 hFA_FB,\n Bits1 hFC_FD,\n Bits1 hFE_FF\n > hi,\n { Bit 0, Bit 1, Bit 2, Bit 3, Bit 4, Bit 5, Bit 6, Bit 7 }=Bits8 lo\n } bmp_common,\n {\n <\n { Bit 0, Bit 1, Bit 2, Bit 3 } hE0_EF,\n Bits3 hF0_F7,\n {} hF8\n > hi,\n Bits8 lo\n } bmp_private_use,\n {\n Bit 0, Bit 1, Bit 2, Bit 3, Bit 4, Bit 5, Bit 6, Bit 7, Bit 8, Bit 9, Bit 10\n } plane0,\n { Bits8 mid, Bits8 lo } supplementary_plane1,\n {\n <\n Bits1 p02_03,\n { Bit 0, Bit 1 } p04_07,\n Bits3 p08_0F,\n {} p10\n > plane,\n Bits8 mid,\n Bits8 lo\n } supplementary_planes2_16\n > car,\n String cdr\n } cons\n > = String label,\n < Bits 0, Bits 1, {} _ > = Bits target\n } car,\n Edges cdr\n } cons\n > = Edges open-product,\n Edges open-union,\n Edges closed-product,\n Edges closed-union\n > car,\n Pattern cdr\n } cons\n> = Pattern;\n\npattern\n",
|
|
5
5
|
"arithmetics.k": "-- Examples for defining arithmetic functions natively\nswap_yes_no = < /yes |no, /no |yes >;\nyes = {}|yes;\nno = yes swap_yes_no;\n\nbits = ?< {} _, bits 0, bits 1 > = bits;\n\n0b = {} | _;\n-- |1 = | 1;\n-- |0 = | 0;\n\n0 = 0b |0;\nsucc = {inv x, 0b y} _inc;\n1 = 0 succ;\n2 = 1 succ;\n3 = 2 succ;\n4 = 3 succ;\n5 = 4 succ;\n6 = 5 succ;\n7 = 6 succ;\n8 = 7 succ;\n9 = 8 succ;\n10 = 9 succ;\n11 = 10 succ;\n12 = 11 succ;\n\nempty = < /_ yes, no >;\nempty? = {empty /yes if, () then} .then;\n\nconcat = {.x inv x, .y y} concat_; \nconcat_ = ?{ <X 0, X 1, {} _>=X x, ... }\n < \n {.x empty? if, .y then} .then,\n < \n {.x /0 x, .y |0 y}, \n {.x /1 x, .y |1 y}\n > concat_ \n >;\ninv = {()x, 0b r} _inv; \n_inv = ?{ <X 0, X 1, {} _>=X x, ... }\n < \n {.x empty? if, .r then} .then,\n < \n {.x /0 x, .r |0 r}, \n {.x /1 x, .r |1 r}\n > _inv\n >;\n\ninOrder = {.x inv x, .y inv y, yes inOrder} inOrder_;\ninOrder_ = ?{ <X 0, X 1, {} _>=X x, ... }\n < \n {.x </0,/1> if1, .y /_ if2, .inOrder swap_yes_no then} .then,\n {.x /_ if, .inOrder then} .then,\n < \n {.x /0 x, .y /0 y, .inOrder inOrder}, \n {.x /1 x, .y /1 y, .inOrder inOrder},\n {.x /0 x, .y /1 y, .inOrder inOrder}, \n {.x /1 y, .y /0 x, .inOrder swap_yes_no inOrder}\n > inOrder_\n >;\n\nGT = {inOrder /no if, () then} . then;\n\nEQ = {inOrder /yes if, {.x y, .y x} inOrder /yes and, () then} .then;\n\n--------------------------------------\nint = ?< <bits 0, bits 1, {} _>=bits \"+\", bits \"-\" >;\n\nremove_leading_zeros = <\n /0 remove_leading_zeros, \n ()\n>;\nnat = remove_leading_zeros <empty? 0, ()>;\nint = nat | '+';\nabs = </'+',/'-'>;\n\npos? = {/'+' remove_leading_zeros /1 if, () then} .then;\nneg? = {/'-' if, () then} .then; \nzero_int? = {/'+' /0 empty? if, () then} .then;\nminus_one? = {/'-' /1 empty? if, () then} .then;\n\ninc = <\n /'+' {inv x, 0b y} _inc nat |'+',\n minus_one? 0 int,\n /'-' {inv x, 0b y} _dec nat |'-'\n>;\n\n_inc = ?{ <X 0, X 1, {} _>=X x, ... } <\n {.x empty? if, .y |1 then} .then,\n {.x /0 x, .y |1 y} concat_, \n {.x /1 x, .y |0 y} _inc\n>; \n\ndec = <\n zero_int? 1 |'-',\n /'+' {inv x, 0b y} _dec nat |'+',\n /'-' {inv x, 0b y} _inc nat |'-'\n>;\n\n_dec = ?{ <X 0, X 1, {} _>=X x, ... } <\n {.x /1 x, .y |0 y} concat_, \n {.x /0 x, .y |1 y} _dec\n>;\n\nc0 = {}|0;\nc1 = {}|1;\n\nplus =\n< \n {.x/'+' inv x, .y/'+' inv y, c0 c } _plus inv nat | '+',\n {.x/'-' inv x, .y/'-' inv y, c0 c } _plus inv nat | '-',\n {.y/'-' x, .x/'+' y} GT {.x inv x, .y inv y, c0 c} _minus inv nat | '-',\n {.x/'+' inv x, .y/'-' inv y, c0 c} _minus inv nat |'+',\n {.x/'-' x, .y/'+' y} GT {.x inv x, .y inv y, c0 c} _minus inv nat | '-',\n {.y/'+' inv x, .x/'-' inv y, c0 c} _minus inv nat | '+'\n>;\n\nswap_sign = <\n zero_int?,\n /'+' | '-',\n /'-' | '+'\n>;\n\nminus =\n {.x x, .y swap_sign y} plus;\n\n_plus = {.x x, .y y, .c c, 0b r} _plus_;\n\n_plus_ =\n -- least significant bit first addition with carry\n ?{ <X 0, X 1, {} _>=X x, ... }\n <\n {.x empty? if, .y empty? and, .c/0 c, .r inv then} .then,\n {.x empty? if, .y empty? and, {.r x, 1 r} _inv then} .then,\n {.x empty? if, .c/0 and, {.r x, .y r} _inv then} .then,\n\n <\n {.x y, .y empty? x, .c c, .r r},\n {<.c/0 0, 1> x, .y y, .x empty? c0 c, .r r},\n\n {.x /0 x, .y /0 y, .c/0 c0 c, .r |0 r},\n {.x /0 x, .y /0 y, .c/1 c0 c, .r |1 r},\n\n {.x /0 x, .y /1 y, .c/0 c0 c, .r |1 r},\n {.x /0 x, .y /1 y, .c/1 c1 c, .r |0 r},\n {.x /1 x, .y /0 y, .c/0 c0 c, .r |1 r},\n {.x /1 x, .y /0 y, .c/1 c1 c, .r |0 r},\n\n {.x /1 x, .y /1 y, .c/0 c1 c, .r |0 r},\n {.x /1 x, .y /1 y, .c/1 c1 c, .r |1 r}\n > _plus_\n >\n;\n\n_minus = {.x x, .y y, .c c, 0b r} _minus_;\n\n_minus_ =\n -- least significant bit first substraction with carry\n ?{ <X 0, X 1, {} _>=X x, ... }\n <\n {.y empty? if, .c/0 and, {.r x, .x r} _inv then} .then,\n <\n {.x/0 x, .y empty? y, .c/1 c1 c, .r |1 r},\n {.x/1 x, .y empty? y, .c/1 c0 c, .r |0 r},\n\n {.x/0 x, .y/0 y, .c/0 c0 c, .r |0 r},\n {.x/0 x, .y/0 y, .c/1 c1 c, .r |1 r}, \n {.x/0 x, .y/1 y, .c/0 c1 c, .r |1 r},\n {.x/0 x, .y/1 y, .c/1 c1 c, .r |0 r},\n {.x/1 x, .y/0 y, .c/0 c0 c, .r |1 r},\n {.x/1 x, .y/0 y, .c/1 c0 c, .r |0 r},\n {.x/1 x, .y/1 y, .c/0 c0 c, .r |0 r},\n {.x/1 x, .y/1 y, .c/1 c1 c, .r |1 r}\n > _minus_\n >\n;\n\nsign_plus = {}|'+';\nsign_minus = {}|'-';\nget_sign = </'+' sign_plus, /'-' sign_minus>;\n\ntimes =\n {\n <\n {.x/'+' if, .y /'+' and} sign_plus, \n {.x/'-' if, .y /'-' and} sign_plus, \n sign_minus\n > sign,\n {\n 0b result,\n .x abs inv _x,\n .y abs y\n } _times result\n }\n <\n {.sign/'+' if, .result inv int then} .then,\n {.sign/'-' if, .result inv int swap_sign then} .then \n >\n;\n\n_times =\n ?{ <Y 0, Y 1, {} _>=Y y, ... }\n <\n { .y empty? if, .result then } .then,\n <\n { \n .result |0 result, \n ._x _x, \n .y /0 y\n },\n { \n {._x x, .result |0 y, c0 c } _plus result, \n ._x _x, \n .y /1 y \n }\n > _times\n >\n;\n\n// ----------------------------------------------------------------------\n// Integer Division: Adaptive Hybrid Algorithm\n// ----------------------------------------------------------------------\n\nshift_in_0 = inv |0 inv nat;\nshift_in_1 = inv |1 inv nat;\nsub_bits = {.x inv x, .y inv y, c0 c} _minus inv nat;\n\n_div_restoring =\n ?{ <X 0, X 1, {} _>=X x, ... }\n <\n {.x empty? if, {.q inv int div, .r rem} then} .then,\n <\n {.x /0 x, .r shift_in_0 r, .y y, .q q},\n {.x /1 x, .r shift_in_1 r, .y y, .q q}\n >\n {\n .x x,\n .y y,\n .q q,\n .r r,\n {.y x, .r y} inOrder test\n }\n <\n {.test /yes if, {.x x, .y y, .q |1 q, {.r x, .y y} sub_bits r} then} .then,\n {.test /no if, {.x x, .y y, .q |0 q, .r r} then} .then\n >\n _div_restoring\n >;\n\n_pad_down = ?{ <X 0, X 1, {} _>=X x, <S 0, S 1, {} _>=S steps, ... }\n <\n {.x empty? if, {._y _y, .steps steps} then} .then,\n {\n .x </0, /1> x,\n ._y |0 _y,\n .steps |0 steps\n } _pad_down\n >;\n\n_align_down = ?{ <X 0, X 1, {} _>=X x, <Y 0, Y 1, {} _>=Y y, ... }\n <\n {\n .x </0, /1> x,\n .y </0, /1> y,\n ._y _y\n } _align_down,\n\n {\n .y empty? if,\n {.x x, ._y _y, 0b steps} _pad_down then\n } .then,\n\n {\n .x empty? if,\n .y empty? and,\n {._y _y, 0b steps} then\n } .then\n >;\n\n_div_down = ?{ <X 0, X 1, {} _>=X steps, ... }\n {\n .x x,\n ._y _y,\n .steps steps,\n .q q,\n ._y inv nat y_scaled\n }\n {\n .x x,\n ._y _y,\n .steps steps,\n .q q,\n .y_scaled y_scaled,\n {.y_scaled x, .x y} inOrder test\n }\n <\n {\n .test /yes if,\n {\n {.x int x, .y_scaled int y} minus /+ x,\n .q |1 q,\n ._y _y,\n .steps steps\n } then\n } .then,\n {\n .test /no if,\n {.x x, .q |0 q, ._y _y, .steps steps} then\n } .then\n >\n <\n {\n .steps empty? if,\n {.q inv int div, .x rem} then\n } .then,\n {\n .x x,\n ._y /0 _y,\n .steps /0 steps,\n .q q\n } _div_down\n >;\n\n_check_2y = ?{ <X 0, X 1, {} _>=X x, <Y 0, Y 1, {} _>=Y y, ... }\n <\n {.x empty? if, {} | down_shift then} .then,\n {.y empty? if, {} | restore then} .then,\n {.x </0, /1> empty? if, {} | down_shift then} .then,\n {\n .x </0, /1> </0, /1> x,\n .y </0, /1> y\n } _check_2y\n >;\n\ndiv =\n {\n <\n { .x/'+' if, .y get_sign then } .then,\n { .x/'-' if, .y swap_sign get_sign then } .then\n > sign,\n .x abs remove_leading_zeros x_norm,\n .y abs remove_leading_zeros y_norm\n }\n <\n {\n .x_norm empty? if,\n { 0 int div, 0 rem } then\n } .then,\n {\n {.y_norm x, .x_norm y} GT if,\n { 0 int div, .x_norm rem } then\n } .then,\n {\n .sign sign,\n {\n .x_norm x,\n .y_norm y,\n {.x_norm x, .y_norm y} _check_2y choice\n }\n <\n {\n .choice /down_shift if,\n {\n .x x,\n {.x x, .y y, .y inv _y} _align_down aligned\n }\n {\n .x x,\n .aligned._y _y,\n .aligned.steps steps,\n 0b q\n } _div_down then\n } .then,\n {\n .choice /restore if,\n {.x x, .y y, 0 r, 0b q} _div_restoring then\n } .then\n > res\n }\n <\n {.sign/'+' if, {.res.div div, .res.rem rem} then} .then,\n { .res.div swap_sign div, .res.rem rem }\n >\n >;\n\nmod =\n {{.x int x, .y int y} div .rem rem} .rem;\n\ngcd =\n <\n {.y x, .x y} GT, \n ()\n > _gcd\n;\n\n_gcd = ?{ <X 0, X 1, {} _>=X x, ... } <\n { .y remove_leading_zeros empty? if, .x nat then } .then,\n { .y x, mod y } _gcd\n>;\n\nlcm = \n {{.x int x, gcd int y} div .div x, .y int y} times abs\n;\n\nfactorial = {()n, 1 int acc} factorial_;\nfactorial_ = <\n {.n zero_int? if, .acc then} .then,\n {.n pos? dec n, {.n x, .acc y} times acc} factorial_\n>;\n\nfib_ =\n <\n {.n int zero_int? if, .\"fib(n),fib(n.1)\" then} .then,\n { .n int dec /\"+\" n,\n .\"fib(n),fib(n.1)\" {plus x, .x y} \"fib(n),fib(n.1)\"\n } fib_\n >\n;\n\nfib = {() n, {1 int x, 0 int y} \"fib(n),fib(n.1)\"} fib_ .x;\n\n--------------------\n\nrat = {.n int n, .d int d } normalize_rat;\n\nnormalize_rat =\n < \n { .d/\"-\" if, \n {\n .n swap_sign n, \n .d swap_sign d\n } then\n } .then,\n ()\n >\n {() x, {.n abs x,.d abs y} gcd int y}\n {\n {.x.n x,.y y} div .div n, \n {.x.d x,.y y} div .div d\n }\n;\n\nplus_rat = \n { \n .x x, \n .y y,\n {.x.d abs x,.y.d abs y} lcm int lcm\n }\n { \n {\n {.x.n x, {.lcm x, .x.d y} div .div y} times x,\n {.y.n x, {.lcm x, .y.d y} div .div y} times y\n } plus n, \n .lcm d\n }\n normalize_rat\n;\n\ntimes_rat = \n { \n {.x.n x,.y.n y} times n, \n {.x.d x,.y.d y} times d\n } \n normalize_rat;\n\nminus_rat = \n {\n .x x, \n .y {.n swap_sign n, .d d} y\n } \n plus_rat;\n\ninverse_rat = \n { .d n, .n d }\n normalize_rat\n;\n\ndiv_rat = \n { .x x, .y inverse_rat y} times_rat\n;\n\nint_rat = \n { .n n, .d d, {.n x,.d y} div div }\n { .div.div int, {.div.rem int n, .d d} rat}\n;\n\n()",
|
|
6
6
|
"Examples/arithmetics.k": "-- Examples for defining arithmetic functions natively\nswap_yes_no = < /yes |no, /no |yes >;\nyes = {}|yes;\nno = yes swap_yes_no;\n\nbits = ?< {} _, bits 0, bits 1 > = bits;\n\n0b = {} | _;\n-- |1 = | 1;\n-- |0 = | 0;\n\n0 = 0b |0;\nsucc = {inv x, 0b y} _inc;\n1 = 0 succ;\n2 = 1 succ;\n3 = 2 succ;\n4 = 3 succ;\n5 = 4 succ;\n6 = 5 succ;\n7 = 6 succ;\n8 = 7 succ;\n9 = 8 succ;\n10 = 9 succ;\n11 = 10 succ;\n12 = 11 succ;\n\nempty = < /_ yes, no >;\nempty? = {empty /yes if, () then} .then;\n\nconcat = {.x inv x, .y y} concat_; \nconcat_ = ?{ <X 0, X 1, {} _>=X x, ... }\n < \n {.x empty? if, .y then} .then,\n < \n {.x /0 x, .y |0 y}, \n {.x /1 x, .y |1 y}\n > concat_ \n >;\ninv = {()x, 0b r} _inv; \n_inv = ?{ <X 0, X 1, {} _>=X x, ... }\n < \n {.x empty? if, .r then} .then,\n < \n {.x /0 x, .r |0 r}, \n {.x /1 x, .r |1 r}\n > _inv\n >;\n\ninOrder = {.x inv x, .y inv y, yes inOrder} inOrder_;\ninOrder_ = ?{ <X 0, X 1, {} _>=X x, ... }\n < \n {.x </0,/1> if1, .y /_ if2, .inOrder swap_yes_no then} .then,\n {.x /_ if, .inOrder then} .then,\n < \n {.x /0 x, .y /0 y, .inOrder inOrder}, \n {.x /1 x, .y /1 y, .inOrder inOrder},\n {.x /0 x, .y /1 y, .inOrder inOrder}, \n {.x /1 y, .y /0 x, .inOrder swap_yes_no inOrder}\n > inOrder_\n >;\n\nGT = {inOrder /no if, () then} . then;\n\nEQ = {inOrder /yes if, {.x y, .y x} inOrder /yes and, () then} .then;\n\n--------------------------------------\nint = ?< <bits 0, bits 1, {} _>=bits \"+\", bits \"-\" >;\n\nremove_leading_zeros = <\n /0 remove_leading_zeros, \n ()\n>;\nnat = remove_leading_zeros <empty? 0, ()>;\nint = nat | '+';\nabs = </'+',/'-'>;\n\npos? = {/'+' remove_leading_zeros /1 if, () then} .then;\nneg? = {/'-' if, () then} .then; \nzero_int? = {/'+' /0 empty? if, () then} .then;\nminus_one? = {/'-' /1 empty? if, () then} .then;\n\ninc = <\n /'+' {inv x, 0b y} _inc nat |'+',\n minus_one? 0 int,\n /'-' {inv x, 0b y} _dec nat |'-'\n>;\n\n_inc = ?{ <X 0, X 1, {} _>=X x, ... } <\n {.x empty? if, .y |1 then} .then,\n {.x /0 x, .y |1 y} concat_, \n {.x /1 x, .y |0 y} _inc\n>; \n\ndec = <\n zero_int? 1 |'-',\n /'+' {inv x, 0b y} _dec nat |'+',\n /'-' {inv x, 0b y} _inc nat |'-'\n>;\n\n_dec = ?{ <X 0, X 1, {} _>=X x, ... } <\n {.x /1 x, .y |0 y} concat_, \n {.x /0 x, .y |1 y} _dec\n>;\n\nc0 = {}|0;\nc1 = {}|1;\n\nplus =\n< \n {.x/'+' inv x, .y/'+' inv y, c0 c } _plus inv nat | '+',\n {.x/'-' inv x, .y/'-' inv y, c0 c } _plus inv nat | '-',\n {.y/'-' x, .x/'+' y} GT {.x inv x, .y inv y, c0 c} _minus inv nat | '-',\n {.x/'+' inv x, .y/'-' inv y, c0 c} _minus inv nat |'+',\n {.x/'-' x, .y/'+' y} GT {.x inv x, .y inv y, c0 c} _minus inv nat | '-',\n {.y/'+' inv x, .x/'-' inv y, c0 c} _minus inv nat | '+'\n>;\n\nswap_sign = <\n zero_int?,\n /'+' | '-',\n /'-' | '+'\n>;\n\nminus =\n {.x x, .y swap_sign y} plus;\n\n_plus = {.x x, .y y, .c c, 0b r} _plus_;\n\n_plus_ =\n -- least significant bit first addition with carry\n ?{ <X 0, X 1, {} _>=X x, ... }\n <\n {.x empty? if, .y empty? and, .c/0 c, .r inv then} .then,\n {.x empty? if, .y empty? and, {.r x, 1 r} _inv then} .then,\n {.x empty? if, .c/0 and, {.r x, .y r} _inv then} .then,\n\n <\n {.x y, .y empty? x, .c c, .r r},\n {<.c/0 0, 1> x, .y y, .x empty? c0 c, .r r},\n\n {.x /0 x, .y /0 y, .c/0 c0 c, .r |0 r},\n {.x /0 x, .y /0 y, .c/1 c0 c, .r |1 r},\n\n {.x /0 x, .y /1 y, .c/0 c0 c, .r |1 r},\n {.x /0 x, .y /1 y, .c/1 c1 c, .r |0 r},\n {.x /1 x, .y /0 y, .c/0 c0 c, .r |1 r},\n {.x /1 x, .y /0 y, .c/1 c1 c, .r |0 r},\n\n {.x /1 x, .y /1 y, .c/0 c1 c, .r |0 r},\n {.x /1 x, .y /1 y, .c/1 c1 c, .r |1 r}\n > _plus_\n >\n;\n\n_minus = {.x x, .y y, .c c, 0b r} _minus_;\n\n_minus_ =\n -- least significant bit first substraction with carry\n ?{ <X 0, X 1, {} _>=X x, ... }\n <\n {.y empty? if, .c/0 and, {.r x, .x r} _inv then} .then,\n <\n {.x/0 x, .y empty? y, .c/1 c1 c, .r |1 r},\n {.x/1 x, .y empty? y, .c/1 c0 c, .r |0 r},\n\n {.x/0 x, .y/0 y, .c/0 c0 c, .r |0 r},\n {.x/0 x, .y/0 y, .c/1 c1 c, .r |1 r}, \n {.x/0 x, .y/1 y, .c/0 c1 c, .r |1 r},\n {.x/0 x, .y/1 y, .c/1 c1 c, .r |0 r},\n {.x/1 x, .y/0 y, .c/0 c0 c, .r |1 r},\n {.x/1 x, .y/0 y, .c/1 c0 c, .r |0 r},\n {.x/1 x, .y/1 y, .c/0 c0 c, .r |0 r},\n {.x/1 x, .y/1 y, .c/1 c1 c, .r |1 r}\n > _minus_\n >\n;\n\nsign_plus = {}|'+';\nsign_minus = {}|'-';\nget_sign = </'+' sign_plus, /'-' sign_minus>;\n\ntimes =\n {\n <\n {.x/'+' if, .y /'+' and} sign_plus, \n {.x/'-' if, .y /'-' and} sign_plus, \n sign_minus\n > sign,\n {\n 0b result,\n .x abs inv _x,\n .y abs y\n } _times result\n }\n <\n {.sign/'+' if, .result inv int then} .then,\n {.sign/'-' if, .result inv int swap_sign then} .then \n >\n;\n\n_times =\n ?{ <Y 0, Y 1, {} _>=Y y, ... }\n <\n { .y empty? if, .result then } .then,\n <\n { \n .result |0 result, \n ._x _x, \n .y /0 y\n },\n { \n {._x x, .result |0 y, c0 c } _plus result, \n ._x _x, \n .y /1 y \n }\n > _times\n >\n;\n\n// ----------------------------------------------------------------------\n// Integer Division: Adaptive Hybrid Algorithm\n// ----------------------------------------------------------------------\n\nshift_in_0 = inv |0 inv nat;\nshift_in_1 = inv |1 inv nat;\nsub_bits = {.x inv x, .y inv y, c0 c} _minus inv nat;\n\n_div_restoring =\n ?{ <X 0, X 1, {} _>=X x, ... }\n <\n {.x empty? if, {.q inv int div, .r rem} then} .then,\n <\n {.x /0 x, .r shift_in_0 r, .y y, .q q},\n {.x /1 x, .r shift_in_1 r, .y y, .q q}\n >\n {\n .x x,\n .y y,\n .q q,\n .r r,\n {.y x, .r y} inOrder test\n }\n <\n {.test /yes if, {.x x, .y y, .q |1 q, {.r x, .y y} sub_bits r} then} .then,\n {.test /no if, {.x x, .y y, .q |0 q, .r r} then} .then\n >\n _div_restoring\n >;\n\n_pad_down = ?{ <X 0, X 1, {} _>=X x, <S 0, S 1, {} _>=S steps, ... }\n <\n {.x empty? if, {._y _y, .steps steps} then} .then,\n {\n .x </0, /1> x,\n ._y |0 _y,\n .steps |0 steps\n } _pad_down\n >;\n\n_align_down = ?{ <X 0, X 1, {} _>=X x, <Y 0, Y 1, {} _>=Y y, ... }\n <\n {\n .x </0, /1> x,\n .y </0, /1> y,\n ._y _y\n } _align_down,\n\n {\n .y empty? if,\n {.x x, ._y _y, 0b steps} _pad_down then\n } .then,\n\n {\n .x empty? if,\n .y empty? and,\n {._y _y, 0b steps} then\n } .then\n >;\n\n_div_down = ?{ <X 0, X 1, {} _>=X steps, ... }\n {\n .x x,\n ._y _y,\n .steps steps,\n .q q,\n ._y inv nat y_scaled\n }\n {\n .x x,\n ._y _y,\n .steps steps,\n .q q,\n .y_scaled y_scaled,\n {.y_scaled x, .x y} inOrder test\n }\n <\n {\n .test /yes if,\n {\n {.x int x, .y_scaled int y} minus /+ x,\n .q |1 q,\n ._y _y,\n .steps steps\n } then\n } .then,\n {\n .test /no if,\n {.x x, .q |0 q, ._y _y, .steps steps} then\n } .then\n >\n <\n {\n .steps empty? if,\n {.q inv int div, .x rem} then\n } .then,\n {\n .x x,\n ._y /0 _y,\n .steps /0 steps,\n .q q\n } _div_down\n >;\n\n_check_2y = ?{ <X 0, X 1, {} _>=X x, <Y 0, Y 1, {} _>=Y y, ... }\n <\n {.x empty? if, {} | down_shift then} .then,\n {.y empty? if, {} | restore then} .then,\n {.x </0, /1> empty? if, {} | down_shift then} .then,\n {\n .x </0, /1> </0, /1> x,\n .y </0, /1> y\n } _check_2y\n >;\n\ndiv =\n {\n <\n { .x/'+' if, .y get_sign then } .then,\n { .x/'-' if, .y swap_sign get_sign then } .then\n > sign,\n .x abs remove_leading_zeros x_norm,\n .y abs remove_leading_zeros y_norm\n }\n <\n {\n .x_norm empty? if,\n { 0 int div, 0 rem } then\n } .then,\n {\n {.y_norm x, .x_norm y} GT if,\n { 0 int div, .x_norm rem } then\n } .then,\n {\n .sign sign,\n {\n .x_norm x,\n .y_norm y,\n {.x_norm x, .y_norm y} _check_2y choice\n }\n <\n {\n .choice /down_shift if,\n {\n .x x,\n {.x x, .y y, .y inv _y} _align_down aligned\n }\n {\n .x x,\n .aligned._y _y,\n .aligned.steps steps,\n 0b q\n } _div_down then\n } .then,\n {\n .choice /restore if,\n {.x x, .y y, 0 r, 0b q} _div_restoring then\n } .then\n > res\n }\n <\n {.sign/'+' if, {.res.div div, .res.rem rem} then} .then,\n { .res.div swap_sign div, .res.rem rem }\n >\n >;\n\nmod =\n {{.x int x, .y int y} div .rem rem} .rem;\n\ngcd =\n <\n {.y x, .x y} GT, \n ()\n > _gcd\n;\n\n_gcd = ?{ <X 0, X 1, {} _>=X x, ... } <\n { .y remove_leading_zeros empty? if, .x nat then } .then,\n { .y x, mod y } _gcd\n>;\n\nlcm = \n {{.x int x, gcd int y} div .div x, .y int y} times abs\n;\n\nfactorial = {()n, 1 int acc} factorial_;\nfactorial_ = <\n {.n zero_int? if, .acc then} .then,\n {.n pos? dec n, {.n x, .acc y} times acc} factorial_\n>;\n\nfib_ =\n <\n {.n int zero_int? if, .\"fib(n),fib(n.1)\" then} .then,\n { .n int dec /\"+\" n,\n .\"fib(n),fib(n.1)\" {plus x, .x y} \"fib(n),fib(n.1)\"\n } fib_\n >\n;\n\nfib = {() n, {1 int x, 0 int y} \"fib(n),fib(n.1)\"} fib_ .x;\n\n--------------------\n\nrat = {.n int n, .d int d } normalize_rat;\n\nnormalize_rat =\n < \n { .d/\"-\" if, \n {\n .n swap_sign n, \n .d swap_sign d\n } then\n } .then,\n ()\n >\n {() x, {.n abs x,.d abs y} gcd int y}\n {\n {.x.n x,.y y} div .div n, \n {.x.d x,.y y} div .div d\n }\n;\n\nplus_rat = \n { \n .x x, \n .y y,\n {.x.d abs x,.y.d abs y} lcm int lcm\n }\n { \n {\n {.x.n x, {.lcm x, .x.d y} div .div y} times x,\n {.y.n x, {.lcm x, .y.d y} div .div y} times y\n } plus n, \n .lcm d\n }\n normalize_rat\n;\n\ntimes_rat = \n { \n {.x.n x,.y.n y} times n, \n {.x.d x,.y.d y} times d\n } \n normalize_rat;\n\nminus_rat = \n {\n .x x, \n .y {.n swap_sign n, .d d} y\n } \n plus_rat;\n\ninverse_rat = \n { .d n, .n d }\n normalize_rat\n;\n\ndiv_rat = \n { .x x, .y inverse_rat y} times_rat\n;\n\nint_rat = \n { .n n, .d d, {.n x,.d y} div div }\n { .div.div int, {.div.rem int n, .d d} rat}\n;\n\n()",
|
package/kir.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import { compileObject, decodeObject, hydrateObject } from "./object.mjs";
|
|
|
8
8
|
import { exportPatternGraph } from "./codecs/runtime/codec.mjs";
|
|
9
9
|
import { patternToPropertyList } from "./codecs/runtime/pattern-json.mjs";
|
|
10
10
|
import { propertyListToFilter } from "./codecs/runtime/show-value.mjs";
|
|
11
|
+
import { TypePatternGraph } from "./TypePatternGraph.mjs";
|
|
11
12
|
|
|
12
13
|
const KIR_FORMAT = "k-ir";
|
|
13
14
|
const KIR_VERSION = 1;
|
|
@@ -240,6 +241,11 @@ function resolveRelation(object, relationName = null) {
|
|
|
240
241
|
const alias = object.relAlias?.[name];
|
|
241
242
|
if (alias && object.rels?.[alias]) return { name: alias, rel: object.rels[alias] };
|
|
242
243
|
if (alias && object.rels?.[name]) return { name, rel: object.rels[name] };
|
|
244
|
+
if (alias) {
|
|
245
|
+
for (const [k, rel] of Object.entries(object.rels || {})) {
|
|
246
|
+
if (object.relAlias?.[k] === alias) return { name: k, rel };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
243
249
|
|
|
244
250
|
throw new Error(`Relation '${name}' not found`);
|
|
245
251
|
}
|
|
@@ -264,7 +270,188 @@ function relationLibraryWithTarget(object, targetRel) {
|
|
|
264
270
|
};
|
|
265
271
|
}
|
|
266
272
|
|
|
267
|
-
function
|
|
273
|
+
function addPropertyListToPatternGraph(graph, propertyList) {
|
|
274
|
+
const kindToPattern = {
|
|
275
|
+
any: "(...)",
|
|
276
|
+
"open-product": "{...}",
|
|
277
|
+
"open-union": "<...>",
|
|
278
|
+
"closed-product": "{}",
|
|
279
|
+
"closed-union": "<>"
|
|
280
|
+
};
|
|
281
|
+
const nodes = propertyList.map(([kind]) =>
|
|
282
|
+
graph.addNewNode({ pattern: kindToPattern[kind] })
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
propertyList.forEach(([, edges], nodeIndex) => {
|
|
286
|
+
for (const [label, target] of edges) {
|
|
287
|
+
graph.edges[nodes[nodeIndex]][label] = [nodes[target]];
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
return nodes[0];
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function cloneRelation(rel) {
|
|
295
|
+
const clonedGraph = new TypePatternGraph(
|
|
296
|
+
rel.typePatternGraph.registerCodeDef,
|
|
297
|
+
rel.typePatternGraph.findCode
|
|
298
|
+
);
|
|
299
|
+
clonedGraph.patterns.nodes = JSON.parse(JSON.stringify(rel.typePatternGraph.patterns.nodes));
|
|
300
|
+
clonedGraph.patterns.parent = [...rel.typePatternGraph.patterns.parent];
|
|
301
|
+
clonedGraph.edges = JSON.parse(JSON.stringify(rel.typePatternGraph.edges));
|
|
302
|
+
clonedGraph.codeId = { ...rel.typePatternGraph.codeId };
|
|
303
|
+
|
|
304
|
+
return {
|
|
305
|
+
...rel,
|
|
306
|
+
def: JSON.parse(JSON.stringify(rel.def)),
|
|
307
|
+
typePatternGraph: clonedGraph,
|
|
308
|
+
typeDerivation: { ...(rel.typeDerivation || {}) }
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function findCallSites(exp) {
|
|
313
|
+
const calls = [];
|
|
314
|
+
function visit(node) {
|
|
315
|
+
if (!node) return;
|
|
316
|
+
if (node.op === "ref") calls.push(node);
|
|
317
|
+
if (node.comp) node.comp.forEach(visit);
|
|
318
|
+
if (node.items) node.items.forEach(visit);
|
|
319
|
+
if (node.union) node.union.forEach(visit);
|
|
320
|
+
if (node.product) node.product.forEach((item) => visit(item.exp));
|
|
321
|
+
if (node.fields) node.fields.forEach((item) => visit(item.expr));
|
|
322
|
+
}
|
|
323
|
+
visit(exp);
|
|
324
|
+
return calls;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function findRelation(rels, relAlias, name) {
|
|
328
|
+
if (!name) return null;
|
|
329
|
+
if (rels[name]) return { name, rel: rels[name] };
|
|
330
|
+
const alias = relAlias?.[name];
|
|
331
|
+
if (alias && rels[alias]) return { name: alias, rel: rels[alias] };
|
|
332
|
+
if (alias) {
|
|
333
|
+
for (const [k, rel] of Object.entries(rels)) {
|
|
334
|
+
if (relAlias?.[k] === alias) return { name: k, rel };
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export function specializeObjectRelations(object, entryRelationName = null, inputPattern = null) {
|
|
341
|
+
if (object?.format !== "k-object") {
|
|
342
|
+
throw new Error("specializeObjectRelations requires a k object");
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const baseRels = Object.fromEntries(
|
|
346
|
+
Object.entries(object.rels || {}).map(([name, rel]) => [name, cloneRelation(rel)])
|
|
347
|
+
);
|
|
348
|
+
for (const [aliasName, hash] of Object.entries(object.relAlias || {})) {
|
|
349
|
+
if (!baseRels[aliasName]) {
|
|
350
|
+
const match = Object.entries(baseRels).find(([k]) => object.relAlias?.[k] === hash);
|
|
351
|
+
if (match) baseRels[aliasName] = match[1];
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const entry = findRelation(
|
|
356
|
+
baseRels,
|
|
357
|
+
object.relAlias,
|
|
358
|
+
entryRelationName || object.main || "__main__"
|
|
359
|
+
);
|
|
360
|
+
if (!entry) return object;
|
|
361
|
+
|
|
362
|
+
const specializedRels = new Map();
|
|
363
|
+
const specializedCounts = new Map();
|
|
364
|
+
const newRels = {};
|
|
365
|
+
|
|
366
|
+
const entryRel = cloneRelation(entry.rel);
|
|
367
|
+
newRels[entry.name] = entryRel;
|
|
368
|
+
|
|
369
|
+
if (Array.isArray(inputPattern) && inputPattern.length > 0) {
|
|
370
|
+
const inRoot = addPropertyListToPatternGraph(entryRel.typePatternGraph, inputPattern);
|
|
371
|
+
try {
|
|
372
|
+
entryRel.typePatternGraph.unify("specialize", entryRel.def.patterns[0], inRoot);
|
|
373
|
+
} catch {
|
|
374
|
+
// Best-effort entry unification
|
|
375
|
+
}
|
|
376
|
+
specializedRels.set(entry.name + "::" + JSON.stringify(inputPattern), entry.name);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const worklist = [entry.name];
|
|
380
|
+
while (worklist.length > 0) {
|
|
381
|
+
const currentName = worklist.shift();
|
|
382
|
+
const currentRel = newRels[currentName];
|
|
383
|
+
if (!currentRel) continue;
|
|
384
|
+
|
|
385
|
+
const calls = findCallSites(currentRel.def);
|
|
386
|
+
for (const call of calls) {
|
|
387
|
+
const target = findRelation(baseRels, object.relAlias, call.ref);
|
|
388
|
+
if (!target) continue;
|
|
389
|
+
|
|
390
|
+
const argPatternId = call.patterns?.[0];
|
|
391
|
+
if (argPatternId != null) {
|
|
392
|
+
const argRoot = currentRel.typePatternGraph.find(argPatternId);
|
|
393
|
+
const argPropList = patternToPropertyList(
|
|
394
|
+
exportPatternGraph(currentRel.typePatternGraph, argRoot)
|
|
395
|
+
);
|
|
396
|
+
const argSig = JSON.stringify(argPropList);
|
|
397
|
+
const specKey = target.name + "::" + argSig;
|
|
398
|
+
|
|
399
|
+
let specName = specializedRels.get(specKey);
|
|
400
|
+
if (!specName) {
|
|
401
|
+
const count = (specializedCounts.get(target.name) || 0) + 1;
|
|
402
|
+
specializedCounts.set(target.name, count);
|
|
403
|
+
specName = count === 1 ? target.name : `${target.name}$${count}`;
|
|
404
|
+
specializedRels.set(specKey, specName);
|
|
405
|
+
|
|
406
|
+
const targetRel = cloneRelation(target.rel);
|
|
407
|
+
const tInRoot = addPropertyListToPatternGraph(targetRel.typePatternGraph, argPropList);
|
|
408
|
+
try {
|
|
409
|
+
targetRel.typePatternGraph.unify("specialize", targetRel.def.patterns[0], tInRoot);
|
|
410
|
+
} catch {
|
|
411
|
+
// Unification failure: keep target unspecialized
|
|
412
|
+
}
|
|
413
|
+
newRels[specName] = targetRel;
|
|
414
|
+
worklist.push(specName);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
call.ref = specName;
|
|
418
|
+
|
|
419
|
+
const targetRel = newRels[specName];
|
|
420
|
+
const outPatternId = targetRel.def.patterns?.[1];
|
|
421
|
+
const retPatternId = call.patterns?.[1];
|
|
422
|
+
if (outPatternId != null && retPatternId != null) {
|
|
423
|
+
const outRoot = targetRel.typePatternGraph.find(outPatternId);
|
|
424
|
+
const retPropList = patternToPropertyList(
|
|
425
|
+
exportPatternGraph(targetRel.typePatternGraph, outRoot)
|
|
426
|
+
);
|
|
427
|
+
const rRetRoot = addPropertyListToPatternGraph(currentRel.typePatternGraph, retPropList);
|
|
428
|
+
try {
|
|
429
|
+
currentRel.typePatternGraph.unify("specialize", retPatternId, rRetRoot);
|
|
430
|
+
} catch {
|
|
431
|
+
// Keep existing call return pattern on unification error
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const resultRels = {
|
|
439
|
+
...baseRels,
|
|
440
|
+
...newRels
|
|
441
|
+
};
|
|
442
|
+
const resultRelAlias = { ...(object.relAlias || {}) };
|
|
443
|
+
for (const name of Object.keys(newRels)) {
|
|
444
|
+
if (!resultRelAlias[name]) resultRelAlias[name] = name;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return {
|
|
448
|
+
...object,
|
|
449
|
+
rels: resultRels,
|
|
450
|
+
relAlias: resultRelAlias
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export function relationPatternPropertyList(rel, index) {
|
|
268
455
|
return expPatternPropertyList(rel, rel.def, index);
|
|
269
456
|
}
|
|
270
457
|
|
|
@@ -284,28 +471,35 @@ export function retypeObjectRelationForBackend(object, relationName, inputPatter
|
|
|
284
471
|
}
|
|
285
472
|
|
|
286
473
|
const target = resolveRelation(object, relationName);
|
|
287
|
-
const
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
libraries: [relationLibraryWithTarget(object, target.rel)]
|
|
291
|
-
}));
|
|
292
|
-
const kir = objectToKIRP(retypedObject);
|
|
293
|
-
const entryName = retypedObject.main || "__main__";
|
|
294
|
-
const retypedRel = retypedObject.rels[entryName];
|
|
295
|
-
const entryInputPattern = relationPatternPropertyList(retypedRel, 0);
|
|
474
|
+
const specializedObject = specializeObjectRelations(object, target.name, inputPattern);
|
|
475
|
+
const specializedRel = resolveRelation(specializedObject, target.name);
|
|
476
|
+
const kir = objectToKIRP(specializedObject);
|
|
296
477
|
|
|
297
478
|
return {
|
|
298
|
-
relation:
|
|
299
|
-
retypedObject,
|
|
479
|
+
relation: target.name,
|
|
480
|
+
retypedObject: specializedObject,
|
|
300
481
|
kir,
|
|
301
|
-
entryName,
|
|
302
|
-
inputPattern:
|
|
303
|
-
outputPattern: relationPatternPropertyList(
|
|
482
|
+
entryName: target.name,
|
|
483
|
+
inputPattern: relationPatternPropertyList(specializedRel.rel, 0),
|
|
484
|
+
outputPattern: relationPatternPropertyList(specializedRel.rel, 1)
|
|
304
485
|
};
|
|
305
486
|
}
|
|
306
487
|
|
|
307
488
|
export function retypeObjectRelation(object, relationName, inputPattern, options = {}) {
|
|
308
|
-
|
|
489
|
+
if (object?.format !== "k-object") {
|
|
490
|
+
throw new Error("KIR retyping requires a k object");
|
|
491
|
+
}
|
|
492
|
+
if (!Array.isArray(inputPattern)) {
|
|
493
|
+
throw new Error("KIR retyping requires an input pattern property list");
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const target = resolveRelation(object, relationName);
|
|
497
|
+
const source = `?${propertyListToFilter(inputPattern)} __kir_target__`;
|
|
498
|
+
const retypedObject = hydrateObject(compileObject(source, {
|
|
499
|
+
source: options.source || "<kir-retype>",
|
|
500
|
+
libraries: [relationLibraryWithTarget(object, target.rel)]
|
|
501
|
+
}));
|
|
502
|
+
return objectToKIRP(retypedObject);
|
|
309
503
|
}
|
|
310
504
|
|
|
311
505
|
export { KIR_FORMAT, KIR_VERSION };
|
|
@@ -314,8 +508,10 @@ export default {
|
|
|
314
508
|
KIR_FORMAT,
|
|
315
509
|
KIR_VERSION,
|
|
316
510
|
objectToKIRP,
|
|
511
|
+
relationPatternPropertyList,
|
|
317
512
|
retypeObjectRelation,
|
|
318
|
-
retypeObjectRelationForBackend
|
|
513
|
+
retypeObjectRelationForBackend,
|
|
514
|
+
specializeObjectRelations
|
|
319
515
|
};
|
|
320
516
|
|
|
321
517
|
function helpText() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fraczak/k",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.1",
|
|
4
4
|
"description": "k — a language for content-addressable schemas and data transformations over algebraic data types (products and tagged unions); includes runtime, contract derivation, REPL, and codec toolchain",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|