@fraczak/k 6.1.3 → 6.2.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/AGENTS.md +4 -4
- package/CLAUDE.md +1 -1
- package/DOCS/KIR_SPECIALIZATION_DESIGN.md +11 -2
- package/DOCS/KVM_EXECUTION_MODEL.md +413 -0
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +6 -0
- package/DOCS/REPL.md +22 -17
- package/{NAT.k~ → Examples/arithmetics.k} +1 -1
- package/Examples/bnat.k +291 -0
- package/Examples/byte.k +67 -0
- package/Examples/code.k +44 -0
- package/Examples/esc.k +9 -0
- package/Examples/ieee.k +15766 -0
- package/Examples/list.k +7 -0
- package/Examples/nat.k +12 -0
- package/Examples/vhdl_to_k.k +50 -0
- package/README.md +14 -5
- package/backend-api.mjs +12 -0
- package/codecs/runtime/codec.mjs +1 -0
- package/export.mjs +10 -0
- package/k-vscode/README.md +10 -1
- package/k-vscode/extension.js +273 -0
- package/k-vscode/package.json +22 -0
- package/k-vscode/syntaxes/k.tmLanguage.json +493 -137
- package/k-vscode/test/background-ranges.test.js +50 -0
- package/k-vscode/themes/k-contrast-color-theme.json +104 -0
- package/kvm.mjs +523 -0
- package/package.json +4 -3
- package/repl.mjs +43 -2
- package/run.mjs +1 -1
- package/scripts/run-tests.mjs +55 -0
- package/tests/README.md +35 -0
- package/{Code-derivation-tests → tests/code-derivation}/codec-witness-pattern.mjs +4 -4
- package/{Code-derivation-tests → tests/code-derivation}/convergence-strategy.mjs +1 -1
- package/{Code-derivation-tests → tests/code-derivation}/dot.mjs +1 -1
- package/{Code-derivation-tests → tests/code-derivation}/filter-syntax.mjs +1 -1
- package/{Code-derivation-tests → tests/code-derivation}/index.mjs +1 -1
- package/{Code-derivation-tests → tests/code-derivation}/pattern-envelope.mjs +5 -5
- package/{Code-derivation-tests → tests/code-derivation}/product.mjs +1 -1
- package/{tests.sh → tests/integration.sh} +11 -6
- package/{test-fingerprint.mjs → tests/test-fingerprint.mjs} +3 -3
- package/{test-hash-fuzz.mjs → tests/test-hash-fuzz.mjs} +1 -1
- package/{test-hash-normalization.mjs → tests/test-hash-normalization.mjs} +2 -2
- package/{test-ieee-arithmetic.mjs → tests/test-ieee-arithmetic.mjs} +14 -27
- package/{test-k-object.mjs → tests/test-k-object.mjs} +8 -8
- package/tests/test-kvm.mjs +163 -0
- package/{test-repl.mjs → tests/test-repl.mjs} +22 -4
- package/{test.mjs → tests/test.mjs} +1 -1
- package/.codex +0 -0
- package/DOCS/a.html +0 -398
- package/DOCS/dependency-graph.html +0 -140
- package/DOCS/dependency-graph.md +0 -144
- package/UU/ta.k +0 -1
- package/UU/ta.k~ +0 -0
- package/UU/tb.k +0 -0
- package/UU/tc.k +0 -0
- package/a.k +0 -138
- package/a.klib +0 -1
- package/codecs/BINARY_FORMAT.html +0 -471
- package/codecs/POLYMORPHIC_BINARY_FORMAT.html +0 -355
- package/core.json +0 -1
- package/core.klib +0 -1
- package/core.klib~ +0 -0
- package/ieee.klib +0 -1
- package/k-vscode/package-lock.json +0 -4176
- package/kkk.k +0 -197
- package/kkk.klib +0 -0
- package/kkk.klib~ +0 -0
- package/nak.klib +0 -1
- package/nak2.klib +0 -1
- package/nat.k +0 -10
- package/nat2.k +0 -9
- package/p.json +0 -5475
- package/type_registry/registry.mjs~ +0 -105
- package/type_registry/typeParser.jison~ +0 -132
- /package/{Code-derivation-tests → tests/code-derivation}/blowup.mjs +0 -0
- /package/{Code-derivation-tests → tests/code-derivation}/bnat.mjs +0 -0
- /package/{Code-derivation-tests → tests/code-derivation}/loop.mjs +0 -0
- /package/{Code-derivation-tests → tests/code-derivation}/nat.mjs +0 -0
- /package/{Code-derivation-tests → tests/code-derivation}/not-not.mjs +0 -0
- /package/{Code-derivation-tests → tests/code-derivation}/rlz.mjs +0 -0
package/repl.mjs
CHANGED
|
@@ -213,6 +213,40 @@ function mergeLibrary(state, lib, source = "<load>", options = {}) {
|
|
|
213
213
|
restoreCodes(state);
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
+
function reachableRelations(rels, roots) {
|
|
217
|
+
const reachable = new Set();
|
|
218
|
+
const queue = [...roots];
|
|
219
|
+
|
|
220
|
+
function walkExp(exp) {
|
|
221
|
+
if (!exp) return;
|
|
222
|
+
switch (exp.op) {
|
|
223
|
+
case "ref":
|
|
224
|
+
if (exp.ref in rels && !reachable.has(exp.ref)) queue.push(exp.ref);
|
|
225
|
+
break;
|
|
226
|
+
case "comp":
|
|
227
|
+
exp.comp.forEach(walkExp);
|
|
228
|
+
break;
|
|
229
|
+
case "union":
|
|
230
|
+
exp.union.forEach(walkExp);
|
|
231
|
+
break;
|
|
232
|
+
case "product":
|
|
233
|
+
exp.product.forEach(({ exp: child }) => walkExp(child));
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
while (queue.length > 0) {
|
|
239
|
+
const hash = queue.shift();
|
|
240
|
+
if (reachable.has(hash) || !(hash in rels)) continue;
|
|
241
|
+
reachable.add(hash);
|
|
242
|
+
walkExp(rels[hash].def);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return Object.fromEntries(
|
|
246
|
+
Object.entries(rels).filter(([hash]) => reachable.has(hash))
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
216
250
|
function savedLibrary(state) {
|
|
217
251
|
const meta = cloneJSON(state.meta);
|
|
218
252
|
const now = new Date().toISOString();
|
|
@@ -226,13 +260,20 @@ function savedLibrary(state) {
|
|
|
226
260
|
if (meta[hash].type == null) meta[hash].type = "rel";
|
|
227
261
|
meta[hash].origins.push({ source: "<repl>", name, compiledAt: now });
|
|
228
262
|
}
|
|
263
|
+
const rels = reachableRelations(state.rels, Object.values(state.relAliases));
|
|
264
|
+
const storedHashes = new Set([
|
|
265
|
+
...Object.keys(state.codes),
|
|
266
|
+
...Object.keys(rels)
|
|
267
|
+
]);
|
|
229
268
|
return {
|
|
230
269
|
format: "k-object",
|
|
231
270
|
codes: state.codes,
|
|
232
|
-
rels
|
|
271
|
+
rels,
|
|
233
272
|
relAlias: state.relAliases,
|
|
234
273
|
compileStats: { sccs: [], sccCount: 0 },
|
|
235
|
-
meta
|
|
274
|
+
meta: Object.fromEntries(
|
|
275
|
+
Object.entries(meta).filter(([hash]) => storedHashes.has(hash))
|
|
276
|
+
),
|
|
236
277
|
main: null
|
|
237
278
|
};
|
|
238
279
|
}
|
package/run.mjs
CHANGED
|
@@ -483,4 +483,4 @@ run_converged.defs = null;
|
|
|
483
483
|
const run_coverged = run_converged;
|
|
484
484
|
|
|
485
485
|
export default run;
|
|
486
|
-
export { run, run_rel, run_converged, run_coverged };
|
|
486
|
+
export { run, run_rel, run_converged, run_coverged, constrainWithPattern, intersectPatterns, projectionPattern, verify };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
9
|
+
const node = process.execPath;
|
|
10
|
+
const derivationTests = fs.readdirSync(path.join(root, "tests/code-derivation"))
|
|
11
|
+
.filter((name) => name.endsWith(".mjs"))
|
|
12
|
+
.sort()
|
|
13
|
+
.map((name) => [`tests/code-derivation/${name}`, node, [`tests/code-derivation/${name}`]]);
|
|
14
|
+
|
|
15
|
+
const tests = [
|
|
16
|
+
["tests/test.mjs", node, ["tests/test.mjs"]],
|
|
17
|
+
...derivationTests,
|
|
18
|
+
["tests/test-fingerprint.mjs", node, ["tests/test-fingerprint.mjs"]],
|
|
19
|
+
["tests/test-hash-fuzz.mjs", node, ["tests/test-hash-fuzz.mjs"]],
|
|
20
|
+
["tests/test-hash-normalization.mjs", node, ["tests/test-hash-normalization.mjs"]],
|
|
21
|
+
["tests/test-k-object.mjs", node, ["tests/test-k-object.mjs"]],
|
|
22
|
+
["tests/test-repl.mjs", node, ["tests/test-repl.mjs"]],
|
|
23
|
+
["tests/test-kvm.mjs", node, ["tests/test-kvm.mjs"]],
|
|
24
|
+
["tests/test-ieee-arithmetic.mjs", node, ["tests/test-ieee-arithmetic.mjs"]],
|
|
25
|
+
["tests/integration.sh", path.join(root, "tests/integration.sh"), []]
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
function formatDuration(milliseconds) {
|
|
29
|
+
if (milliseconds < 1000) return `${milliseconds.toFixed(0)} ms`;
|
|
30
|
+
return `${(milliseconds / 1000).toFixed(2)} s`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const suiteStarted = performance.now();
|
|
34
|
+
for (const [name, command, args] of tests) {
|
|
35
|
+
console.log(`\n==> ${name}`);
|
|
36
|
+
const started = performance.now();
|
|
37
|
+
const result = spawnSync(command, args, {
|
|
38
|
+
cwd: root,
|
|
39
|
+
stdio: "inherit"
|
|
40
|
+
});
|
|
41
|
+
const duration = formatDuration(performance.now() - started);
|
|
42
|
+
|
|
43
|
+
if (result.error) {
|
|
44
|
+
console.error(`<== ${name} FAILED (${duration})`);
|
|
45
|
+
console.error(result.error.message);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
if (result.status !== 0) {
|
|
49
|
+
console.error(`<== ${name} FAILED (${duration})`);
|
|
50
|
+
process.exit(result.status ?? 1);
|
|
51
|
+
}
|
|
52
|
+
console.log(`<== ${name} passed (${duration})`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
console.log(`\nAll tests passed (${formatDuration(performance.now() - suiteStarted)})`);
|
package/tests/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# k Test Suite
|
|
2
|
+
|
|
3
|
+
This directory contains the automated tests, type-derivation validation cases, and integration test scripts for the `k` compiler and runtime.
|
|
4
|
+
|
|
5
|
+
## Running the Tests
|
|
6
|
+
|
|
7
|
+
### 1. Run Unit and Integration Test Suite
|
|
8
|
+
To run all core unit tests, type-derivation checks, and integration tests:
|
|
9
|
+
```bash
|
|
10
|
+
npm test
|
|
11
|
+
```
|
|
12
|
+
This executes `node scripts/run-tests.mjs`, which discovers and runs the test scripts sequentially.
|
|
13
|
+
|
|
14
|
+
## Test Directory Structure
|
|
15
|
+
|
|
16
|
+
### Core Test Files
|
|
17
|
+
- **[test.mjs](file:///Users/wojtek/gits/k/tests/test.mjs)**: Verifies basic parser and runtime execution behavior.
|
|
18
|
+
- **[test-kvm.mjs](file:///Users/wojtek/gits/k/tests/test-kvm.mjs)**: Comprehensive conformance testing for the kVM register compiler and interpreter under both envelope-aware and envelope-free modes.
|
|
19
|
+
- **[test-ieee-arithmetic.mjs](file:///Users/wojtek/gits/k/tests/test-ieee-arithmetic.mjs)**: Verifies IEEE-754 double precision float arithmetic implementation (`Examples/ieee.k`).
|
|
20
|
+
- **[test-repl.mjs](file:///Users/wojtek/gits/k/tests/test-repl.mjs)**: Tests interactive REPL commands, state transitions, imports, and auto-completions.
|
|
21
|
+
- **[test-k-object.mjs](file:///Users/wojtek/gits/k/tests/test-k-object.mjs)**: Tests serialization, compilation, and loading of `.ko` binary objects.
|
|
22
|
+
- **[test-fingerprint.mjs](file:///Users/wojtek/gits/k/tests/test-fingerprint.mjs)**: Tests semantic expression and relation hashing/fingerprinting.
|
|
23
|
+
- **[test-hash-normalization.mjs](file:///Users/wojtek/gits/k/tests/test-hash-normalization.mjs)**: Verifies consistent normalization of type pattern graphs and hashes.
|
|
24
|
+
- **[test-hash-fuzz.mjs](file:///Users/wojtek/gits/k/tests/test-hash-fuzz.mjs)**: Fuzzes expression/relation hashing to guard against collisions.
|
|
25
|
+
|
|
26
|
+
### WebAssembly Benchmarks
|
|
27
|
+
|
|
28
|
+
The WebAssembly execution benchmark moved to the separate
|
|
29
|
+
[`fraczak/k-wasm`](https://github.com/fraczak/k-wasm) backend repository.
|
|
30
|
+
|
|
31
|
+
### Type Derivation Suites
|
|
32
|
+
- **[code-derivation/](file:///Users/wojtek/gits/k/tests/code-derivation)**: Houses individual files (each containing a focused test scenario) validating type-derivation convergence and pattern-envelope generation.
|
|
33
|
+
|
|
34
|
+
### Integration Scripts
|
|
35
|
+
- **[integration.sh](file:///Users/wojtek/gits/k/tests/integration.sh)**: Shell script checking boundary data pipelines (`k-parse`, `k-show`, `k-print`) against binary outputs.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from 'assert';
|
|
2
|
-
import { parseValue } from '
|
|
3
|
-
import { encodeToWire, decodeWire } from '
|
|
4
|
-
import { Product, Variant } from '
|
|
2
|
+
import { parseValue } from '../../valueIO.mjs';
|
|
3
|
+
import { encodeToWire, decodeWire } from '../../codecs/runtime/prefix-codec.mjs';
|
|
4
|
+
import { Product, Variant } from '../../Value.mjs';
|
|
5
5
|
|
|
6
6
|
const value = parseValue('{a:{b:x,c:{}}}');
|
|
7
7
|
const repeatedClosedValue = parseValue('{a:{m:{},n:{}},b:{m:{},n:{}}}');
|
|
@@ -20,7 +20,7 @@ const explicitProductWire = decodeWire(encodeToWire(value, [
|
|
|
20
20
|
["any", []]
|
|
21
21
|
]));
|
|
22
22
|
assert.deepEqual(explicitProductWire.pattern, [
|
|
23
|
-
["
|
|
23
|
+
["closed-product", [["a", 1]]],
|
|
24
24
|
["closed-product", [["b", 2], ["c", 3]]],
|
|
25
25
|
["open-union", [["x", 3]]],
|
|
26
26
|
["closed-product", []]
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import k from "
|
|
3
|
-
import { Product, Variant, mergePatterns, withPattern } from "
|
|
4
|
-
import { decodeWire, encodeToWire } from "
|
|
5
|
-
import run from "
|
|
6
|
-
import codes from "
|
|
2
|
+
import k from "../../index.mjs";
|
|
3
|
+
import { Product, Variant, mergePatterns, withPattern } from "../../Value.mjs";
|
|
4
|
+
import { decodeWire, encodeToWire } from "../../codecs/runtime/prefix-codec.mjs";
|
|
5
|
+
import run from "../../run.mjs";
|
|
6
|
+
import codes from "../../codes.mjs";
|
|
7
7
|
|
|
8
8
|
const INT_PATTERN = [
|
|
9
9
|
["closed-union", [["+", 1], ["-", 1]]],
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
1
3
|
|
|
2
4
|
# This script is used to run the k.mjs interpreter with various test cases.
|
|
3
5
|
|
|
4
|
-
echo "Running './tests.sh' ..."
|
|
6
|
+
echo "Running './tests/integration.sh' ..."
|
|
7
|
+
|
|
8
|
+
TMP_DIR=`mktemp -d`
|
|
9
|
+
trap 'rm -rf "$TMP_DIR"' EXIT
|
|
5
10
|
|
|
6
11
|
echo '["zebara", "ela", "kupa", ala, owca]' | node ./codecs/k-parse.mjs | ./k.mjs '{.1 0,.3 1}' | node ./codecs/k-print.mjs
|
|
7
12
|
echo
|
|
@@ -11,14 +16,14 @@ node ./codecs/unit.mjs --parse | node ./codecs/unit.mjs --print | grep -qx '{}'
|
|
|
11
16
|
echo
|
|
12
17
|
printf 'A🙂\nBé~\t' | ./codecs/utf8.mjs --parse | ./k.mjs -k Examples/byte.k |./codecs/utf8.mjs --print
|
|
13
18
|
echo
|
|
14
|
-
./objects/compile-lib.mjs Examples/ieee.k ieee.klib
|
|
15
|
-
MUL=`./objects/extract-aliases.mjs ieee.klib | grep '^mul = @' | sed 's/^mul = \(@[^;]*\);.*$/\1/'`
|
|
16
|
-
DIV=`./objects/extract-aliases.mjs ieee.klib | grep '^div = @' | sed 's/^div = \(@[^;]*\);.*$/\1/'`
|
|
19
|
+
./objects/compile-lib.mjs Examples/ieee.k "$TMP_DIR/ieee.klib"
|
|
20
|
+
MUL=`./objects/extract-aliases.mjs "$TMP_DIR/ieee.klib" | grep '^mul = @' | sed 's/^mul = \(@[^;]*\);.*$/\1/'`
|
|
21
|
+
DIV=`./objects/extract-aliases.mjs "$TMP_DIR/ieee.klib" | grep '^div = @' | sed 's/^div = \(@[^;]*\);.*$/\1/'`
|
|
17
22
|
ARG=0.12
|
|
18
23
|
printf "$ARG * $ARG = "
|
|
19
|
-
echo $ARG | ./codecs/ieee.mjs --parse | ./k.mjs --lib ieee.klib "{()x,()y} $MUL .result" | ./codecs/ieee.mjs --print
|
|
24
|
+
echo $ARG | ./codecs/ieee.mjs --parse | ./k.mjs --lib "$TMP_DIR/ieee.klib" "{()x,()y} $MUL .result" | ./codecs/ieee.mjs --print
|
|
20
25
|
printf "$ARG / $ARG = "
|
|
21
|
-
DIV_RESULT=`echo $ARG | ./codecs/ieee.mjs --parse | ./k.mjs --lib ieee.klib "{()x,()y} $DIV .result" | ./codecs/ieee.mjs --print`
|
|
26
|
+
DIV_RESULT=`echo $ARG | ./codecs/ieee.mjs --parse | ./k.mjs --lib "$TMP_DIR/ieee.klib" "{()x,()y} $DIV .result" | ./codecs/ieee.mjs --print`
|
|
22
27
|
echo "$DIV_RESULT"
|
|
23
28
|
test "$DIV_RESULT" = "1"
|
|
24
29
|
echo
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import k from '
|
|
2
|
-
import { encodeCodeToString, find } from '
|
|
3
|
-
import hash from '
|
|
1
|
+
import k from '../index.mjs';
|
|
2
|
+
import { encodeCodeToString, find } from '../codes.mjs';
|
|
3
|
+
import hash from '../hash.mjs';
|
|
4
4
|
[
|
|
5
5
|
`
|
|
6
6
|
$bits = < bits 0, bits 1, {} _ >;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import assert from "node:assert/strict";
|
|
4
|
-
import { annotate } from "
|
|
5
|
-
import { hash } from "
|
|
4
|
+
import { annotate } from "../index.mjs";
|
|
5
|
+
import { hash } from "../hash.mjs";
|
|
6
6
|
|
|
7
7
|
const nameRegex = /^[a-zA-Z0-9_+][a-zA-Z0-9_?!+]*$/;
|
|
8
8
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
|
|
3
|
-
import { createState, evaluateInput } from "
|
|
4
|
-
import { Product } from "
|
|
5
|
-
import codes from "
|
|
6
|
-
import { parse as parseFloat64, print as printFloat64 } from "
|
|
7
|
-
import { singletonPatternToCodeHash, valueForCode } from "
|
|
3
|
+
import { createState, evaluateInput } from "../repl.mjs";
|
|
4
|
+
import { Product } from "../Value.mjs";
|
|
5
|
+
import codes from "../codes.mjs";
|
|
6
|
+
import { parse as parseFloat64, print as printFloat64 } from "../codecs/ieee.mjs";
|
|
7
|
+
import { singletonPatternToCodeHash, valueForCode } from "../repl-codecs.mjs";
|
|
8
8
|
|
|
9
9
|
const state = createState();
|
|
10
10
|
await evaluateInput(":load Examples/ieee.k", state);
|
|
@@ -44,7 +44,7 @@ function assertFloatResult(value, expected, label) {
|
|
|
44
44
|
assert.equal(printFloat64(value), expected, label);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
async function assertOperation({ op, x, y, result, flags = null }) {
|
|
47
|
+
async function assertOperation({ op, x, y, result, flags = null, projection = false }) {
|
|
48
48
|
const bundle = await runOperation(op, x, y);
|
|
49
49
|
assert.ok(bundle instanceof Product, `${op} should return a result bundle`);
|
|
50
50
|
assert.deepEqual(Object.keys(bundle.product).sort(), ["flags", "result"]);
|
|
@@ -53,37 +53,24 @@ async function assertOperation({ op, x, y, result, flags = null }) {
|
|
|
53
53
|
assert.equal(bundle.product.flags.tag, flags, `${op}(${x}, ${y}).flags`);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
if (projection) {
|
|
57
|
+
const projected = await runProjectedResult(op, x, y);
|
|
58
|
+
assertFloatResult(projected, result, `${op}(${x}, ${y}).result projection`);
|
|
59
|
+
}
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
const cases = [
|
|
61
|
-
//
|
|
62
|
-
{ op: "add", x: "
|
|
63
|
-
{ op: "add", x: "-1", y: "1", result: "0", flags: "none" },
|
|
64
|
-
{ op: "add", x: "0.5", y: "0.25", result: "0.75", flags: "none" },
|
|
63
|
+
// Keep this as a fast smoke matrix. Examples/ieee.k has detailed relation-level coverage.
|
|
64
|
+
{ op: "add", x: "0.5", y: "0.25", result: "0.75", flags: "none", projection: true },
|
|
65
65
|
{ op: "add", x: "Infinity", y: "-Infinity", result: "NaN", flags: "invalid" },
|
|
66
|
-
{ op: "add", x: "NaN", y: "1", result: "NaN", flags: "none" },
|
|
67
66
|
|
|
68
|
-
//
|
|
69
|
-
{ op: "sub", x: "4", y: "2", result: "2", flags: "none" },
|
|
67
|
+
// Cover each finite arithmetic implementation, including sign and fractional results.
|
|
70
68
|
{ op: "sub", x: "2", y: "4", result: "-2", flags: "none" },
|
|
71
|
-
{ op: "sub", x: "0", y: "0", result: "0", flags: "none" },
|
|
72
|
-
{ op: "sub", x: "Infinity", y: "Infinity", result: "NaN", flags: "invalid" },
|
|
73
|
-
|
|
74
|
-
// Multiplication: positive, signed, zero, and invalid infinity-by-zero.
|
|
75
|
-
{ op: "mul", x: "4", y: "2", result: "8", flags: "none" },
|
|
76
69
|
{ op: "mul", x: "-2", y: "4", result: "-8", flags: "none" },
|
|
77
|
-
{ op: "mul", x: "0", y: "5", result: "0", flags: "none" },
|
|
78
70
|
{ op: "mul", x: "Infinity", y: "0", result: "NaN", flags: "invalid" },
|
|
79
|
-
|
|
80
|
-
// Division: normalized exact cases, fractional quotient, divide-by-zero, invalid zero-over-zero, and infinity quotient.
|
|
81
|
-
{ op: "div", x: "4", y: "2", result: "2" },
|
|
82
|
-
{ op: "div", x: "1", y: "1", result: "1" },
|
|
83
71
|
{ op: "div", x: "1", y: "2", result: "0.5" },
|
|
84
72
|
{ op: "div", x: "1", y: "0", result: "Infinity", flags: "div_by_zero" },
|
|
85
|
-
{ op: "div", x: "0", y: "0", result: "NaN", flags: "invalid" }
|
|
86
|
-
{ op: "div", x: "Infinity", y: "2", result: "Infinity", flags: "none" }
|
|
73
|
+
{ op: "div", x: "0", y: "0", result: "NaN", flags: "invalid" }
|
|
87
74
|
];
|
|
88
75
|
|
|
89
76
|
for (const testCase of cases) {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
-
import k from "
|
|
4
|
-
import { parseValue } from "
|
|
5
|
-
import { decodeWire, encodeToWire, CORE_PATTERN_PROPERTY_LIST } from "
|
|
6
|
-
import { Product, Variant } from "
|
|
7
|
-
import { exportPatternGraph } from "
|
|
8
|
-
import { patternToPropertyList } from "
|
|
3
|
+
import k from "../index.mjs";
|
|
4
|
+
import { parseValue } from "../valueIO.mjs";
|
|
5
|
+
import { decodeWire, encodeToWire, CORE_PATTERN_PROPERTY_LIST } from "../codecs/runtime/prefix-codec.mjs";
|
|
6
|
+
import { Product, Variant } from "../Value.mjs";
|
|
7
|
+
import { exportPatternGraph } from "../codecs/runtime/codec.mjs";
|
|
8
|
+
import { patternToPropertyList } from "../codecs/runtime/pattern-json.mjs";
|
|
9
9
|
import {
|
|
10
10
|
compileObjectBuffer,
|
|
11
11
|
compileLibraryBuffer,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
runConvergedObject,
|
|
15
15
|
decompileObjectBuffer,
|
|
16
16
|
extractAliasesFromObject
|
|
17
|
-
} from "
|
|
17
|
+
} from "../object.mjs";
|
|
18
18
|
|
|
19
19
|
const value = parseValue("{a:{b:x,c:{}}}");
|
|
20
20
|
const wire = encodeToWire(value, null);
|
|
@@ -96,7 +96,7 @@ const natDecompiledSource = decompileObjectBuffer(natLibraryBuffer);
|
|
|
96
96
|
const natRoundTripSource = decompileObjectBuffer(
|
|
97
97
|
compileLibraryBuffer(natDecompiledSource, { source: "Examples/nat.decompiled.k" })
|
|
98
98
|
);
|
|
99
|
-
const recursiveNatPattern =
|
|
99
|
+
const recursiveNatPattern = /\$dTww <\{\.x PgmQ x, \.y h4Wg y\} re64, \.y> \$e9WP;/;
|
|
100
100
|
assert.equal(natRoundTripSource, natDecompiledSource);
|
|
101
101
|
assert.match(natDecompiledSource, recursiveNatPattern);
|
|
102
102
|
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import k from "../index.mjs";
|
|
3
|
+
import { lowerToKVM, executeKVM } from "../kvm.mjs";
|
|
4
|
+
import codes from "../codes.mjs";
|
|
5
|
+
import { fromObject, Product, Variant } from "../Value.mjs";
|
|
6
|
+
import run, { run_converged } from "../run.mjs";
|
|
7
|
+
|
|
8
|
+
function runKVM(script, data, options = {}) {
|
|
9
|
+
const defs = k.annotate(script, {
|
|
10
|
+
convergence: { strategy: "auto" }
|
|
11
|
+
});
|
|
12
|
+
const mainRel = defs.rels.__main__;
|
|
13
|
+
const kvmFunc = lowerToKVM(mainRel, "__main__");
|
|
14
|
+
const context = { rels: defs.rels, findCode: codes.find, options };
|
|
15
|
+
return executeKVM(kvmFunc, fromObject(data), context);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function toJSON(value) {
|
|
19
|
+
if (value === undefined) return undefined;
|
|
20
|
+
return JSON.parse(JSON.stringify(value));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log("==> Starting kVM Tests");
|
|
24
|
+
|
|
25
|
+
// Test 1: Identity
|
|
26
|
+
{
|
|
27
|
+
const result = runKVM("()", { unit: {} });
|
|
28
|
+
assert.deepEqual(toJSON(result), "unit");
|
|
29
|
+
console.log("Test 1 (Identity): Passed");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Test 2: Product field projection
|
|
33
|
+
{
|
|
34
|
+
const result = runKVM(".name", { name: { ala: {} }, age: { twentythree: {} } });
|
|
35
|
+
assert.deepEqual(toJSON(result), "ala");
|
|
36
|
+
console.log("Test 2 (Field projection): Passed");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Test 3: Variant projections and lifting
|
|
40
|
+
{
|
|
41
|
+
const result = runKVM("/tag |tag", { tag: { payload: {} } });
|
|
42
|
+
assert.deepEqual(toJSON(result), { tag: "payload" });
|
|
43
|
+
console.log("Test 3 (Variant projection & construction): Passed");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Test 4: Composition
|
|
47
|
+
{
|
|
48
|
+
const result = runKVM("(.x .y)", { x: { y: { ok: {}, dummy: {} }, dummy: {} }, dummy: {} });
|
|
49
|
+
assert.deepEqual(toJSON(result), { ok: {}, dummy: {} });
|
|
50
|
+
console.log("Test 4 (Composition): Passed");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Test 5: Union (Speculative choice)
|
|
54
|
+
{
|
|
55
|
+
const result1 = runKVM("< /x, /y >", { x: { val: {} } });
|
|
56
|
+
assert.deepEqual(toJSON(result1), "val");
|
|
57
|
+
|
|
58
|
+
const result2 = runKVM("< /x, /y >", { y: { val: {} } });
|
|
59
|
+
assert.deepEqual(toJSON(result2), "val");
|
|
60
|
+
|
|
61
|
+
const result3 = runKVM("< /x, /y >", { z: { val: {} } });
|
|
62
|
+
assert.equal(result3, undefined);
|
|
63
|
+
console.log("Test 5 (Union / Choice): Passed");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Test 6: Parallel Product
|
|
67
|
+
{
|
|
68
|
+
const result = runKVM("{ .x fieldA, .y fieldB }", { x: { valA: {} }, y: { valB: {} } });
|
|
69
|
+
assert.deepEqual(toJSON(result), {
|
|
70
|
+
fieldA: "valA",
|
|
71
|
+
fieldB: "valB"
|
|
72
|
+
});
|
|
73
|
+
console.log("Test 6 (Product): Passed");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Test 7: Filters & Types
|
|
77
|
+
{
|
|
78
|
+
const script = `
|
|
79
|
+
$ bool = < {} true, {} false >;
|
|
80
|
+
$ bool
|
|
81
|
+
`;
|
|
82
|
+
const result1 = runKVM(script, { true: {} });
|
|
83
|
+
assert.deepEqual(toJSON(result1), "true");
|
|
84
|
+
|
|
85
|
+
const result2 = runKVM(script, { unknown_tag: {} });
|
|
86
|
+
assert.deepEqual(toJSON(result2), "unknown_tag");
|
|
87
|
+
console.log("Test 7 (Filters & Type Guards): Passed");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Test 8: Recursive List traversal (Cons/Nil)
|
|
91
|
+
{
|
|
92
|
+
const script = `
|
|
93
|
+
$ bool = < {} true, {} false >;
|
|
94
|
+
true = {} | true $bool;
|
|
95
|
+
false = {} | false $bool;
|
|
96
|
+
|
|
97
|
+
list? = ?< {} nil, {X car, Y cdr} cons > = Y;
|
|
98
|
+
nil = {}|nil list?;
|
|
99
|
+
nil? = list? /nil nil;
|
|
100
|
+
car = list? /cons .car;
|
|
101
|
+
list? {
|
|
102
|
+
< nil? true, false > nil_test,
|
|
103
|
+
< car, {}|none > car
|
|
104
|
+
}
|
|
105
|
+
`;
|
|
106
|
+
|
|
107
|
+
// Test Nil list
|
|
108
|
+
const res1 = runKVM(script, { nil: {} });
|
|
109
|
+
assert.deepEqual(toJSON(res1), { nil_test: "true", car: "none" });
|
|
110
|
+
|
|
111
|
+
// Test Cons list
|
|
112
|
+
const res2 = runKVM(script, {
|
|
113
|
+
cons: {
|
|
114
|
+
car: { unit: {} },
|
|
115
|
+
cdr: { nil: {} }
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
assert.deepEqual(toJSON(res2), { nil_test: "false", car: "unit" });
|
|
119
|
+
console.log("Test 8 (Recursive lists): Passed");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Test 9: Comparing KVM execution vs Native JS run (Envelope-aware and Envelope-free)
|
|
123
|
+
{
|
|
124
|
+
const script = `
|
|
125
|
+
$ nat = < {} 0, nat +1 >;
|
|
126
|
+
0 = {} | 0 $ nat;
|
|
127
|
+
inc = | +1 $ nat;
|
|
128
|
+
dec = $ nat / +1;
|
|
129
|
+
add = $ { nat x, nat y } <
|
|
130
|
+
{ . x dec x, . y inc y } add,
|
|
131
|
+
. y
|
|
132
|
+
>;
|
|
133
|
+
`;
|
|
134
|
+
|
|
135
|
+
const inputData = {
|
|
136
|
+
x: { "+1": { "+1": { "0": {} } } }, // 2
|
|
137
|
+
y: { "+1": { "0": {} } } // 1
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// Run in native JS envelope-aware mode
|
|
141
|
+
const nativeAware = k.run(script + " add", inputData);
|
|
142
|
+
// Run in native JS envelope-free mode
|
|
143
|
+
const defs = k.annotate(script + " add", {
|
|
144
|
+
convergence: { strategy: "auto" }
|
|
145
|
+
});
|
|
146
|
+
run.defs = defs;
|
|
147
|
+
run_converged.defs = defs;
|
|
148
|
+
const nativeFree = run_converged(codes.find, defs.rels.__main__.def, fromObject(inputData), defs.rels.__main__.typePatternGraph);
|
|
149
|
+
|
|
150
|
+
// Run in kVM envelope-aware mode
|
|
151
|
+
const kvmAware = runKVM(script + " add", inputData, { envelopeFree: false });
|
|
152
|
+
// Run in kVM envelope-free mode
|
|
153
|
+
const kvmFree = runKVM(script + " add", inputData, { envelopeFree: true });
|
|
154
|
+
|
|
155
|
+
assert.deepEqual(toJSON(kvmAware), toJSON(nativeAware));
|
|
156
|
+
assert.deepEqual(toJSON(kvmFree), toJSON(nativeFree));
|
|
157
|
+
|
|
158
|
+
// Verify that the output is 3 (i.e. +1(+1(+1(0))))
|
|
159
|
+
assert.deepEqual(toJSON(kvmAware), { "+1": { "+1": { "+1": "0" } } });
|
|
160
|
+
console.log("Test 9 (KVM vs JS run equivalence & Peano addition): Passed");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
console.log("==> All kVM Tests Passed!");
|
|
@@ -16,9 +16,9 @@ import {
|
|
|
16
16
|
isMainEntrypoint,
|
|
17
17
|
lineHasExplicitContinuation,
|
|
18
18
|
lineTerminatesSnippet
|
|
19
|
-
} from "
|
|
20
|
-
import { decodeObject, objectToFunction } from "
|
|
21
|
-
import { Product } from "
|
|
19
|
+
} from "../repl.mjs";
|
|
20
|
+
import { decodeObject, objectToFunction } from "../object.mjs";
|
|
21
|
+
import { Product } from "../Value.mjs";
|
|
22
22
|
|
|
23
23
|
const state = createState();
|
|
24
24
|
|
|
@@ -90,6 +90,7 @@ await assert.rejects(
|
|
|
90
90
|
|
|
91
91
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "k-repl-"));
|
|
92
92
|
const libPath = path.join(tmpDir, "session.klib");
|
|
93
|
+
const recursiveLibPath = path.join(tmpDir, "recursive.klib");
|
|
93
94
|
const koPath = path.join(tmpDir, "succ.ko");
|
|
94
95
|
const sourcePath = path.join(tmpDir, "session.k");
|
|
95
96
|
const codecPath = path.join(tmpDir, "bool-codec.mjs");
|
|
@@ -97,6 +98,23 @@ const symlinkPath = path.resolve(".test-k-repl-link");
|
|
|
97
98
|
output = await evaluateInput(`:klib ${libPath}`, state);
|
|
98
99
|
assert.equal(output[0], `saved ${libPath}`);
|
|
99
100
|
|
|
101
|
+
const recursiveState = createState();
|
|
102
|
+
await evaluateInput(":type $ nat = < {}_, nat 0, nat 1 >;", recursiveState);
|
|
103
|
+
await evaluateInput(":rel 0? = $ nat < / _ {}|0, / 0 0? >;", recursiveState);
|
|
104
|
+
const staleZeroHash = recursiveState.relAliases["0?"];
|
|
105
|
+
await evaluateInput(":rel 0? = $ nat < / _ {}|_, / 0 0? >;", recursiveState);
|
|
106
|
+
const zeroHash = recursiveState.relAliases["0?"];
|
|
107
|
+
assert.notEqual(zeroHash, staleZeroHash);
|
|
108
|
+
await evaluateInput("{}|_|0|0 $nat", recursiveState);
|
|
109
|
+
assert.deepEqual(
|
|
110
|
+
Object.keys(recursiveState.rels).sort(),
|
|
111
|
+
[staleZeroHash, zeroHash].sort()
|
|
112
|
+
);
|
|
113
|
+
output = await evaluateInput(`:klib ${recursiveLibPath}`, recursiveState);
|
|
114
|
+
assert.equal(output[0], `saved ${recursiveLibPath}`);
|
|
115
|
+
const recursiveLibrary = JSON.parse(fs.readFileSync(recursiveLibPath, "utf8"));
|
|
116
|
+
assert.deepEqual(Object.keys(recursiveLibrary.rels), [zeroHash]);
|
|
117
|
+
|
|
100
118
|
completions = completeInput(`:load ${tmpDir}/ses`, state)[0];
|
|
101
119
|
assert(completions.includes(`:load ${libPath}`));
|
|
102
120
|
completions = completeInput(`:load --no-alias ${tmpDir}/ses`, state)[0];
|
|
@@ -176,7 +194,7 @@ assert.equal(output[0], `input ${utf8State.typeAliases.string} using utf8: enter
|
|
|
176
194
|
output = await evaluateInput("hello", utf8State);
|
|
177
195
|
assert.match(output[0], /utf8: hello/);
|
|
178
196
|
|
|
179
|
-
const replPath = fileURLToPath(new URL("
|
|
197
|
+
const replPath = fileURLToPath(new URL("../repl.mjs", import.meta.url));
|
|
180
198
|
try {
|
|
181
199
|
fs.rmSync(symlinkPath, { force: true });
|
|
182
200
|
} catch {}
|
package/.codex
DELETED
|
File without changes
|