@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fraczak/k",
3
- "version": "6.6.0",
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 decodeWire(encodeToWire(value, pattern)).value;
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
- valueForCode
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", "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
 
@@ -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 /^\$?[a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*$/.test(rawType.trim());
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, typeToken = "", afterTypeWhitespace = "", codecPartial = ""] = match;
790
+ const [, leading, firstToken = "", afterWhitespace = "", secondPartial = ""] = match;
710
791
 
711
- if (!typeToken || !afterTypeWhitespace) {
792
+ if (!firstToken || !afterWhitespace) {
712
793
  if (/@[A-Za-z0-9_?!+-]*$/.test(line)) return completeCanonicalCode(line, state);
713
- return completeTypeIdentifier(line, state);
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 (typeToken) {
717
- const prefix = line.slice(0, argStart + leading.length + typeToken.length + afterTypeWhitespace.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, typeToken));
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(codecPartial))
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
- 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
- }
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 codeHash;
1242
+ let pattern = null;
1243
+ let codeHash = null;
1230
1244
  let codecName = null;
1231
- try {
1232
- codeHash = resolveInputTypeHash(state, trimmed);
1233
- } catch (wholeError) {
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 (!split) throw wholeError;
1236
- try {
1237
- codeHash = resolveInputTypeHash(state, split[1]);
1238
- codecName = split[2];
1239
- } catch (typeError) {
1240
- throw typeError;
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 = { codeHash, codecName, promptName: codec.name };
1246
- return [`input ${codeHash}${codecName ? ` using ${codecName}` : ""}: enter value text`];
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 = valueForCode(parsed, pending.codeHash, codes.find);
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
- 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
- }
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
- const wasTiming = state.showTiming;
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 or definition");
1400
+ if (!arg) throw new Error(":type requires a type name");
1326
1401
  if (arg.includes("=")) {
1327
- const definition = arg.trim().startsWith("$") ? arg : `$ ${arg}`;
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
- if (!arg) throw new Error(`:${command} requires a relation definition`);
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 type [codec] read next line as codec 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,