@fraczak/k 6.6.0 → 6.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DOCS/REPL.md +23 -36
- package/TODO.md +112 -0
- package/backends/arm64/README.md +0 -1
- package/backends/arm64/bin/k-arm64-compile.mjs +0 -8
- 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/src/cli.mjs +153 -0
- package/backends/llvm/src/executable.mjs +1 -1
- package/backends/llvm/src/kvm2llvm.mjs +11 -1
- package/backends/llvm/tests/integration.sh +18 -22
- package/backends/wasm/bin/k-wasm-compile.mjs +19 -4
- package/browser/repl-browser.mjs +146 -94
- package/browser/vfs-data.mjs +2 -0
- package/package.json +1 -1
- package/repl-codecs.mjs +26 -5
- package/repl.mjs +214 -148
- package/scripts/build-repl-html.mjs +213 -72
- package/tests/test-repl-html.mjs +84 -6
- package/tests/test-repl.mjs +90 -40
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fraczak/k",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.8.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-codecs.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import * as utf8Codec from "./codecs/utf8.mjs";
|
|
|
11
11
|
import * as jsonCodec from "./codecs/json.mjs";
|
|
12
12
|
import * as ieeeCodec from "./codecs/ieee.mjs";
|
|
13
13
|
import * as unitCodec from "./codecs/unit.mjs";
|
|
14
|
+
import { patternFromJsonValue } from "./codecs/json-codec.mjs";
|
|
14
15
|
|
|
15
16
|
const BUILTIN_CODECS = {
|
|
16
17
|
int: intCodec,
|
|
@@ -72,9 +73,23 @@ function codeHashToPattern(codeHash, findCode) {
|
|
|
72
73
|
return patternToPropertyList(deriveClosedPattern(codeHash, code, resolveType));
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
function valueForPattern(value, pattern = null, rawInput = null, codecName = null) {
|
|
77
|
+
if (pattern) {
|
|
78
|
+
return decodeWire(encodeToWire(value, pattern)).value;
|
|
79
|
+
}
|
|
80
|
+
if (codecName === "json" && rawInput != null) {
|
|
81
|
+
try {
|
|
82
|
+
const json = JSON.parse(rawInput);
|
|
83
|
+
const jsonPattern = patternFromJsonValue(json);
|
|
84
|
+
return decodeWire(encodeToWire(value, jsonPattern)).value;
|
|
85
|
+
} catch {}
|
|
86
|
+
}
|
|
87
|
+
return decodeWire(encodeToWire(value)).value;
|
|
88
|
+
}
|
|
89
|
+
|
|
75
90
|
function valueForCode(value, codeHash, findCode) {
|
|
76
91
|
const pattern = codeHashToPattern(codeHash, findCode);
|
|
77
|
-
return
|
|
92
|
+
return valueForPattern(value, pattern);
|
|
78
93
|
}
|
|
79
94
|
|
|
80
95
|
function normalizeCodecModule(mod, fallbackName) {
|
|
@@ -255,17 +270,21 @@ function resolveCodec(state, codeHash, codecName = null, capability = null) {
|
|
|
255
270
|
const matchesCodec = (codec) =>
|
|
256
271
|
(codecName == null || codec.name === codecName) &&
|
|
257
272
|
(capability == null || typeof codec[capability] === "function");
|
|
258
|
-
const exactMatches = (store[codeHash] || []).filter(matchesCodec);
|
|
273
|
+
const exactMatches = (codeHash ? store[codeHash] || [] : []).filter(matchesCodec);
|
|
259
274
|
const universalMatches = (store[UNIVERSAL_CODE] || []).filter(matchesCodec);
|
|
260
275
|
const matches = codecName == null && exactMatches.length > 0
|
|
261
276
|
? exactMatches
|
|
262
277
|
: [...exactMatches, ...universalMatches];
|
|
263
278
|
if (matches.length === 0) {
|
|
279
|
+
if (codecName) {
|
|
280
|
+
const namedMatches = Object.values(store).flat().filter(matchesCodec);
|
|
281
|
+
if (namedMatches.length > 0) return namedMatches[0];
|
|
282
|
+
}
|
|
264
283
|
const suffix = codecName ? ` named '${codecName}'` : "";
|
|
265
|
-
throw new Error(`No ${capability || "usable"} codec${suffix} for ${codeHash}`);
|
|
284
|
+
throw new Error(`No ${capability || "usable"} codec${suffix}${codeHash ? ` for ${codeHash}` : ""}`);
|
|
266
285
|
}
|
|
267
286
|
if (matches.length > 1 && codecName == null) {
|
|
268
|
-
throw new Error(`Multiple codecs for ${codeHash}; specify one of: ${matches.map((codec) => codec.name).join(", ")}`);
|
|
287
|
+
throw new Error(`Multiple codecs for ${codeHash || "target"}; specify one of: ${matches.map((codec) => codec.name).join(", ")}`);
|
|
269
288
|
}
|
|
270
289
|
return matches[0];
|
|
271
290
|
}
|
|
@@ -282,5 +301,7 @@ export {
|
|
|
282
301
|
unregisterCodec,
|
|
283
302
|
closedPatternToCodeHash,
|
|
284
303
|
UNIVERSAL_CODE,
|
|
285
|
-
valueForCode
|
|
304
|
+
valueForCode,
|
|
305
|
+
valueForPattern,
|
|
306
|
+
matchBuiltinCodec
|
|
286
307
|
};
|
package/repl.mjs
CHANGED
|
@@ -36,26 +36,24 @@ import {
|
|
|
36
36
|
codeHashToPattern,
|
|
37
37
|
listCodecs,
|
|
38
38
|
loadCodecModule,
|
|
39
|
-
normalizeCodecModule,
|
|
40
39
|
registerCodec,
|
|
41
40
|
resolveCodec,
|
|
42
41
|
unregisterCodec,
|
|
43
42
|
BUILTIN_CODECS,
|
|
44
43
|
closedPatternToCodeHash,
|
|
45
44
|
UNIVERSAL_CODE,
|
|
46
|
-
|
|
45
|
+
valueForPattern,
|
|
46
|
+
matchBuiltinCodec
|
|
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
|
|
|
@@ -360,12 +358,11 @@ function printValue(value, state = null) {
|
|
|
360
358
|
if (!state || value === undefined) return lines[0];
|
|
361
359
|
|
|
362
360
|
const codeHash = closedPatternToCodeHash(value.pattern);
|
|
363
|
-
if (!codeHash) return lines[0];
|
|
364
|
-
|
|
365
361
|
const codecs = [
|
|
366
|
-
...(state.codecs?.[codeHash] || []),
|
|
362
|
+
...(codeHash ? state.codecs?.[codeHash] || [] : []),
|
|
367
363
|
...(state.codecs?.[UNIVERSAL_CODE] || [])
|
|
368
364
|
];
|
|
365
|
+
if (codecs.length === 0) return lines[0];
|
|
369
366
|
for (const codec of codecs) {
|
|
370
367
|
if (typeof codec.print !== "function") continue;
|
|
371
368
|
try {
|
|
@@ -663,14 +660,14 @@ function completeCodecCommand(line, argStart, arg, state) {
|
|
|
663
660
|
|
|
664
661
|
function resolveTypeHash(state, rawName) {
|
|
665
662
|
const token = rawName.trim();
|
|
666
|
-
const bareName = token.startsWith("$") ? token.slice(1) : token;
|
|
663
|
+
const bareName = token.startsWith("$") ? token.slice(1).trim() : token;
|
|
667
664
|
const hash = state.typeAliases[bareName] || (token.startsWith("@") ? token : null);
|
|
668
665
|
if (!hash || !(hash in state.codes)) throw new Error(`Unknown type '${rawName}'`);
|
|
669
666
|
return hash;
|
|
670
667
|
}
|
|
671
668
|
|
|
672
669
|
function isSimpleTypeReference(rawType) {
|
|
673
|
-
return
|
|
670
|
+
return /^\$?\s*[a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*$/.test(rawType.trim());
|
|
674
671
|
}
|
|
675
672
|
|
|
676
673
|
function resolveTypeExpressionHash(state, rawType) {
|
|
@@ -703,28 +700,117 @@ function resolveInputTypeHash(state, rawType) {
|
|
|
703
700
|
return resolveTypeExpressionHash(state, rawType);
|
|
704
701
|
}
|
|
705
702
|
|
|
703
|
+
function resolvePatternExpression(state, patternExpr) {
|
|
704
|
+
restoreCodes(state);
|
|
705
|
+
const preamble = aliasPreamble(state);
|
|
706
|
+
const source = [
|
|
707
|
+
preamble,
|
|
708
|
+
patternExpr
|
|
709
|
+
].filter(Boolean).join("\n");
|
|
710
|
+
try {
|
|
711
|
+
const annotated = annotate(source, { libraries: [stateLibrary(state)] });
|
|
712
|
+
const mainRel = annotated.rels.__main__;
|
|
713
|
+
if (!mainRel || !mainRel.typePatternGraph) {
|
|
714
|
+
throw new Error(`Invalid pattern expression '${patternExpr}'`);
|
|
715
|
+
}
|
|
716
|
+
if (mainRel.def.op !== "filter" && mainRel.def.op !== "code") {
|
|
717
|
+
throw new Error(`Expected a pattern expression, got ${mainRel.def.op}`);
|
|
718
|
+
}
|
|
719
|
+
const rootPatternId = mainRel.typePatternGraph.find(mainRel.def.patterns[0]);
|
|
720
|
+
const pattern = patternToPropertyList(exportPatternGraph(mainRel.typePatternGraph, rootPatternId));
|
|
721
|
+
const codeHash = closedPatternToCodeHash(pattern);
|
|
722
|
+
state.codes = codes.dump();
|
|
723
|
+
return { pattern, codeHash, display: codeHash || patternExpr };
|
|
724
|
+
} catch (error) {
|
|
725
|
+
restoreCodes(state);
|
|
726
|
+
throw remapError(error, preambleLineCount(preamble));
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
function resolveInputPattern(state, rawExpr) {
|
|
731
|
+
const token = (rawExpr || "").trim();
|
|
732
|
+
if (!token || token === "*" || token === "(...)") {
|
|
733
|
+
return { pattern: [["any", []]], codeHash: null, display: "(...)" };
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// 1. Explicit filter / pattern expression
|
|
737
|
+
if (token.startsWith("?")) {
|
|
738
|
+
const res = resolvePatternExpression(state, token);
|
|
739
|
+
return { ...res, display: token };
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// 2. Pattern as type prefixed by dollar: $ name, $ {typeExpr}, $ <typeExpr>
|
|
743
|
+
if (token.startsWith("$")) {
|
|
744
|
+
const bare = token.slice(1).trim();
|
|
745
|
+
if (bare in (state.typeAliases || {})) {
|
|
746
|
+
const hash = state.typeAliases[bare];
|
|
747
|
+
const pattern = codeHashToPattern(hash, (h) => state.codes?.[h] || codes.find(h));
|
|
748
|
+
return { pattern, codeHash: hash, display: `$ ${bare}` };
|
|
749
|
+
}
|
|
750
|
+
try {
|
|
751
|
+
const res = resolvePatternExpression(state, `? ${token}`);
|
|
752
|
+
return { ...res, display: token };
|
|
753
|
+
} catch {}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// 3. Known type alias or canonical code hash
|
|
757
|
+
try {
|
|
758
|
+
const hash = resolveTypeHash(state, token);
|
|
759
|
+
const pattern = codeHashToPattern(hash, (h) => state.codes?.[h] || codes.find(h));
|
|
760
|
+
return { pattern, codeHash: hash, display: hash };
|
|
761
|
+
} catch (aliasErr) {
|
|
762
|
+
if (isSimpleTypeReference(token)) {
|
|
763
|
+
try {
|
|
764
|
+
const res = resolvePatternExpression(state, `? ${token}`);
|
|
765
|
+
return { ...res, display: token };
|
|
766
|
+
} catch {}
|
|
767
|
+
throw aliasErr;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// 4. Composite expression ({...}, <...>): try type expression first
|
|
772
|
+
try {
|
|
773
|
+
const hash = resolveTypeExpressionHash(state, token);
|
|
774
|
+
const pattern = codeHashToPattern(hash, (h) => state.codes?.[h] || codes.find(h));
|
|
775
|
+
return { pattern, codeHash: hash, display: hash };
|
|
776
|
+
} catch (typeErr) {
|
|
777
|
+
// 5. Fall back to pattern expression: ? <expr>
|
|
778
|
+
try {
|
|
779
|
+
const res = resolvePatternExpression(state, `? ${token}`);
|
|
780
|
+
return { ...res, display: token };
|
|
781
|
+
} catch {
|
|
782
|
+
throw typeErr;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
706
787
|
function completeInputCommand(line, state, argStart, arg) {
|
|
707
788
|
const match = arg.match(/^(\s*)(\S+)?(\s+)?(\S*)?$/);
|
|
708
789
|
if (!match) return [[], line];
|
|
709
|
-
const [, leading,
|
|
790
|
+
const [, leading, firstToken = "", afterWhitespace = "", secondPartial = ""] = match;
|
|
710
791
|
|
|
711
|
-
if (!
|
|
792
|
+
if (!firstToken || !afterWhitespace) {
|
|
712
793
|
if (/@[A-Za-z0-9_?!+-]*$/.test(line)) return completeCanonicalCode(line, state);
|
|
713
|
-
|
|
794
|
+
const [types] = completeTypeIdentifier(line, state);
|
|
795
|
+
const prefix = line.slice(0, argStart + leading.length);
|
|
796
|
+
const codecMatches = codecNames(state)
|
|
797
|
+
.filter((name) => name.startsWith(firstToken))
|
|
798
|
+
.map((name) => `${prefix}${name}`);
|
|
799
|
+
return [[...new Set([...types, ...codecMatches])], line];
|
|
714
800
|
}
|
|
715
801
|
|
|
716
|
-
if (
|
|
717
|
-
const prefix = line.slice(0, argStart + leading.length +
|
|
802
|
+
if (firstToken) {
|
|
803
|
+
const prefix = line.slice(0, argStart + leading.length + firstToken.length + afterWhitespace.length);
|
|
718
804
|
const names = (() => {
|
|
719
805
|
try {
|
|
720
|
-
return codecNames(state, resolveTypeHash(state,
|
|
806
|
+
return codecNames(state, resolveTypeHash(state, firstToken));
|
|
721
807
|
} catch {
|
|
722
808
|
return codecNames(state);
|
|
723
809
|
}
|
|
724
810
|
})();
|
|
725
811
|
return [
|
|
726
812
|
names
|
|
727
|
-
.filter((name) => name.startsWith(
|
|
813
|
+
.filter((name) => name.startsWith(secondPartial))
|
|
728
814
|
.map((name) => `${prefix}${name}`),
|
|
729
815
|
line
|
|
730
816
|
];
|
|
@@ -981,57 +1067,6 @@ function compileSnippetArtifacts(source, state, sourceName = "<repl>") {
|
|
|
981
1067
|
}
|
|
982
1068
|
}
|
|
983
1069
|
|
|
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
1070
|
async function executeExpressionWithWasm(annotated, state, lineOffset = 0) {
|
|
1036
1071
|
const mainRel = annotated.rels.__main__;
|
|
1037
1072
|
if (!mainRel) return { result: undefined, wasmCompileMs: 0, executeMs: 0 };
|
|
@@ -1186,27 +1221,8 @@ async function loadCodec(input, state) {
|
|
|
1186
1221
|
if (removed === 0) throw new Error(`Codec '${codecName}' is not loaded`);
|
|
1187
1222
|
return [`unloaded codec ${codecName}`];
|
|
1188
1223
|
}
|
|
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
|
-
}
|
|
1224
|
+
case "define":
|
|
1225
|
+
throw new Error(":codec define has been removed; use :codec load <name|file> or the Custom Codec Studio UI");
|
|
1210
1226
|
case "list":
|
|
1211
1227
|
if (rest.length > 0) throw new Error(":codec list does not accept arguments");
|
|
1212
1228
|
return [listCodecs(state)];
|
|
@@ -1221,29 +1237,105 @@ async function loadCodec(input, state) {
|
|
|
1221
1237
|
}
|
|
1222
1238
|
|
|
1223
1239
|
async function requestCodecInput(input, state) {
|
|
1224
|
-
const trimmed = input.trim();
|
|
1225
|
-
if (!trimmed) {
|
|
1226
|
-
throw new Error(":input requires a type and optional codec name");
|
|
1227
|
-
}
|
|
1240
|
+
const trimmed = input.trim() || "(...)";
|
|
1228
1241
|
|
|
1229
|
-
let
|
|
1242
|
+
let pattern = null;
|
|
1243
|
+
let codeHash = null;
|
|
1230
1244
|
let codecName = null;
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1245
|
+
let targetDisplay = null;
|
|
1246
|
+
let rawExpr = trimmed;
|
|
1247
|
+
|
|
1248
|
+
const isJustCodec = (() => {
|
|
1249
|
+
if (trimmed.startsWith("?") || trimmed.startsWith("$") || trimmed.startsWith("{") || trimmed.startsWith("<") || trimmed.startsWith("(")) {
|
|
1250
|
+
return false;
|
|
1251
|
+
}
|
|
1252
|
+
const allCodecs = codecNames(state);
|
|
1253
|
+
const builtin = matchBuiltinCodec(trimmed);
|
|
1254
|
+
const isCodecName = allCodecs.includes(trimmed) || builtin != null;
|
|
1255
|
+
const isTypeAlias = Boolean(state.typeAliases && state.typeAliases[trimmed]);
|
|
1256
|
+
return isCodecName && !isTypeAlias;
|
|
1257
|
+
})();
|
|
1258
|
+
|
|
1259
|
+
if (isJustCodec) {
|
|
1260
|
+
codecName = trimmed;
|
|
1261
|
+
targetDisplay = trimmed;
|
|
1262
|
+
ensureCodecDependencies(state, codecName);
|
|
1263
|
+
if (!state.codecs?.[codecName] && matchBuiltinCodec(codecName)) {
|
|
1264
|
+
await loadCodecModule(state, codecName);
|
|
1265
|
+
}
|
|
1266
|
+
if (state.typeAliases?.[codecName]) {
|
|
1267
|
+
codeHash = state.typeAliases[codecName];
|
|
1268
|
+
pattern = codeHashToPattern(codeHash, codes.find);
|
|
1269
|
+
targetDisplay = codeHash;
|
|
1270
|
+
}
|
|
1271
|
+
} else {
|
|
1272
|
+
let splitPattern = null;
|
|
1273
|
+
let splitCodec = null;
|
|
1274
|
+
|
|
1234
1275
|
const split = trimmed.match(/^(.*\S)\s+([a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*)$/);
|
|
1235
|
-
if (
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1276
|
+
if (split && split[1].trim() !== "?" && split[1].trim() !== "$") {
|
|
1277
|
+
splitPattern = split[1];
|
|
1278
|
+
splitCodec = split[2];
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
if (splitCodec) {
|
|
1282
|
+
try {
|
|
1283
|
+
const resolved = resolveInputPattern(state, splitPattern);
|
|
1284
|
+
pattern = resolved.pattern;
|
|
1285
|
+
codeHash = resolved.codeHash;
|
|
1286
|
+
codecName = splitCodec;
|
|
1287
|
+
targetDisplay = resolved.display;
|
|
1288
|
+
rawExpr = splitPattern;
|
|
1289
|
+
} catch (patternErr) {
|
|
1290
|
+
const resolved = resolveInputPattern(state, trimmed);
|
|
1291
|
+
pattern = resolved.pattern;
|
|
1292
|
+
codeHash = resolved.codeHash;
|
|
1293
|
+
targetDisplay = resolved.display;
|
|
1294
|
+
rawExpr = trimmed;
|
|
1295
|
+
}
|
|
1296
|
+
} else {
|
|
1297
|
+
const resolved = resolveInputPattern(state, trimmed);
|
|
1298
|
+
pattern = resolved.pattern;
|
|
1299
|
+
codeHash = resolved.codeHash;
|
|
1300
|
+
targetDisplay = resolved.display;
|
|
1301
|
+
rawExpr = trimmed;
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
const explicitCodec = Boolean(codecName);
|
|
1306
|
+
if (!codecName && codeHash) {
|
|
1307
|
+
for (const [alias, hash] of Object.entries(state.typeAliases || {})) {
|
|
1308
|
+
if (hash === codeHash && (matchBuiltinCodec(alias) || codecNames(state).includes(alias))) {
|
|
1309
|
+
codecName = alias;
|
|
1310
|
+
break;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
if (codecName) {
|
|
1316
|
+
ensureCodecDependencies(state, codecName);
|
|
1317
|
+
if (!state.codecs?.[codecName] && matchBuiltinCodec(codecName)) {
|
|
1318
|
+
await loadCodecModule(state, codecName);
|
|
1319
|
+
}
|
|
1320
|
+
} else {
|
|
1321
|
+
const hasExactCodec = Boolean(codeHash && state.codecs?.[codeHash]?.some((c) => typeof c.parse === "function"));
|
|
1322
|
+
const hasUniversal = Boolean(state.codecs?.[UNIVERSAL_CODE]?.some((c) => typeof c.parse === "function"));
|
|
1323
|
+
if (!hasExactCodec && !hasUniversal && matchBuiltinCodec("json")) {
|
|
1324
|
+
await loadCodecModule(state, "json");
|
|
1241
1325
|
}
|
|
1242
1326
|
}
|
|
1243
1327
|
|
|
1244
1328
|
const codec = resolveCodec(state, codeHash, codecName, "parse");
|
|
1245
|
-
state.pendingInput = {
|
|
1246
|
-
|
|
1329
|
+
state.pendingInput = {
|
|
1330
|
+
pattern,
|
|
1331
|
+
codeHash,
|
|
1332
|
+
codecName: codec.name,
|
|
1333
|
+
promptName: codec.name,
|
|
1334
|
+
rawExpr
|
|
1335
|
+
};
|
|
1336
|
+
|
|
1337
|
+
const usingPart = explicitCodec && codecName && codecName !== targetDisplay ? ` using ${codecName}` : "";
|
|
1338
|
+
return [`input ${targetDisplay}${usingPart}: enter value text`];
|
|
1247
1339
|
}
|
|
1248
1340
|
|
|
1249
1341
|
async function consumeCodecInput(input, state) {
|
|
@@ -1253,13 +1345,13 @@ async function consumeCodecInput(input, state) {
|
|
|
1253
1345
|
|
|
1254
1346
|
restoreCodes(state);
|
|
1255
1347
|
const codec = resolveCodec(state, pending.codeHash, pending.codecName, "parse");
|
|
1256
|
-
const pattern = codeHashToPattern(pending.codeHash, codes.find);
|
|
1348
|
+
const pattern = pending.pattern || (pending.codeHash ? codeHashToPattern(pending.codeHash, codes.find) : null);
|
|
1257
1349
|
const parsed = await codec.parse(input, {
|
|
1258
1350
|
codeHash: pending.codeHash,
|
|
1259
1351
|
pattern,
|
|
1260
1352
|
state
|
|
1261
1353
|
});
|
|
1262
|
-
const value =
|
|
1354
|
+
const value = valueForPattern(parsed, pattern, input, codec.name);
|
|
1263
1355
|
state.value = value;
|
|
1264
1356
|
state.lastResult = value;
|
|
1265
1357
|
return [printValue(value, state)];
|
|
@@ -1298,34 +1390,16 @@ async function evaluateCommand(line, state) {
|
|
|
1298
1390
|
exit(0);
|
|
1299
1391
|
case "help":
|
|
1300
1392
|
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
|
-
}
|
|
1393
|
+
case "timing":
|
|
1394
|
+
throw new Error(":timing has been removed; timing is now always enabled");
|
|
1314
1395
|
case "time": {
|
|
1315
1396
|
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
|
-
}
|
|
1397
|
+
return evaluateInput(arg, state);
|
|
1323
1398
|
}
|
|
1324
1399
|
case "type": {
|
|
1325
|
-
if (!arg) throw new Error(":type requires a name
|
|
1400
|
+
if (!arg) throw new Error(":type requires a type name");
|
|
1326
1401
|
if (arg.includes("=")) {
|
|
1327
|
-
|
|
1328
|
-
return defineType(definition, state);
|
|
1402
|
+
throw new Error("Type definitions use syntax: $ name = typeExpr; Use :type <name> to show a type definition");
|
|
1329
1403
|
}
|
|
1330
1404
|
return evaluateCommand(`:C ${arg}`, state);
|
|
1331
1405
|
}
|
|
@@ -1333,8 +1407,7 @@ async function evaluateCommand(line, state) {
|
|
|
1333
1407
|
return evaluateCommand(`:C ${arg}`, state);
|
|
1334
1408
|
case "rel":
|
|
1335
1409
|
case "def":
|
|
1336
|
-
|
|
1337
|
-
return defineRelation(arg, state);
|
|
1410
|
+
throw new Error("Relation definitions use syntax: name = relExpr;");
|
|
1338
1411
|
case "run":
|
|
1339
1412
|
case "eval":
|
|
1340
1413
|
return runExpression(arg, state);
|
|
@@ -1342,11 +1415,6 @@ async function evaluateCommand(line, state) {
|
|
|
1342
1415
|
return [listAliases(state.typeAliases)];
|
|
1343
1416
|
case "rels":
|
|
1344
1417
|
return [listAliases(state.relAliases)];
|
|
1345
|
-
case "val":
|
|
1346
|
-
return [
|
|
1347
|
-
printValue(state.value, state),
|
|
1348
|
-
JSON.stringify(state.value, null, 2)
|
|
1349
|
-
];
|
|
1350
1418
|
case "codec":
|
|
1351
1419
|
case "codecs":
|
|
1352
1420
|
return loadCodec(arg, state);
|
|
@@ -1403,26 +1471,21 @@ async function evaluateCommand(line, state) {
|
|
|
1403
1471
|
|
|
1404
1472
|
function helpText() {
|
|
1405
1473
|
return [
|
|
1406
|
-
":type name = <...> define a type",
|
|
1407
|
-
":rel name = expr define a relation",
|
|
1408
1474
|
":run expr run an expression on the current value",
|
|
1409
1475
|
":time expr run an expression and report compilation/execution time",
|
|
1410
|
-
":timing [on|off] toggle timing reporting after every evaluation",
|
|
1411
1476
|
":t name show relation type",
|
|
1412
1477
|
":d name show relation definition",
|
|
1413
1478
|
":type name show type definition",
|
|
1414
1479
|
":codes list type aliases",
|
|
1415
1480
|
":rels list relation aliases",
|
|
1416
|
-
":codec load file load a REPL codec module (or built-in: int, utf8, json, ieee)",
|
|
1417
|
-
":codec define n t b define a custom codec: name type { parse: ..., print: ... }",
|
|
1481
|
+
":codec load file load a REPL codec module (or built-in: int, utf8, json, ieee, unit)",
|
|
1418
1482
|
":codec unload name unload a registered codec",
|
|
1419
1483
|
":codec list list loaded codecs (or simply :codecs)",
|
|
1420
|
-
":input
|
|
1484
|
+
":input [<filter=(...)> [codec]] read next line as codec input",
|
|
1421
1485
|
":load [--no-alias] file",
|
|
1422
1486
|
" load .k source or .klib",
|
|
1423
1487
|
":klib file export state as a library",
|
|
1424
1488
|
":ko file expr export executable .ko using expr as main",
|
|
1425
|
-
":val print current value",
|
|
1426
1489
|
":reset clear state",
|
|
1427
1490
|
":help show this help",
|
|
1428
1491
|
"",
|
|
@@ -1568,6 +1631,9 @@ export {
|
|
|
1568
1631
|
registerCodec,
|
|
1569
1632
|
unregisterCodec,
|
|
1570
1633
|
resolveCodec,
|
|
1634
|
+
resolveInputTypeHash,
|
|
1635
|
+
resolveInputPattern,
|
|
1636
|
+
valueForPattern,
|
|
1571
1637
|
codeHashToPattern,
|
|
1572
1638
|
BUILTIN_CODECS,
|
|
1573
1639
|
printValue,
|