@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
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
import { isProduct, isVariant } from "../Value.mjs";
|
|
2
|
+
import { propertyListToFilter, valueToK } from "../codecs/runtime/show-value.mjs";
|
|
3
|
+
|
|
4
|
+
function escapeHtml(str) {
|
|
5
|
+
return String(str)
|
|
6
|
+
.replace(/&/g, "&")
|
|
7
|
+
.replace(/</g, "<")
|
|
8
|
+
.replace(/>/g, ">")
|
|
9
|
+
.replace(/"/g, """)
|
|
10
|
+
.replace(/'/g, "'");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function previewProductFields(val, maxItems = 3) {
|
|
14
|
+
if (!isProduct(val)) return "";
|
|
15
|
+
const keys = Object.keys(val.product);
|
|
16
|
+
if (keys.length === 0) return "{}";
|
|
17
|
+
const previewKeys = keys.slice(0, maxItems).map((k) => `.${k}`);
|
|
18
|
+
if (keys.length > maxItems) previewKeys.push("...");
|
|
19
|
+
return previewKeys.join(", ");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function previewValue(val, maxItems = 4) {
|
|
23
|
+
if (val === null || val === undefined) return String(val);
|
|
24
|
+
if (isProduct(val)) {
|
|
25
|
+
const keys = Object.keys(val.product);
|
|
26
|
+
if (keys.length === 0) return "{}";
|
|
27
|
+
const previewKeys = keys.slice(0, maxItems).map((k) => `.${k}`);
|
|
28
|
+
if (keys.length > maxItems) previewKeys.push("...");
|
|
29
|
+
return `{ ${previewKeys.join(", ")} }`;
|
|
30
|
+
}
|
|
31
|
+
if (isVariant(val)) {
|
|
32
|
+
const tags = [];
|
|
33
|
+
let curr = val;
|
|
34
|
+
while (isVariant(curr) && tags.length < maxItems) {
|
|
35
|
+
tags.push(curr.tag);
|
|
36
|
+
curr = curr.value;
|
|
37
|
+
}
|
|
38
|
+
if (isVariant(curr)) tags.push("...");
|
|
39
|
+
return `| ${tags.join(" | ")}`;
|
|
40
|
+
}
|
|
41
|
+
return String(val);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function renderValueNode(val, currentDepth = 1, depthLimit = 3) {
|
|
45
|
+
if (val === null || val === undefined) {
|
|
46
|
+
const el = document.createElement("span");
|
|
47
|
+
el.className = "k-tree-leaf k-val-nil";
|
|
48
|
+
el.textContent = String(val);
|
|
49
|
+
return el;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (isProduct(val)) {
|
|
53
|
+
const keys = Object.keys(val.product);
|
|
54
|
+
if (keys.length === 0) {
|
|
55
|
+
const el = document.createElement("span");
|
|
56
|
+
el.className = "k-tree-leaf k-val-empty";
|
|
57
|
+
el.textContent = "{}";
|
|
58
|
+
return el;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const details = document.createElement("details");
|
|
62
|
+
details.className = "k-tree-node k-tree-product";
|
|
63
|
+
const isOpen = currentDepth <= depthLimit;
|
|
64
|
+
details.open = isOpen;
|
|
65
|
+
|
|
66
|
+
const summary = document.createElement("summary");
|
|
67
|
+
summary.className = "k-tree-summary";
|
|
68
|
+
|
|
69
|
+
const arrow = document.createElement("span");
|
|
70
|
+
arrow.className = "k-tree-arrow";
|
|
71
|
+
arrow.textContent = "▶";
|
|
72
|
+
summary.appendChild(arrow);
|
|
73
|
+
|
|
74
|
+
const openBrace = document.createElement("span");
|
|
75
|
+
openBrace.className = "k-val-brace";
|
|
76
|
+
openBrace.textContent = "{";
|
|
77
|
+
summary.appendChild(openBrace);
|
|
78
|
+
|
|
79
|
+
const badge = document.createElement("span");
|
|
80
|
+
badge.className = "k-tree-badge";
|
|
81
|
+
badge.textContent = `${keys.length} ${keys.length === 1 ? "field" : "fields"}`;
|
|
82
|
+
summary.appendChild(badge);
|
|
83
|
+
|
|
84
|
+
const preview = document.createElement("span");
|
|
85
|
+
preview.className = "k-val-preview";
|
|
86
|
+
preview.textContent = previewProductFields(val, 3);
|
|
87
|
+
summary.appendChild(preview);
|
|
88
|
+
|
|
89
|
+
const closeBrace = document.createElement("span");
|
|
90
|
+
closeBrace.className = "k-val-brace";
|
|
91
|
+
closeBrace.textContent = "}";
|
|
92
|
+
summary.appendChild(closeBrace);
|
|
93
|
+
|
|
94
|
+
details.appendChild(summary);
|
|
95
|
+
|
|
96
|
+
const childrenContainer = document.createElement("div");
|
|
97
|
+
childrenContainer.className = "k-tree-children";
|
|
98
|
+
details.appendChild(childrenContainer);
|
|
99
|
+
|
|
100
|
+
let populated = false;
|
|
101
|
+
const populateChildren = () => {
|
|
102
|
+
if (populated) return;
|
|
103
|
+
populated = true;
|
|
104
|
+
for (const k of keys) {
|
|
105
|
+
const row = document.createElement("div");
|
|
106
|
+
row.className = "k-tree-row";
|
|
107
|
+
|
|
108
|
+
const keySpan = document.createElement("span");
|
|
109
|
+
keySpan.className = "k-tree-field-key";
|
|
110
|
+
keySpan.textContent = `.${k}:`;
|
|
111
|
+
row.appendChild(keySpan);
|
|
112
|
+
|
|
113
|
+
const childContainer = document.createElement("div");
|
|
114
|
+
childContainer.className = "k-tree-child-val";
|
|
115
|
+
childContainer.appendChild(
|
|
116
|
+
renderValueNode(val.product[k], isOpen ? currentDepth + 1 : 1, depthLimit)
|
|
117
|
+
);
|
|
118
|
+
row.appendChild(childContainer);
|
|
119
|
+
|
|
120
|
+
childrenContainer.appendChild(row);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
if (isOpen) {
|
|
125
|
+
populateChildren();
|
|
126
|
+
} else {
|
|
127
|
+
details.addEventListener("toggle", () => {
|
|
128
|
+
if (details.open) {
|
|
129
|
+
populateChildren();
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return details;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (isVariant(val)) {
|
|
138
|
+
const isTerminal = isProduct(val.value) && Object.keys(val.value.product).length === 0;
|
|
139
|
+
if (isTerminal) {
|
|
140
|
+
const el = document.createElement("span");
|
|
141
|
+
el.className = "k-tree-leaf k-tree-variant-leaf";
|
|
142
|
+
el.innerHTML = `<span class="k-val-pipe">|</span> <span class="k-val-tag">${escapeHtml(val.tag)}</span>`;
|
|
143
|
+
return el;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const details = document.createElement("details");
|
|
147
|
+
details.className = "k-tree-node k-tree-variant";
|
|
148
|
+
const isOpen = currentDepth <= depthLimit;
|
|
149
|
+
details.open = isOpen;
|
|
150
|
+
|
|
151
|
+
const summary = document.createElement("summary");
|
|
152
|
+
summary.className = "k-tree-summary";
|
|
153
|
+
|
|
154
|
+
const arrow = document.createElement("span");
|
|
155
|
+
arrow.className = "k-tree-arrow";
|
|
156
|
+
arrow.textContent = "▶";
|
|
157
|
+
summary.appendChild(arrow);
|
|
158
|
+
|
|
159
|
+
const pipeSpan = document.createElement("span");
|
|
160
|
+
pipeSpan.className = "k-val-pipe";
|
|
161
|
+
pipeSpan.textContent = "|";
|
|
162
|
+
summary.appendChild(pipeSpan);
|
|
163
|
+
|
|
164
|
+
const tagSpan = document.createElement("span");
|
|
165
|
+
tagSpan.className = "k-val-tag";
|
|
166
|
+
tagSpan.textContent = val.tag;
|
|
167
|
+
summary.appendChild(tagSpan);
|
|
168
|
+
|
|
169
|
+
if (isVariant(val.value)) {
|
|
170
|
+
let chainCount = 0;
|
|
171
|
+
let curr = val.value;
|
|
172
|
+
while (isVariant(curr)) {
|
|
173
|
+
chainCount++;
|
|
174
|
+
curr = curr.value;
|
|
175
|
+
}
|
|
176
|
+
const badge = document.createElement("span");
|
|
177
|
+
badge.className = "k-tree-badge";
|
|
178
|
+
badge.textContent = `+${chainCount} tags`;
|
|
179
|
+
summary.appendChild(badge);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const preview = document.createElement("span");
|
|
183
|
+
preview.className = "k-val-preview";
|
|
184
|
+
preview.textContent = previewValue(val.value, 3);
|
|
185
|
+
summary.appendChild(preview);
|
|
186
|
+
|
|
187
|
+
details.appendChild(summary);
|
|
188
|
+
|
|
189
|
+
const childrenContainer = document.createElement("div");
|
|
190
|
+
childrenContainer.className = "k-tree-children";
|
|
191
|
+
details.appendChild(childrenContainer);
|
|
192
|
+
|
|
193
|
+
let populated = false;
|
|
194
|
+
const populateChild = () => {
|
|
195
|
+
if (populated) return;
|
|
196
|
+
populated = true;
|
|
197
|
+
const row = document.createElement("div");
|
|
198
|
+
row.className = "k-tree-row";
|
|
199
|
+
const childContainer = document.createElement("div");
|
|
200
|
+
childContainer.className = "k-tree-child-val";
|
|
201
|
+
childContainer.appendChild(
|
|
202
|
+
renderValueNode(val.value, isOpen ? currentDepth + 1 : 1, depthLimit)
|
|
203
|
+
);
|
|
204
|
+
row.appendChild(childContainer);
|
|
205
|
+
childrenContainer.appendChild(row);
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
if (isOpen) {
|
|
209
|
+
populateChild();
|
|
210
|
+
} else {
|
|
211
|
+
details.addEventListener("toggle", () => {
|
|
212
|
+
if (details.open) {
|
|
213
|
+
populateChild();
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return details;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const el = document.createElement("span");
|
|
222
|
+
el.className = "k-tree-leaf k-val-primitive";
|
|
223
|
+
el.textContent = String(val);
|
|
224
|
+
return el;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function renderPatternTree(propertyList, depthLimit = 3) {
|
|
228
|
+
if (!propertyList || propertyList.length === 0) {
|
|
229
|
+
const el = document.createElement("span");
|
|
230
|
+
el.className = "k-tree-leaf k-pat-empty";
|
|
231
|
+
el.textContent = "(...)";
|
|
232
|
+
return el;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const refCount = new Array(propertyList.length).fill(0);
|
|
236
|
+
for (const [, edges] of propertyList) {
|
|
237
|
+
for (const [, target] of edges) {
|
|
238
|
+
if (target >= 0 && target < propertyList.length) {
|
|
239
|
+
refCount[target]++;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
refCount[0]++;
|
|
244
|
+
|
|
245
|
+
const varNames = new Map();
|
|
246
|
+
let varCounter = 0;
|
|
247
|
+
for (let i = 0; i < propertyList.length; i++) {
|
|
248
|
+
if (refCount[i] > 1) {
|
|
249
|
+
varNames.set(i, `X${varCounter++}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const definedNodes = new Set();
|
|
254
|
+
|
|
255
|
+
function renderNode(nodeId, currentDepth, maxDepth, ancestors = new Set()) {
|
|
256
|
+
if (nodeId == null || nodeId < 0 || nodeId >= propertyList.length) {
|
|
257
|
+
const el = document.createElement("span");
|
|
258
|
+
el.className = "k-tree-leaf k-pat-empty";
|
|
259
|
+
el.textContent = "(...)";
|
|
260
|
+
return el;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const hasVar = varNames.has(nodeId);
|
|
264
|
+
const varName = hasVar ? varNames.get(nodeId) : null;
|
|
265
|
+
|
|
266
|
+
// Cycle detection
|
|
267
|
+
if (ancestors.has(nodeId)) {
|
|
268
|
+
const el = document.createElement("span");
|
|
269
|
+
el.className = "k-tree-leaf k-pat-cycle";
|
|
270
|
+
el.innerHTML = `<span class="k-pat-var">${escapeHtml(varName || `X${nodeId}`)}</span> <span class="k-tree-badge k-badge-cycle">recursive</span>`;
|
|
271
|
+
return el;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Already defined on an earlier branch
|
|
275
|
+
if (hasVar && definedNodes.has(nodeId)) {
|
|
276
|
+
const el = document.createElement("span");
|
|
277
|
+
el.className = "k-tree-leaf k-pat-ref";
|
|
278
|
+
el.innerHTML = `<span class="k-pat-var">${escapeHtml(varName)}</span>`;
|
|
279
|
+
return el;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (hasVar) {
|
|
283
|
+
definedNodes.add(nodeId);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const newAncestors = new Set(ancestors);
|
|
287
|
+
newAncestors.add(nodeId);
|
|
288
|
+
|
|
289
|
+
const [kind, edges] = propertyList[nodeId];
|
|
290
|
+
|
|
291
|
+
if (kind === "any") {
|
|
292
|
+
const el = document.createElement("span");
|
|
293
|
+
el.className = "k-tree-leaf k-pat-any";
|
|
294
|
+
el.textContent = hasVar ? varName : "(...)";
|
|
295
|
+
return el;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const isProductPat = kind.endsWith("product");
|
|
299
|
+
const isOpenPat = kind.startsWith("open-");
|
|
300
|
+
const openChar = isProductPat ? "{" : "<";
|
|
301
|
+
const closeChar = isProductPat ? "}" : ">";
|
|
302
|
+
const itemLabel = isProductPat ? "field" : "variant";
|
|
303
|
+
|
|
304
|
+
if (edges.length === 0 && !isOpenPat) {
|
|
305
|
+
const el = document.createElement("span");
|
|
306
|
+
el.className = "k-tree-leaf k-pat-empty";
|
|
307
|
+
const varSuffix = hasVar ? `=${varName}` : "";
|
|
308
|
+
el.innerHTML = `<span class="k-pat-brace">${openChar}${closeChar}</span>${varSuffix ? `<span class="k-pat-vardef">${varSuffix}</span>` : ""}`;
|
|
309
|
+
return el;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const details = document.createElement("details");
|
|
313
|
+
details.className = `k-tree-node ${isProductPat ? "k-pat-product" : "k-pat-union"}`;
|
|
314
|
+
const isOpen = currentDepth <= maxDepth;
|
|
315
|
+
details.open = isOpen;
|
|
316
|
+
|
|
317
|
+
const summary = document.createElement("summary");
|
|
318
|
+
summary.className = "k-tree-summary";
|
|
319
|
+
|
|
320
|
+
const arrow = document.createElement("span");
|
|
321
|
+
arrow.className = "k-tree-arrow";
|
|
322
|
+
arrow.textContent = "▶";
|
|
323
|
+
summary.appendChild(arrow);
|
|
324
|
+
|
|
325
|
+
const openSpan = document.createElement("span");
|
|
326
|
+
openSpan.className = isProductPat ? "k-pat-brace" : "k-pat-union";
|
|
327
|
+
openSpan.textContent = openChar;
|
|
328
|
+
summary.appendChild(openSpan);
|
|
329
|
+
|
|
330
|
+
const badge = document.createElement("span");
|
|
331
|
+
badge.className = "k-tree-badge";
|
|
332
|
+
badge.textContent = `${edges.length} ${edges.length === 1 ? itemLabel : itemLabel + "s"}${isOpenPat ? ", ..." : ""}`;
|
|
333
|
+
summary.appendChild(badge);
|
|
334
|
+
|
|
335
|
+
if (hasVar) {
|
|
336
|
+
const varDefSpan = document.createElement("span");
|
|
337
|
+
varDefSpan.className = "k-pat-vardef";
|
|
338
|
+
varDefSpan.textContent = `=${varName}`;
|
|
339
|
+
summary.appendChild(varDefSpan);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const closeSpan = document.createElement("span");
|
|
343
|
+
closeSpan.className = isProductPat ? "k-pat-brace" : "k-pat-union";
|
|
344
|
+
closeSpan.textContent = closeChar;
|
|
345
|
+
summary.appendChild(closeSpan);
|
|
346
|
+
|
|
347
|
+
details.appendChild(summary);
|
|
348
|
+
|
|
349
|
+
const childrenContainer = document.createElement("div");
|
|
350
|
+
childrenContainer.className = "k-tree-children";
|
|
351
|
+
details.appendChild(childrenContainer);
|
|
352
|
+
|
|
353
|
+
let populated = false;
|
|
354
|
+
const populateChildren = () => {
|
|
355
|
+
if (populated) return;
|
|
356
|
+
populated = true;
|
|
357
|
+
for (const [label, target] of edges) {
|
|
358
|
+
const row = document.createElement("div");
|
|
359
|
+
row.className = "k-tree-row";
|
|
360
|
+
|
|
361
|
+
const labelSpan = document.createElement("span");
|
|
362
|
+
if (isProductPat) {
|
|
363
|
+
labelSpan.className = "k-pat-field-key";
|
|
364
|
+
labelSpan.textContent = `.${label}:`;
|
|
365
|
+
} else {
|
|
366
|
+
labelSpan.className = "k-pat-tag";
|
|
367
|
+
labelSpan.innerHTML = `<span class="k-val-pipe">|</span> ${escapeHtml(label)}`;
|
|
368
|
+
}
|
|
369
|
+
row.appendChild(labelSpan);
|
|
370
|
+
|
|
371
|
+
const childContainer = document.createElement("div");
|
|
372
|
+
childContainer.className = "k-tree-child-pat";
|
|
373
|
+
childContainer.appendChild(
|
|
374
|
+
renderNode(target, isOpen ? currentDepth + 1 : 1, maxDepth, newAncestors)
|
|
375
|
+
);
|
|
376
|
+
row.appendChild(childContainer);
|
|
377
|
+
|
|
378
|
+
childrenContainer.appendChild(row);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (isOpenPat) {
|
|
382
|
+
const row = document.createElement("div");
|
|
383
|
+
row.className = "k-tree-row";
|
|
384
|
+
const ellipsis = document.createElement("span");
|
|
385
|
+
ellipsis.className = "k-pat-ellipsis";
|
|
386
|
+
ellipsis.textContent = "...";
|
|
387
|
+
row.appendChild(ellipsis);
|
|
388
|
+
childrenContainer.appendChild(row);
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
if (isOpen) {
|
|
393
|
+
populateChildren();
|
|
394
|
+
} else {
|
|
395
|
+
details.addEventListener("toggle", () => {
|
|
396
|
+
if (details.open) {
|
|
397
|
+
populateChildren();
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return details;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return renderNode(0, 1, depthLimit);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
export function createOutputEntryElement({
|
|
409
|
+
value,
|
|
410
|
+
rawText,
|
|
411
|
+
codecOutputs = [],
|
|
412
|
+
timingText = null
|
|
413
|
+
}) {
|
|
414
|
+
const container = document.createElement("div");
|
|
415
|
+
container.className = "entry-line line-output k-entry-container";
|
|
416
|
+
|
|
417
|
+
// Toolbar
|
|
418
|
+
const toolbar = document.createElement("div");
|
|
419
|
+
toolbar.className = "k-output-toolbar";
|
|
420
|
+
|
|
421
|
+
// Mode buttons group
|
|
422
|
+
const modeGroup = document.createElement("div");
|
|
423
|
+
modeGroup.className = "k-mode-group";
|
|
424
|
+
|
|
425
|
+
const treeBtn = document.createElement("button");
|
|
426
|
+
treeBtn.type = "button";
|
|
427
|
+
treeBtn.className = "k-btn-mode active";
|
|
428
|
+
treeBtn.innerHTML = `<span class="k-mode-icon">🌲</span> Tree`;
|
|
429
|
+
treeBtn.title = "View structured Value & Pattern trees";
|
|
430
|
+
|
|
431
|
+
const rawBtn = document.createElement("button");
|
|
432
|
+
rawBtn.type = "button";
|
|
433
|
+
rawBtn.className = "k-btn-mode";
|
|
434
|
+
rawBtn.innerHTML = `<span class="k-mode-icon">📄</span> Raw`;
|
|
435
|
+
rawBtn.title = "View raw K expression & pattern text";
|
|
436
|
+
|
|
437
|
+
modeGroup.appendChild(treeBtn);
|
|
438
|
+
modeGroup.appendChild(rawBtn);
|
|
439
|
+
toolbar.appendChild(modeGroup);
|
|
440
|
+
|
|
441
|
+
// Tree Action buttons group (Expand / Collapse / Copy)
|
|
442
|
+
const actionGroup = document.createElement("div");
|
|
443
|
+
actionGroup.className = "k-action-group";
|
|
444
|
+
|
|
445
|
+
const expandAllBtn = document.createElement("button");
|
|
446
|
+
expandAllBtn.type = "button";
|
|
447
|
+
expandAllBtn.className = "k-btn-action";
|
|
448
|
+
expandAllBtn.innerHTML = `⊞ Expand`;
|
|
449
|
+
expandAllBtn.title = "Expand tree nodes";
|
|
450
|
+
|
|
451
|
+
const collapseAllBtn = document.createElement("button");
|
|
452
|
+
collapseAllBtn.type = "button";
|
|
453
|
+
collapseAllBtn.className = "k-btn-action";
|
|
454
|
+
collapseAllBtn.innerHTML = `⊟ Collapse`;
|
|
455
|
+
collapseAllBtn.title = "Reset trees to top 3 levels";
|
|
456
|
+
|
|
457
|
+
const copyBtn = document.createElement("button");
|
|
458
|
+
copyBtn.type = "button";
|
|
459
|
+
copyBtn.className = "k-btn-action";
|
|
460
|
+
copyBtn.innerHTML = `📋 Copy`;
|
|
461
|
+
copyBtn.title = "Copy raw output to clipboard";
|
|
462
|
+
copyBtn.onclick = (e) => {
|
|
463
|
+
e.stopPropagation();
|
|
464
|
+
navigator.clipboard.writeText(rawText);
|
|
465
|
+
copyBtn.innerHTML = `✓ Copied`;
|
|
466
|
+
setTimeout(() => {
|
|
467
|
+
copyBtn.innerHTML = `📋 Copy`;
|
|
468
|
+
}, 1500);
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
actionGroup.appendChild(expandAllBtn);
|
|
472
|
+
actionGroup.appendChild(collapseAllBtn);
|
|
473
|
+
actionGroup.appendChild(copyBtn);
|
|
474
|
+
toolbar.appendChild(actionGroup);
|
|
475
|
+
|
|
476
|
+
container.appendChild(toolbar);
|
|
477
|
+
|
|
478
|
+
// 1. Tree View Wrapper
|
|
479
|
+
const treeWrapper = document.createElement("div");
|
|
480
|
+
treeWrapper.className = "k-tree-view";
|
|
481
|
+
|
|
482
|
+
// Value Section
|
|
483
|
+
const valSection = document.createElement("details");
|
|
484
|
+
valSection.className = "k-tree-section k-tree-value-section";
|
|
485
|
+
valSection.open = true;
|
|
486
|
+
|
|
487
|
+
const valSummary = document.createElement("summary");
|
|
488
|
+
valSummary.className = "k-tree-section-summary";
|
|
489
|
+
valSummary.innerHTML = `<span class="k-tree-arrow">▶</span> <span class="k-section-title">🌲 Value</span> <span class="k-section-preview">${escapeHtml(valueToK(value))}</span>`;
|
|
490
|
+
valSection.appendChild(valSummary);
|
|
491
|
+
|
|
492
|
+
const valBody = document.createElement("div");
|
|
493
|
+
valBody.className = "k-tree-section-body";
|
|
494
|
+
valBody.appendChild(renderValueNode(value, 1, 3));
|
|
495
|
+
valSection.appendChild(valBody);
|
|
496
|
+
treeWrapper.appendChild(valSection);
|
|
497
|
+
|
|
498
|
+
// Pattern Section (if pattern present)
|
|
499
|
+
let patBody = null;
|
|
500
|
+
if (value && value.pattern) {
|
|
501
|
+
const patSection = document.createElement("details");
|
|
502
|
+
patSection.className = "k-tree-section k-tree-pat-section";
|
|
503
|
+
patSection.open = true;
|
|
504
|
+
|
|
505
|
+
const patSummary = document.createElement("summary");
|
|
506
|
+
patSummary.className = "k-tree-section-summary";
|
|
507
|
+
const patPreview = propertyListToFilter(value.pattern);
|
|
508
|
+
patSummary.innerHTML = `<span class="k-tree-arrow">▶</span> <span class="k-section-title">🌿 Pattern</span> <span class="k-section-preview">?${escapeHtml(patPreview)}</span>`;
|
|
509
|
+
patSection.appendChild(patSummary);
|
|
510
|
+
|
|
511
|
+
patBody = document.createElement("div");
|
|
512
|
+
patBody.className = "k-tree-section-body";
|
|
513
|
+
patBody.appendChild(renderPatternTree(value.pattern, 3));
|
|
514
|
+
patSection.appendChild(patBody);
|
|
515
|
+
treeWrapper.appendChild(patSection);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Codecs Section (if any codec outputs)
|
|
519
|
+
if (codecOutputs.length > 0) {
|
|
520
|
+
const codecsBox = document.createElement("div");
|
|
521
|
+
codecsBox.className = "k-codecs-box";
|
|
522
|
+
for (const c of codecOutputs) {
|
|
523
|
+
const cRow = document.createElement("div");
|
|
524
|
+
cRow.className = "k-codec-row";
|
|
525
|
+
cRow.textContent = c;
|
|
526
|
+
codecsBox.appendChild(cRow);
|
|
527
|
+
}
|
|
528
|
+
treeWrapper.appendChild(codecsBox);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Timing (if any)
|
|
532
|
+
if (timingText) {
|
|
533
|
+
const timingRow = document.createElement("div");
|
|
534
|
+
timingRow.className = "k-timing-row";
|
|
535
|
+
timingRow.textContent = timingText;
|
|
536
|
+
treeWrapper.appendChild(timingRow);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
container.appendChild(treeWrapper);
|
|
540
|
+
|
|
541
|
+
// 2. Raw Text View Wrapper
|
|
542
|
+
const rawWrapper = document.createElement("div");
|
|
543
|
+
rawWrapper.className = "k-raw-view";
|
|
544
|
+
rawWrapper.style.display = "none";
|
|
545
|
+
|
|
546
|
+
const rawPre = document.createElement("pre");
|
|
547
|
+
rawPre.className = "k-raw-pre";
|
|
548
|
+
rawPre.textContent = rawText;
|
|
549
|
+
rawWrapper.appendChild(rawPre);
|
|
550
|
+
|
|
551
|
+
container.appendChild(rawWrapper);
|
|
552
|
+
|
|
553
|
+
// Mode switching logic
|
|
554
|
+
treeBtn.onclick = () => {
|
|
555
|
+
treeBtn.classList.add("active");
|
|
556
|
+
rawBtn.classList.remove("active");
|
|
557
|
+
treeWrapper.style.display = "";
|
|
558
|
+
rawWrapper.style.display = "none";
|
|
559
|
+
expandAllBtn.style.display = "";
|
|
560
|
+
collapseAllBtn.style.display = "";
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
rawBtn.onclick = () => {
|
|
564
|
+
rawBtn.classList.add("active");
|
|
565
|
+
treeBtn.classList.remove("active");
|
|
566
|
+
treeWrapper.style.display = "none";
|
|
567
|
+
rawWrapper.style.display = "";
|
|
568
|
+
expandAllBtn.style.display = "none";
|
|
569
|
+
collapseAllBtn.style.display = "none";
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
// Expand All / Collapse All logic
|
|
573
|
+
expandAllBtn.onclick = () => {
|
|
574
|
+
const allDetails = treeWrapper.querySelectorAll("details");
|
|
575
|
+
allDetails.forEach((d) => {
|
|
576
|
+
d.open = true;
|
|
577
|
+
d.dispatchEvent(new Event("toggle"));
|
|
578
|
+
});
|
|
579
|
+
// Repeat once to expand lazily populated children
|
|
580
|
+
setTimeout(() => {
|
|
581
|
+
const moreDetails = treeWrapper.querySelectorAll("details:not([open])");
|
|
582
|
+
moreDetails.forEach((d) => {
|
|
583
|
+
d.open = true;
|
|
584
|
+
d.dispatchEvent(new Event("toggle"));
|
|
585
|
+
});
|
|
586
|
+
}, 10);
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
collapseAllBtn.onclick = () => {
|
|
590
|
+
valBody.innerHTML = "";
|
|
591
|
+
valBody.appendChild(renderValueNode(value, 1, 3));
|
|
592
|
+
if (value && value.pattern && patBody) {
|
|
593
|
+
patBody.innerHTML = "";
|
|
594
|
+
patBody.appendChild(renderPatternTree(value.pattern, 3));
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
return container;
|
|
599
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fraczak/k",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.7.0",
|
|
4
4
|
"description": "k — a language for composing first-order partial functions over algebraic data types (products and tagged unions); includes runtime, type derivation, REPL, and codec toolchain",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"exports": {
|
package/repl.mjs
CHANGED
|
@@ -87,6 +87,7 @@ function createState() {
|
|
|
87
87
|
pendingInput: null,
|
|
88
88
|
meta: {},
|
|
89
89
|
value: emptyValue(),
|
|
90
|
+
lastResult: null,
|
|
90
91
|
lastMain: null,
|
|
91
92
|
lastTiming: null,
|
|
92
93
|
showTiming: false
|
|
@@ -381,6 +382,7 @@ function commitResult(state, result, lastMain) {
|
|
|
381
382
|
if (result !== undefined) {
|
|
382
383
|
state.value = result;
|
|
383
384
|
state.lastMain = lastMain;
|
|
385
|
+
state.lastResult = result;
|
|
384
386
|
}
|
|
385
387
|
return [printValue(result, state)];
|
|
386
388
|
}
|
|
@@ -1259,11 +1261,13 @@ async function consumeCodecInput(input, state) {
|
|
|
1259
1261
|
});
|
|
1260
1262
|
const value = valueForCode(parsed, pending.codeHash, codes.find);
|
|
1261
1263
|
state.value = value;
|
|
1264
|
+
state.lastResult = value;
|
|
1262
1265
|
return [printValue(value, state)];
|
|
1263
1266
|
}
|
|
1264
1267
|
|
|
1265
1268
|
async function evaluateInput(input, state) {
|
|
1266
1269
|
state.lastTiming = null;
|
|
1270
|
+
state.lastResult = null;
|
|
1267
1271
|
if (state.pendingInput) {
|
|
1268
1272
|
return consumeCodecInput(input, state);
|
|
1269
1273
|
}
|