@fraczak/k 6.7.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 CHANGED
@@ -33,29 +33,23 @@ complete.
33
33
  | Command | Meaning |
34
34
  | --- | --- |
35
35
  | `:help` | Show command summary |
36
- | `:type name = <...>` | Define a type alias |
37
36
  | `:type name` | Show the canonical definition of a type |
38
37
  | `:code name` | Alias for `:C name` |
39
- | `:rel name = expr` | Define a relation alias |
40
- | `:def name = expr` | Alias for `:rel` |
41
38
  | `:run expr` | Evaluate `expr` on the current value |
42
39
  | `:eval expr` | Alias for `:run` |
43
40
  | `:time expr` | Evaluate `expr` and report compilation and execution time |
44
- | `:timing [on\|off]` | Toggle compilation and execution timing reporting for all evaluations |
45
41
  | `:t name` | Show relation input/output filters |
46
42
  | `:d name` | Show relation definition |
47
43
  | `:C name` | Show canonical code definition |
48
44
  | `:codes` | List type aliases |
49
45
  | `:rels` | List relation aliases |
50
46
  | `:codec load name\|file` | Load a built-in codec (`int`, `utf8`, `json`, `ieee`, `unit`) or an ES module file |
51
- | `:codec define n t b` | Define an inline custom codec: `name type { parse: ..., print: ... }` |
52
47
  | `:codec unload name` | Unload a registered codec |
53
48
  | `:codec list` | List loaded REPL codecs (alias: `:codecs`) |
54
- | `:input type [codec]` | Read the next line as codec input for a type |
49
+ | `:input [<filter=(...)> [codec]]` | Read next input line using specified codec (opens dialog in browser) |
55
50
  | `:load [--no-alias] file` | Load `.k` source or `.klib` into the current state |
56
51
  | `:klib file` | Export the active relation closure as a `.klib` library |
57
52
  | `:ko file expr` | Export a `.ko` executable with `expr` as main |
58
- | `:val` | Print current value and JSON form |
59
53
  | `:reset` | Reset interpreter state |
60
54
  | `:quit` / `:exit` | Exit |
61
55
 
@@ -143,8 +137,8 @@ Tab completion covers:
143
137
  Type aliases also complete in `$name` position inside raw k input.
144
138
 
145
139
  For codec commands, completion covers `:codec load`, `:codec list`, file paths
146
- after `:codec load`, type names after `:input`, and loaded codec names
147
- after the input type.
140
+ after `:codec load`, type names or codec names after `:input`, and loaded codec names
141
+ after the input pattern or type.
148
142
 
149
143
  ## Loading
150
144
 
@@ -193,44 +187,39 @@ property-list pattern that can be canonicalized to a code hash, and that hash is
193
187
  recalculated by the REPL. A universal codec exports `universal = true` instead
194
188
  of `codes` or `patterns`.
195
189
 
196
- ### `:codec define name type { parse: ..., print: ... }`
197
-
198
- Registers a dynamic custom codec directly from the REPL:
199
-
200
- ```k
201
- > :type bool = <{} true, {} false>
202
- > :codec define yn bool ({ parse: (t) => Value.variant(t.trim() === 'yes' ? 'true' : 'false', Value.product({})), print: (v) => v.tag === 'true' ? 'YES' : 'NO' })
203
- ```
204
190
 
205
191
  ### `:codec unload name`
206
192
 
207
193
  Unloads a previously registered codec by name.
208
194
 
209
- ### `:input type [codec]`
195
+ ### `:input [<filter=(...)> [codec]]`
210
196
 
211
- Resolves `type` to a canonical code hash, selects the registered codec, and
212
- consumes the next line verbatim as codec input. `type` may be a type alias, a
213
- canonical code hash, or an inline type expression such as
214
- `<{} true, {} false>`. The parsed value is validated against that type before
215
- becoming the current value.
197
+ Resolves a pattern expression (e.g. `? {string a, float64 n}`, `? (...)`, `? <{} true, {} false>`), a type alias, or a canonical code hash, selects the registered codec, and consumes the next line verbatim as codec input.
216
198
 
217
- ## Timing and Profiling
199
+ If only a universal codec is specified (e.g. `:input json`), no pre-declared type or pattern is required; input is parsed directly and derives its pattern from the input payload.
218
200
 
219
- ### `:time expr`
201
+ When a pattern expression or type is provided, the parsed value is validated and constrained against that pattern before becoming the current value:
220
202
 
221
- Evaluates `expr` and reports the elapsed time broken down into compilation
222
- (type derivation + WebAssembly lowering/compilation) and runtime execution:
203
+ ```text
204
+ > :input ? {string a, float64 n} json
205
+ input ? {string a, float64 n} using json: enter value text
206
+ json> {"a": "Woj", "n": 123}
207
+ ```
208
+
209
+ ## Timing and Profiling
210
+
211
+ Timing reporting is enabled by default for all evaluations, reporting elapsed time
212
+ broken down into compilation (type derivation + WebAssembly lowering/compilation) and runtime execution:
223
213
 
