@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/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
- matchBuiltinCodec
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 codeHash = closedPatternToCodeHash(value.pattern);
361
- const codecs = [
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
- lines.push(`${codec.name}: ${formatCodecOutput(codec.print(value, { codeHash, state }))}`);
370
- } catch (error) {
371
- if (codec.universal) continue;
372
- lines.push(`${codec.name}: <error: ${error.message || String(error)}>`);
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
- const match = arg.match(/^(\s*)load(?:\s+(.*))?$/);
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+)?(\s+)?(\S*)?$/);
789
- if (!match) return [[], line];
790
- const [, leading, firstToken = "", afterWhitespace = "", secondPartial = ""] = match;
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 codecMatches = codecNames(state)
797
- .filter((name) => name.startsWith(firstToken))
784
+ const names = codecNames(state);
785
+ const matches = names
786
+ .filter((name) => name.startsWith(partial))
798
787
  .map((name) => `${prefix}${name}`);
799
- return [[...new Set([...types, ...codecMatches])], line];
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 or codec name");
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, codeHash }) =>
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; use :codec load <name|file> or the Custom Codec Studio UI");
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, codeHash }) =>
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
- const trimmed = input.trim() || "(...)";
1241
-
1242
- let pattern = null;
1243
- let codeHash = null;
1244
- let codecName = null;
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
-
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
- 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
- }
1189
+ if (trimmed.startsWith("$")) {
1190
+ trimmed = trimmed.slice(1).trim();
1303
1191
  }
1304
1192
 
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
- }
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
- 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");
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.codeHash, pending.codecName, "parse");
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
- codeHash: pending.codeHash,
1351
- pattern,
1352
- state
1226
+ state,
1227
+ codecName: codec.name
1353
1228
  });
1354
- const value = valueForPattern(parsed, pattern, input, codec.name);
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 REPL codec module (or built-in: int, utf8, json, ieee, unit)",
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 [<filter=(...)> [codec]] read next line as codec 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
  };