@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 +23 -36
- package/TODO.md +112 -0
- package/browser/repl-browser.mjs +146 -94
- package/browser/vfs-data.mjs +2 -0
- package/package.json +1 -1
- package/repl-codecs.mjs +26 -5
- package/repl.mjs +214 -148
- package/scripts/build-repl-html.mjs +213 -72
- package/tests/test-repl-html.mjs +84 -6
- package/tests/test-repl.mjs +90 -40
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
|
|
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
|
|
195
|
+
### `:input [<filter=(...)> [codec]]`
|
|
210
196
|
|
|
211
|
-
Resolves
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
222
|
-
|
|
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
|
-
>
|
|
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
|
-
### `:
|
|
220
|
+
### `:time expr`
|
|
231
221
|
|
|
232
|
-
|
|
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
|
-
>
|
|
283
|
-
|
|
284
|
-
>
|
|
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;` — Relation alias (e.g. `swap = { . y x, . x y };`).
|
|
25
|
+
- `$ name = typeExpr;` — 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 }` — 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 >` — Union type of arity $k$ (tagged sum of variants, e.g. `<int ok, string err>`).
|
|
29
|
+
- `name` / `@hash` — 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 }` — Product of arity $k$ (evaluates relations into record fields; special case: `{}` evaluates 0 relations, producing empty record `{}`).
|
|
32
|
+
- `< rel_1, ..., rel_k >` — Union / ordered choice of arity $k$ (evaluates relations in order until one succeeds).
|
|
33
|
+
- `( rel_1 ... rel_k )` — 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` — Field projection (extracts field `label`).
|
|
35
|
+
- `/ tag` — Variant branch projection (extracts payload of variant `tag`).
|
|
36
|
+
- `| tag` — Variant constructor (wraps document into variant `tag`).
|
|
37
|
+
- `$ typeExpr` — Type constraint (asserts input matches schema; compile-time Type Error on mismatch).
|
|
38
|
+
- `name` / `@hash` — 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
|
+
|
package/browser/repl-browser.mjs
CHANGED
|
@@ -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
|
-
|
|
18
|
-
|
|
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: "
|
|
572
|
+
type: "yes_no",
|
|
573
573
|
universal: false,
|
|
574
|
-
parse: `// Parse 'yes', 'no', 'true', 'false' into $
|
|
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("
|
|
577
|
+
return Value.variant("yes", Value.product({}));
|
|
578
578
|
}
|
|
579
579
|
if (tag === "no" || tag === "false" || tag === "0") {
|
|
580
|
-
return Value.variant("
|
|
580
|
+
return Value.variant("no", Value.product({}));
|
|
581
581
|
}
|
|
582
582
|
throw new Error("Expected yes/no or true/false");`,
|
|
583
|
-
print: `// Serialize
|
|
584
|
-
return value.tag === "
|
|
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
|
|
656
|
-
|
|
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 =
|
|
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 =
|
|
842
|
-
if (
|
|
843
|
-
|
|
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: ['{"
|
|
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
|
|
1012
|
-
|
|
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
|
|
1021
|
+
inputPopupHint.innerHTML = `Choose target pattern & 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
|
|
1022
|
-
|
|
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
|
-
...(
|
|
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
|
-
|
|
1043
|
-
|
|
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(
|
|
1104
|
+
function populateInputPopupDropdowns(preselectedPattern = null, preselectedCodec = null) {
|
|
1068
1105
|
if (!inputPopupType) return;
|
|
1069
1106
|
|
|
1070
|
-
|
|
1071
|
-
if (
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
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
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
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
|
-
|
|
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
|
|
1153
|
+
const rawPattern = inputPopupType ? inputPopupType.value.trim() : "(...)";
|
|
1118
1154
|
const codecName = inputPopupCodec?.value;
|
|
1119
|
-
if (!
|
|
1120
|
-
inputPopupStatus.innerHTML = `<span class="line-warning" style="margin:0;padding:2px 6px;">
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
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
|
|
1157
|
-
|
|
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"
|
|
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"
|
|
1201
|
-
<div class="welcome-desc">First-order partial functions over algebraic data types • WebAssembly execution engine • Custom Serializers & 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) {
|