@fraczak/k 6.5.0 → 6.7.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/backends/arm64/README.md +0 -1
- package/backends/arm64/bin/k-arm64-compile.mjs +0 -8
- package/backends/arm64/src/runtime.c +20 -4
- package/backends/arm64/tests/integration.sh +1 -1
- package/backends/llvm/README.md +13 -11
- package/backends/llvm/bin/k-llvm-build.mjs +40 -38
- package/backends/llvm/bin/k-llvm-compile.mjs +42 -32
- package/backends/llvm/runtime/krt.c +387 -2
- package/backends/llvm/runtime/krt.h +1 -0
- package/backends/llvm/src/cli.mjs +153 -0
- package/backends/llvm/src/executable.mjs +1 -1
- package/backends/llvm/src/kvm2llvm.mjs +109 -30
- package/backends/llvm/tests/integration.sh +18 -22
- package/backends/llvm/tests/perf-support.mjs +1 -1
- package/backends/wasm/bin/k-wasm-compile.mjs +19 -4
- package/browser/repl-browser.mjs +24 -7
- package/browser/tree-view.mjs +599 -0
- package/package.json +1 -1
- package/repl.mjs +4 -0
- package/scripts/build-repl-html.mjs +301 -0
- package/scripts/perf-ieee.mjs +5 -2
- package/scripts/perf-int.mjs +331 -0
- package/scripts/run-tests.mjs +1 -0
- package/tests/test-repl-html.mjs +141 -0
- package/tests/test-tree-view.mjs +185 -0
package/tests/test-repl-html.mjs
CHANGED
|
@@ -287,6 +287,147 @@ if (chromiumBin) {
|
|
|
287
287
|
})()`);
|
|
288
288
|
assert.strictEqual(selectionCheck, true, "Selection in terminal output should hold after click");
|
|
289
289
|
|
|
290
|
+
// === Test Tree View Functionality ===
|
|
291
|
+
console.log(" Verifying Tree View: initial depth <= 3, folding/unfolding, mode toggle, and pattern cycles...");
|
|
292
|
+
|
|
293
|
+
// 1. Evaluate a 5-level deep nested product: { { { { {} e } d } c } b }
|
|
294
|
+
await evaluateAsync(`window.kRepl.executeCommand("{ { { { {} e } d } c } b }")`);
|
|
295
|
+
|
|
296
|
+
const treeCheck = await evaluateAsync(`(() => {
|
|
297
|
+
const entries = document.querySelectorAll(".terminal-entry");
|
|
298
|
+
const lastEntry = entries[entries.length - 1];
|
|
299
|
+
const container = lastEntry.querySelector(".k-entry-container");
|
|
300
|
+
if (!container) return { error: "No .k-entry-container found" };
|
|
301
|
+
|
|
302
|
+
const treeView = container.querySelector(".k-tree-view");
|
|
303
|
+
const rawView = container.querySelector(".k-raw-view");
|
|
304
|
+
const treeBtn = container.querySelector(".k-btn-mode.active");
|
|
305
|
+
const rawBtn = container.querySelectorAll(".k-btn-mode")[1];
|
|
306
|
+
|
|
307
|
+
// Check Value section
|
|
308
|
+
const valSection = container.querySelector(".k-tree-value-section");
|
|
309
|
+
if (!valSection) return { error: "No value section" };
|
|
310
|
+
|
|
311
|
+
// Check product details nodes hierarchy
|
|
312
|
+
// Level 1: root product (depth 1)
|
|
313
|
+
const level1 = valSection.querySelector(".k-tree-section-body > .k-tree-node.k-tree-product");
|
|
314
|
+
if (!level1 || !level1.open) return { error: "Level 1 product should be open" };
|
|
315
|
+
|
|
316
|
+
// Level 2: .b product (depth 2)
|
|
317
|
+
const level2 = level1.querySelector(".k-tree-children .k-tree-node.k-tree-product");
|
|
318
|
+
if (!level2 || !level2.open) return { error: "Level 2 product should be open" };
|
|
319
|
+
|
|
320
|
+
// Level 3: .c product (depth 3)
|
|
321
|
+
const level3 = level2.querySelector(".k-tree-children .k-tree-node.k-tree-product");
|
|
322
|
+
if (!level3 || !level3.open) return { error: "Level 3 product should be open" };
|
|
323
|
+
|
|
324
|
+
// Level 4: .d product (depth 4) -> MUST BE COLLAPSED (open === false)
|
|
325
|
+
const level4 = level3.querySelector(".k-tree-children .k-tree-node.k-tree-product");
|
|
326
|
+
if (!level4) return { error: "Level 4 product node missing" };
|
|
327
|
+
if (level4.open) return { error: "Level 4 product should be collapsed (open === false)" };
|
|
328
|
+
|
|
329
|
+
// Check pattern section exists and has cycle or vardef
|
|
330
|
+
const patSection = container.querySelector(".k-tree-pat-section");
|
|
331
|
+
if (!patSection) return { error: "No pattern section" };
|
|
332
|
+
|
|
333
|
+
// Test Mode Toggle: click Raw
|
|
334
|
+
rawBtn.click();
|
|
335
|
+
const rawVisible = rawView.style.display !== "none" && treeView.style.display === "none";
|
|
336
|
+
|
|
337
|
+
// Click Tree back
|
|
338
|
+
container.querySelector(".k-btn-mode").click();
|
|
339
|
+
const treeVisible = treeView.style.display !== "none" && rawView.style.display === "none";
|
|
340
|
+
|
|
341
|
+
// Test Unfolding Level 4
|
|
342
|
+
level4.open = true;
|
|
343
|
+
level4.dispatchEvent(new Event("toggle"));
|
|
344
|
+
|
|
345
|
+
// After toggle, level 4's children should be populated with .e
|
|
346
|
+
const level4Text = level4.textContent;
|
|
347
|
+
const unfoldedHasE = level4Text.includes(".e") || level4Text.includes("42");
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
success: true,
|
|
351
|
+
rawVisible,
|
|
352
|
+
treeVisible,
|
|
353
|
+
unfoldedHasE
|
|
354
|
+
};
|
|
355
|
+
})()`);
|
|
356
|
+
|
|
357
|
+
assert.strictEqual(treeCheck.success, true, `Tree view validation failed: ${treeCheck.error}`);
|
|
358
|
+
assert.strictEqual(treeCheck.rawVisible, true, "Clicking Raw should show raw view and hide tree view");
|
|
359
|
+
assert.strictEqual(treeCheck.treeVisible, true, "Clicking Tree should restore tree view");
|
|
360
|
+
assert.strictEqual(treeCheck.unfoldedHasE, true, "Unfolding level 4 should lazily populate its children");
|
|
361
|
+
|
|
362
|
+
// 2. Test recursive pattern tree with cycle detection
|
|
363
|
+
await evaluateAsync(`window.kRepl.executeCommand("10")`);
|
|
364
|
+
const patCycleCheck = await evaluateAsync(`(() => {
|
|
365
|
+
const entries = document.querySelectorAll(".terminal-entry");
|
|
366
|
+
const lastEntry = entries[entries.length - 1];
|
|
367
|
+
const patSection = lastEntry.querySelector(".k-tree-pat-section");
|
|
368
|
+
if (!patSection) return { error: "No pattern section for 10" };
|
|
369
|
+
|
|
370
|
+
const cycleBadges = patSection.querySelectorAll(".k-badge-cycle");
|
|
371
|
+
const varDefs = patSection.querySelectorAll(".k-pat-vardef");
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
hasCycleBadge: cycleBadges.length > 0,
|
|
375
|
+
hasVarDef: varDefs.length > 0
|
|
376
|
+
};
|
|
377
|
+
})()`);
|
|
378
|
+
|
|
379
|
+
assert.strictEqual(patCycleCheck.hasCycleBadge, true, "Pattern tree for recursive type should have cycle badge");
|
|
380
|
+
assert.strictEqual(patCycleCheck.hasVarDef, true, "Pattern tree for recursive type should have variable definition (=X0)");
|
|
381
|
+
|
|
382
|
+
// 3. Test variant chain depth limits and Expand All / Collapse All on 10
|
|
383
|
+
const variantDepthAndActionsCheck = await evaluateAsync(`(() => {
|
|
384
|
+
const entries = document.querySelectorAll(".terminal-entry");
|
|
385
|
+
const lastEntry = entries[entries.length - 1];
|
|
386
|
+
const valSection = lastEntry.querySelector(".k-tree-value-section");
|
|
387
|
+
|
|
388
|
+
// Hierarchy for 10 (variant chain: + -> 1 -> 0 -> 1 -> ...)
|
|
389
|
+
const v1 = valSection?.querySelector(".k-tree-section-body > .k-tree-node.k-tree-variant");
|
|
390
|
+
const v2 = v1?.querySelector(".k-tree-children .k-tree-node.k-tree-variant");
|
|
391
|
+
const v3 = v2?.querySelector(".k-tree-children .k-tree-node.k-tree-variant");
|
|
392
|
+
const v4 = v3?.querySelector(".k-tree-children .k-tree-node.k-tree-variant");
|
|
393
|
+
|
|
394
|
+
const v1Open = !!v1?.open;
|
|
395
|
+
const v2Open = !!v2?.open;
|
|
396
|
+
const v3Open = !!v3?.open;
|
|
397
|
+
const v4Collapsed = v4 ? !v4.open : false;
|
|
398
|
+
|
|
399
|
+
// Click Expand All
|
|
400
|
+
const actionBtns = lastEntry.querySelectorAll(".k-btn-action");
|
|
401
|
+
const expandBtn = actionBtns[0];
|
|
402
|
+
const collapseBtn = actionBtns[1];
|
|
403
|
+
|
|
404
|
+
expandBtn.click();
|
|
405
|
+
const allOpenAfterExpand = v4.open;
|
|
406
|
+
|
|
407
|
+
// Click Collapse All
|
|
408
|
+
collapseBtn.click();
|
|
409
|
+
const newV4 = valSection?.querySelector(
|
|
410
|
+
".k-tree-section-body > .k-tree-node.k-tree-variant .k-tree-children .k-tree-node.k-tree-variant .k-tree-children .k-tree-node.k-tree-variant .k-tree-children .k-tree-node.k-tree-variant"
|
|
411
|
+
);
|
|
412
|
+
const v4CollapsedAgain = newV4 ? !newV4.open : false;
|
|
413
|
+
|
|
414
|
+
return {
|
|
415
|
+
v1Open,
|
|
416
|
+
v2Open,
|
|
417
|
+
v3Open,
|
|
418
|
+
v4Collapsed,
|
|
419
|
+
allOpenAfterExpand,
|
|
420
|
+
v4CollapsedAgain
|
|
421
|
+
};
|
|
422
|
+
})()`);
|
|
423
|
+
|
|
424
|
+
assert.strictEqual(variantDepthAndActionsCheck.v1Open, true, "Variant level 1 must be open");
|
|
425
|
+
assert.strictEqual(variantDepthAndActionsCheck.v2Open, true, "Variant level 2 must be open");
|
|
426
|
+
assert.strictEqual(variantDepthAndActionsCheck.v3Open, true, "Variant level 3 must be open");
|
|
427
|
+
assert.strictEqual(variantDepthAndActionsCheck.v4Collapsed, true, "Variant level 4 must be collapsed by default");
|
|
428
|
+
assert.strictEqual(variantDepthAndActionsCheck.allOpenAfterExpand, true, "Expand button should expand collapsed nodes");
|
|
429
|
+
assert.strictEqual(variantDepthAndActionsCheck.v4CollapsedAgain, true, "Collapse button should reset deeper nodes to collapsed");
|
|
430
|
+
|
|
290
431
|
ws.close();
|
|
291
432
|
console.log(" Browser execution verified successfully!");
|
|
292
433
|
} finally {
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { Value } from "../Value.mjs";
|
|
3
|
+
import {
|
|
4
|
+
renderValueNode,
|
|
5
|
+
renderPatternTree,
|
|
6
|
+
createOutputEntryElement
|
|
7
|
+
} from "../browser/tree-view.mjs";
|
|
8
|
+
|
|
9
|
+
console.log("=== Testing Tree View (browser/tree-view.mjs) ===");
|
|
10
|
+
|
|
11
|
+
// Lightweight DOM mock for testing tree construction in Node.js
|
|
12
|
+
class MockElement {
|
|
13
|
+
constructor(tagName) {
|
|
14
|
+
this.tagName = tagName.toUpperCase();
|
|
15
|
+
this.className = "";
|
|
16
|
+
this._textContent = "";
|
|
17
|
+
this._innerHTML = "";
|
|
18
|
+
this.open = false;
|
|
19
|
+
this.children = [];
|
|
20
|
+
this.listeners = {};
|
|
21
|
+
this.style = {};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get textContent() {
|
|
25
|
+
if (this._textContent) return this._textContent;
|
|
26
|
+
return this.children.map((c) => (typeof c === "string" ? c : c.textContent)).join("");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
set textContent(val) {
|
|
30
|
+
this._textContent = String(val);
|
|
31
|
+
this.children = [];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get innerHTML() {
|
|
35
|
+
return this._innerHTML || this.textContent;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
set innerHTML(val) {
|
|
39
|
+
this._innerHTML = String(val);
|
|
40
|
+
this._textContent = String(val).replace(/<[^>]*>/g, "");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
appendChild(child) {
|
|
44
|
+
this.children.push(child);
|
|
45
|
+
return child;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
addEventListener(type, fn) {
|
|
49
|
+
if (!this.listeners[type]) this.listeners[type] = [];
|
|
50
|
+
this.listeners[type].push(fn);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
dispatchEvent(event) {
|
|
54
|
+
const list = this.listeners[event.type] || [];
|
|
55
|
+
for (const fn of list) fn(event);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
querySelectorAll(selector) {
|
|
59
|
+
const results = [];
|
|
60
|
+
const check = (el) => {
|
|
61
|
+
for (const child of el.children) {
|
|
62
|
+
if (child instanceof MockElement) {
|
|
63
|
+
let matches = false;
|
|
64
|
+
if (selector === "details" && child.tagName === "DETAILS") matches = true;
|
|
65
|
+
if (selector.startsWith(".") && child.className.split(" ").includes(selector.slice(1))) matches = true;
|
|
66
|
+
if (matches) results.push(child);
|
|
67
|
+
check(child);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
check(this);
|
|
72
|
+
return results;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
querySelector(selector) {
|
|
76
|
+
return this.querySelectorAll(selector)[0] || null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
globalThis.document = {
|
|
81
|
+
createElement(tag) {
|
|
82
|
+
return new MockElement(tag);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
globalThis.Event = class Event {
|
|
86
|
+
constructor(type) {
|
|
87
|
+
this.type = type;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// 1. Test renderValueNode for empty product
|
|
92
|
+
const emptyVal = Value.product({});
|
|
93
|
+
const emptyEl = renderValueNode(emptyVal);
|
|
94
|
+
assert.strictEqual(emptyEl.textContent, "{}", "Empty product must render as '{}'");
|
|
95
|
+
|
|
96
|
+
// 2. Test renderValueNode for nested products (depth tracking)
|
|
97
|
+
// { a: { b: { c: { d: {} } } } }
|
|
98
|
+
const deepProduct = Value.product({
|
|
99
|
+
a: Value.product({
|
|
100
|
+
b: Value.product({
|
|
101
|
+
c: Value.product({
|
|
102
|
+
d: Value.product({})
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const treeRoot = renderValueNode(deepProduct, 1, 3);
|
|
109
|
+
assert.strictEqual(treeRoot.tagName, "DETAILS", "Root product should be a <details> element");
|
|
110
|
+
assert.strictEqual(treeRoot.open, true, "Root product (depth 1) must be open");
|
|
111
|
+
|
|
112
|
+
// Find child .a
|
|
113
|
+
const nodeA = treeRoot.querySelectorAll(".k-tree-node")[0];
|
|
114
|
+
assert(nodeA, "Node .a must exist");
|
|
115
|
+
assert.strictEqual(nodeA.open, true, "Node .a (depth 2) must be open");
|
|
116
|
+
|
|
117
|
+
// Find child .b
|
|
118
|
+
const nodeB = nodeA.querySelectorAll(".k-tree-node")[0];
|
|
119
|
+
assert(nodeB, "Node .b must exist");
|
|
120
|
+
assert.strictEqual(nodeB.open, true, "Node .b (depth 3) must be open");
|
|
121
|
+
|
|
122
|
+
// Find child .c
|
|
123
|
+
const nodeC = nodeB.querySelectorAll(".k-tree-node")[0];
|
|
124
|
+
assert(nodeC, "Node .c must exist");
|
|
125
|
+
assert.strictEqual(nodeC.open, false, "Node .c (depth 4) must be collapsed (open === false)");
|
|
126
|
+
|
|
127
|
+
// Test lazy unfolding of node .c
|
|
128
|
+
assert.strictEqual(nodeC.querySelector(".k-tree-children").children.length, 0, "Children of collapsed node must not be populated initially");
|
|
129
|
+
nodeC.open = true;
|
|
130
|
+
nodeC.dispatchEvent(new Event("toggle"));
|
|
131
|
+
assert(nodeC.querySelector(".k-tree-children").children.length > 0, "Unfolding node .c must lazily populate its children");
|
|
132
|
+
assert(nodeC.textContent.includes(".d:"), "Unfolded children must include .d:");
|
|
133
|
+
|
|
134
|
+
// 3. Test renderValueNode for variant chain (integers: + -> 1 -> 0 -> 1 -> _ -> {})
|
|
135
|
+
const variantVal = Value.variant(
|
|
136
|
+
"+",
|
|
137
|
+
Value.variant(
|
|
138
|
+
"1",
|
|
139
|
+
Value.variant(
|
|
140
|
+
"0",
|
|
141
|
+
Value.variant(
|
|
142
|
+
"1",
|
|
143
|
+
Value.variant("_", Value.product({}))
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
)
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const varTree = renderValueNode(variantVal, 1, 3);
|
|
150
|
+
assert.strictEqual(varTree.tagName, "DETAILS");
|
|
151
|
+
assert.strictEqual(varTree.open, true, "Variant depth 1 (+) must be open");
|
|
152
|
+
|
|
153
|
+
const var2 = varTree.querySelectorAll(".k-tree-variant")[0];
|
|
154
|
+
assert.strictEqual(var2.open, true, "Variant depth 2 (1) must be open");
|
|
155
|
+
|
|
156
|
+
const var3 = var2.querySelectorAll(".k-tree-variant")[0];
|
|
157
|
+
assert.strictEqual(var3.open, true, "Variant depth 3 (0) must be open");
|
|
158
|
+
|
|
159
|
+
const var4 = var3.querySelectorAll(".k-tree-variant")[0];
|
|
160
|
+
assert.strictEqual(var4.open, false, "Variant depth 4 (1) must be collapsed");
|
|
161
|
+
|
|
162
|
+
// 4. Test renderPatternTree with cycles (recursive integer type: ?<<X0 0, X0 1, {} _>=X0 +, X0 ->)
|
|
163
|
+
const recursivePattern = [
|
|
164
|
+
["closed-union", [["+", 1], ["-", 1]]],
|
|
165
|
+
["closed-union", [["0", 1], ["1", 1], ["_", 2]]],
|
|
166
|
+
["closed-product", []]
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
const patTree = renderPatternTree(recursivePattern, 3);
|
|
170
|
+
assert.strictEqual(patTree.tagName, "DETAILS");
|
|
171
|
+
assert.strictEqual(patTree.open, true, "Root pattern node must be open");
|
|
172
|
+
assert(patTree.textContent.includes("recursive"), "Recursive cycle must be detected and marked 'recursive'");
|
|
173
|
+
|
|
174
|
+
// 5. Test createOutputEntryElement
|
|
175
|
+
const entryEl = createOutputEntryElement({
|
|
176
|
+
value: variantVal,
|
|
177
|
+
rawText: "{}|_|1|0|1|+ ?<<X0 0, X0 1, {} _>=X0 +, X0 ->\nint: 11",
|
|
178
|
+
codecOutputs: ["int: 11"]
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
assert(entryEl.querySelector(".k-tree-view"), "Tree view container must exist");
|
|
182
|
+
assert(entryEl.querySelector(".k-raw-view"), "Raw view container must exist");
|
|
183
|
+
assert(entryEl.textContent.includes("int: 11"), "Codec output must be visible in container");
|
|
184
|
+
|
|
185
|
+
console.log("OK - All tree view unit tests passed.");
|