@fraczak/k 6.3.2 → 6.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DOCS/CODECS.md +258 -0
- package/DOCS/DICTIONARY.md +1172 -0
- package/DOCS/KIR_SPECIALIZATION_DESIGN.md +51 -46
- package/DOCS/KIR_V1.md +221 -0
- package/DOCS/KVM_EXECUTION_MODEL.md +56 -19
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +2 -1
- package/DOCS/PROFILING_AND_TRACING.md +196 -0
- package/DOCS/REPL.md +76 -14
- package/DOCS/WEB_REPL_REQUIREMENTS.md +135 -0
- package/DOCS/dependency-graph.md +144 -0
- package/Examples/arithmetics.k +51 -48
- package/Examples/bnat.k +1 -1
- package/Examples/buda.k +44 -0
- package/Examples/ieee.k +300 -8671
- package/Examples/poly.k +67 -0
- package/README.md +68 -19
- package/augmentation.mjs +9 -7
- package/backend-api.mjs +11 -2
- package/backends/arm64/README.md +104 -0
- package/backends/arm64/bin/k-arm64-compile.mjs +101 -0
- package/backends/arm64/bin/k-arm64-run.mjs +58 -0
- package/backends/arm64/bin/k-arm64.mjs +88 -0
- package/backends/arm64/scripts/run-tests.mjs +41 -0
- package/backends/arm64/src/arm64.mjs +229 -0
- package/backends/arm64/src/cli.mjs +155 -0
- package/backends/arm64/src/kvm2arm64.mjs +906 -0
- package/backends/arm64/src/runtime.c +728 -0
- package/backends/arm64/tests/integration.sh +112 -0
- package/backends/arm64/tests/test-arm64.mjs +240 -0
- package/backends/llvm/README.md +135 -0
- package/backends/llvm/bin/k-llvm-build.mjs +71 -0
- package/backends/llvm/bin/k-llvm-compile.mjs +68 -0
- package/backends/llvm/bin/k-llvm-jit.mjs +160 -0
- package/backends/llvm/bin/k-llvm-run.mjs +90 -0
- package/backends/llvm/runtime/krt.c +1508 -0
- package/backends/llvm/runtime/krt.h +109 -0
- package/backends/llvm/scripts/conformance.mjs +76 -0
- package/backends/llvm/scripts/run-tests.mjs +28 -0
- package/backends/llvm/src/executable.mjs +562 -0
- package/backends/llvm/src/kvm2llvm.mjs +998 -0
- package/backends/llvm/src/llvm.mjs +1092 -0
- package/backends/llvm/tests/composition-driver.c +22 -0
- package/backends/llvm/tests/identity-driver.c +27 -0
- package/backends/llvm/tests/integration.sh +153 -0
- package/backends/llvm/tests/perf-support.mjs +735 -0
- package/backends/llvm/tests/product-driver.c +23 -0
- package/backends/llvm/tests/projection-driver.c +19 -0
- package/backends/llvm/tests/relation-driver.c +29 -0
- package/backends/llvm/tests/test-llvm.mjs +240 -0
- package/backends/llvm/tests/union-driver.c +31 -0
- package/backends/llvm/tests/variant-driver.c +19 -0
- package/backends/llvm/tests/variant-projection-driver.c +21 -0
- package/backends/wasm/DOCS/USE-WEB-ASSEMBLY.md +233 -0
- package/backends/wasm/README.md +97 -0
- package/backends/wasm/TECHNICAL-DEBT.md +60 -0
- package/backends/wasm/bin/k-wasm-compile.mjs +50 -0
- package/backends/wasm/bin/k-wasm-run.mjs +140 -0
- package/backends/wasm/bin/k-wasm.mjs +49 -0
- package/backends/wasm/demo_ieee.mjs +319 -0
- package/backends/wasm/runtime.wat +102 -0
- package/backends/wasm/scripts/run-tests.mjs +41 -0
- package/backends/wasm/src/cli.mjs +133 -0
- package/backends/wasm/src/kvm2wasm.mjs +661 -0
- package/backends/wasm/src/wasm.mjs +1010 -0
- package/backends/wasm/tests/integration.sh +182 -0
- package/backends/wasm/tests/perf-support.mjs +336 -0
- package/backends/wasm/tests/test-wasm.mjs +617 -0
- package/browser/repl-browser.mjs +1584 -0
- package/browser/shims/assert.mjs +19 -0
- package/browser/shims/buffer.mjs +224 -0
- package/browser/shims/crypto.mjs +103 -0
- package/browser/shims/fs.mjs +159 -0
- package/browser/shims/os.mjs +17 -0
- package/browser/shims/path.mjs +54 -0
- package/browser/shims/process.mjs +44 -0
- package/browser/shims/readline.mjs +11 -0
- package/browser/shims/url.mjs +12 -0
- package/browser/vfs-data.mjs +19 -0
- package/codecs/README.md +3 -0
- package/codecs/json.mjs +16 -2
- package/codecs/runtime/show-value.mjs +2 -2
- package/conformance/helper-reuse/case.json +8 -0
- package/conformance/helper-reuse/expected.kv +1 -0
- package/conformance/helper-reuse/input.kv +1 -0
- package/conformance/helper-reuse/program.k +2 -0
- package/conformance/identity/case.json +8 -0
- package/conformance/identity/expected.kv +1 -0
- package/conformance/identity/input.kv +1 -0
- package/conformance/identity/program.k +1 -0
- package/conformance/product-projection/case.json +8 -0
- package/conformance/product-projection/expected.kv +1 -0
- package/conformance/product-projection/input.kv +1 -0
- package/conformance/product-projection/program.k +1 -0
- package/conformance/run.mjs +149 -0
- package/conformance/variant-construction/case.json +8 -0
- package/conformance/variant-construction/expected.kv +1 -0
- package/conformance/variant-construction/input.kv +1 -0
- package/conformance/variant-construction/program.k +1 -0
- package/harness/adapters/arm64.mjs +105 -0
- package/harness/adapters/kvm.mjs +144 -0
- package/harness/adapters/llvm.mjs +118 -0
- package/harness/adapters/native.mjs +77 -0
- package/harness/adapters/wasm.mjs +169 -0
- package/harness/backend.mjs +95 -0
- package/harness/cli.mjs +130 -0
- package/harness/format.mjs +95 -0
- package/harness/index.mjs +25 -0
- package/harness/persistent.mjs +121 -0
- package/harness/registry.mjs +44 -0
- package/harness/suite.mjs +257 -0
- package/harness/suites/poly.mjs +156 -0
- package/harness/trace.mjs +50 -0
- package/harness/util.mjs +92 -0
- package/intrinsics.mjs +41 -0
- package/kir.mjs +399 -0
- package/kvm.mjs +585 -81
- package/objects/README.md +37 -3
- package/objects/compile.mjs +15 -22
- package/objects/inspect.mjs +96 -0
- package/objects/validate.mjs +415 -0
- package/package.json +51 -19
- package/repl-codecs.mjs +133 -20
- package/repl.mjs +408 -59
- package/run.mjs +2 -7
- package/scripts/benchmark.mjs +406 -22
- package/scripts/build-repl-html.mjs +1784 -0
- package/scripts/perf-ieee.mjs +586 -0
- package/scripts/perf-int.mjs +531 -0
- package/scripts/perf-poly.mjs +945 -0
- package/scripts/render-benchmark.mjs +12 -8
- package/scripts/run-tests.mjs +6 -0
- package/tests/integration.sh +19 -2
- package/tests/test-arithmetics.mjs +54 -0
- package/tests/test-ieee-arithmetic.mjs +6 -2
- package/tests/test-kir.mjs +85 -0
- package/tests/test-kvm-polymorphic.mjs +278 -0
- package/tests/test-kvm.mjs +25 -6
- package/tests/test-poly.mjs +129 -0
- package/tests/test-repl-html.mjs +303 -0
- package/tests/test-repl.mjs +175 -1
- package/tests/test-validate-object.mjs +40 -0
- package/a.klib +0 -1
- package/a.kvm +0 -142676
- package/a.wasm +0 -0
- package/benchmark-results.html +0 -532
- package/benchmark-results.json +0 -480
- package/k-vscode/.vscode/launch.json +0 -14
- package/k-vscode/README.md +0 -75
- package/k-vscode/extension.js +0 -273
- package/k-vscode/language-configuration.json +0 -42
- package/k-vscode/package.json +0 -79
- package/k-vscode/snippets/k.json +0 -92
- package/k-vscode/syntaxes/k.tmLanguage.json +0 -599
- package/k-vscode/test/background-ranges.test.js +0 -50
- package/k-vscode/themes/k-contrast-color-theme.json +0 -104
- package/scripts/git-hooks/pre-commit +0 -10
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
import { createState, evaluateInput } from "../backend-api.mjs";
|
|
7
|
+
|
|
8
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
9
|
+
const arithmeticsPath = path.join(root, "Examples/arithmetics.k");
|
|
10
|
+
const polyPath = path.join(root, "Examples/poly.k");
|
|
11
|
+
|
|
12
|
+
console.log("==> Running Polymorphic List Tests (Examples/poly.k)");
|
|
13
|
+
|
|
14
|
+
const state = createState();
|
|
15
|
+
await evaluateInput(`:load ${arithmeticsPath}`, state);
|
|
16
|
+
await evaluateInput(`:load ${polyPath}`, state);
|
|
17
|
+
|
|
18
|
+
async function evalK(snippet) {
|
|
19
|
+
const res = await evaluateInput(snippet, state);
|
|
20
|
+
assert.ok(res && res.length > 0 && !res[0].includes("undefined"), `Evaluation failed for: ${snippet}`);
|
|
21
|
+
return res[0];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Test 1: Concat
|
|
25
|
+
{
|
|
26
|
+
const res = await evalK(`
|
|
27
|
+
l1 = {{}|a car, {{}|b car, nil cdr} cons cdr} cons;
|
|
28
|
+
l2 = {{}|c car, {{}|d car, nil cdr} cons cdr} cons;
|
|
29
|
+
{l1 xs, l2 ys} concat
|
|
30
|
+
`);
|
|
31
|
+
assert.match(res, /\|a car.*\|b car.*\|c car.*\|d car/);
|
|
32
|
+
console.log("Test 1 (concat): Passed");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Test 2: Reverse
|
|
36
|
+
{
|
|
37
|
+
const res = await evalK(`
|
|
38
|
+
l = {{}|first car, {{}|second car, {{}|third car, nil cdr} cons cdr} cons cdr} cons;
|
|
39
|
+
l reverse
|
|
40
|
+
`);
|
|
41
|
+
assert.match(res, /\|third car.*\|second car.*\|first car/);
|
|
42
|
+
console.log("Test 2 (reverse): Passed");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Test 3: Length
|
|
46
|
+
{
|
|
47
|
+
const res = await evalK(`
|
|
48
|
+
l = {{}|a car, {{}|b car, {{}|c car, nil cdr} cons cdr} cons cdr} cons;
|
|
49
|
+
l length
|
|
50
|
+
`);
|
|
51
|
+
// 3 in LSB-first binary with '+' sign: |_|1|1|+
|
|
52
|
+
assert.match(res, /1.*1.*\+/);
|
|
53
|
+
console.log("Test 3 (length): Passed");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Test 4: Get N-th
|
|
57
|
+
{
|
|
58
|
+
const res0 = await evalK(`
|
|
59
|
+
l = {{}|item0 car, {{}|item1 car, {{}|item2 car, nil cdr} cons cdr} cons cdr} cons;
|
|
60
|
+
{0 int n, l xs} get_nth
|
|
61
|
+
`);
|
|
62
|
+
assert.match(res0, /\|item0/);
|
|
63
|
+
|
|
64
|
+
const res1 = await evalK(`{1 int n, l xs} get_nth`);
|
|
65
|
+
assert.match(res1, /\|item1/);
|
|
66
|
+
|
|
67
|
+
const res2 = await evalK(`{2 int n, l xs} get_nth`);
|
|
68
|
+
assert.match(res2, /\|item2/);
|
|
69
|
+
console.log("Test 4 (get_nth): Passed");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Test 5: Split By
|
|
73
|
+
{
|
|
74
|
+
const res = await evalK(`
|
|
75
|
+
l = {{}|a car, {{}|b car, {{}|c car, {{}|d car, nil cdr} cons cdr} cons cdr} cons cdr} cons;
|
|
76
|
+
{2 int n, l xs} split_by
|
|
77
|
+
`);
|
|
78
|
+
assert.match(res, /first/);
|
|
79
|
+
assert.match(res, /second/);
|
|
80
|
+
console.log("Test 5 (split_by): Passed");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Test 6: Take and Drop
|
|
84
|
+
{
|
|
85
|
+
const resTake = await evalK(`
|
|
86
|
+
l = {{}|a car, {{}|b car, {{}|c car, nil cdr} cons cdr} cons cdr} cons;
|
|
87
|
+
{2 int n, l xs} take
|
|
88
|
+
`);
|
|
89
|
+
assert.match(resTake, /\|a car.*\|b car/);
|
|
90
|
+
|
|
91
|
+
const resDrop = await evalK(`
|
|
92
|
+
{2 int n, l xs} drop
|
|
93
|
+
`);
|
|
94
|
+
assert.match(resDrop, /\|c car/);
|
|
95
|
+
console.log("Test 6 (take & drop): Passed");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Test 7: Repeat
|
|
99
|
+
{
|
|
100
|
+
const res = await evalK(`
|
|
101
|
+
{3 int n, {}|val x} repeat
|
|
102
|
+
`);
|
|
103
|
+
assert.match(res, /\|val car.*\|val car.*\|val car/);
|
|
104
|
+
console.log("Test 7 (repeat): Passed");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Test 8: Append
|
|
108
|
+
{
|
|
109
|
+
const res = await evalK(`
|
|
110
|
+
l = {{}|a car, {{}|b car, nil cdr} cons cdr} cons;
|
|
111
|
+
{{}|c x, l xs} append
|
|
112
|
+
`);
|
|
113
|
+
assert.match(res, /\|a car.*\|b car.*\|c car/);
|
|
114
|
+
console.log("Test 8 (append): Passed");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Test 9: Zip
|
|
118
|
+
{
|
|
119
|
+
const res = await evalK(`
|
|
120
|
+
l1 = {{}|k1 car, {{}|k2 car, nil cdr} cons cdr} cons;
|
|
121
|
+
l2 = {1 int car, {2 int car, nil cdr} cons cdr} cons;
|
|
122
|
+
{l1 xs, l2 ys} zip
|
|
123
|
+
`);
|
|
124
|
+
assert.match(res, /\|k1 x/);
|
|
125
|
+
assert.match(res, /\|k2 x/);
|
|
126
|
+
console.log("Test 9 (zip): Passed");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log("==> All Polymorphic List Tests Passed Successfully!");
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const root = path.resolve(__dirname, "..");
|
|
9
|
+
const replHtmlPath = path.join(root, "repl.html");
|
|
10
|
+
|
|
11
|
+
console.log("=== Testing Web REPL (repl.html) ===");
|
|
12
|
+
|
|
13
|
+
// 1. Build repl.html
|
|
14
|
+
console.log("1. Running scripts/build-repl-html.mjs...");
|
|
15
|
+
const buildResult = spawnSync(process.execPath, [path.join(root, "scripts/build-repl-html.mjs")], {
|
|
16
|
+
cwd: root,
|
|
17
|
+
stdio: "pipe",
|
|
18
|
+
encoding: "utf8"
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
if (buildResult.status !== 0) {
|
|
22
|
+
console.error(buildResult.stderr || buildResult.stdout);
|
|
23
|
+
process.exit(buildResult.status ?? 1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 2. Validate self-contained HTML
|
|
27
|
+
console.log("2. Validating generated repl.html...");
|
|
28
|
+
assert(fs.existsSync(replHtmlPath), "repl.html must exist");
|
|
29
|
+
const html = fs.readFileSync(replHtmlPath, "utf8");
|
|
30
|
+
|
|
31
|
+
assert(html.length > 500000, `repl.html size (${html.length} bytes) is suspiciously small`);
|
|
32
|
+
assert(html.includes("<!DOCTYPE html>"), "repl.html must be valid HTML5");
|
|
33
|
+
assert(html.includes("<style>"), "repl.html must contain inlined CSS styles");
|
|
34
|
+
assert(html.includes('<script type="module">'), "repl.html must contain inlined module script");
|
|
35
|
+
assert(!html.includes('<script src="http'), "repl.html must NOT contain external remote scripts");
|
|
36
|
+
assert(html.includes("wasm-in-process"), "repl.html must mention wasm-in-process engine");
|
|
37
|
+
assert(html.includes("arithmetics.k"), "repl.html must embed standard examples");
|
|
38
|
+
assert(html.includes("btn-codecs"), "repl.html must contain Codecs button");
|
|
39
|
+
assert(html.includes("codecs-modal"), "repl.html must contain Codecs modal");
|
|
40
|
+
assert(html.includes("active-codecs-tbody"), "repl.html must contain active codecs table");
|
|
41
|
+
assert(html.includes("codec-preset-select"), "repl.html must contain codec preset templates");
|
|
42
|
+
assert(html.includes("btn-input-popup"), "repl.html must contain input button");
|
|
43
|
+
assert(html.includes("input-popup-modal"), "repl.html must contain input popup modal");
|
|
44
|
+
console.log(" repl.html is valid, self-contained, and has size:", (html.length / 1024).toFixed(1), "KB");
|
|
45
|
+
|
|
46
|
+
// 3. If Chromium is available, run end-to-end browser test
|
|
47
|
+
const chromiumBin = ["chromium-browser", "chromium", "google-chrome"].find(bin => {
|
|
48
|
+
const res = spawnSync("which", [bin], { stdio: "ignore" });
|
|
49
|
+
return res.status === 0;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (chromiumBin) {
|
|
53
|
+
console.log(`3. Running browser verification with ${chromiumBin}...`);
|
|
54
|
+
const tmpDir = `/tmp/chrome-test-${Date.now()}`;
|
|
55
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
56
|
+
|
|
57
|
+
const cdpPort = 9333 + Math.floor(Math.random() * 500);
|
|
58
|
+
const chrome = spawn(chromiumBin, [
|
|
59
|
+
"--headless=new",
|
|
60
|
+
"--disable-gpu",
|
|
61
|
+
`--remote-debugging-port=${cdpPort}`,
|
|
62
|
+
"--no-sandbox",
|
|
63
|
+
"--disable-extensions",
|
|
64
|
+
`--user-data-dir=${tmpDir}`,
|
|
65
|
+
`file://${replHtmlPath}`
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
let wsUrl = null;
|
|
70
|
+
for (let i = 0; i < 30; i++) {
|
|
71
|
+
try {
|
|
72
|
+
const res = await fetch(`http://127.0.0.1:${cdpPort}/json/list`);
|
|
73
|
+
const list = await res.json();
|
|
74
|
+
const page = list.find(item => item.url && item.url.includes("repl.html"));
|
|
75
|
+
if (page && page.webSocketDebuggerUrl) {
|
|
76
|
+
wsUrl = page.webSocketDebuggerUrl;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
} catch {}
|
|
80
|
+
await new Promise(r => setTimeout(r, 200));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
assert(wsUrl, `Could not connect to Chromium CDP on port ${cdpPort}`);
|
|
84
|
+
|
|
85
|
+
const ws = new WebSocket(wsUrl);
|
|
86
|
+
await new Promise(r => ws.onopen = r);
|
|
87
|
+
|
|
88
|
+
let idCounter = 1;
|
|
89
|
+
const pending = new Map();
|
|
90
|
+
ws.onmessage = (event) => {
|
|
91
|
+
const msg = JSON.parse(event.data);
|
|
92
|
+
if (msg.id && pending.has(msg.id)) {
|
|
93
|
+
const { resolve, reject } = pending.get(msg.id);
|
|
94
|
+
pending.delete(msg.id);
|
|
95
|
+
if (msg.error) reject(new Error(msg.error.message));
|
|
96
|
+
else resolve(msg.result);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
function send(method, params = {}) {
|
|
101
|
+
const id = idCounter++;
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
pending.set(id, { resolve, reject });
|
|
104
|
+
ws.send(JSON.stringify({ id, method, params }));
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
await send("Page.enable");
|
|
109
|
+
await send("Runtime.enable");
|
|
110
|
+
await new Promise(r => setTimeout(r, 1200));
|
|
111
|
+
|
|
112
|
+
async function evaluateAsync(expression) {
|
|
113
|
+
const res = await send("Runtime.evaluate", {
|
|
114
|
+
expression,
|
|
115
|
+
awaitPromise: true,
|
|
116
|
+
returnByValue: true
|
|
117
|
+
});
|
|
118
|
+
if (res.exceptionDetails) {
|
|
119
|
+
throw new Error(res.exceptionDetails.exception?.description || "Evaluation error");
|
|
120
|
+
}
|
|
121
|
+
return res.result?.value;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Wait for window.kRepl to be ready
|
|
125
|
+
await evaluateAsync(`new Promise((resolve, reject) => {
|
|
126
|
+
let tries = 0;
|
|
127
|
+
const check = () => {
|
|
128
|
+
if (window.kRepl && typeof window.kRepl.executeCommand === "function") resolve(true);
|
|
129
|
+
else if (++tries > 100) reject(new Error("Timeout waiting for window.kRepl"));
|
|
130
|
+
else setTimeout(check, 50);
|
|
131
|
+
};
|
|
132
|
+
check();
|
|
133
|
+
})`);
|
|
134
|
+
|
|
135
|
+
// Check UI initialized
|
|
136
|
+
const title = await evaluateAsync(`document.querySelector(".brand-name")?.textContent`);
|
|
137
|
+
assert.strictEqual(title, "k repl", "Brand name should be 'k repl'");
|
|
138
|
+
|
|
139
|
+
// Verify VFS curated files
|
|
140
|
+
const vfsList = await evaluateAsync(`window.kRepl.getAllVfsFiles().sort()`);
|
|
141
|
+
const expectedFiles = [
|
|
142
|
+
"arithmetics.k",
|
|
143
|
+
"core.k",
|
|
144
|
+
"ieee.k",
|
|
145
|
+
"ieee.mjs",
|
|
146
|
+
"int.mjs",
|
|
147
|
+
"json.mjs",
|
|
148
|
+
"unit.mjs",
|
|
149
|
+
"utf8.mjs"
|
|
150
|
+
].sort();
|
|
151
|
+
assert.deepStrictEqual(vfsList, expectedFiles, `getAllVfsFiles() must match curated files list, got: ${JSON.stringify(vfsList)}`);
|
|
152
|
+
|
|
153
|
+
// Test :load arithmetics.k
|
|
154
|
+
const loadOut = await evaluateAsync(`(async () => {
|
|
155
|
+
await window.kRepl.executeCommand(":load arithmetics.k");
|
|
156
|
+
const lines = Array.from(document.querySelectorAll(".entry-line")).map(el => el.textContent);
|
|
157
|
+
return lines[lines.length - 1];
|
|
158
|
+
})()`);
|
|
159
|
+
assert(loadOut.includes("loaded arithmetics.k"), `Expected loaded arithmetics.k, got: ${loadOut}`);
|
|
160
|
+
|
|
161
|
+
// Test evaluating 10
|
|
162
|
+
const valOut = await evaluateAsync(`(async () => {
|
|
163
|
+
await window.kRepl.executeCommand("10");
|
|
164
|
+
const lines = Array.from(document.querySelectorAll(".entry-line")).map(el => el.textContent);
|
|
165
|
+
return lines[lines.length - 1];
|
|
166
|
+
})()`);
|
|
167
|
+
assert(valOut.includes("{}|_|0|1|0|1"), `Expected 10 in output, got: ${valOut}`);
|
|
168
|
+
|
|
169
|
+
// Test {10 int x, 5 int y} plus
|
|
170
|
+
const plusOut = await evaluateAsync(`(async () => {
|
|
171
|
+
await window.kRepl.executeCommand("{10 int x, 5 int y} plus");
|
|
172
|
+
const lines = Array.from(document.querySelectorAll(".entry-line")).map(el => el.textContent);
|
|
173
|
+
return lines[lines.length - 1];
|
|
174
|
+
})()`);
|
|
175
|
+
assert(plusOut.includes("{}|_|1|1|1|1|+"), `Expected 15 in output, got: ${plusOut}`);
|
|
176
|
+
|
|
177
|
+
// Test :codecs initially empty
|
|
178
|
+
const codecsOut = await evaluateAsync(`(async () => {
|
|
179
|
+
await window.kRepl.executeCommand(":codecs");
|
|
180
|
+
const lines = Array.from(document.querySelectorAll(".entry-line")).map(el => el.textContent);
|
|
181
|
+
return lines[lines.length - 1];
|
|
182
|
+
})()`);
|
|
183
|
+
assert.strictEqual(codecsOut, "(none)");
|
|
184
|
+
|
|
185
|
+
// Test :codec load int
|
|
186
|
+
const loadIntOut = await evaluateAsync(`(async () => {
|
|
187
|
+
await window.kRepl.executeCommand(":codec load int");
|
|
188
|
+
const lines = Array.from(document.querySelectorAll(".entry-line")).map(el => el.textContent);
|
|
189
|
+
return lines[lines.length - 1];
|
|
190
|
+
})()`);
|
|
191
|
+
assert(loadIntOut.includes("loaded codec int"), `Expected loaded codec int, got: ${loadIntOut}`);
|
|
192
|
+
|
|
193
|
+
// Check codecs badge updated to 1
|
|
194
|
+
const badgeCount = await evaluateAsync(`document.getElementById("codecs-count-badge")?.textContent`);
|
|
195
|
+
assert.strictEqual(badgeCount, "1", "Codecs badge count should be 1 after loading int codec");
|
|
196
|
+
|
|
197
|
+
// Test evaluating 10 int with int codec active -> output should format using int serializer
|
|
198
|
+
const formattedVal = await evaluateAsync(`(async () => {
|
|
199
|
+
await window.kRepl.executeCommand("10 int");
|
|
200
|
+
const lines = Array.from(document.querySelectorAll(".entry-line")).map(el => el.textContent);
|
|
201
|
+
return lines[lines.length - 1];
|
|
202
|
+
})()`);
|
|
203
|
+
assert(formattedVal.includes("int: 10"), `Expected 'int: 10' in output, got: ${formattedVal}`);
|
|
204
|
+
|
|
205
|
+
// Test defining inline custom codec via :codec define
|
|
206
|
+
await evaluateAsync(`window.kRepl.executeCommand(":type bool = <{} true, {} false>")`);
|
|
207
|
+
const defineCodecOut = await evaluateAsync(`(async () => {
|
|
208
|
+
await window.kRepl.executeCommand(":codec define yn bool ({ parse: (t) => Value.variant(t.trim() === 'yes' ? 'true' : 'false', Value.product({})), print: (v) => v.tag === 'true' ? 'YES' : 'NO' })");
|
|
209
|
+
const lines = Array.from(document.querySelectorAll(".entry-line")).map(el => el.textContent);
|
|
210
|
+
return lines[lines.length - 1];
|
|
211
|
+
})()`);
|
|
212
|
+
assert(defineCodecOut.includes("defined codec yn"), `Expected defined codec yn, got: ${defineCodecOut}`);
|
|
213
|
+
|
|
214
|
+
// Test modal interaction via openCodecsModal
|
|
215
|
+
const modalIsOpen = await evaluateAsync(`(() => {
|
|
216
|
+
window.kRepl.openCodecsModal();
|
|
217
|
+
return document.getElementById("codecs-modal")?.classList.contains("open");
|
|
218
|
+
})()`);
|
|
219
|
+
assert.strictEqual(modalIsOpen, true, "Codecs modal should open");
|
|
220
|
+
|
|
221
|
+
// Close codecs modal
|
|
222
|
+
await evaluateAsync(`(() => {
|
|
223
|
+
document.getElementById("codecs-modal")?.classList.remove("open");
|
|
224
|
+
})()`);
|
|
225
|
+
|
|
226
|
+
// Test :input opens input popup modal
|
|
227
|
+
await evaluateAsync(`window.kRepl.executeCommand(":input int")`);
|
|
228
|
+
const inputModalOpen = await evaluateAsync(`document.getElementById("input-popup-modal")?.classList.contains("open")`);
|
|
229
|
+
assert.strictEqual(inputModalOpen, true, "Executing :input int should open input popup modal");
|
|
230
|
+
|
|
231
|
+
// Enter value '55' in popup and submit
|
|
232
|
+
const submitResult = await evaluateAsync(`(async () => {
|
|
233
|
+
const textEl = document.getElementById("input-popup-text");
|
|
234
|
+
textEl.value = "55";
|
|
235
|
+
await window.kRepl.submitInputPopup();
|
|
236
|
+
const lines = Array.from(document.querySelectorAll(".entry-line")).map(el => el.textContent);
|
|
237
|
+
return lines[lines.length - 1];
|
|
238
|
+
})()`);
|
|
239
|
+
assert(submitResult.includes("int: 55"), `Expected int: 55 after submitting input popup, got: ${submitResult}`);
|
|
240
|
+
|
|
241
|
+
// Verify modal is closed after submission
|
|
242
|
+
const modalClosedAfterSubmit = await evaluateAsync(`!document.getElementById("input-popup-modal")?.classList.contains("open")`);
|
|
243
|
+
assert.strictEqual(modalClosedAfterSubmit, true, "Input modal should be closed after submit");
|
|
244
|
+
|
|
245
|
+
// Test :input directive without args opens input popup
|
|
246
|
+
await evaluateAsync(`window.kRepl.executeCommand(":input")`);
|
|
247
|
+
const inputWithoutArgsOpensModal = await evaluateAsync(`document.getElementById("input-popup-modal")?.classList.contains("open")`);
|
|
248
|
+
assert.strictEqual(inputWithoutArgsOpensModal, true, "Executing :input without args should open input popup");
|
|
249
|
+
|
|
250
|
+
// Test cancelInputPopup
|
|
251
|
+
await evaluateAsync(`window.kRepl.cancelInputPopup()`);
|
|
252
|
+
const modalClosedAfterCancel = await evaluateAsync(`!document.getElementById("input-popup-modal")?.classList.contains("open")`);
|
|
253
|
+
assert.strictEqual(modalClosedAfterCancel, true, "Input modal should close on cancel");
|
|
254
|
+
|
|
255
|
+
// Test JSON codec with float64 in browser: :input {string a, float64 n} then {"a":"Woj","n":123}
|
|
256
|
+
await evaluateAsync(`(async () => {
|
|
257
|
+
await window.kRepl.executeCommand(":load core.k");
|
|
258
|
+
await window.kRepl.executeCommand(":load ieee.k");
|
|
259
|
+
await window.kRepl.executeCommand(":codec load json");
|
|
260
|
+
})()`);
|
|
261
|
+
const inputPromptOut = await evaluateAsync(`(async () => {
|
|
262
|
+
await window.kRepl.executeCommand(":input {string a, float64 n}");
|
|
263
|
+
const lines = Array.from(document.querySelectorAll(".entry-line")).map(el => el.textContent);
|
|
264
|
+
return lines[lines.length - 1];
|
|
265
|
+
})()`);
|
|
266
|
+
assert(inputPromptOut.includes("enter value text"), `Expected input prompt, got: ${inputPromptOut}`);
|
|
267
|
+
|
|
268
|
+
// Enter JSON value with float64
|
|
269
|
+
const jsonResult = await evaluateAsync(`(async () => {
|
|
270
|
+
await window.kRepl.executeCommand('{"a":"Woj","n":123}');
|
|
271
|
+
const lines = Array.from(document.querySelectorAll(".entry-line")).map(el => el.textContent);
|
|
272
|
+
return lines[lines.length - 1];
|
|
273
|
+
})()`);
|
|
274
|
+
assert(jsonResult.includes('json: {"a":"Woj","n":123}'), `Expected json: {"a":"Woj","n":123}, got: ${jsonResult}`);
|
|
275
|
+
|
|
276
|
+
// Verify text selection holds in output element
|
|
277
|
+
const selectionCheck = await evaluateAsync(`(() => {
|
|
278
|
+
const output = document.getElementById("terminal-output");
|
|
279
|
+
const range = document.createRange();
|
|
280
|
+
range.selectNodeContents(output);
|
|
281
|
+
const sel = window.getSelection();
|
|
282
|
+
sel.removeAllRanges();
|
|
283
|
+
sel.addRange(range);
|
|
284
|
+
// Simulate click on terminal container/output
|
|
285
|
+
output.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
|
286
|
+
return window.getSelection().toString().length > 0;
|
|
287
|
+
})()`);
|
|
288
|
+
assert.strictEqual(selectionCheck, true, "Selection in terminal output should hold after click");
|
|
289
|
+
|
|
290
|
+
ws.close();
|
|
291
|
+
console.log(" Browser execution verified successfully!");
|
|
292
|
+
} finally {
|
|
293
|
+
chrome.kill();
|
|
294
|
+
await new Promise(r => setTimeout(r, 500));
|
|
295
|
+
try {
|
|
296
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
297
|
+
} catch {}
|
|
298
|
+
}
|
|
299
|
+
} else {
|
|
300
|
+
console.log("3. Chromium not found, skipping headless browser CDP step.");
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
console.log("OK - Web REPL test passed.");
|
package/tests/test-repl.mjs
CHANGED
|
@@ -15,7 +15,8 @@ import {
|
|
|
15
15
|
helpText,
|
|
16
16
|
isMainEntrypoint,
|
|
17
17
|
lineHasExplicitContinuation,
|
|
18
|
-
lineTerminatesSnippet
|
|
18
|
+
lineTerminatesSnippet,
|
|
19
|
+
promptForState
|
|
19
20
|
} from "../repl.mjs";
|
|
20
21
|
import { decodeObject, objectToFunction } from "../object.mjs";
|
|
21
22
|
import { Value } from "../Value.mjs";
|
|
@@ -194,6 +195,36 @@ assert.equal(output[0], `input ${utf8State.typeAliases.string} using utf8: enter
|
|
|
194
195
|
output = await evaluateInput("hello", utf8State);
|
|
195
196
|
assert.match(output[0], /utf8: hello/);
|
|
196
197
|
|
|
198
|
+
const jsonState = createState();
|
|
199
|
+
output = await evaluateInput(":type bool = <{} true, {} false>", jsonState);
|
|
200
|
+
assert.match(output[0], /^\$ bool = @/);
|
|
201
|
+
output = await evaluateInput(":codec load ./codecs/json.mjs", jsonState);
|
|
202
|
+
assert.match(output[0], /^loaded codec json for all types/);
|
|
203
|
+
output = await evaluateInput(":codec list", jsonState);
|
|
204
|
+
assert.match(output[0], /^json all /);
|
|
205
|
+
completions = completeInput(":input bool j", jsonState)[0];
|
|
206
|
+
assert(completions.includes(":input bool json"));
|
|
207
|
+
output = await evaluateInput(":input bool", jsonState);
|
|
208
|
+
assert.equal(output[0], `input ${jsonState.typeAliases.bool}: enter value text`);
|
|
209
|
+
assert.equal(promptForState(jsonState), "json> ");
|
|
210
|
+
output = await evaluateInput("true", jsonState);
|
|
211
|
+
assert.match(output[0], /\{\}\|true \?</);
|
|
212
|
+
assert.match(output[0], /json: true/);
|
|
213
|
+
assert.equal(promptForState(jsonState), "> ");
|
|
214
|
+
output = await evaluateInput(":input <{}true,{}false>", jsonState);
|
|
215
|
+
assert.equal(output[0], `input ${jsonState.typeAliases.bool}: enter value text`);
|
|
216
|
+
assert.equal(promptForState(jsonState), "json> ");
|
|
217
|
+
output = await evaluateInput("false", jsonState);
|
|
218
|
+
assert.match(output[0], /\{\}\|false \?</);
|
|
219
|
+
assert.match(output[0], /json: false/);
|
|
220
|
+
assert.equal(promptForState(jsonState), "> ");
|
|
221
|
+
output = await evaluateInput(":input {bool left, bool right} json", jsonState);
|
|
222
|
+
assert.match(output[0], /^input @[^ ]+ using json: enter value text$/);
|
|
223
|
+
assert.equal(promptForState(jsonState), "json> ");
|
|
224
|
+
output = await evaluateInput('{"left":true,"right":false}', jsonState);
|
|
225
|
+
assert.match(output[0], /json: \{"left":true,"right":false\}/);
|
|
226
|
+
assert.equal(promptForState(jsonState), "> ");
|
|
227
|
+
|
|
197
228
|
const replPath = fileURLToPath(new URL("../repl.mjs", import.meta.url));
|
|
198
229
|
try {
|
|
199
230
|
fs.rmSync(symlinkPath, { force: true });
|
|
@@ -246,6 +277,149 @@ await assert.rejects(
|
|
|
246
277
|
/Type Error|Unknown ref: 'type'|Parse error|Parse Error/
|
|
247
278
|
);
|
|
248
279
|
|
|
280
|
+
// Verify WebAssembly in-process backend supports TCO / stack safety on deep recursion
|
|
281
|
+
const tcoState = createState();
|
|
282
|
+
await evaluateInput(":type nat = <{} zero, nat succ>", tcoState);
|
|
283
|
+
await evaluateInput(":rel countdown = $nat < /zero {}|zero, /succ countdown >", tcoState);
|
|
284
|
+
|
|
285
|
+
let deepNat = Value.variant("zero", Value.product({}, [["closed-product", []]]), [["open-union", [["zero", 1]]], ["closed-product", []]]);
|
|
286
|
+
for (let i = 0; i < 5000; i++) {
|
|
287
|
+
deepNat = Value.variant("succ", deepNat, [["open-union", [["succ", 1]]], ...deepNat.pattern]);
|
|
288
|
+
}
|
|
289
|
+
tcoState.value = deepNat;
|
|
290
|
+
|
|
291
|
+
output = await evaluateInput("countdown", tcoState);
|
|
292
|
+
assert.match(output[0], /^\{\}\|zero/);
|
|
293
|
+
assert.equal(tcoState.value.toJSON(), "zero");
|
|
294
|
+
|
|
295
|
+
// Verify loading library and evaluating constant/recursive relation from library
|
|
296
|
+
const arithState = createState();
|
|
297
|
+
output = await evaluateInput(":load Examples/arithmetics.k", arithState);
|
|
298
|
+
assert.equal(output[0], "loaded Examples/arithmetics.k");
|
|
299
|
+
output = await evaluateInput("10", arithState);
|
|
300
|
+
assert.match(output[0], /^\{\}\|_\|0\|1\|0\|1/);
|
|
301
|
+
output = await evaluateInput("{10 int x, 5 int y} plus", arithState);
|
|
302
|
+
assert.match(output[0], /^\{\}\|_\|1\|1\|1\|1\|\+/);
|
|
303
|
+
|
|
304
|
+
// Verify built-in codecs, :codecs, :codec define, and :codec unload
|
|
305
|
+
const codecTestState = createState();
|
|
306
|
+
output = await evaluateInput(":codecs", codecTestState);
|
|
307
|
+
assert.equal(output[0], "(none)");
|
|
308
|
+
|
|
309
|
+
output = await evaluateInput(":codec load int", codecTestState);
|
|
310
|
+
assert.match(output[0], /^loaded codec int for @/);
|
|
311
|
+
|
|
312
|
+
output = await evaluateInput(":codecs", codecTestState);
|
|
313
|
+
assert.match(output[0], /^int @/);
|
|
314
|
+
|
|
315
|
+
completions = completeInput(":codec un", codecTestState)[0];
|
|
316
|
+
assert(completions.includes(":codec unload"));
|
|
317
|
+
completions = completeInput(":codec unload ", codecTestState)[0];
|
|
318
|
+
assert(completions.includes(":codec unload int"));
|
|
319
|
+
completions = completeInput(":codec load i", codecTestState)[0];
|
|
320
|
+
assert(completions.includes(":codec load int"));
|
|
321
|
+
|
|
322
|
+
output = await evaluateInput(":codec unload int", codecTestState);
|
|
323
|
+
assert.equal(output[0], "unloaded codec int");
|
|
324
|
+
output = await evaluateInput(":codecs", codecTestState);
|
|
325
|
+
assert.equal(output[0], "(none)");
|
|
326
|
+
|
|
327
|
+
output = await evaluateInput(":type bool = <{} true, {} false>", codecTestState);
|
|
328
|
+
output = await evaluateInput(
|
|
329
|
+
`:codec define yn bool ({ parse: (text) => Value.variant(text.trim() === "yes" ? "true" : "false", Value.product({})), print: (v) => v.tag === "true" ? "YES" : "NO" })`,
|
|
330
|
+
codecTestState
|
|
331
|
+
);
|
|
332
|
+
assert.match(output[0], /^defined codec yn for @/);
|
|
333
|
+
|
|
334
|
+
output = await evaluateInput(":input bool yn", codecTestState);
|
|
335
|
+
output = await evaluateInput("yes", codecTestState);
|
|
336
|
+
assert.match(output[0], /yn: YES/);
|
|
337
|
+
|
|
338
|
+
// Projection on product with variant field (avoids filter variable collisions)
|
|
339
|
+
const projState = createState();
|
|
340
|
+
output = await evaluateInput("{{}|a x, {} y}", projState);
|
|
341
|
+
assert.match(output[0], /^\{\{\}\|a x, \{\} y\}/);
|
|
342
|
+
output = await evaluateInput(".x", projState);
|
|
343
|
+
assert.match(output[0], /^\{\}\|a/);
|
|
344
|
+
|
|
345
|
+
const projState2 = createState();
|
|
346
|
+
output = await evaluateInput("{{}|a a, {} y}", projState2);
|
|
347
|
+
assert.match(output[0], /^\{\{\}\|a a, \{\} y\}/);
|
|
348
|
+
output = await evaluateInput(".a", projState2);
|
|
349
|
+
assert.match(output[0], /^\{\}\|a/);
|
|
350
|
+
|
|
351
|
+
// Auto-loading dependencies and bidirectional type aliasing tests
|
|
352
|
+
const autoLoadState = createState();
|
|
353
|
+
output = await evaluateInput(":codec load int", autoLoadState);
|
|
354
|
+
assert.match(output[0], /auto-loaded (?:Examples\/)?arithmetics\.k/);
|
|
355
|
+
assert.ok(autoLoadState.typeAliases.int, "int type alias should be registered");
|
|
356
|
+
output = await evaluateInput(":input int", autoLoadState);
|
|
357
|
+
output = await evaluateInput("123", autoLoadState);
|
|
358
|
+
assert.match(output[0], /int: 123/);
|
|
359
|
+
|
|
360
|
+
const utf8AutoState = createState();
|
|
361
|
+
output = await evaluateInput(":codec load utf8", utf8AutoState);
|
|
362
|
+
assert.match(output[0], /auto-loaded core\.k/);
|
|
363
|
+
assert.ok(utf8AutoState.typeAliases.utf8, "utf8 type alias should be registered");
|
|
364
|
+
assert.ok(utf8AutoState.typeAliases.string, "string type alias should be registered");
|
|
365
|
+
assert.equal(utf8AutoState.typeAliases.utf8, utf8AutoState.typeAliases.string);
|
|
366
|
+
output = await evaluateInput(":input utf8", utf8AutoState);
|
|
367
|
+
output = await evaluateInput("test utf8", utf8AutoState);
|
|
368
|
+
assert.match(output[0], /utf8: test utf8/);
|
|
369
|
+
|
|
370
|
+
const ieeeAutoState = createState();
|
|
371
|
+
output = await evaluateInput(":codec load ieee", ieeeAutoState);
|
|
372
|
+
assert.match(output[0], /auto-loaded (?:Examples\/)?ieee\.k/);
|
|
373
|
+
assert.ok(ieeeAutoState.typeAliases.ieee, "ieee type alias should be registered");
|
|
374
|
+
assert.ok(ieeeAutoState.typeAliases.float64, "float64 type alias should be registered");
|
|
375
|
+
assert.equal(ieeeAutoState.typeAliases.ieee, ieeeAutoState.typeAliases.float64);
|
|
376
|
+
|
|
377
|
+
const jsonInputState = createState();
|
|
378
|
+
await evaluateInput(":load core.k", jsonInputState);
|
|
379
|
+
await evaluateInput(":load ieee.k", jsonInputState);
|
|
380
|
+
await evaluateInput(":codec load json", jsonInputState);
|
|
381
|
+
output = await evaluateInput(":input {string a, float64 n}", jsonInputState);
|
|
382
|
+
assert.match(output[0], /enter value text/);
|
|
383
|
+
output = await evaluateInput('{"a":"Woj","n":123}', jsonInputState);
|
|
384
|
+
assert.match(output[0], /json: \{"a":"Woj","n":123\}/);
|
|
385
|
+
|
|
386
|
+
const unitState = createState();
|
|
387
|
+
output = await evaluateInput(":codec load unit", unitState);
|
|
388
|
+
assert.match(output[0], /loaded codec unit/);
|
|
389
|
+
output = await evaluateInput(":input unit", unitState);
|
|
390
|
+
assert.match(output[0], /enter value text/);
|
|
391
|
+
output = await evaluateInput("{}", unitState);
|
|
392
|
+
assert.match(output[0], /unit: \{\}/);
|
|
393
|
+
|
|
394
|
+
const polyAutoState = createState();
|
|
395
|
+
output = await evaluateInput(":load Examples/poly.k", polyAutoState);
|
|
396
|
+
assert.equal(output[0], "loaded Examples/poly.k");
|
|
397
|
+
assert.ok(polyAutoState.relAliases.reverse, "poly relations should be loaded");
|
|
398
|
+
assert.ok(polyAutoState.typeAliases.int, "arithmetics int should be auto-loaded");
|
|
399
|
+
|
|
400
|
+
// Verify :timing and :time commands
|
|
401
|
+
const timingState = createState();
|
|
402
|
+
output = await evaluateInput(":time ()", timingState);
|
|
403
|
+
assert.equal(output[0], "{} ?{}");
|
|
404
|
+
assert.match(output[1], /\/\* comp: .*, exec: .* \*\//);
|
|
405
|
+
assert.ok(timingState.lastTiming);
|
|
406
|
+
assert.ok(typeof timingState.lastTiming.compileMs === "number");
|
|
407
|
+
assert.ok(typeof timingState.lastTiming.executeMs === "number");
|
|
408
|
+
|
|
409
|
+
output = await evaluateInput(":timing on", timingState);
|
|
410
|
+
assert.equal(output[0], "timing reporting enabled");
|
|
411
|
+
assert.equal(timingState.showTiming, true);
|
|
412
|
+
output = await evaluateInput("()", timingState);
|
|
413
|
+
assert.equal(output[0], "{} ?{}");
|
|
414
|
+
assert.match(output[1], /\/\* comp: .*, exec: .* \*\//);
|
|
415
|
+
|
|
416
|
+
output = await evaluateInput(":timing off", timingState);
|
|
417
|
+
assert.equal(output[0], "timing reporting disabled");
|
|
418
|
+
assert.equal(timingState.showTiming, false);
|
|
419
|
+
output = await evaluateInput("()", timingState);
|
|
420
|
+
assert.equal(output.length, 1);
|
|
421
|
+
assert.equal(output[0], "{} ?{}");
|
|
422
|
+
|
|
249
423
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
250
424
|
fs.rmSync(symlinkPath, { force: true });
|
|
251
425
|
console.log("OK");
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { compileLibraryBuffer, compileObjectBuffer, decodeObject } from "../object.mjs";
|
|
3
|
+
import { objectToKIRP, retypeObjectRelation } from "../kir.mjs";
|
|
4
|
+
import { validateKIRP, validateObjectPayload } from "../objects/validate.mjs";
|
|
5
|
+
|
|
6
|
+
const object = decodeObject(compileObjectBuffer("id = (); id", { source: "validate.k" }));
|
|
7
|
+
assert.equal(validateObjectPayload(object), object);
|
|
8
|
+
assert.equal(validateKIRP(objectToKIRP(object)).format, "k-ir");
|
|
9
|
+
const retyped = validateKIRP(retypeObjectRelation(object, "__main__", [["open-product", []]]));
|
|
10
|
+
assert.equal(retyped.layer, "KIR-P");
|
|
11
|
+
assert.equal(retyped.kind, "executable");
|
|
12
|
+
assert(!("instanceKey" in retyped));
|
|
13
|
+
|
|
14
|
+
const library = decodeObject(compileLibraryBuffer("succ = |succ;\n", { source: "validate-lib.k" }));
|
|
15
|
+
assert.equal(validateObjectPayload(library), library);
|
|
16
|
+
assert.equal(validateKIRP(objectToKIRP(library)).kind, "library");
|
|
17
|
+
|
|
18
|
+
const badObject = {
|
|
19
|
+
...object,
|
|
20
|
+
rels: {
|
|
21
|
+
...object.rels,
|
|
22
|
+
__main__: {
|
|
23
|
+
...object.rels.__main__,
|
|
24
|
+
typeDerivation: { status: "maybe" }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
assert.throws(
|
|
29
|
+
() => validateObjectPayload(badObject),
|
|
30
|
+
/typeDerivation\.status must be one of/
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const badKir = objectToKIRP(object);
|
|
34
|
+
badKir.rels.__main__.inputPattern = 999;
|
|
35
|
+
assert.throws(
|
|
36
|
+
() => validateKIRP(badKir),
|
|
37
|
+
/inputPattern is out of range/
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
console.log("OK");
|