@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/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
- UNIVERSAL_CODE,
46
- valueForCode
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", "rel", "def", "run", "eval", "t", "d", "C",
54
- "codes", "codecs", "rels", "val", "codec", "input", "reset", "klib", "ko", "load",
55
- "timing", "time",
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", "define"];
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: false
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 codeHash = closedPatternToCodeHash(value.pattern);
363
- if (!codeHash) return lines[0];
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
- lines.push(`${codec.name}: ${formatCodecOutput(codec.print(value, { codeHash, state }))}`);
373
- } catch (error) {
374
- if (codec.universal) continue;
375
- 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
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
- const match = arg.match(/^(\s*)load(?:\s+(.*))?$/);
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 /^\$?[a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*$/.test(rawType.trim());
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 completeInputCommand(line, state, argStart, arg) {
707
- const match = arg.match(/^(\s*)(\S+)?(\s+)?(\S*)?$/);
708
- if (!match) return [[], line];
709
- const [, leading, typeToken = "", afterTypeWhitespace = "", codecPartial = ""] = match;
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
- if (!typeToken || !afterTypeWhitespace) {
712
- if (/@[A-Za-z0-9_?!+-]*$/.test(line)) return completeCanonicalCode(line, state);
713
- return completeTypeIdentifier(line, state);
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
- if (typeToken) {
717
- const prefix = line.slice(0, argStart + leading.length + typeToken.length + afterTypeWhitespace.length);
718
- const names = (() => {
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
- return codecNames(state, resolveTypeHash(state, typeToken));
721
- } catch {
722
- return codecNames(state);
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 or codec name");
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, codeHash }) =>
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
- const match = rest.join(" ").match(/^([a-zA-Z0-9_+-]+)\s+(\S+)\s+([\s\S]+)$/);
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, codeHash }) =>
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
- const trimmed = input.trim();
1179
+ let trimmed = input.trim();
1225
1180
  if (!trimmed) {
1226
- throw new Error(":input requires a type and optional codec name");
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
- let codeHash;
1230
- let codecName = null;
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 codec = resolveCodec(state, codeHash, codecName, "parse");
1245
- state.pendingInput = { codeHash, codecName, promptName: codec.name };
1246
- return [`input ${codeHash}${codecName ? ` using ${codecName}` : ""}: enter value text`];
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.codeHash, pending.codecName, "parse");
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
- codeHash: pending.codeHash,
1259
- pattern,
1260
- state
1226
+ state,
1227
+ codecName: codec.name
1261
1228
  });
1262
- const value = valueForCode(parsed, pending.codeHash, codes.find);
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
- const trimmed = arg.trim().toLowerCase();
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
- const wasTiming = state.showTiming;
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 or definition");
1275
+ if (!arg) throw new Error(":type requires a type name");
1326
1276
  if (arg.includes("=")) {
1327
- const definition = arg.trim().startsWith("$") ? arg : `$ ${arg}`;
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
- if (!arg) throw new Error(`:${command} requires a relation definition`);
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 REPL codec module (or built-in: int, utf8, json, ieee)",
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 type [codec] read next line as codec 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
  };