@fraczak/k 6.8.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 +41 -39
- package/Examples/arithmetics.k +1 -0
- package/Examples/poly.k +46 -0
- package/browser/repl-browser.mjs +109 -782
- package/browser/shims/process.mjs +7 -0
- package/browser/tree-view.mjs +5 -2
- package/browser/vfs-data.mjs +14 -14
- 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 +69 -196
- package/repl.mjs +63 -187
- package/scripts/build-repl-html.mjs +87 -379
- 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 +123 -64
- package/tests/test-repl.mjs +120 -134
- 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 {
|
|
@@ -39,11 +41,9 @@ import {
|
|
|
39
41
|
registerCodec,
|
|
40
42
|
resolveCodec,
|
|
41
43
|
unregisterCodec,
|
|
42
|
-
BUILTIN_CODECS,
|
|
43
44
|
closedPatternToCodeHash,
|
|
44
|
-
UNIVERSAL_CODE,
|
|
45
45
|
valueForPattern,
|
|
46
|
-
|
|
46
|
+
ensureEnveloped
|
|
47
47
|
} from "./repl-codecs.mjs";
|
|
48
48
|
|
|
49
49
|
const NAME_RE = /^[a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*$/;
|
|
@@ -349,7 +349,8 @@ function listAliases(aliases) {
|
|
|
349
349
|
}
|
|
350
350
|
|
|
351
351
|
function formatCodecOutput(output) {
|
|
352
|
-
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);
|
|
353
354
|
return String(output);
|
|
354
355
|
}
|
|
355
356
|
|
|
@@ -357,19 +358,16 @@ function printValue(value, state = null) {
|
|
|
357
358
|
const lines = [valueWithEnvelopeToK(value)];
|
|
358
359
|
if (!state || value === undefined) return lines[0];
|
|
359
360
|
|
|
360
|
-
const
|
|
361
|
-
const
|
|
362
|
-
...(codeHash ? state.codecs?.[codeHash] || [] : []),
|
|
363
|
-
...(state.codecs?.[UNIVERSAL_CODE] || [])
|
|
364
|
-
];
|
|
365
|
-
if (codecs.length === 0) return lines[0];
|
|
366
|
-
for (const codec of codecs) {
|
|
361
|
+
const store = state.codecs || {};
|
|
362
|
+
for (const codec of Object.values(store)) {
|
|
367
363
|
if (typeof codec.print !== "function") continue;
|
|
368
364
|
try {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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
|
|
373
371
|
}
|
|
374
372
|
}
|
|
375
373
|
return lines.join("\n");
|
|
@@ -649,13 +647,7 @@ function completeCodecCommand(line, argStart, arg, state) {
|
|
|
649
647
|
const tokenStart = codecLoadPathCompletionStart(argStart, arg);
|
|
650
648
|
if (tokenStart == null) return [[], line];
|
|
651
649
|
const [pathMatches] = completePath(line, tokenStart);
|
|
652
|
-
|
|
653
|
-
const partial = match && match[2] != null ? match[2].trim() : "";
|
|
654
|
-
const prefix = line.slice(0, tokenStart);
|
|
655
|
-
const builtinMatches = ["int", "utf8", "json", "ieee"]
|
|
656
|
-
.filter((name) => name.startsWith(partial))
|
|
657
|
-
.map((name) => `${prefix}${name}`);
|
|
658
|
-
return [[...builtinMatches, ...pathMatches], line];
|
|
650
|
+
return [pathMatches, line];
|
|
659
651
|
}
|
|
660
652
|
|
|
661
653
|
function resolveTypeHash(state, rawName) {
|
|
@@ -785,37 +777,16 @@ function resolveInputPattern(state, rawExpr) {
|
|
|
785
777
|
}
|
|
786
778
|
|
|
787
779
|
function completeInputCommand(line, state, argStart, arg) {
|
|
788
|
-
const match = arg.match(/^(\s*)(\S
|
|
789
|
-
if (
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
if (!firstToken || !afterWhitespace) {
|
|
793
|
-
if (/@[A-Za-z0-9_?!+-]*$/.test(line)) return completeCanonicalCode(line, state);
|
|
794
|
-
const [types] = completeTypeIdentifier(line, state);
|
|
780
|
+
const match = arg.match(/^(\s*)(\S*)$/);
|
|
781
|
+
if (match) {
|
|
782
|
+
const [, leading, partial] = match;
|
|
795
783
|
const prefix = line.slice(0, argStart + leading.length);
|
|
796
|
-
const
|
|
797
|
-
|
|
784
|
+
const names = codecNames(state);
|
|
785
|
+
const matches = names
|
|
786
|
+
.filter((name) => name.startsWith(partial))
|
|
798
787
|
.map((name) => `${prefix}${name}`);
|
|
799
|
-
return [
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
if (firstToken) {
|
|
803
|
-
const prefix = line.slice(0, argStart + leading.length + firstToken.length + afterWhitespace.length);
|
|
804
|
-
const names = (() => {
|
|
805
|
-
try {
|
|
806
|
-
return codecNames(state, resolveTypeHash(state, firstToken));
|
|
807
|
-
} catch {
|
|
808
|
-
return codecNames(state);
|
|
809
|
-
}
|
|
810
|
-
})();
|
|
811
|
-
return [
|
|
812
|
-
names
|
|
813
|
-
.filter((name) => name.startsWith(secondPartial))
|
|
814
|
-
.map((name) => `${prefix}${name}`),
|
|
815
|
-
line
|
|
816
|
-
];
|
|
788
|
+
return [matches, line];
|
|
817
789
|
}
|
|
818
|
-
|
|
819
790
|
return [[], line];
|
|
820
791
|
}
|
|
821
792
|
|
|
@@ -905,14 +876,6 @@ function parseLoadArgs(arg, usagePrefix = ":") {
|
|
|
905
876
|
return { path: arg.trim(), loadAliases: true };
|
|
906
877
|
}
|
|
907
878
|
|
|
908
|
-
const CODEC_FILES = {
|
|
909
|
-
int: "arithmetics.k",
|
|
910
|
-
utf8: "core.k",
|
|
911
|
-
json: "core.k",
|
|
912
|
-
ieee: "ieee.k",
|
|
913
|
-
unit: "core.k"
|
|
914
|
-
};
|
|
915
|
-
|
|
916
879
|
function normalizeFilePath(p) {
|
|
917
880
|
return p.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
918
881
|
}
|
|
@@ -983,24 +946,6 @@ function loadSourceOrKlib(state, targetPath, options = {}) {
|
|
|
983
946
|
return targetPath;
|
|
984
947
|
}
|
|
985
948
|
|
|
986
|
-
function ensureCodecDependencies(state, codecTarget) {
|
|
987
|
-
const norm = normalizeFilePath(codecTarget).toLowerCase();
|
|
988
|
-
const base = path.basename(norm, path.extname(norm));
|
|
989
|
-
const depFile = CODEC_FILES[base] || CODEC_FILES[norm];
|
|
990
|
-
if (!depFile) return null;
|
|
991
|
-
if (!state.loadedFiles) state.loadedFiles = new Set();
|
|
992
|
-
const depNormKey = normalizeFilePath(depFile);
|
|
993
|
-
const baseName = path.basename(depNormKey);
|
|
994
|
-
const alreadyLoaded = state.loadedFiles.has(depNormKey) ||
|
|
995
|
-
state.loadedFiles.has(`Examples/${depNormKey}`) ||
|
|
996
|
-
Array.from(state.loadedFiles).some(f => path.basename(f) === baseName);
|
|
997
|
-
if (!alreadyLoaded) {
|
|
998
|
-
loadSourceOrKlib(state, depFile);
|
|
999
|
-
return depFile;
|
|
1000
|
-
}
|
|
1001
|
-
return null;
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
949
|
function lineForContinuation(line) {
|
|
1005
950
|
return line.replace(/\\\s*$/, "");
|
|
1006
951
|
}
|
|
@@ -1206,13 +1151,10 @@ async function loadCodec(input, state) {
|
|
|
1206
1151
|
const [subcommand, ...rest] = trimmed.split(/\s+/);
|
|
1207
1152
|
switch (subcommand) {
|
|
1208
1153
|
case "load": {
|
|
1209
|
-
const filePath = rest.join(" ");
|
|
1210
|
-
if (!filePath) throw new Error(":codec load requires a file path
|
|
1211
|
-
const autoLoaded = ensureCodecDependencies(state, filePath);
|
|
1154
|
+
const filePath = rest.join(" ").trim();
|
|
1155
|
+
if (!filePath) throw new Error(":codec load requires a file path");
|
|
1212
1156
|
const registered = await loadCodecModule(state, expandHome(filePath));
|
|
1213
|
-
return registered.map(({ name
|
|
1214
|
-
`loaded codec ${name} for ${codeHash === UNIVERSAL_CODE ? "all types" : codeHash}${autoLoaded ? ` (auto-loaded ${autoLoaded})` : ""}`
|
|
1215
|
-
);
|
|
1157
|
+
return registered.map(({ name }) => `loaded codec ${name}`);
|
|
1216
1158
|
}
|
|
1217
1159
|
case "unload": {
|
|
1218
1160
|
const codecName = rest.join(" ").trim();
|
|
@@ -1222,120 +1164,55 @@ async function loadCodec(input, state) {
|
|
|
1222
1164
|
return [`unloaded codec ${codecName}`];
|
|
1223
1165
|
}
|
|
1224
1166
|
case "define":
|
|
1225
|
-
throw new Error(":codec define has been removed;
|
|
1167
|
+
throw new Error(":codec define has been removed; all codecs are loaded from files via :codec load <file.mjs>");
|
|
1226
1168
|
case "list":
|
|
1227
1169
|
if (rest.length > 0) throw new Error(":codec list does not accept arguments");
|
|
1228
1170
|
return [listCodecs(state)];
|
|
1229
1171
|
default: {
|
|
1230
|
-
const autoLoaded = ensureCodecDependencies(state, trimmed);
|
|
1231
1172
|
const registered = await loadCodecModule(state, expandHome(trimmed));
|
|
1232
|
-
return registered.map(({ name
|
|
1233
|
-
`loaded codec ${name} for ${codeHash === UNIVERSAL_CODE ? "all types" : codeHash}${autoLoaded ? ` (auto-loaded ${autoLoaded})` : ""}`
|
|
1234
|
-
);
|
|
1173
|
+
return registered.map(({ name }) => `loaded codec ${name}`);
|
|
1235
1174
|
}
|
|
1236
1175
|
}
|
|
1237
1176
|
}
|
|
1238
1177
|
|
|
1239
1178
|
async function requestCodecInput(input, state) {
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
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
|
-
|
|
1275
|
-
const split = trimmed.match(/^(.*\S)\s+([a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*)$/);
|
|
1276
|
-
if (split && split[1].trim() !== "?" && split[1].trim() !== "$") {
|
|
1277
|
-
splitPattern = split[1];
|
|
1278
|
-
splitCodec = split[2];
|
|
1279
|
-
}
|
|
1179
|
+
let trimmed = input.trim();
|
|
1180
|
+
if (!trimmed) {
|
|
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
|
+
];
|
|
1187
|
+
}
|
|
1280
1188
|
|
|
1281
|
-
|
|
1282
|
-
|
|
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
|
-
}
|
|
1189
|
+
if (trimmed.startsWith("$")) {
|
|
1190
|
+
trimmed = trimmed.slice(1).trim();
|
|
1303
1191
|
}
|
|
1304
1192
|
|
|
1305
|
-
const
|
|
1306
|
-
if (!
|
|
1307
|
-
|
|
1308
|
-
if (hash === codeHash && (matchBuiltinCodec(alias) || codecNames(state).includes(alias))) {
|
|
1309
|
-
codecName = alias;
|
|
1310
|
-
break;
|
|
1311
|
-
}
|
|
1312
|
-
}
|
|
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]");
|
|
1313
1196
|
}
|
|
1314
1197
|
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
const
|
|
1322
|
-
const
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
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)];
|
|
1326
1209
|
}
|
|
1327
1210
|
|
|
1328
|
-
const codec = resolveCodec(state, codeHash, codecName, "parse");
|
|
1329
1211
|
state.pendingInput = {
|
|
1330
|
-
pattern,
|
|
1331
|
-
codeHash,
|
|
1332
1212
|
codecName: codec.name,
|
|
1333
|
-
promptName: codec.name
|
|
1334
|
-
rawExpr
|
|
1213
|
+
promptName: codec.name
|
|
1335
1214
|
};
|
|
1336
|
-
|
|
1337
|
-
const usingPart = explicitCodec && codecName && codecName !== targetDisplay ? ` using ${codecName}` : "";
|
|
1338
|
-
return [`input ${targetDisplay}${usingPart}: enter value text`];
|
|
1215
|
+
return [];
|
|
1339
1216
|
}
|
|
1340
1217
|
|
|
1341
1218
|
async function consumeCodecInput(input, state) {
|
|
@@ -1344,14 +1221,12 @@ async function consumeCodecInput(input, state) {
|
|
|
1344
1221
|
state.pendingInput = null;
|
|
1345
1222
|
|
|
1346
1223
|
restoreCodes(state);
|
|
1347
|
-
const codec = resolveCodec(state, pending.
|
|
1348
|
-
const pattern = pending.pattern || (pending.codeHash ? codeHashToPattern(pending.codeHash, codes.find) : null);
|
|
1224
|
+
const codec = resolveCodec(state, pending.codecName, "parse");
|
|
1349
1225
|
const parsed = await codec.parse(input, {
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
state
|
|
1226
|
+
state,
|
|
1227
|
+
codecName: codec.name
|
|
1353
1228
|
});
|
|
1354
|
-
const value =
|
|
1229
|
+
const value = ensureEnveloped(parsed, codec);
|
|
1355
1230
|
state.value = value;
|
|
1356
1231
|
state.lastResult = value;
|
|
1357
1232
|
return [printValue(value, state)];
|
|
@@ -1478,10 +1353,10 @@ function helpText() {
|
|
|
1478
1353
|
":type name show type definition",
|
|
1479
1354
|
":codes list type aliases",
|
|
1480
1355
|
":rels list relation aliases",
|
|
1481
|
-
":codec load file load a
|
|
1356
|
+
":codec load file load a codec module from file (e.g. :codec load codecs/int.mjs)",
|
|
1482
1357
|
":codec unload name unload a registered codec",
|
|
1483
1358
|
":codec list list loaded codecs (or simply :codecs)",
|
|
1484
|
-
":input
|
|
1359
|
+
":input <codec.mjs> [text] parse input using specified codec",
|
|
1485
1360
|
":load [--no-alias] file",
|
|
1486
1361
|
" load .k source or .klib",
|
|
1487
1362
|
":klib file export state as a library",
|
|
@@ -1635,13 +1510,14 @@ export {
|
|
|
1635
1510
|
resolveInputPattern,
|
|
1636
1511
|
valueForPattern,
|
|
1637
1512
|
codeHashToPattern,
|
|
1638
|
-
BUILTIN_CODECS,
|
|
1639
1513
|
printValue,
|
|
1640
1514
|
promptForState,
|
|
1641
1515
|
propertyListToFilter,
|
|
1642
1516
|
savedLibrary,
|
|
1643
1517
|
loadSourceOrKlib,
|
|
1644
|
-
ensureCodecDependencies,
|
|
1645
1518
|
valueToK,
|
|
1646
|
-
formatDuration
|
|
1519
|
+
formatDuration,
|
|
1520
|
+
codecNames,
|
|
1521
|
+
ensureEnveloped,
|
|
1522
|
+
patternFromFilter
|
|
1647
1523
|
};
|