224
214
  ```text
225
- > :time {10 int x, 5 int y} plus
215
+ > {10 int x, 5 int y} plus
226
216
  {}|_|1|1|1|1|+ ?<{} _, ...>
227
217
  /* comp: 12.4ms, exec: 0.8ms */
228
218
  ```
229
219
 
230
- ### `:timing [on|off]`
220
+ ### `:time expr`
231
221
 
232
- Toggles automatic timing reporting for every snippet or expression evaluated in
233
- the session.
222
+ Evaluates `expr` with explicit timing and records performance metrics in session state.
234
223
 
235
224
  ## Web REPL (Studio)
236
225
 
@@ -279,11 +268,9 @@ Evaluated values print in k syntax together with the inferred envelope:
279
268
  ## Example Session
280
269
 
281
270
  ```text
282
- > :type nat = <{} 0, nat +1>
283
- $ nat = @...
284
- > :rel inc = |+1
285
- inc = @...
286
- > {} |0
271
+ > $ nat = < {} 0, nat +1 >;
272
+ > inc = | +1;
273
+ > {} | 0
287
274
  {}|0 ?<{} 0, ...>
288
275
  > :t inc
289
276
  inc : ?X0 --> ?<X0 +1, ...> (@...)
package/TODO.md ADDED
@@ -0,0 +1,112 @@
1
+ # Web REPL Improvements & Conceptual Cleanup Plan
2
+
3
+ This document details the issues, conceptual improvements, and completed enhancements for the `k` Web REPL (`browser/repl-browser.mjs`, `scripts/build-repl-html.mjs`, and `repl.html`).
4
+
5
+ ---
6
+
7
+ ## 1. General Branding & Visual Cleanup
8
+
9
+ ### Removal of `λ` (Lambda) Symbol
10
+ - [x] Remove `<span class="brand-lambda">λ</span>` from the navbar in `scripts/build-repl-html.mjs`.
11
+ - [x] Remove the `.brand-lambda` CSS class.
12
+ - [x] Update welcome banner in `browser/repl-browser.mjs` to `k interactive repl`.
13
+ - [x] Replace `λ` in string codec sample inputs with ASCII/Unicode text samples (e.g. `"abc"`).
14
+
15
+ ---
16
+
17
+ ## 2. Help Modal (`? Help` / `:help`) Overhaul
18
+
19
+ ### Language Overview & Grammar Categorization
20
+ - [x] **Language Overview**: Short, positive, accessible description: `k` is a concise language of types and relations (functions) over tree-like (JSON-like) documents.
21
+ - [x] **Content-Addressed Identity**: Functions and types are identified by their canonical content hash (`@...`) derived directly from their structure; local names are just temporary aliases used for recursion and readability.
22
+ - [x] **Three Clean Grammar Categories**:
23
+ 1. **Definitions (Local Aliases)**:
24
+ - `name = relExpr;` &mdash; Relation alias (e.g. `swap = { . y x, . x y };`).
25
+ - `$ name = typeExpr;` &mdash; Type alias (e.g. `$ bool = < {} true, {} false >;`).
26
+ 2. **Type Expressions (`typeExpr`)**: Schemas describing tree-like documents:
27
+ - `{ Type_1 label_1, ..., Type_k label_k }` &mdash; Product type of arity $k$ (labeled records, e.g. `{float64 x, string n}`; special case: `{}` is the 0-ary product).
28
+ - `< Type_1 tag_1, ..., Type_k tag_k >` &mdash; Union type of arity $k$ (tagged sum of variants, e.g. `<int ok, string err>`).
29
+ - `name` / `@hash` &mdash; Type reference.
30
+ 3. **Relation Expressions (`relExpr`)**: Functions transforming an input document to an output document:
31
+ - `{ rel_1 label_1, ..., rel_k label_k }` &mdash; Product of arity $k$ (evaluates relations into record fields; special case: `{}` evaluates 0 relations, producing empty record `{}`).
32
+ - `< rel_1, ..., rel_k >` &mdash; Union / ordered choice of arity $k$ (evaluates relations in order until one succeeds).
33
+ - `( rel_1 ... rel_k )` &mdash; Composition of arity $k$ (sequentially composes relations; special case: `()` is the 0-ary composition denoting identity; parentheses can be omitted when non-empty: `f g`).
34
+ - `. label` &mdash; Field projection (extracts field `label`).
35
+ - `/ tag` &mdash; Variant branch projection (extracts payload of variant `tag`).
36
+ - `| tag` &mdash; Variant constructor (wraps document into variant `tag`).
37
+ - `$ typeExpr` &mdash; Type constraint (asserts input matches schema; compile-time Type Error on mismatch).
38
+ - `name` / `@hash` &mdash; Relation reference.
39
+ - [x] **Simplified Intro**:
40
+ - Made arbitrary arity explicit across products `{...}`, unions `<...>`, and compositions `(...)`; explained `{}` and `()` as the natural 0-ary special cases; omitted the pattern/filter expressions table from the introductory reference.
41
+ - [x] **Replaced Misleading Expressions & Snippets**:
42
+ - Removed phantom concepts (`10` as literal, "bit-path", `{10 int x, 5 int y} plus` in default state).
43
+ - Added self-contained snippets (`{} | ok`, `not = < / true | false, / false | true >;`, `$ bool = < {} true, {} false >;`, `not = $ bool < / true | false, {} | true > $ bool;`, `swap = { . y x, . x y };`).
44
+ - [x] **Command Reference Table**:
45
+ - Added `:time <expr>`.
46
+ - Removed obsolete commands: `:rel <name> = expr`, `:val`, `:timing [on|off]`, `:codec define n t b`.
47
+ - Updated `:type <name>` to purely inspect and display type definitions (type definition is native via `$ name = typeExpr;`).
48
+ - Timing is now always enabled by default across all evaluations.
49
+ - Updated `:input` documentation to note the interactive browser dialog.
50
+ - Clarified `:codes` (type aliases/hashes) and `:rels` (relation aliases).
51
+
52
+ ---
53
+
54
+ ## 3. Codecs Modal Overhaul
55
+
56
+ ### Concept Correction: External Format Adapters
57
+ - [x] Reframed codecs as format adapters translating external text to/from $k$ values defined as pairs `(filter, tree)`.
58
+ - [x] **Type-Specific Codecs**:
59
+ - `int`: Converts decimal integers to/from `$ int = < bits '+', bits '-' >`.
60
+ - `ieee`: Converts decimal floats to/from `$ float64` records (`sign`, `exponent`, `fraction`).
61
+ - `utf8`: Converts text to/from `$ string` (aliased to `$ utf8`), a list of Unicode scalar values (`$ unicode`) partitioned by Unicode planes.
62
+ - `unit`: Converts to/from `{}`.
63
+ - [x] **Parameterized Codecs**:
64
+ - `json`: JSON text is untyped; deserializing JSON requires a target $k$ type schema parameter (e.g. `:input {float64 x, string n} json`).
65
+ - [x] **Custom Codec Studio**:
66
+ - Updated explanation to emphasize pairs `(filter, tree)`.
67
+ - Fixed `yn` preset to provide self-contained type definition `$ bool = < {} true, {} false >;`.
68
+ - Fixed `blank` preset with proper Value payload formatting.
69
+
70
+ ---
71
+
72
+ ## 4. 'Input' Modal Overhaul
73
+
74
+ - [x] **Parameterized Codec Support**:
75
+ - Converted "Target Type" control into an editable combobox / text input with datalist autocomplete for known aliases, while allowing arbitrary composite type expressions (`{float64 x, string n}`, `<int ok, string err>`).
76
+ - Integrated `resolveInputTypeHash(state, rawType)` into live validation and modal submit.
77
+ - Marked `json` as a Parameterized Codec requiring a target type schema.
78
+ - Added composite quick samples demonstrating type + JSON payload pairs (e.g. `{float64 x, string n}` with `{"x":12, "n":"Woj"}`).
79
+ - Preserved CLI-specified type expressions when opening via `:input <type> [codec]`.
80
+
81
+ ---
82
+
83
+ ## 5. Welcome Banner
84
+
85
+ - [x] Shortened initial welcome message to just the title line: `k interactive repl`.
86
+ - [x] Removed secondary description and quick link lists to keep the terminal prompt clean and uncluttered.
87
+
88
+ ---
89
+
90
+ ## 6. Curated VFS & Navbar Examples Dropdown
91
+
92
+ - [x] Added `poly.k` to `verifiedExamples` and `#example-select`.
93
+ - [x] Structured Navbar Examples dropdown into self-contained basic expressions vs. library-dependent workflows.
94
+ - [x] Removed `:val` from example dropdown list.
95
+ - [x] Fixed `() ()` option display text.
96
+
97
+ ---
98
+
99
+ ## 7. Responsive Navbar & Obsolete Command Removal
100
+
101
+ - [x] **Responsive Navbar on Narrow Screens**:
102
+ - Changed `.navbar` from fixed `height: 48px;` to `min-height: 48px; height: auto; flex-wrap: wrap; padding: 6px 16px; gap: 8px 12px;`.
103
+ - Styled `.nav-right` with `flex-wrap: wrap; justify-content: flex-end; min-width: 0;` so controls wrap smoothly without overflowing or clipping buttons (such as `? Help`).
104
+ - Added comprehensive responsive media query for narrow screens ($\le 768\text{px}$) with full width wrapping and adaptable control inputs.
105
+ - Verified via headless Chromium CDP automated tests that `#btn-help` is completely visible and interactable at narrow viewport widths (e.g. 600px).
106
+ - [x] **Removed Obsolete Commands**:
107
+ - Removed `:rel <name> = expr` and `:def` (relations are defined using native syntax `name = expr;`).
108
+ - Removed `:type <name> = <...>` definition syntax from `:type` (types are defined using native syntax `$ name = typeExpr;`; `:type <name>` remains for inspecting type definitions).
109
+ - Removed `:val` completely.
110
+ - Removed `:timing [on|off]` (timing reporting is now permanently enabled by default for all evaluations; `:time <expr>` remains for explicit timing).
111
+ - Removed `:codec define n t b` (custom codecs are created using the browser Custom Codec Studio UI or loaded from modules via `:codec load`).
112
+
@@ -3,19 +3,18 @@ import {
3
3
  evaluateInput,
4
4
  createCompleter,
5
5
  promptForState,
6
- helpText,
7
6
  analyzeRawSnippet,
8
7
  lineTerminatesSnippet,
9
8
  lineHasExplicitContinuation,
10
9
  savedLibrary,
11
- listCodecs,
12
- loadCodecModule,
13
10
  registerCodec,
14
11
  unregisterCodec,
15
12
  resolveCodec,
16
13
  codeHashToPattern,
17
- BUILTIN_CODECS,
18
- formatDuration
14
+ formatDuration,
15
+ resolveInputTypeHash,
16
+ resolveInputPattern,
17
+ valueForPattern
19
18
  } from "../repl.mjs";
