@fraczak/k 6.7.0 → 6.9.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 +10 -0
- package/DOCS/CODECS.md +80 -181
- package/DOCS/CODEC_ARCHITECTURE.md +194 -0
- package/DOCS/REPL.md +49 -60
- package/Examples/arithmetics.k +1 -0
- package/Examples/poly.k +46 -0
- package/TODO.md +112 -0
- package/browser/repl-browser.mjs +112 -733
- package/browser/shims/process.mjs +7 -0
- package/browser/tree-view.mjs +5 -2
- package/browser/vfs-data.mjs +14 -12
- package/codecs/README.md +7 -0
- package/codecs/ieee.mjs +50 -75
- package/codecs/int.mjs +83 -62
- package/codecs/json-codec.mjs +5 -77
- package/codecs/json.mjs +12 -56
- package/codecs/k-parse.mjs +3 -19
- package/codecs/runtime/codec-sdk.mjs +94 -0
- package/codecs/runtime/pattern-k.mjs +45 -0
- package/codecs/string-codec.mjs +48 -74
- package/codecs/unit.mjs +13 -61
- package/codecs/utf16.mjs +13 -56
- package/codecs/utf8.mjs +11 -55
- package/package.json +1 -1
- package/patterns/from-k.mjs +2 -22
- package/repl-codecs.mjs +78 -184
- package/repl.mjs +168 -226
- package/scripts/build-repl-html.mjs +273 -424
- package/scripts/run-tests.mjs +1 -0
- package/tests/test-codecs.mjs +186 -0
- package/tests/test-poly.mjs +44 -0
- package/tests/test-repl-html.mjs +190 -53
- package/tests/test-repl.mjs +150 -114
- package/tests/test-structural-values.mjs +17 -0
- package/tests/test-tree-view.mjs +3 -0
- package/codecs/example-pipeline.mjs +0 -163
- package/codecs/runtime/ieee-pattern.mjs +0 -30
- package/codecs/runtime/test-codec.mjs +0 -89
- package/codecs/runtime/typeFromValue.mjs +0 -34
package/repl.mjs
CHANGED
|
@@ -14,10 +14,12 @@ import { exportPatternGraph } from "./codecs/runtime/codec.mjs";
|
|
|
14
14
|
import { patternToPropertyList } from "./codecs/runtime/pattern-json.mjs";
|
|
15
15
|
import codes from "./codes.mjs";
|
|
16
16
|
import { Value, isProduct, isVariant } from "./Value.mjs";
|
|
17
|
+
import { patternFromFilter } from "./codecs/runtime/pattern-k.mjs";
|
|
17
18
|
if (typeof globalThis !== "undefined") {
|
|
18
19
|
globalThis.Value = Value;
|
|
19
20
|
globalThis.isProduct = isProduct;
|
|
20
21
|
globalThis.isVariant = isVariant;
|
|
22
|
+
globalThis.patternFromFilter = patternFromFilter;
|
|
21
23
|
}
|
|
22
24
|
import { patterns2filters, prettyCode, prettyRel } from "./pretty.mjs";
|
|
23
25
|
import {
|
|
@@ -36,26 +38,22 @@ import {
|
|
|
36
38
|
codeHashToPattern,
|
|
37
39
|
listCodecs,
|
|
38
40
|
loadCodecModule,
|
|
39
|
-
normalizeCodecModule,
|
|
40
41
|
registerCodec,
|
|
41
42
|
resolveCodec,
|
|
42
43
|
unregisterCodec,
|
|
43
|
-
BUILTIN_CODECS,
|
|
44
44
|
closedPatternToCodeHash,
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
valueForPattern,
|
|
46
|
+
ensureEnveloped
|
|
47
47
|
} from "./repl-codecs.mjs";
|
|
48
48
|
|
|
49
49
|
const NAME_RE = /^[a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*$/;
|
|
50
|
-
const TYPE_DEF_RE = /^\s*\$\s*([a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*)\s*=/;
|
|
51
|
-
const REL_DEF_RE = /^\s*([a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*)\s*=/;
|
|
52
50
|
const COMMAND_NAMES = [
|
|
53
|
-
"help", "type", "code", "
|
|
54
|
-
"codes", "codecs", "rels", "
|
|
55
|
-
"
|
|
51
|
+
"help", "type", "code", "run", "eval", "t", "d", "C",
|
|
52
|
+
"codes", "codecs", "rels", "codec", "input", "reset", "klib", "ko", "load",
|
|
53
|
+
"time",
|
|
56
54
|
"quit", "exit"
|
|
57
55
|
];
|
|
58
|
-
const CODEC_COMMAND_NAMES = ["load", "unload", "list"
|
|
56
|
+
const CODEC_COMMAND_NAMES = ["load", "unload", "list"];
|
|
59
57
|
const PATH_COMMANDS = new Set(["klib", "ko", "load"]);
|
|
60
58
|
const INPUT_TYPE_NAME = "__input__";
|
|
61
59
|
const initialCodes = codes.dump();
|
|
@@ -90,7 +88,7 @@ function createState() {
|
|
|
90
88
|
lastResult: null,
|
|
91
89
|
lastMain: null,
|
|
92
90
|
lastTiming: null,
|
|
93
|
-
showTiming:
|
|
91
|
+
showTiming: true
|
|
94
92
|
};
|
|
95
93
|
}
|
|
96
94
|
|
|
@@ -351,7 +349,8 @@ function listAliases(aliases) {
|
|
|
351
349
|
}
|
|
352
350
|
|
|
353
351
|
function formatCodecOutput(output) {
|
|
354
|
-
if (Buffer.isBuffer(output)) return output.toString("utf8");
|
|
352
|
+
if (typeof Buffer !== "undefined" && Buffer.isBuffer(output)) return output.toString("utf8");
|
|
353
|
+
if (typeof Uint8Array !== "undefined" && output instanceof Uint8Array) return new TextDecoder().decode(output);
|
|
355
354
|
return String(output);
|
|
356
355
|
}
|
|
357
356
|
|
|
@@ -359,20 +358,16 @@ function printValue(value, state = null) {
|
|
|
359
358
|
const lines = [valueWithEnvelopeToK(value)];
|
|
360
359
|
if (!state || value === undefined) return lines[0];
|
|
361
360
|
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
const codecs = [
|
|
366
|
-
...(state.codecs?.[codeHash] || []),
|
|
367
|
-
...(state.codecs?.[UNIVERSAL_CODE] || [])
|
|
368
|
-
];
|
|
369
|
-
for (const codec of codecs) {
|
|
361
|
+
const store = state.codecs || {};
|
|
362
|
+
for (const codec of Object.values(store)) {
|
|
370
363
|
if (typeof codec.print !== "function") continue;
|
|
371
364
|
try {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
365
|
+
const output = codec.print(value, { state });
|
|
366
|
+
if (output != null) {
|
|
367
|
+
lines.push(`${codec.name}: ${formatCodecOutput(output)}`);
|
|
368
|
+
}
|
|
369
|
+
} catch {
|
|
370
|
+
// Codec cannot format this value; silently skip
|
|
376
371
|
}
|
|
377
372
|
}
|
|
378
373
|
return lines.join("\n");
|
|
@@ -652,25 +647,19 @@ function completeCodecCommand(line, argStart, arg, state) {
|
|
|
652
647
|
const tokenStart = codecLoadPathCompletionStart(argStart, arg);
|
|
653
648
|
if (tokenStart == null) return [[], line];
|
|
654
649
|
const [pathMatches] = completePath(line, tokenStart);
|
|
655
|
-
|
|
656
|
-
const partial = match && match[2] != null ? match[2].trim() : "";
|
|
657
|
-
const prefix = line.slice(0, tokenStart);
|
|
658
|
-
const builtinMatches = ["int", "utf8", "json", "ieee"]
|
|
659
|
-
.filter((name) => name.startsWith(partial))
|
|
660
|
-
.map((name) => `${prefix}${name}`);
|
|
661
|
-
return [[...builtinMatches, ...pathMatches], line];
|
|
650
|
+
return [pathMatches, line];
|
|
662
651
|
}
|
|
663
652
|
|
|
664
653
|
function resolveTypeHash(state, rawName) {
|
|
665
654
|
const token = rawName.trim();
|
|
666
|
-
const bareName = token.startsWith("$") ? token.slice(1) : token;
|
|
655
|
+
const bareName = token.startsWith("$") ? token.slice(1).trim() : token;
|
|
667
656
|
const hash = state.typeAliases[bareName] || (token.startsWith("@") ? token : null);
|
|
668
657
|
if (!hash || !(hash in state.codes)) throw new Error(`Unknown type '${rawName}'`);
|
|
669
658
|
return hash;
|
|
670
659
|
}
|
|
671
660
|
|
|
672
661
|
function isSimpleTypeReference(rawType) {
|
|
673
|
-
return
|
|
662
|
+
return /^\$?\s*[a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*$/.test(rawType.trim());
|
|
674
663
|
}
|
|
675
664
|
|
|
676
665
|
function resolveTypeExpressionHash(state, rawType) {
|
|
@@ -703,33 +692,101 @@ function resolveInputTypeHash(state, rawType) {
|
|
|
703
692
|
return resolveTypeExpressionHash(state, rawType);
|
|
704
693
|
}
|
|
705
694
|
|
|
706
|
-
function
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
const
|
|
695
|
+
function resolvePatternExpression(state, patternExpr) {
|
|
696
|
+
restoreCodes(state);
|
|
697
|
+
const preamble = aliasPreamble(state);
|
|
698
|
+
const source = [
|
|
699
|
+
preamble,
|
|
700
|
+
patternExpr
|
|
701
|
+
].filter(Boolean).join("\n");
|
|
702
|
+
try {
|
|
703
|
+
const annotated = annotate(source, { libraries: [stateLibrary(state)] });
|
|
704
|
+
const mainRel = annotated.rels.__main__;
|
|
705
|
+
if (!mainRel || !mainRel.typePatternGraph) {
|
|
706
|
+
throw new Error(`Invalid pattern expression '${patternExpr}'`);
|
|
707
|
+
}
|
|
708
|
+
if (mainRel.def.op !== "filter" && mainRel.def.op !== "code") {
|
|
709
|
+
throw new Error(`Expected a pattern expression, got ${mainRel.def.op}`);
|
|
710
|
+
}
|
|
711
|
+
const rootPatternId = mainRel.typePatternGraph.find(mainRel.def.patterns[0]);
|
|
712
|
+
const pattern = patternToPropertyList(exportPatternGraph(mainRel.typePatternGraph, rootPatternId));
|
|
713
|
+
const codeHash = closedPatternToCodeHash(pattern);
|
|
714
|
+
state.codes = codes.dump();
|
|
715
|
+
return { pattern, codeHash, display: codeHash || patternExpr };
|
|
716
|
+
} catch (error) {
|
|
717
|
+
restoreCodes(state);
|
|
718
|
+
throw remapError(error, preambleLineCount(preamble));
|
|
719
|
+
}
|
|
720
|
+
}
|
|
710
721
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
722
|
+
function resolveInputPattern(state, rawExpr) {
|
|
723
|
+
const token = (rawExpr || "").trim();
|
|
724
|
+
if (!token || token === "*" || token === "(...)") {
|
|
725
|
+
return { pattern: [["any", []]], codeHash: null, display: "(...)" };
|
|
714
726
|
}
|
|
715
727
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
const
|
|
728
|
+
// 1. Explicit filter / pattern expression
|
|
729
|
+
if (token.startsWith("?")) {
|
|
730
|
+
const res = resolvePatternExpression(state, token);
|
|
731
|
+
return { ...res, display: token };
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// 2. Pattern as type prefixed by dollar: $ name, $ {typeExpr}, $ <typeExpr>
|
|
735
|
+
if (token.startsWith("$")) {
|
|
736
|
+
const bare = token.slice(1).trim();
|
|
737
|
+
if (bare in (state.typeAliases || {})) {
|
|
738
|
+
const hash = state.typeAliases[bare];
|
|
739
|
+
const pattern = codeHashToPattern(hash, (h) => state.codes?.[h] || codes.find(h));
|
|
740
|
+
return { pattern, codeHash: hash, display: `$ ${bare}` };
|
|
741
|
+
}
|
|
742
|
+
try {
|
|
743
|
+
const res = resolvePatternExpression(state, `? ${token}`);
|
|
744
|
+
return { ...res, display: token };
|
|
745
|
+
} catch {}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// 3. Known type alias or canonical code hash
|
|
749
|
+
try {
|
|
750
|
+
const hash = resolveTypeHash(state, token);
|
|
751
|
+
const pattern = codeHashToPattern(hash, (h) => state.codes?.[h] || codes.find(h));
|
|
752
|
+
return { pattern, codeHash: hash, display: hash };
|
|
753
|
+
} catch (aliasErr) {
|
|
754
|
+
if (isSimpleTypeReference(token)) {
|
|
719
755
|
try {
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
}
|
|
725
|
-
return [
|
|
726
|
-
names
|
|
727
|
-
.filter((name) => name.startsWith(codecPartial))
|
|
728
|
-
.map((name) => `${prefix}${name}`),
|
|
729
|
-
line
|
|
730
|
-
];
|
|
756
|
+
const res = resolvePatternExpression(state, `? ${token}`);
|
|
757
|
+
return { ...res, display: token };
|
|
758
|
+
} catch {}
|
|
759
|
+
throw aliasErr;
|
|
760
|
+
}
|
|
731
761
|
}
|
|
732
762
|
|
|
763
|
+
// 4. Composite expression ({...}, <...>): try type expression first
|
|
764
|
+
try {
|
|
765
|
+
const hash = resolveTypeExpressionHash(state, token);
|
|
766
|
+
const pattern = codeHashToPattern(hash, (h) => state.codes?.[h] || codes.find(h));
|
|
767
|
+
return { pattern, codeHash: hash, display: hash };
|
|
768
|
+
} catch (typeErr) {
|
|
769
|
+
// 5. Fall back to pattern expression: ? <expr>
|
|
770
|
+
try {
|
|
771
|
+
const res = resolvePatternExpression(state, `? ${token}`);
|
|
772
|
+
return { ...res, display: token };
|
|
773
|
+
} catch {
|
|
774
|
+
throw typeErr;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
function completeInputCommand(line, state, argStart, arg) {
|
|
780
|
+
const match = arg.match(/^(\s*)(\S*)$/);
|
|
781
|
+
if (match) {
|
|
782
|
+
const [, leading, partial] = match;
|
|
783
|
+
const prefix = line.slice(0, argStart + leading.length);
|
|
784
|
+
const names = codecNames(state);
|
|
785
|
+
const matches = names
|
|
786
|
+
.filter((name) => name.startsWith(partial))
|
|
787
|
+
.map((name) => `${prefix}${name}`);
|
|
788
|
+
return [matches, line];
|
|
789
|
+
}
|
|
733
790
|
return [[], line];
|
|
734
791
|
}
|
|
735
792
|
|
|
@@ -819,14 +876,6 @@ function parseLoadArgs(arg, usagePrefix = ":") {
|
|
|
819
876
|
return { path: arg.trim(), loadAliases: true };
|
|
820
877
|
}
|
|
821
878
|
|
|
822
|
-
const CODEC_FILES = {
|
|
823
|
-
int: "arithmetics.k",
|
|
824
|
-
utf8: "core.k",
|
|
825
|
-
json: "core.k",
|
|
826
|
-
ieee: "ieee.k",
|
|
827
|
-
unit: "core.k"
|
|
828
|
-
};
|
|
829
|
-
|
|
830
879
|
function normalizeFilePath(p) {
|
|
831
880
|
return p.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
832
881
|
}
|
|
@@ -897,24 +946,6 @@ function loadSourceOrKlib(state, targetPath, options = {}) {
|
|
|
897
946
|
return targetPath;
|
|
898
947
|
}
|
|
899
948
|
|
|
900
|
-
function ensureCodecDependencies(state, codecTarget) {
|
|
901
|
-
const norm = normalizeFilePath(codecTarget).toLowerCase();
|
|
902
|
-
const base = path.basename(norm, path.extname(norm));
|
|
903
|
-
const depFile = CODEC_FILES[base] || CODEC_FILES[norm];
|
|
904
|
-
if (!depFile) return null;
|
|
905
|
-
if (!state.loadedFiles) state.loadedFiles = new Set();
|
|
906
|
-
const depNormKey = normalizeFilePath(depFile);
|
|
907
|
-
const baseName = path.basename(depNormKey);
|
|
908
|
-
const alreadyLoaded = state.loadedFiles.has(depNormKey) ||
|
|
909
|
-
state.loadedFiles.has(`Examples/${depNormKey}`) ||
|
|
910
|
-
Array.from(state.loadedFiles).some(f => path.basename(f) === baseName);
|
|
911
|
-
if (!alreadyLoaded) {
|
|
912
|
-
loadSourceOrKlib(state, depFile);
|
|
913
|
-
return depFile;
|
|
914
|
-
}
|
|
915
|
-
return null;
|
|
916
|
-
}
|
|
917
|
-
|
|
918
949
|
function lineForContinuation(line) {
|
|
919
950
|
return line.replace(/\\\s*$/, "");
|
|
920
951
|
}
|
|
@@ -981,57 +1012,6 @@ function compileSnippetArtifacts(source, state, sourceName = "<repl>") {
|
|
|
981
1012
|
}
|
|
982
1013
|
}
|
|
983
1014
|
|
|
984
|
-
async function defineType(input, state) {
|
|
985
|
-
const typeMatch = input.match(TYPE_DEF_RE);
|
|
986
|
-
if (!typeMatch) {
|
|
987
|
-
throw new Error("Type definitions use: :type name = <...>");
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
const name = typeMatch[1];
|
|
991
|
-
restoreCodes(state);
|
|
992
|
-
const preamble = aliasPreamble(state, name);
|
|
993
|
-
const source = [preamble, ensureSemicolon(input), "()"].filter(Boolean).join("\n");
|
|
994
|
-
let annotated;
|
|
995
|
-
try {
|
|
996
|
-
annotated = annotate(source, { libraries: [stateLibrary(state)] });
|
|
997
|
-
} catch (error) {
|
|
998
|
-
throw remapError(error, preambleLineCount(preamble));
|
|
999
|
-
}
|
|
1000
|
-
const hash = annotated.representatives[name];
|
|
1001
|
-
if (!hash) throw new Error(`Type definition did not produce an alias for '${name}'`);
|
|
1002
|
-
state.codes = codes.dump();
|
|
1003
|
-
state.typeAliases[name] = hash;
|
|
1004
|
-
rememberOrigin(state, hash, name, "code");
|
|
1005
|
-
return [`$ ${name} = ${hash}`];
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
async function defineRelation(input, state) {
|
|
1009
|
-
const relMatch = input.match(REL_DEF_RE);
|
|
1010
|
-
if (!relMatch) {
|
|
1011
|
-
throw new Error("Relation definitions use: :rel name = expression");
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
const name = relMatch[1];
|
|
1015
|
-
restoreCodes(state);
|
|
1016
|
-
const preamble = aliasPreamble(state, name);
|
|
1017
|
-
const source = [preamble, ensureSemicolon(input), name].filter(Boolean).join("\n");
|
|
1018
|
-
let lib;
|
|
1019
|
-
try {
|
|
1020
|
-
lib = hydrateObject(compileLibrary(source, { source: "<repl>", libraries: [stateLibrary(state)] }));
|
|
1021
|
-
} catch (error) {
|
|
1022
|
-
throw remapError(error, preambleLineCount(preamble));
|
|
1023
|
-
}
|
|
1024
|
-
const hash = lib.relAlias?.[name];
|
|
1025
|
-
if (!hash || !lib.rels?.[hash]) throw new Error(`Relation definition did not produce an alias for '${name}'`);
|
|
1026
|
-
state.codes = codes.dump();
|
|
1027
|
-
state.rels[hash] = lib.rels[hash];
|
|
1028
|
-
state.relAliases[name] = hash;
|
|
1029
|
-
state.lastMain = name;
|
|
1030
|
-
mergeMeta(state, lib.meta);
|
|
1031
|
-
rememberOrigin(state, hash, name, "rel");
|
|
1032
|
-
return [`${name} = ${hash}`];
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
1015
|
async function executeExpressionWithWasm(annotated, state, lineOffset = 0) {
|
|
1036
1016
|
const mainRel = annotated.rels.__main__;
|
|
1037
1017
|
if (!mainRel) return { result: undefined, wasmCompileMs: 0, executeMs: 0 };
|
|
@@ -1171,13 +1151,10 @@ async function loadCodec(input, state) {
|
|
|
1171
1151
|
const [subcommand, ...rest] = trimmed.split(/\s+/);
|
|
1172
1152
|
switch (subcommand) {
|
|
1173
1153
|
case "load": {
|
|
1174
|
-
const filePath = rest.join(" ");
|
|
1175
|
-
if (!filePath) throw new Error(":codec load requires a file path
|
|
1176
|
-
const autoLoaded = ensureCodecDependencies(state, filePath);
|
|
1154
|
+
const filePath = rest.join(" ").trim();
|
|
1155
|
+
if (!filePath) throw new Error(":codec load requires a file path");
|
|
1177
1156
|
const registered = await loadCodecModule(state, expandHome(filePath));
|
|
1178
|
-
return registered.map(({ name
|
|
1179
|
-
`loaded codec ${name} for ${codeHash === UNIVERSAL_CODE ? "all types" : codeHash}${autoLoaded ? ` (auto-loaded ${autoLoaded})` : ""}`
|
|
1180
|
-
);
|
|
1157
|
+
return registered.map(({ name }) => `loaded codec ${name}`);
|
|
1181
1158
|
}
|
|
1182
1159
|
case "unload": {
|
|
1183
1160
|
const codecName = rest.join(" ").trim();
|
|
@@ -1186,64 +1163,56 @@ async function loadCodec(input, state) {
|
|
|
1186
1163
|
if (removed === 0) throw new Error(`Codec '${codecName}' is not loaded`);
|
|
1187
1164
|
return [`unloaded codec ${codecName}`];
|
|
1188
1165
|
}
|
|
1189
|
-
case "define":
|
|
1190
|
-
|
|
1191
|
-
if (!match) {
|
|
1192
|
-
throw new Error(":codec define requires: name type { parse: ..., print: ... }");
|
|
1193
|
-
}
|
|
1194
|
-
const [, name, rawType, bodyStr] = match;
|
|
1195
|
-
const fn = new Function("Value", "isProduct", "isVariant", "state", `return (${bodyStr});`);
|
|
1196
|
-
const obj = fn(Value, isProduct, isVariant, state);
|
|
1197
|
-
const codeHash = rawType === "*" ? UNIVERSAL_CODE : resolveInputTypeHash(state, rawType);
|
|
1198
|
-
const codec = normalizeCodecModule({
|
|
1199
|
-
name,
|
|
1200
|
-
codes: [codeHash],
|
|
1201
|
-
universal: codeHash === UNIVERSAL_CODE,
|
|
1202
|
-
parse: obj.parse,
|
|
1203
|
-
print: obj.print
|
|
1204
|
-
}, name);
|
|
1205
|
-
const registered = registerCodec(state, codec, "<inline>");
|
|
1206
|
-
return registered.map(({ name, codeHash }) =>
|
|
1207
|
-
`defined codec ${name} for ${codeHash === UNIVERSAL_CODE ? "all types" : codeHash}`
|
|
1208
|
-
);
|
|
1209
|
-
}
|
|
1166
|
+
case "define":
|
|
1167
|
+
throw new Error(":codec define has been removed; all codecs are loaded from files via :codec load <file.mjs>");
|
|
1210
1168
|
case "list":
|
|
1211
1169
|
if (rest.length > 0) throw new Error(":codec list does not accept arguments");
|
|
1212
1170
|
return [listCodecs(state)];
|
|
1213
1171
|
default: {
|
|
1214
|
-
const autoLoaded = ensureCodecDependencies(state, trimmed);
|
|
1215
1172
|
const registered = await loadCodecModule(state, expandHome(trimmed));
|
|
1216
|
-
return registered.map(({ name
|
|
1217
|
-
`loaded codec ${name} for ${codeHash === UNIVERSAL_CODE ? "all types" : codeHash}${autoLoaded ? ` (auto-loaded ${autoLoaded})` : ""}`
|
|
1218
|
-
);
|
|
1173
|
+
return registered.map(({ name }) => `loaded codec ${name}`);
|
|
1219
1174
|
}
|
|
1220
1175
|
}
|
|
1221
1176
|
}
|
|
1222
1177
|
|
|
1223
1178
|
async function requestCodecInput(input, state) {
|
|
1224
|
-
|
|
1179
|
+
let trimmed = input.trim();
|
|
1225
1180
|
if (!trimmed) {
|
|
1226
|
-
|
|
1181
|
+
const loaded = Object.keys(state.codecs || {});
|
|
1182
|
+
const loadedStr = loaded.length > 0 ? `Loaded: ${loaded.join(", ")}` : "None loaded";
|
|
1183
|
+
return [
|
|
1184
|
+
"Usage: :input <codec.mjs> [text]",
|
|
1185
|
+
`Loaded codecs: ${loadedStr}`
|
|
1186
|
+
];
|
|
1227
1187
|
}
|
|
1228
1188
|
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
try {
|
|
1232
|
-
codeHash = resolveInputTypeHash(state, trimmed);
|
|
1233
|
-
} catch (wholeError) {
|
|
1234
|
-
const split = trimmed.match(/^(.*\S)\s+([a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*)$/);
|
|
1235
|
-
if (!split) throw wholeError;
|
|
1236
|
-
try {
|
|
1237
|
-
codeHash = resolveInputTypeHash(state, split[1]);
|
|
1238
|
-
codecName = split[2];
|
|
1239
|
-
} catch (typeError) {
|
|
1240
|
-
throw typeError;
|
|
1241
|
-
}
|
|
1189
|
+
if (trimmed.startsWith("$")) {
|
|
1190
|
+
trimmed = trimmed.slice(1).trim();
|
|
1242
1191
|
}
|
|
1243
1192
|
|
|
1244
|
-
const
|
|
1245
|
-
|
|
1246
|
-
|
|
1193
|
+
const match = trimmed.match(/^([a-zA-Z0-9_?!+.-]+)(?:\s+([\s\S]*))?$/);
|
|
1194
|
+
if (!match) {
|
|
1195
|
+
throw new Error("Invalid :input syntax. Usage: :input <codec.mjs> [text]");
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
const codecName = match[1];
|
|
1199
|
+
const inlineText = match[2];
|
|
1200
|
+
|
|
1201
|
+
const codec = resolveCodec(state, codecName, "parse");
|
|
1202
|
+
|
|
1203
|
+
if (inlineText !== undefined) {
|
|
1204
|
+
const parsed = await codec.parse(inlineText, { state, codecName: codec.name });
|
|
1205
|
+
const value = ensureEnveloped(parsed, codec);
|
|
1206
|
+
state.value = value;
|
|
1207
|
+
state.lastResult = value;
|
|
1208
|
+
return [printValue(value, state)];
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
state.pendingInput = {
|
|
1212
|
+
codecName: codec.name,
|
|
1213
|
+
promptName: codec.name
|
|
1214
|
+
};
|
|
1215
|
+
return [];
|
|
1247
1216
|
}
|
|
1248
1217
|
|
|
1249
1218
|
async function consumeCodecInput(input, state) {
|
|
@@ -1252,14 +1221,12 @@ async function consumeCodecInput(input, state) {
|
|
|
1252
1221
|
state.pendingInput = null;
|
|
1253
1222
|
|
|
1254
1223
|
restoreCodes(state);
|
|
1255
|
-
const codec = resolveCodec(state, pending.
|
|
1256
|
-
const pattern = codeHashToPattern(pending.codeHash, codes.find);
|
|
1224
|
+
const codec = resolveCodec(state, pending.codecName, "parse");
|
|
1257
1225
|
const parsed = await codec.parse(input, {
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
state
|
|
1226
|
+
state,
|
|
1227
|
+
codecName: codec.name
|
|
1261
1228
|
});
|
|
1262
|
-
const value =
|
|
1229
|
+
const value = ensureEnveloped(parsed, codec);
|
|
1263
1230
|
state.value = value;
|
|
1264
1231
|
state.lastResult = value;
|
|
1265
1232
|
return [printValue(value, state)];
|
|
@@ -1298,34 +1265,16 @@ async function evaluateCommand(line, state) {
|
|
|
1298
1265
|
exit(0);
|
|
1299
1266
|
case "help":
|
|
1300
1267
|
return [helpText()];
|
|
1301
|
-
case "timing":
|
|
1302
|
-
|
|
1303
|
-
if (trimmed === "on" || trimmed === "true" || trimmed === "1") {
|
|
1304
|
-
state.showTiming = true;
|
|
1305
|
-
} else if (trimmed === "off" || trimmed === "false" || trimmed === "0") {
|
|
1306
|
-
state.showTiming = false;
|
|
1307
|
-
} else if (!trimmed) {
|
|
1308
|
-
state.showTiming = !state.showTiming;
|
|
1309
|
-
} else {
|
|
1310
|
-
throw new Error(":timing accepts 'on', 'off', or no arguments to toggle");
|
|
1311
|
-
}
|
|
1312
|
-
return [`timing reporting ${state.showTiming ? "enabled" : "disabled"}`];
|
|
1313
|
-
}
|
|
1268
|
+
case "timing":
|
|
1269
|
+
throw new Error(":timing has been removed; timing is now always enabled");
|
|
1314
1270
|
case "time": {
|
|
1315
1271
|
if (!arg) throw new Error(":time requires an expression");
|
|
1316
|
-
|
|
1317
|
-
state.showTiming = true;
|
|
1318
|
-
try {
|
|
1319
|
-
return await evaluateInput(arg, state);
|
|
1320
|
-
} finally {
|
|
1321
|
-
state.showTiming = wasTiming;
|
|
1322
|
-
}
|
|
1272
|
+
return evaluateInput(arg, state);
|
|
1323
1273
|
}
|
|
1324
1274
|
case "type": {
|
|
1325
|
-
if (!arg) throw new Error(":type requires a name
|
|
1275
|
+
if (!arg) throw new Error(":type requires a type name");
|
|
1326
1276
|
if (arg.includes("=")) {
|
|
1327
|
-
|
|
1328
|
-
return defineType(definition, state);
|
|
1277
|
+
throw new Error("Type definitions use syntax: $ name = typeExpr; Use :type <name> to show a type definition");
|
|
1329
1278
|
}
|
|
1330
1279
|
return evaluateCommand(`:C ${arg}`, state);
|
|
1331
1280
|
}
|
|
@@ -1333,8 +1282,7 @@ async function evaluateCommand(line, state) {
|
|
|
1333
1282
|
return evaluateCommand(`:C ${arg}`, state);
|
|
1334
1283
|
case "rel":
|
|
1335
1284
|
case "def":
|
|
1336
|
-
|
|
1337
|
-
return defineRelation(arg, state);
|
|
1285
|
+
throw new Error("Relation definitions use syntax: name = relExpr;");
|
|
1338
1286
|
case "run":
|
|
1339
1287
|
case "eval":
|
|
1340
1288
|
return runExpression(arg, state);
|
|
@@ -1342,11 +1290,6 @@ async function evaluateCommand(line, state) {
|
|
|
1342
1290
|
return [listAliases(state.typeAliases)];
|
|
1343
1291
|
case "rels":
|
|
1344
1292
|
return [listAliases(state.relAliases)];
|
|
1345
|
-
case "val":
|
|
1346
|
-
return [
|
|
1347
|
-
printValue(state.value, state),
|
|
1348
|
-
JSON.stringify(state.value, null, 2)
|
|
1349
|
-
];
|
|
1350
1293
|
case "codec":
|
|
1351
1294
|
case "codecs":
|
|
1352
1295
|
return loadCodec(arg, state);
|
|
@@ -1403,26 +1346,21 @@ async function evaluateCommand(line, state) {
|
|
|
1403
1346
|
|
|
1404
1347
|
function helpText() {
|
|
1405
1348
|
return [
|
|
1406
|
-
":type name = <...> define a type",
|
|
1407
|
-
":rel name = expr define a relation",
|
|
1408
1349
|
":run expr run an expression on the current value",
|
|
1409
1350
|
":time expr run an expression and report compilation/execution time",
|
|
1410
|
-
":timing [on|off] toggle timing reporting after every evaluation",
|
|
1411
1351
|
":t name show relation type",
|
|
1412
1352
|
":d name show relation definition",
|
|
1413
1353
|
":type name show type definition",
|
|
1414
1354
|
":codes list type aliases",
|
|
1415
1355
|
":rels list relation aliases",
|
|
1416
|
-
":codec load file load a
|
|
1417
|
-
":codec define n t b define a custom codec: name type { parse: ..., print: ... }",
|
|
1356
|
+
":codec load file load a codec module from file (e.g. :codec load codecs/int.mjs)",
|
|
1418
1357
|
":codec unload name unload a registered codec",
|
|
1419
1358
|
":codec list list loaded codecs (or simply :codecs)",
|
|
1420
|
-
":input
|
|
1359
|
+
":input <codec.mjs> [text] parse input using specified codec",
|
|
1421
1360
|
":load [--no-alias] file",
|
|
1422
1361
|
" load .k source or .klib",
|
|
1423
1362
|
":klib file export state as a library",
|
|
1424
1363
|
":ko file expr export executable .ko using expr as main",
|
|
1425
|
-
":val print current value",
|
|
1426
1364
|
":reset clear state",
|
|
1427
1365
|
":help show this help",
|
|
1428
1366
|
"",
|
|
@@ -1568,14 +1506,18 @@ export {
|
|
|
1568
1506
|
registerCodec,
|
|
1569
1507
|
unregisterCodec,
|
|
1570
1508
|
resolveCodec,
|
|
1509
|
+
resolveInputTypeHash,
|
|
1510
|
+
resolveInputPattern,
|
|
1511
|
+
valueForPattern,
|
|
1571
1512
|
codeHashToPattern,
|
|
1572
|
-
BUILTIN_CODECS,
|
|
1573
1513
|
printValue,
|
|
1574
1514
|
promptForState,
|
|
1575
1515
|
propertyListToFilter,
|
|
1576
1516
|
savedLibrary,
|
|
1577
1517
|
loadSourceOrKlib,
|
|
1578
|
-
ensureCodecDependencies,
|
|
1579
1518
|
valueToK,
|
|
1580
|
-
formatDuration
|
|
1519
|
+
formatDuration,
|
|
1520
|
+
codecNames,
|
|
1521
|
+
ensureEnveloped,
|
|
1522
|
+
patternFromFilter
|
|
1581
1523
|
};
|