@fraczak/k 7.1.0 → 8.0.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/CHANGELOG.md +99 -0
- package/DOCS/BIG_PICTURE.md +54 -7
- package/DOCS/CODECS.md +47 -2
- package/DOCS/CODEC_ARCHITECTURE.md +14 -5
- package/DOCS/DICTIONARY.md +13 -4
- package/DOCS/FILE_FORMATS.md +3 -2
- package/DOCS/JSON_APPROACHES_COMPARISON.md +306 -0
- package/DOCS/NAMED_PATTERNS.md +317 -0
- package/DOCS/PATTERNS.md +59 -11
- package/DOCS/REPL.md +24 -5
- package/DOCS/WEB_REPL_REQUIREMENTS.md +4 -2
- package/Examples/arithmetics.k +22 -17
- package/Examples/bnat.k +21 -11
- package/Examples/buda.k +37 -20
- package/Examples/byte.k +3 -1
- package/Examples/code.k +38 -39
- package/Examples/djson-btrie.k +414 -0
- package/Examples/djson-ptrie.k +663 -0
- package/Examples/djson.k +323 -0
- package/Examples/esc.k +1 -1
- package/Examples/karatsuba-mult.k +16 -13
- package/Examples/list.k +8 -4
- package/Examples/nat.k +17 -11
- package/Examples/poly.k +11 -3
- package/Examples/vhdl_to_k.k +43 -31
- package/README.md +42 -15
- package/Value.mjs +28 -3
- package/backend-api.mjs +5 -1
- package/browser/repl-browser.mjs +6 -0
- package/browser/tree-view.mjs +82 -24
- package/browser/vfs-data.mjs +21 -12
- package/codecs/README.md +3 -0
- package/codecs/djson-btrie.mjs +560 -0
- package/codecs/djson-ptrie.mjs +587 -0
- package/codecs/djson.mjs +270 -0
- package/codecs/int.mjs +2 -1
- package/codecs/json.mjs +7 -2
- package/codecs/k-parse.mjs +7 -1
- package/codecs/k-print.mjs +4 -0
- package/codecs/runtime/codec-sdk.mjs +16 -2
- package/codecs/runtime/pattern-json.mjs +80 -1
- package/codecs/runtime/prefix-codec.mjs +629 -67
- package/codecs/runtime/show-value.mjs +93 -51
- package/codecs/runtime/unicode-string.mjs +29 -6
- package/codecs/show.mjs +4 -0
- package/codecs/utf16.mjs +2 -1
- package/core.k +79 -115
- package/index.mjs +3 -0
- package/named-patterns.mjs +267 -0
- package/object.mjs +31 -3
- package/package.json +5 -1
- package/parser.jison +68 -5
- package/parser.mjs +111 -60
- package/pretty.mjs +5 -0
- package/repl.mjs +245 -28
- package/scripts/build-repl-html.mjs +76 -5
- package/scripts/perf-djson.mjs +1142 -0
- package/scripts/run-tests.mjs +5 -0
- package/symbol-table.mjs +20 -2
- package/tests/bench-djson-tries.mjs +340 -0
- package/tests/test-codecs.mjs +211 -0
- package/tests/test-named-patterns.mjs +189 -0
- package/tests/test-repl-html.mjs +52 -4
- package/tests/test-repl.mjs +223 -0
package/repl.mjs
CHANGED
|
@@ -21,7 +21,7 @@ if (typeof globalThis !== "undefined") {
|
|
|
21
21
|
globalThis.isVariant = isVariant;
|
|
22
22
|
globalThis.patternFromFilter = patternFromFilter;
|
|
23
23
|
}
|
|
24
|
-
import { prettyCode } from "./pretty.mjs";
|
|
24
|
+
import { prettyCode, prettyFilter } from "./pretty.mjs";
|
|
25
25
|
import {
|
|
26
26
|
compileObject,
|
|
27
27
|
compileLibrary,
|
|
@@ -32,7 +32,11 @@ import {
|
|
|
32
32
|
loadLibrary,
|
|
33
33
|
prettyRelation
|
|
34
34
|
} from "./object.mjs";
|
|
35
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
propertyListToFilter,
|
|
37
|
+
valueToK,
|
|
38
|
+
valueWithEnvelopeToK
|
|
39
|
+
} from "./codecs/runtime/show-value.mjs";
|
|
36
40
|
import {
|
|
37
41
|
codecNames,
|
|
38
42
|
codeHashToPattern,
|
|
@@ -48,9 +52,9 @@ import {
|
|
|
48
52
|
|
|
49
53
|
const NAME_RE = /^[a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*$/;
|
|
50
54
|
const COMMAND_NAMES = [
|
|
51
|
-
"help", "rel",
|
|
55
|
+
"help", "rel", "pattern", "pat", "patterns", "pats",
|
|
52
56
|
"codecs", "rels", "codec", "input", "reset", "klib", "ko", "load",
|
|
53
|
-
"engine", "wasm", "pkir", "js",
|
|
57
|
+
"engine", "wasm", "pkir", "js", "limit",
|
|
54
58
|
"quit", "exit"
|
|
55
59
|
];
|
|
56
60
|
const CODEC_COMMAND_NAMES = ["load", "unload", "list"];
|
|
@@ -87,6 +91,7 @@ function createState(options = {}) {
|
|
|
87
91
|
rels: {},
|
|
88
92
|
relAliases: {},
|
|
89
93
|
typeAliases: {},
|
|
94
|
+
patterns: {},
|
|
90
95
|
codecs: {},
|
|
91
96
|
loadedFiles: new Set(),
|
|
92
97
|
pendingInput: null,
|
|
@@ -95,7 +100,8 @@ function createState(options = {}) {
|
|
|
95
100
|
lastResult: null,
|
|
96
101
|
lastMain: null,
|
|
97
102
|
lastTiming: null,
|
|
98
|
-
showTiming: true
|
|
103
|
+
showTiming: true,
|
|
104
|
+
printLimit: options.printLimit ?? Infinity
|
|
99
105
|
};
|
|
100
106
|
}
|
|
101
107
|
|
|
@@ -105,6 +111,7 @@ function stateLibrary(state) {
|
|
|
105
111
|
codes: state.codes,
|
|
106
112
|
rels: state.rels,
|
|
107
113
|
relAlias: state.relAliases,
|
|
114
|
+
patterns: state.patterns,
|
|
108
115
|
meta: state.meta,
|
|
109
116
|
main: null
|
|
110
117
|
};
|
|
@@ -226,6 +233,11 @@ function mergeLibrary(state, lib, source = "<load>", options = {}) {
|
|
|
226
233
|
const loadAliases = options.loadAliases ?? true;
|
|
227
234
|
state.codes = { ...state.codes, ...(lib.codes || {}) };
|
|
228
235
|
state.rels = { ...state.rels, ...(lib.rels || {}) };
|
|
236
|
+
state.patterns = {
|
|
237
|
+
...state.patterns,
|
|
238
|
+
...(lib.patterns || lib.sourceDefs?.patterns ||
|
|
239
|
+
lib.defs?.patterns || {})
|
|
240
|
+
};
|
|
229
241
|
mergeMeta(state, lib.meta);
|
|
230
242
|
|
|
231
243
|
if (loadAliases) {
|
|
@@ -309,6 +321,7 @@ function savedLibrary(state) {
|
|
|
309
321
|
rels,
|
|
310
322
|
relAlias: state.relAliases,
|
|
311
323
|
compileStats: { sccs: [], sccCount: 0 },
|
|
324
|
+
patterns: state.patterns || {},
|
|
312
325
|
meta: Object.fromEntries(
|
|
313
326
|
Object.entries(meta).filter(([hash]) => storedHashes.has(hash))
|
|
314
327
|
),
|
|
@@ -319,7 +332,10 @@ function savedLibrary(state) {
|
|
|
319
332
|
function executableObject(state, mainExpression) {
|
|
320
333
|
const main = (mainExpression || state.lastMain || "").trim();
|
|
321
334
|
if (!main) {
|
|
322
|
-
throw new Error(
|
|
335
|
+
throw new Error(
|
|
336
|
+
":ko file expr requires a main expression unless one has " +
|
|
337
|
+
"already been evaluated or defined"
|
|
338
|
+
);
|
|
323
339
|
}
|
|
324
340
|
|
|
325
341
|
restoreCodes(state);
|
|
@@ -327,7 +343,8 @@ function executableObject(state, mainExpression) {
|
|
|
327
343
|
try {
|
|
328
344
|
return compileObject([preamble, main].filter(Boolean).join("\n"), {
|
|
329
345
|
source: "<repl>",
|
|
330
|
-
libraries: [stateLibrary(state)]
|
|
346
|
+
libraries: [stateLibrary(state)],
|
|
347
|
+
patterns: state.patterns
|
|
331
348
|
});
|
|
332
349
|
} catch (error) {
|
|
333
350
|
throw remapError(error, preambleLineCount(preamble));
|
|
@@ -342,11 +359,72 @@ function resolveRel(state, name) {
|
|
|
342
359
|
}
|
|
343
360
|
|
|
344
361
|
function listAliases(aliases) {
|
|
345
|
-
const entries = Object.entries(aliases).sort(([a], [b]) =>
|
|
362
|
+
const entries = Object.entries(aliases).sort(([a], [b]) =>
|
|
363
|
+
a.localeCompare(b)
|
|
364
|
+
);
|
|
346
365
|
if (entries.length === 0) return "(none)";
|
|
347
366
|
return entries.map(([name, hash]) => `${name} = ${hash}`).join("\n");
|
|
348
367
|
}
|
|
349
368
|
|
|
369
|
+
function resolvePatternCanonicalName(state, name, pat) {
|
|
370
|
+
if (!state || !pat) return null;
|
|
371
|
+
if (pat._canonical) return pat._canonical;
|
|
372
|
+
const params = pat.params || [];
|
|
373
|
+
const source = `? ${name}[${params.join(", ")}]`;
|
|
374
|
+
try {
|
|
375
|
+
restoreCodes(state);
|
|
376
|
+
const annotated = annotate(source, {
|
|
377
|
+
libraries: [stateLibrary(state)],
|
|
378
|
+
patterns: state.patterns
|
|
379
|
+
});
|
|
380
|
+
const mainRel = annotated.rels.__main__;
|
|
381
|
+
if (
|
|
382
|
+
mainRel?.typePatternGraph &&
|
|
383
|
+
mainRel?.def?.patterns?.[0] != null
|
|
384
|
+
) {
|
|
385
|
+
const rootId = mainRel.typePatternGraph.find(
|
|
386
|
+
mainRel.def.patterns[0]
|
|
387
|
+
);
|
|
388
|
+
const propList = patternToPropertyList(
|
|
389
|
+
exportPatternGraph(mainRel.typePatternGraph, rootId)
|
|
390
|
+
);
|
|
391
|
+
const codeHash = closedPatternToCodeHash(propList);
|
|
392
|
+
if (codeHash) {
|
|
393
|
+
state.codes = codes.dump();
|
|
394
|
+
pat._canonical = codeHash;
|
|
395
|
+
return codeHash;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
const relHash = annotated.relAlias?.__main__ || null;
|
|
399
|
+
if (relHash) {
|
|
400
|
+
pat._canonical = relHash;
|
|
401
|
+
return relHash;
|
|
402
|
+
}
|
|
403
|
+
} catch {
|
|
404
|
+
restoreCodes(state);
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function prettyPattern(name, pat, state = null) {
|
|
411
|
+
const params = (pat?.params || []).join(", ");
|
|
412
|
+
const filterStr = pat?.filter ? prettyFilter(pat.filter) : "()";
|
|
413
|
+
const canonical = resolvePatternCanonicalName(state, name, pat);
|
|
414
|
+
const suffix = canonical ? ` -- ${canonical}` : "";
|
|
415
|
+
return `${name}[${params}] = ${filterStr};${suffix}`;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function listPatterns(patterns) {
|
|
419
|
+
const entries = Object.entries(patterns || {}).sort(([a], [b]) =>
|
|
420
|
+
a.localeCompare(b)
|
|
421
|
+
);
|
|
422
|
+
if (entries.length === 0) return "(none)";
|
|
423
|
+
return entries
|
|
424
|
+
.map(([name, pat]) => `${name}[${(pat?.params || []).join(", ")}]`)
|
|
425
|
+
.join("\n");
|
|
426
|
+
}
|
|
427
|
+
|
|
350
428
|
function formatCodecOutput(output) {
|
|
351
429
|
if (typeof Buffer !== "undefined" && Buffer.isBuffer(output)) return output.toString("utf8");
|
|
352
430
|
if (typeof Uint8Array !== "undefined" && output instanceof Uint8Array) return new TextDecoder().decode(output);
|
|
@@ -354,7 +432,8 @@ function formatCodecOutput(output) {
|
|
|
354
432
|
}
|
|
355
433
|
|
|
356
434
|
function printValue(value, state = null) {
|
|
357
|
-
const
|
|
435
|
+
const limit = state?.printLimit ?? Infinity;
|
|
436
|
+
const lines = [valueWithEnvelopeToK(value, limit)];
|
|
358
437
|
if (!state || value === undefined) return lines[0];
|
|
359
438
|
|
|
360
439
|
const store = state.codecs || {};
|
|
@@ -363,7 +442,11 @@ function printValue(value, state = null) {
|
|
|
363
442
|
try {
|
|
364
443
|
const output = codec.print(value, { state });
|
|
365
444
|
if (output != null) {
|
|
366
|
-
|
|
445
|
+
let text = formatCodecOutput(output);
|
|
446
|
+
if (Number.isFinite(limit) && text.length > limit) {
|
|
447
|
+
text = text.slice(0, limit) + "...";
|
|
448
|
+
}
|
|
449
|
+
lines.push(`${codec.name}: ${text}`);
|
|
367
450
|
}
|
|
368
451
|
} catch {
|
|
369
452
|
// Codec cannot format this value; silently skip
|
|
@@ -638,9 +721,16 @@ function resolveTypeExpressionHash(state, rawType) {
|
|
|
638
721
|
].filter(Boolean).join("\n");
|
|
639
722
|
|
|
640
723
|
try {
|
|
641
|
-
const annotated = annotate(source, {
|
|
724
|
+
const annotated = annotate(source, {
|
|
725
|
+
libraries: [stateLibrary(state)],
|
|
726
|
+
patterns: state.patterns
|
|
727
|
+
});
|
|
642
728
|
const hash = annotated.representatives[INPUT_TYPE_NAME];
|
|
643
|
-
if (!hash)
|
|
729
|
+
if (!hash) {
|
|
730
|
+
throw new Error(
|
|
731
|
+
":input type expression did not produce a code hash"
|
|
732
|
+
);
|
|
733
|
+
}
|
|
644
734
|
state.codes = codes.dump();
|
|
645
735
|
return hash;
|
|
646
736
|
} catch (error) {
|
|
@@ -666,7 +756,10 @@ function resolvePatternExpression(state, patternExpr) {
|
|
|
666
756
|
patternExpr
|
|
667
757
|
].filter(Boolean).join("\n");
|
|
668
758
|
try {
|
|
669
|
-
const annotated = annotate(source, {
|
|
759
|
+
const annotated = annotate(source, {
|
|
760
|
+
libraries: [stateLibrary(state)],
|
|
761
|
+
patterns: state.patterns
|
|
762
|
+
});
|
|
670
763
|
const mainRel = annotated.rels.__main__;
|
|
671
764
|
if (!mainRel || !mainRel.typePatternGraph) {
|
|
672
765
|
throw new Error(`Invalid pattern expression '${patternExpr}'`);
|
|
@@ -765,6 +858,25 @@ function completeCommandArgument(line, state) {
|
|
|
765
858
|
const argStart = 1 + firstSpace + 1;
|
|
766
859
|
const arg = line.slice(argStart);
|
|
767
860
|
|
|
861
|
+
if (command === "pattern" || command === "pat") {
|
|
862
|
+
const trimmed = arg.trimStart();
|
|
863
|
+
const leadingWhitespace = arg.length - trimmed.length;
|
|
864
|
+
const prefix = line.slice(0, argStart + leadingWhitespace);
|
|
865
|
+
const names = Object.keys(state.patterns || {}).sort();
|
|
866
|
+
const matches = names
|
|
867
|
+
.filter((name) => name.startsWith(trimmed))
|
|
868
|
+
.map((name) => `${prefix}${name}`);
|
|
869
|
+
if (trimmed.startsWith("@")) {
|
|
870
|
+
for (const [name, p] of Object.entries(state.patterns || {})) {
|
|
871
|
+
const hash = resolvePatternCanonicalName(state, name, p);
|
|
872
|
+
if (hash && hash.startsWith(trimmed)) {
|
|
873
|
+
matches.push(`${prefix}${hash}`);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
return [matches, line];
|
|
878
|
+
}
|
|
879
|
+
|
|
768
880
|
if (command === "codec" || command === "codecs") {
|
|
769
881
|
return completeCodecCommand(line, argStart, arg, state);
|
|
770
882
|
}
|
|
@@ -871,7 +983,10 @@ function resolveKFilePath(filePath) {
|
|
|
871
983
|
if (fs.existsSync(strippedEx)) return strippedEx;
|
|
872
984
|
} catch {}
|
|
873
985
|
try {
|
|
874
|
-
const fromRepoStripped = path.resolve(
|
|
986
|
+
const fromRepoStripped = path.resolve(
|
|
987
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
988
|
+
strippedEx
|
|
989
|
+
);
|
|
875
990
|
if (fs.existsSync(fromRepoStripped)) return fromRepoStripped;
|
|
876
991
|
} catch {}
|
|
877
992
|
return filePath;
|
|
@@ -902,7 +1017,8 @@ function loadSourceOrKlib(state, targetPath, options = {}) {
|
|
|
902
1017
|
const fullSource = [preamble, source].filter(Boolean).join("\n");
|
|
903
1018
|
const compileOptions = {
|
|
904
1019
|
source: targetPath,
|
|
905
|
-
libraries: [stateLibrary(state)]
|
|
1020
|
+
libraries: [stateLibrary(state)],
|
|
1021
|
+
patterns: state.patterns
|
|
906
1022
|
};
|
|
907
1023
|
try {
|
|
908
1024
|
const lib = compileWithOptionalIdentity(fullSource, compileOptions);
|
|
@@ -966,15 +1082,29 @@ function compileSnippetArtifacts(source, state, sourceName = "<repl>") {
|
|
|
966
1082
|
const fullSource = [preamble, source].filter(Boolean).join("\n");
|
|
967
1083
|
const options = {
|
|
968
1084
|
source: sourceName,
|
|
969
|
-
libraries: [stateLibrary(state)]
|
|
1085
|
+
libraries: [stateLibrary(state)],
|
|
1086
|
+
patterns: state.patterns
|
|
970
1087
|
};
|
|
971
1088
|
try {
|
|
972
1089
|
const lib = compileWithOptionalIdentity(fullSource, options);
|
|
973
|
-
const { relAlias, meta } = libraryOriginsFromSource(
|
|
1090
|
+
const { relAlias, meta } = libraryOriginsFromSource(
|
|
1091
|
+
source,
|
|
1092
|
+
fullSource,
|
|
1093
|
+
lib,
|
|
1094
|
+
options
|
|
1095
|
+
);
|
|
974
1096
|
const annotated = annotateWithOptionalIdentity(fullSource, options);
|
|
975
1097
|
return {
|
|
976
1098
|
annotated,
|
|
977
|
-
lib: {
|
|
1099
|
+
lib: {
|
|
1100
|
+
...lib,
|
|
1101
|
+
relAlias,
|
|
1102
|
+
meta,
|
|
1103
|
+
patterns: {
|
|
1104
|
+
...(state.patterns || {}),
|
|
1105
|
+
...(annotated.sourceDefs?.patterns || {})
|
|
1106
|
+
}
|
|
1107
|
+
},
|
|
978
1108
|
lineOffset: preambleLineCount(preamble)
|
|
979
1109
|
};
|
|
980
1110
|
} catch (error) {
|
|
@@ -1227,14 +1357,49 @@ async function evaluateCommand(line, state) {
|
|
|
1227
1357
|
case "rel": {
|
|
1228
1358
|
if (!arg) throw new Error(`${usagePrefix}rel requires a relation name`);
|
|
1229
1359
|
if (arg.includes("=")) {
|
|
1230
|
-
throw new Error(
|
|
1360
|
+
throw new Error(
|
|
1361
|
+
"Relation definitions use syntax: name = relExpr; " +
|
|
1362
|
+
"Use :rel <name> to show a relation definition"
|
|
1363
|
+
);
|
|
1231
1364
|
}
|
|
1232
1365
|
const { hash, rel } = resolveRel(state, arg);
|
|
1233
1366
|
if (!rel) throw new Error(`Unknown relation '${arg}'`);
|
|
1234
|
-
|
|
1367
|
+
const prettyRelStr = prettyRelation(
|
|
1368
|
+
rel,
|
|
1369
|
+
displayAliases(state),
|
|
1370
|
+
state.relAliases
|
|
1371
|
+
);
|
|
1372
|
+
return [`${arg} = ${prettyRelStr}; -- ${hash}`];
|
|
1235
1373
|
}
|
|
1236
1374
|
case "rels":
|
|
1237
1375
|
return [listAliases(state.relAliases)];
|
|
1376
|
+
case "pattern":
|
|
1377
|
+
case "pat": {
|
|
1378
|
+
if (!arg) {
|
|
1379
|
+
throw new Error(`${usagePrefix}pattern requires a pattern name`);
|
|
1380
|
+
}
|
|
1381
|
+
const rawName = arg.trim();
|
|
1382
|
+
let patternName = rawName.replace(/\[.*\]$/, "").trim();
|
|
1383
|
+
let pat = state.patterns?.[patternName];
|
|
1384
|
+
if (!pat && patternName.startsWith("@")) {
|
|
1385
|
+
for (const [name, p] of Object.entries(state.patterns || {})) {
|
|
1386
|
+
if (
|
|
1387
|
+
resolvePatternCanonicalName(state, name, p) === patternName
|
|
1388
|
+
) {
|
|
1389
|
+
patternName = name;
|
|
1390
|
+
pat = p;
|
|
1391
|
+
break;
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
if (!pat) {
|
|
1396
|
+
throw new Error(`Unknown pattern '${rawName}'`);
|
|
1397
|
+
}
|
|
1398
|
+
return [prettyPattern(patternName, pat, state)];
|
|
1399
|
+
}
|
|
1400
|
+
case "patterns":
|
|
1401
|
+
case "pats":
|
|
1402
|
+
return [listPatterns(state.patterns)];
|
|
1238
1403
|
case "codec":
|
|
1239
1404
|
case "codecs":
|
|
1240
1405
|
return loadCodec(arg, state);
|
|
@@ -1265,9 +1430,34 @@ async function evaluateCommand(line, state) {
|
|
|
1265
1430
|
state.engine = "js";
|
|
1266
1431
|
return ["engine set to js"];
|
|
1267
1432
|
}
|
|
1433
|
+
case "limit": {
|
|
1434
|
+
const mode = (arg || "").trim().toLowerCase();
|
|
1435
|
+
if (!mode) {
|
|
1436
|
+
const current = Number.isFinite(state.printLimit)
|
|
1437
|
+
? state.printLimit
|
|
1438
|
+
: "none";
|
|
1439
|
+
return [`Current output limit: ${current}`];
|
|
1440
|
+
}
|
|
1441
|
+
if (mode === "none" || mode === "infinity" || mode === "off") {
|
|
1442
|
+
state.printLimit = Infinity;
|
|
1443
|
+
return ["output limit disabled"];
|
|
1444
|
+
}
|
|
1445
|
+
const n = parseInt(mode, 10);
|
|
1446
|
+
if (Number.isNaN(n) || n < 0) {
|
|
1447
|
+
throw new Error(
|
|
1448
|
+
`Invalid limit '${arg.trim()}'. Usage: :limit [num|none]`
|
|
1449
|
+
);
|
|
1450
|
+
}
|
|
1451
|
+
state.printLimit = n;
|
|
1452
|
+
return [`output limit set to ${n}`];
|
|
1453
|
+
}
|
|
1268
1454
|
case "reset": {
|
|
1269
1455
|
const currentEngine = state.engine;
|
|
1270
|
-
const
|
|
1456
|
+
const currentLimit = state.printLimit;
|
|
1457
|
+
const fresh = createState({
|
|
1458
|
+
engine: currentEngine,
|
|
1459
|
+
printLimit: currentLimit
|
|
1460
|
+
});
|
|
1271
1461
|
Object.assign(state, fresh);
|
|
1272
1462
|
return ["reset"];
|
|
1273
1463
|
}
|
|
@@ -1302,18 +1492,23 @@ function helpText() {
|
|
|
1302
1492
|
":engine [wasm|pkir|js] display or switch evaluation engine",
|
|
1303
1493
|
":rel name show relation definition",
|
|
1304
1494
|
":rels list relation aliases",
|
|
1305
|
-
":
|
|
1495
|
+
":pattern name show named pattern definition (alias :pat)",
|
|
1496
|
+
":patterns list defined named patterns (alias :pats)",
|
|
1497
|
+
":limit [num|none] set maximum characters for value output",
|
|
1498
|
+
":codec load file load a codec module from file",
|
|
1306
1499
|
":codec unload name unload a registered codec",
|
|
1307
1500
|
":codec list list loaded codecs (or simply :codecs)",
|
|
1308
|
-
":input <codec
|
|
1309
|
-
":load [--no-alias]
|
|
1310
|
-
" load .k source or .klib",
|
|
1501
|
+
":input <codec> [txt] parse input using specified codec",
|
|
1502
|
+
":load [--no-alias] f load .k source or .klib",
|
|
1311
1503
|
":klib file export state as a library",
|
|
1312
1504
|
":ko file expr export executable .ko using expr as main",
|
|
1313
1505
|
":reset clear state",
|
|
1314
1506
|
":help show this help",
|
|
1315
1507
|
"",
|
|
1316
|
-
"Raw k input
|
|
1508
|
+
"Raw k input:",
|
|
1509
|
+
" name = expr; define relation alias",
|
|
1510
|
+
" name[P1, ...] = filter; define named pattern template",
|
|
1511
|
+
" ? name[...] evaluate named pattern filter",
|
|
1317
1512
|
"A line ending with ';' plus only spaces closes the snippet.",
|
|
1318
1513
|
"Use '\\' to force continuation."
|
|
1319
1514
|
].join("\n");
|
|
@@ -1336,6 +1531,7 @@ function cliUsage() {
|
|
|
1336
1531
|
console.log(" --pkir Shortcut for --engine=pkir.");
|
|
1337
1532
|
console.log(" --wasm Shortcut for --engine=wasm.");
|
|
1338
1533
|
console.log(" --js Shortcut for --engine=js.");
|
|
1534
|
+
console.log(" --limit=<num> Set maximum characters for output.");
|
|
1339
1535
|
console.log(" -h, --help Show this help.");
|
|
1340
1536
|
}
|
|
1341
1537
|
|
|
@@ -1431,6 +1627,7 @@ function startRepl(options = {}) {
|
|
|
1431
1627
|
if (isMainEntrypoint()) {
|
|
1432
1628
|
const args = argv.slice(2);
|
|
1433
1629
|
let engine = "wasm";
|
|
1630
|
+
let printLimit = Infinity;
|
|
1434
1631
|
for (let i = 0; i < args.length; i++) {
|
|
1435
1632
|
const arg = args[i];
|
|
1436
1633
|
if (arg === "-h" || arg === "--help") {
|
|
@@ -1461,13 +1658,30 @@ if (isMainEntrypoint()) {
|
|
|
1461
1658
|
exit(1);
|
|
1462
1659
|
}
|
|
1463
1660
|
engine = val;
|
|
1661
|
+
} else if (arg.startsWith("--limit=")) {
|
|
1662
|
+
const val = parseInt(arg.slice("--limit=".length), 10);
|
|
1663
|
+
if (Number.isNaN(val) || val < 0) {
|
|
1664
|
+
console.error(
|
|
1665
|
+
`Invalid limit '${arg.slice("--limit=".length)}'.`
|
|
1666
|
+
);
|
|
1667
|
+
exit(1);
|
|
1668
|
+
}
|
|
1669
|
+
printLimit = val;
|
|
1670
|
+
} else if (arg === "--limit") {
|
|
1671
|
+
i++;
|
|
1672
|
+
const val = parseInt(args[i] || "", 10);
|
|
1673
|
+
if (Number.isNaN(val) || val < 0) {
|
|
1674
|
+
console.error(`Invalid limit '${args[i]}'.`);
|
|
1675
|
+
exit(1);
|
|
1676
|
+
}
|
|
1677
|
+
printLimit = val;
|
|
1464
1678
|
} else {
|
|
1465
1679
|
console.error(`Unknown option '${arg}'.`);
|
|
1466
1680
|
cliUsage();
|
|
1467
1681
|
exit(1);
|
|
1468
1682
|
}
|
|
1469
1683
|
}
|
|
1470
|
-
startRepl({ engine });
|
|
1684
|
+
startRepl({ engine, printLimit });
|
|
1471
1685
|
}
|
|
1472
1686
|
|
|
1473
1687
|
export {
|
|
@@ -1502,5 +1716,8 @@ export {
|
|
|
1502
1716
|
formatDuration,
|
|
1503
1717
|
codecNames,
|
|
1504
1718
|
ensureEnveloped,
|
|
1505
|
-
patternFromFilter
|
|
1719
|
+
patternFromFilter,
|
|
1720
|
+
prettyPattern,
|
|
1721
|
+
resolvePatternCanonicalName,
|
|
1722
|
+
listPatterns
|
|
1506
1723
|
};
|
|
@@ -78,8 +78,16 @@ if (fs.existsSync(coreKPath)) {
|
|
|
78
78
|
vfsFiles["core.k"] = fs.readFileSync(coreKPath, "utf8");
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
// Add verified k-code files
|
|
82
|
-
const verifiedExamples = [
|
|
81
|
+
// Add verified k-code files
|
|
82
|
+
const verifiedExamples = [
|
|
83
|
+
"arithmetics.k",
|
|
84
|
+
"ieee.k",
|
|
85
|
+
"poly.k",
|
|
86
|
+
"karatsuba-mult.k",
|
|
87
|
+
"djson.k",
|
|
88
|
+
"djson-btrie.k",
|
|
89
|
+
"djson-ptrie.k"
|
|
90
|
+
];
|
|
83
91
|
const examplesDir = path.join(projectRoot, "Examples");
|
|
84
92
|
for (const filename of verifiedExamples) {
|
|
85
93
|
const fullPath = path.join(examplesDir, filename);
|
|
@@ -91,7 +99,16 @@ for (const filename of verifiedExamples) {
|
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
// Bundle verified codecs for browser VFS as 100% self-contained ES modules
|
|
94
|
-
const verifiedCodecs = [
|
|
102
|
+
const verifiedCodecs = [
|
|
103
|
+
"json.mjs",
|
|
104
|
+
"djson.mjs",
|
|
105
|
+
"djson-btrie.mjs",
|
|
106
|
+
"djson-ptrie.mjs",
|
|
107
|
+
"utf8.mjs",
|
|
108
|
+
"int.mjs",
|
|
109
|
+
"unit.mjs",
|
|
110
|
+
"ieee.mjs"
|
|
111
|
+
];
|
|
95
112
|
const codecsDir = path.join(projectRoot, "codecs");
|
|
96
113
|
|
|
97
114
|
const browserCodecSdkShim = `
|
|
@@ -638,6 +655,12 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
638
655
|
text-overflow: ellipsis;
|
|
639
656
|
white-space: nowrap;
|
|
640
657
|
flex: 1;
|
|
658
|
+
min-width: 0;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.k-btn-copy {
|
|
662
|
+
flex-shrink: 0;
|
|
663
|
+
margin-left: auto;
|
|
641
664
|
}
|
|
642
665
|
|
|
643
666
|
.k-tree-section-body {
|
|
@@ -1674,6 +1697,9 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1674
1697
|
<option value="ieee.k">ieee.k</option>
|
|
1675
1698
|
<option value="poly.k">poly.k</option>
|
|
1676
1699
|
<option value="karatsuba-mult.k">karatsuba-mult.k</option>
|
|
1700
|
+
<option value="djson.k">djson.k</option>
|
|
1701
|
+
<option value="djson-btrie.k">djson-btrie.k</option>
|
|
1702
|
+
<option value="djson-ptrie.k">djson-ptrie.k</option>
|
|
1677
1703
|
</optgroup>
|
|
1678
1704
|
<optgroup label="Quick Expressions">
|
|
1679
1705
|
<option value="expr:{} | ok">{} | ok</option>
|
|
@@ -1693,9 +1719,15 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1693
1719
|
<option value="expr::codec load int.mjs">:codec load int.mjs</option>
|
|
1694
1720
|
<option value="expr::codec load utf8.mjs">:codec load utf8.mjs</option>
|
|
1695
1721
|
<option value="expr::codec load json.mjs">:codec load json.mjs</option>
|
|
1722
|
+
<option value="expr::codec load djson.mjs">:codec load djson.mjs</option>
|
|
1723
|
+
<option value="expr::codec load djson-btrie.mjs">:codec load djson-btrie.mjs</option>
|
|
1724
|
+
<option value="expr::codec load djson-ptrie.mjs">:codec load djson-ptrie.mjs</option>
|
|
1696
1725
|
<option value="expr::codec load ieee.mjs">:codec load ieee.mjs</option>
|
|
1697
1726
|
<option value="expr::codec load unit.mjs">:codec load unit.mjs</option>
|
|
1698
1727
|
<option value="expr::input json.mjs {"a":12,"b":"hello"}">:input json.mjs {"a":12,"b":"hello"}</option>
|
|
1728
|
+
<option value="expr::input djson.mjs {"a":12,"b":"hello"}">:input djson.mjs {"a":12,"b":"hello"}</option>
|
|
1729
|
+
<option value="expr::input djson-btrie.mjs {"a":12,"b":"hello"}">:input djson-btrie.mjs {"a":12,"b":"hello"}</option>
|
|
1730
|
+
<option value="expr::input djson-ptrie.mjs {"a":12,"b":"hello"}">:input djson-ptrie.mjs {"a":12,"b":"hello"}</option>
|
|
1699
1731
|
<option value="expr::input int.mjs [0,1,2]">:input int.mjs [0,1,2]</option>
|
|
1700
1732
|
</optgroup>
|
|
1701
1733
|
</select>
|
|
@@ -1830,6 +1862,16 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1830
1862
|
<td><code>name = relExpr;</code></td>
|
|
1831
1863
|
<td><b>Relation Alias:</b> Defines a local alias for a relation expression.<br><i>e.g.</i> <code>swap = { . y x, . x y };</code></td>
|
|
1832
1864
|
</tr>
|
|
1865
|
+
<tr>
|
|
1866
|
+
<td><code>name[P1, ..., Pn] = filter;</code></td>
|
|
1867
|
+
<td>
|
|
1868
|
+
<b>Named Pattern Template:</b> Defines a reusable
|
|
1869
|
+
pattern template.<br>
|
|
1870
|
+
<i>e.g.</i><br>
|
|
1871
|
+
<code>bit[] = < {} 0, {} 1 >;</code> or<br>
|
|
1872
|
+
<code>list[X] = < {} nil, ... > = Y;</code>
|
|
1873
|
+
</td>
|
|
1874
|
+
</tr>
|
|
1833
1875
|
</tbody>
|
|
1834
1876
|
</table>
|
|
1835
1877
|
|
|
@@ -1865,8 +1907,15 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1865
1907
|
<td><b>Variant Constructor:</b> Wraps document into variant <code>tag</code></td>
|
|
1866
1908
|
</tr>
|
|
1867
1909
|
<tr>
|
|
1868
|
-
<td
|
|
1869
|
-
|
|
1910
|
+
<td>
|
|
1911
|
+
<code>? filterExpr</code> /
|
|
1912
|
+
<code>? name[...]</code>
|
|
1913
|
+
</td>
|
|
1914
|
+
<td>
|
|
1915
|
+
<b>Pattern Filter:</b> Asserts input matches pattern;
|
|
1916
|
+
acts as partial identity and inductive witness
|
|
1917
|
+
(<i>e.g.</i> <code>? list[X]</code>)
|
|
1918
|
+
</td>
|
|
1870
1919
|
</tr>
|
|
1871
1920
|
<tr>
|
|
1872
1921
|
<td><code>name</code> / <code>@hash</code></td>
|
|
@@ -1881,6 +1930,20 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1881
1930
|
<table class="help-table">
|
|
1882
1931
|
<tr><td><code>:engine [wasm|pkir|js]</code></td><td>Display or switch evaluation engine (<code>wasm</code>, <code>pkir</code>, or <code>js</code>)</td></tr>
|
|
1883
1932
|
<tr><td><code>:load <file></code></td><td>Load a <code>.k</code> source or <code>.klib</code> library into current state</td></tr>
|
|
1933
|
+
<tr>
|
|
1934
|
+
<td><code>:pattern <name></code></td>
|
|
1935
|
+
<td>
|
|
1936
|
+
Display definition of named pattern
|
|
1937
|
+
<code><name></code> (alias <code>:pat</code>)
|
|
1938
|
+
</td>
|
|
1939
|
+
</tr>
|
|
1940
|
+
<tr>
|
|
1941
|
+
<td><code>:patterns</code></td>
|
|
1942
|
+
<td>
|
|
1943
|
+
List defined named pattern templates
|
|
1944
|
+
(alias <code>:pats</code>)
|
|
1945
|
+
</td>
|
|
1946
|
+
</tr>
|
|
1884
1947
|
<tr><td><code>:rel <name></code></td><td>Display definition of relation <code><name></code></td></tr>
|
|
1885
1948
|
<tr><td><code>:rels</code></td><td>List all relation aliases</td></tr>
|
|
1886
1949
|
<tr><td><code>:codecs</code></td><td>List all loaded format codecs</td></tr>
|
|
@@ -1901,6 +1964,14 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1901
1964
|
<li><code>not = < / true | false, / false | true >;</code> — Polymorphic negation via branch projections and ordered choice</li>
|
|
1902
1965
|
<li><code>{} | true not</code> — Compose constant relation <code>{} | true</code> with <code>not</code> (evaluates to <code>{} | false</code>)</li>
|
|
1903
1966
|
<li><code>bool? = ?< {} true, {} false >;</code> — Define a boolean pattern filter relation</li>
|
|
1967
|
+
<li>
|
|
1968
|
+
<code>bit[] = < {} 0, {} 1 >;</code>
|
|
1969
|
+
— Define a 0-arity bit pattern
|
|
1970
|
+
</li>
|
|
1971
|
+
<li>
|
|
1972
|
+
<code>? list[bool[]]</code>
|
|
1973
|
+
— Instantiated pattern filter matching booleans
|
|
1974
|
+
</li>
|
|
1904
1975
|
<li><code>not = bool? ?X < / true | false, {} | true > ?X;</code> — Filtered boolean negation: guarded by <code>bool?</code>, defaulting to <code>{} | true</code></li>
|
|
1905
1976
|
<li><code>swap = { . y x, . x y };</code> — Define a product field-swapping relation</li>
|
|
1906
1977
|
<li><code>{ {} | ok x, {} | nil y } swap</code> — Construct a product and pass it through <code>swap</code></li>
|