20
19
  import { Value, isProduct, isVariant } from "../Value.mjs";
21
20
  import { encodeLibrary } from "../object.mjs";
@@ -71,6 +70,7 @@ let inputPopupCodec;
71
70
  let inputPopupText;
72
71
  let inputPopupStatus;
73
72
  let inputPopupHint;
73
+ let inputPopupCodecHint;
74
74
  let inputSamplesList;
75
75
 
76
76
  function escapeHtml(str) {
@@ -569,19 +569,19 @@ function exportKlib() {
569
569
  const CODEC_PRESETS = {
570
570
  yn: {
571
571
  name: "yn",
572
- type: "bool",
572
+ type: "yes_no",
573
573
  universal: false,
574
- parse: `// Parse 'yes', 'no', 'true', 'false' into $bool
574
+ parse: `// Parse 'yes', 'no', 'true', 'false' into $yes_no (< {} yes, {} no >)
575
575
  const tag = text.trim().toLowerCase();
576
576
  if (tag === "yes" || tag === "true" || tag === "1") {
577
- return Value.variant("true", Value.product({}));
577
+ return Value.variant("yes", Value.product({}));
578
578
  }
579
579
  if (tag === "no" || tag === "false" || tag === "0") {
580
- return Value.variant("false", Value.product({}));
580
+ return Value.variant("no", Value.product({}));
581
581
  }
582
582
  throw new Error("Expected yes/no or true/false");`,
583
- print: `// Serialize boolean value to YES / NO
584
- return value.tag === "true" ? "YES" : "NO";`
583
+ print: `// Serialize yes_no value to YES / NO
584
+ return value.tag === "yes" ? "YES" : "NO";`
585
585
  },
586
586
  hex: {
587
587
  name: "hex",
@@ -652,8 +652,9 @@ return "$" + dollars.toString() + "." + cents;`
652
652
  parse: `// Parse text string and return a K Value
653
653
  // Available: Value.product({ field: val }), Value.variant("tag", val)
654
654
  return Value.product({});`,
655
- print: `// Serialize K Value into string or Buffer
656
- return JSON.stringify(value);`
655
+ print: `// Serialize K Value into string
656
+ // value is a Value instance: inspect value.fields or value.tag & value.value
657
+ return String(value);`
657
658
  }
658
659
  };
659
660
 
@@ -696,7 +697,7 @@ function renderCodecsModal() {
696
697
  for (const [alias, hash] of aliases) {
697
698
  const opt = document.createElement("option");
698
699
  opt.value = alias;
699
- opt.textContent = `$${alias} (${hash.slice(0, 10)}...)`;
700
+ opt.textContent = `$ ${alias} (${hash.slice(0, 10)}...)`;
700
701
  typeGroup.appendChild(opt);
701
702
  }
702
703
  codecTypeSelect.appendChild(typeGroup);
@@ -838,9 +839,9 @@ function registerCustomCodecFromForm() {
838
839
  );
839
840
  }
840
841
 
841
- let codeHash = rawType === "*" ? "*" : state.typeAliases[rawType] || rawType;
842
- if (!codeHash.startsWith("@") && codeHash !== "*") {
843
- throw new Error(`Cannot resolve type '${rawType}' to a canonical code hash. Define the type first (e.g. :type ${rawType} = ...).`);
842
+ let codeHash = "*";
843
+ if (rawType !== "*") {
844
+ codeHash = resolveInputTypeHash(state, rawType);
844
845
  }
845
846
 
846
847
  registerCodec(state, {
@@ -945,8 +946,8 @@ export function print(value, context) {
945
946
 
946
947
  const CODEC_SAMPLES = {
947
948
  int: ["0", "10", "42", "-15", "1000000"],
948
- utf8: ["hello", "hello world", "k repl", "λ"],
949
- json: ['{"name":"Alice"}', '{"x":10,"y":20}', 'true', '[1, 2, 3]'],
949
+ utf8: ["hello", "hello world", "k repl", "abc"],
950
+ json: ['{"x":12,"n":"Woj"}', '{"name":"Alice"}', 'true', '[1, 2, 3]'],
950
951
  ieee: ["0.0", "3.14159", "-2.718", "1e6"],
951
952
  yn: ["yes", "no", "true", "false"],
952
953
  hex: ["0x2A", "0xFF", "0x1000", "-0x10"],
@@ -997,6 +998,10 @@ function renderInputSamples(codecName) {
997
998
  btn.onclick = () => {
998
999
  if (inputPopupText) {
999
1000
  inputPopupText.value = s;
1001
+ if (codecName === "json" && s === '{"x":12,"n":"Woj"}' && (!inputPopupType?.value || inputPopupType.value === "*")) {
1002
+ inputPopupType.value = "{float64 x, string n}";
1003
+ updateInputPopupCodecs();
1004
+ }
1000
1005
  inputPopupText.focus();
1001
1006
  updateLiveValidation();
1002
1007
  }
@@ -1007,19 +1012,36 @@ function renderInputSamples(codecName) {
1007
1012
 
1008
1013
  function updateInputPopupHint() {
1009
1014
  if (!inputPopupHint) return;
1015
+ const rawPattern = inputPopupType ? inputPopupType.value.trim() : "(...)";
1016
+ const codecName = inputPopupCodec?.value || state.pendingInput?.codecName;
1010
1017
  if (state.pendingInput) {
1011
- const rawType = inputPopupType?.value || state.pendingInput.codeHash;
1012
- const codecName = inputPopupCodec?.value || state.pendingInput.codecName;
1013
- inputPopupHint.innerHTML = `Enter input value for <b>$${escapeHtml(rawType)}</b> using codec <b>${escapeHtml(codecName || "default")}</b>:`;
1018
+ const displayPattern = rawPattern || state.pendingInput.rawExpr || "(...)";
1019
+ inputPopupHint.innerHTML = `Enter input value for <b>${escapeHtml(displayPattern)}</b> using codec <b>${escapeHtml(codecName || "default")}</b>:`;
1014
1020
  } else {
1015
- inputPopupHint.innerHTML = `Choose target type &amp; deserializer, then enter input string:`;
1021
+ inputPopupHint.innerHTML = `Choose target pattern &amp; deserializer, then enter input string:`;
1022
+ }
1023
+
1024
+ if (inputPopupCodecHint) {
1025
+ if (codecName === "json") {
1026
+ inputPopupCodecHint.textContent = "Parameterized codec: deserializes JSON into the specified Target Pattern.";
1027
+ } else {
1028
+ inputPopupCodecHint.textContent = "";
1029
+ }
1016
1030
  }
1017
1031
  }
1018
1032
 
1019
1033
  function updateInputPopupCodecs() {
1020
1034
  if (!inputPopupCodec) return;
1021
- const rawType = inputPopupType?.value || "*";
1022
- const codeHash = rawType === "*" ? "*" : state.typeAliases?.[rawType] || rawType;
1035
+ const rawPattern = inputPopupType ? inputPopupType.value.trim() : "(...)";
1036
+ let codeHash = null;
1037
+ if (rawPattern && rawPattern !== "*" && rawPattern !== "(...)") {
1038
+ try {
1039
+ const resolved = resolveInputPattern(state, rawPattern);
1040
+ codeHash = resolved.codeHash;
1041
+ } catch {
1042
+ codeHash = null;
1043
+ }
1044
+ }
1023
1045
  const prev = inputPopupCodec.value;
1024
1046
 
1025
1047
  inputPopupCodec.innerHTML = "";
@@ -1027,8 +1049,8 @@ function updateInputPopupCodecs() {
1027
1049
  const store = state.codecs || {};
1028
1050
 
1029
1051
  const candidates = [
1030
- ...(store[codeHash] || []),
1031
- ...(codeHash !== "*" ? (store["*"] || []) : [])
1052
+ ...(codeHash ? (store[codeHash] || []) : []),
1053
+ ...(store["*"] || [])
1032
1054
  ];
1033
1055
 
1034
1056
  const seen = new Set();
@@ -1039,11 +1061,26 @@ function updateInputPopupCodecs() {
1039
1061
  }
1040
1062
  }
1041
1063
 
1042
- if (matchingCodecs.length === 0) {
1043
- if (rawType === "int") matchingCodecs.push("int");
1044
- if (rawType === "bool") matchingCodecs.push("yn");
1045
- if (rawType === "string") matchingCodecs.push("utf8");
1064
+ const bareName = rawPattern.replace(/^(\?|\$)\s*/, "").trim();
1065
+ if (!seen.has("json")) {
1046
1066
  matchingCodecs.push("json");
1067
+ seen.add("json");
1068
+ }
1069
+ if ((bareName === "int" || (codeHash && codeHash === state.typeAliases?.int)) && !seen.has("int")) {
1070
+ matchingCodecs.push("int");
1071
+ seen.add("int");
1072
+ }
1073
+ if ((bareName === "string" || bareName === "utf8" || (codeHash && (codeHash === state.typeAliases?.string || codeHash === state.typeAliases?.utf8))) && !seen.has("utf8")) {
1074
+ matchingCodecs.push("utf8");
1075
+ seen.add("utf8");
1076
+ }
1077
+ if ((bareName === "float64" || bareName === "ieee" || (codeHash && codeHash === state.typeAliases?.float64)) && !seen.has("ieee")) {
1078
+ matchingCodecs.push("ieee");
1079
+ seen.add("ieee");
1080
+ }
1081
+ if ((bareName === "yes_no" || bareName === "bool" || (codeHash && codeHash === state.typeAliases?.yes_no)) && !seen.has("yn")) {
1082
+ matchingCodecs.push("yn");
1083
+ seen.add("yn");
1047
1084
  }
1048
1085
 
1049
1086
  for (const name of matchingCodecs) {
@@ -1064,39 +1101,38 @@ function updateInputPopupCodecs() {
1064
1101
  updateLiveValidation();
1065
1102
  }
1066
1103
 
1067
- function populateInputPopupDropdowns(preselectedType = null, preselectedCodec = null) {
1104
+ function populateInputPopupDropdowns(preselectedPattern = null, preselectedCodec = null) {
1068
1105
  if (!inputPopupType) return;
1069
1106
 
1070
- let prevType = preselectedType;
1071
- if (!prevType && state.pendingInput) {
1072
- const foundAlias = Object.entries(state.typeAliases || {}).find(([, h]) => h === state.pendingInput.codeHash);
1073
- prevType = foundAlias ? foundAlias[0] : state.pendingInput.codeHash;
1074
- }
1075
- if (!prevType) {
1076
- prevType = inputPopupType.value;
1107
+ const datalist = document.getElementById("input-popup-type-list");
1108
+ if (datalist) {
1109
+ datalist.innerHTML = "";
1110
+ const aliases = Object.entries(state.typeAliases || {}).sort(([a], [b]) => a.localeCompare(b));
1111
+ for (const [alias, hash] of aliases) {
1112
+ const opt = document.createElement("option");
1113
+ opt.value = `$ ${alias}`;
1114
+ opt.textContent = `$ ${alias} (${hash.slice(0, 10)}...)`;
1115
+ datalist.appendChild(opt);
1116
+ }
1117
+ const universalOpt = document.createElement("option");
1118
+ universalOpt.value = "(...)";
1119
+ universalOpt.textContent = "(...) (Any document)";
1120
+ datalist.appendChild(universalOpt);
1077
1121
  }
1078
1122
 
1079
- inputPopupType.innerHTML = "";
1080
-
1081
- const aliases = Object.entries(state.typeAliases || {}).sort(([a], [b]) => a.localeCompare(b));
1082
- for (const [alias, hash] of aliases) {
1083
- const opt = document.createElement("option");
1084
- opt.value = alias;
1085
- opt.textContent = `$${alias} (${hash.slice(0, 10)}...)`;
1086
- inputPopupType.appendChild(opt);
1123
+ let prevPattern = preselectedPattern;
1124
+ if (!prevPattern && state.pendingInput) {
1125
+ prevPattern = state.pendingInput.rawExpr;
1126
+ if (!prevPattern && state.pendingInput.codeHash) {
1127
+ const foundAlias = Object.entries(state.typeAliases || {}).find(([, h]) => h === state.pendingInput.codeHash);
1128
+ prevPattern = foundAlias ? `$ ${foundAlias[0]}` : state.pendingInput.codeHash;
1129
+ }
1087
1130
  }
1088
-
1089
- const universalOpt = document.createElement("option");
1090
- universalOpt.value = "*";
1091
- universalOpt.textContent = "* (Universal / Any)";
1092
- inputPopupType.appendChild(universalOpt);
1093
-
1094
- if (prevType && (aliases.some(([a]) => a === prevType) || prevType === "*")) {
1095
- inputPopupType.value = prevType;
1096
- } else if (aliases.length > 0) {
1097
- inputPopupType.value = aliases[0][0];
1131
+ if (!prevPattern) {
1132
+ prevPattern = inputPopupType.value || "(...)";
1098
1133
  }
1099
1134
 
1135
+ inputPopupType.value = prevPattern;
1100
1136
  updateInputPopupCodecs();
1101
1137
 
1102
1138
  const targetCodec = preselectedCodec || state.pendingInput?.codecName;
@@ -1114,26 +1150,39 @@ function updateLiveValidation() {
1114
1150
  return;
1115
1151
  }
1116
1152
 
1117
- const rawType = inputPopupType?.value;
1153
+ const rawPattern = inputPopupType ? inputPopupType.value.trim() : "(...)";
1118
1154
  const codecName = inputPopupCodec?.value;
1119
- if (!rawType) {
1120
- inputPopupStatus.innerHTML = `<span class="line-warning" style="margin:0;padding:2px 6px;">Select a target type first.</span>`;
1155
+ if (!rawPattern && !codecName) {
1156
+ inputPopupStatus.innerHTML = `<span class="line-warning" style="margin:0;padding:2px 6px;">Specify a target pattern or codec first.</span>`;
1121
1157
  return;
1122
1158
  }
1123
1159
 
1124
- const codeHash = rawType === "*" ? "*" : state.typeAliases?.[rawType] || rawType;
1160
+ let pattern = null;
1161
+ let codeHash = null;
1162
+ if (rawPattern && rawPattern !== "*" && rawPattern !== "(...)") {
1163
+ try {
1164
+ const resolved = resolveInputPattern(state, rawPattern);
1165
+ pattern = resolved.pattern;
1166
+ codeHash = resolved.codeHash;
1167
+ } catch (err) {
1168
+ inputPopupStatus.innerHTML = `<span class="line-warning" style="margin:0;padding:2px 6px;">⚠ Pattern Error: ${escapeHtml(err.message || String(err))}</span>`;
1169
+ return;
1170
+ }
1171
+ }
1172
+
1125
1173
  try {
1126
1174
  const codec = resolveCodec(state, codeHash, codecName || null, "parse");
1127
- const pattern = codeHashToPattern(codeHash, (h) => state.codes?.[h]);
1175
+ const resolvedPattern = pattern || (codeHash && codeHash !== "*" ? codeHashToPattern(codeHash, (h) => state.codes?.[h]) : null);
1128
1176
  const parsed = codec.parse(text, {
1129
1177
  codeHash,
1130
- pattern,
1178
+ pattern: resolvedPattern,
1131
1179
  state,
1132
1180
  Value,
1133
1181
  isProduct,
1134
1182
  isVariant
1135
1183
  });
1136
- const repr = parsed instanceof Value ? parsed.toJSON() : (typeof parsed === "object" ? JSON.stringify(parsed) : String(parsed));
1184
+ const validated = valueForPattern(parsed, resolvedPattern, text, codec.name);
1185
+ const repr = validated instanceof Value ? validated.toJSON() : (typeof validated === "object" ? JSON.stringify(validated) : String(validated));
1137
1186
  inputPopupStatus.innerHTML = `<span class="line-output" style="margin:0;padding:2px 6px;">✓ Valid: <code>${escapeHtml(String(repr))}</code></span>`;
1138
1187
  } catch (err) {
1139
1188
  inputPopupStatus.innerHTML = `<span class="line-warning" style="margin:0;padding:2px 6px;">⚠ ${escapeHtml(err.message || String(err))}</span>`;
@@ -1142,25 +1191,43 @@ function updateLiveValidation() {
1142
1191
 
1143
1192
  export async function submitInputPopup() {
1144
1193
  const text = inputPopupText?.value ?? "";
1194
+ const rawPattern = inputPopupType ? inputPopupType.value.trim() : "(...)";
1195
+ const codecName = inputPopupCodec?.value;
1145
1196
 
1146
- // If state.pendingInput is not set, configure it from the popup selections
1147
- if (!state.pendingInput) {
1148
- const rawType = inputPopupType?.value;
1149
- const codecName = inputPopupCodec?.value;
1150
- if (!rawType) {
1151
- if (inputPopupStatus) inputPopupStatus.innerHTML = `<span class="line-error">Please select a target type.</span>`;
1152
- return;
1153
- }
1154
- const codeHash = rawType === "*" ? "*" : state.typeAliases?.[rawType] || rawType;
1197
+ if (!rawPattern && !codecName) {
1198
+ if (inputPopupStatus) inputPopupStatus.innerHTML = `<span class="line-error">Please specify a target pattern or codec.</span>`;
1199
+ return;
1200
+ }
1201
+
1202
+ let pattern = null;
1203
+ let codeHash = null;
1204
+ let rawExpr = rawPattern || "(...)";
1205
+ if (rawPattern && rawPattern !== "*" && rawPattern !== "(...)") {
1155
1206
  try {
1156
- const codec = resolveCodec(state, codeHash, codecName || null, "parse");
1157
- state.pendingInput = { codeHash, codecName: codec.name, promptName: codec.name };
1207
+ const resolved = resolveInputPattern(state, rawPattern);
1208
+ pattern = resolved.pattern;
1209
+ codeHash = resolved.codeHash;
1210
+ rawExpr = rawPattern;
1158
1211
  } catch (err) {
1159
- if (inputPopupStatus) inputPopupStatus.innerHTML = `<span class="line-error">${escapeHtml(err.message)}</span>`;
1212
+ if (inputPopupStatus) inputPopupStatus.innerHTML = `<span class="line-error">Pattern Error: ${escapeHtml(err.message || String(err))}</span>`;
1160
1213
  return;
1161
1214
  }
1162
1215
  }
1163
1216
 
1217
+ try {
1218
+ const codec = resolveCodec(state, codeHash, codecName || null, "parse");
1219
+ state.pendingInput = {
1220
+ pattern,
1221
+ codeHash,
1222
+ codecName: codec.name,
1223
+ promptName: codec.name,
1224
+ rawExpr: rawExpr || codec.name
1225
+ };
1226
+ } catch (err) {
1227
+ if (inputPopupStatus) inputPopupStatus.innerHTML = `<span class="line-error">${escapeHtml(err.message || String(err))}</span>`;
1228
+ return;
1229
+ }
1230
+
1164
1231
  closeInputPopup();
1165
1232
  await executeCommand(text);
1166
1233
  }
@@ -1190,6 +1257,7 @@ export function initRepl() {
1190
1257
  inputPopupText = document.getElementById("input-popup-text");
1191
1258
  inputPopupStatus = document.getElementById("input-popup-status");
1192
1259
  inputPopupHint = document.getElementById("input-popup-hint");
1260
+ inputPopupCodecHint = document.getElementById("input-popup-codec-hint");
1193
1261
  inputSamplesList = document.getElementById("input-samples-list");
1194
1262
 
1195
1263
  updatePrompt();
@@ -1197,16 +1265,7 @@ export function initRepl() {
1197
1265
  // Welcome message
1198
1266
  appendSystemMessage(`
1199
1267
  <div class="welcome-banner">
1200
- <div class="welcome-title">λ k interactive repl</div>
1201
- <div class="welcome-desc">First-order partial functions over algebraic data types &bull; WebAssembly execution engine &bull; Custom Serializers &amp; Deserializers (:codecs)</div>
1202
- <div class="welcome-tips">
1203
- <span>💡 Try: <a href="javascript:void(0)" class="quick-link" data-code=":load core.k">:load core.k</a></span>
1204
- <span>• <a href="javascript:void(0)" class="quick-link" data-code=":codec load int">:codec load int</a></span>
1205
- <span>• <a href="javascript:void(0)" class="quick-link" data-code="10 int">10 int</a></span>
1206
- <span>• <a href="javascript:void(0)" class="quick-link" data-code="{10 int x, 5 int y} plus">{10 int x, 5 int y} plus</a></span>
1207
- <span>• <a href="javascript:void(0)" class="quick-link" data-code=":codecs">:codecs</a></span>
1208
- <span>• <a href="javascript:void(0)" class="quick-link" data-code=":help">:help</a></span>
1209
- </div>
1268
+ <div class="welcome-title">k interactive repl</div>
1210
1269
  </div>
1211
1270
  `, false, true);
1212
1271
 
@@ -1365,14 +1424,6 @@ export function initRepl() {
1365
1424
  const clearBtn = document.getElementById("btn-clear");
1366
1425
  if (clearBtn) clearBtn.onclick = clearOutput;
1367
1426
 
1368
- // Reset button
1369
- const resetBtn = document.getElementById("btn-reset");
1370
- if (resetBtn) {
1371
- resetBtn.onclick = () => {
1372
- executeCommand(":reset");
1373
- };
1374
- }
1375
-
1376
1427
  // Help button
1377
1428
  const helpBtn = document.getElementById("btn-help");
1378
1429
  if (helpBtn) {
@@ -1458,6 +1509,7 @@ export function initRepl() {
1458
1509
 
1459
1510
  if (inputPopupType) {
1460
1511
  inputPopupType.addEventListener("change", updateInputPopupCodecs);
1512
+ inputPopupType.addEventListener("input", updateInputPopupCodecs);
1461
1513
  }
1462
1514
 
1463
1515
  if (inputPopupCodec) {