@fraczak/k 7.1.0 → 7.2.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/CHANGELOG.md CHANGED
@@ -5,6 +5,39 @@ All notable changes to this project will be documented here.
5
5
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
6
6
  This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [7.2.0] — 2026-10-17 — Dynamic JSON Codec (djson) & Lazy Wire Decoding
9
+
10
+ ### Features & Performance Improvements
11
+
12
+ - **Universal Dynamic JSON Codec (`codecs/djson.mjs`, `k-djson`)**:
13
+ - Added a standalone dynamic JSON codec (`djson.mjs` / `k-djson`)
14
+ based on a single 29-node recursive pattern `JSON = < number,
15
+ string, bool, null, list, object >`, where arrays are encoded as
16
+ arbitrary-length `cons`/`nil` lists and objects as `cons`/`nil`
17
+ lists of `{ key, val }` pairs.
18
+ - Kept the existing schema-specializing `json.mjs` (`k-json`) as an
19
+ alternative record-oriented JSON codec.
20
+ - Integrated `djson.mjs` into the CLI, REPL, and Web REPL (`repl.html`
21
+ VFS).
22
+
23
+ - **Lazy Prefix-Free Wire Decoding & Fast Bitstream I/O**:
24
+ - Implemented lazy wire decoding (`LazyGeneralProduct` and
25
+ `LazyGeneralVariant`) with precomputed fixed-width bit sizes and
26
+ static field offsets on pattern graphs.
27
+ - Optimized bitstream reading/writing (`BitReader` / `BitWriter`) with
28
+ word-level operations, pre-encoded closed pattern bytes, and direct
29
+ bitstream-to-codepoint UTF-8 string decoding without intermediate
30
+ AST allocations.
31
+
32
+ - **Stack-Safe & Bounded Value Rendering**:
33
+ - Converted `valueToK`, `valueWithEnvelopeToK`, and wire serialization
34
+ to iterative stack-safe algorithms with early character truncation
35
+ (`maxChars`).
36
+ - Added `:limit [num|none]` command and `--limit=<num>` CLI flag in
37
+ `repl.mjs` (default: `4096` chars) and bounded DOM tree previews in
38
+ `repl.html`.
39
+ - Handled broken pipe (`EPIPE`) cleanly across codec CLI entrypoints.
40
+
8
41
  ## [7.1.0] — 2026-10-16 — Polymorphic K IR (PKIR) & CLI Standardization
9
42
 
10
43
  ### Features & Architecture Improvements
package/DOCS/CODECS.md CHANGED
@@ -141,6 +141,40 @@ printf 'yes\n' | ./codecs/yesno.mjs --parse | ./codecs/yesno.mjs --print
141
141
  # Output: yes
142
142
  ```
143
143
 
144
+ ## Built-in Codecs
145
+
146
+ The `codecs/` directory provides the following standard codecs:
147
+
148
+ - **`int.mjs` (`k-int`)**: Decimal integers and bracketed integer lists
149
+ (e.g. `42`, `-15`, `[1, 2, 3]`) mapped to signed binary bit trees and
150
+ `cons`/`nil` lists.
151
+ - **`ieee.mjs` (`k-ieee`)**: IEEE 754 64-bit floating-point numbers
152
+ (`sign`, `exponent`, `fraction`).
153
+ - **`utf8.mjs` (`k-utf8`) & `utf16.mjs` (`k-utf16`)**: Unicode strings
154
+ encoded as `cons`/`nil` code-point lists (`str` in `core.k`).
155
+ - **`unit.mjs` (`k-unit`)**: The unit value `{}`.
156
+ - **`json.mjs` (`k-json`) — Schema-Specializing JSON**:
157
+ Derives a document-specific pattern for each JSON value:
158
+ - JSON objects map directly to products `{ field: val, ... }`.
159
+ - JSON arrays map to indexed products `{ "0": a0, "1": a1, ... }`.
160
+ - Ideal for record-oriented projection (e.g. `.user .name`).
161
+ - **`djson.mjs` (`k-djson`) — Universal Dynamic JSON**:
162
+ Maps every JSON document to a single universal recursive pattern
163
+ `JSON`:
164
+ ```k
165
+ ?<
166
+ float64 number,
167
+ str string,
168
+ < {} true, {} false > bool,
169
+ {} null,
170
+ < {} nil, { JSON car, LIST cdr } cons >=LIST list,
171
+ < {} nil, { { str key, JSON val } car, OBJ cdr } cons >=OBJ object
172
+ >=JSON
173
+ ```
174
+ Because every JSON value shares the exact same static 29-node pattern,
175
+ a `k` relation can be compiled once against `JSON` and process
176
+ arbitrary JSON documents of any shape or array/object length.
177
+
144
178
  ## Testing a Codec
145
179
 
146
180
  For CLI codecs, test both directions in a pipeline:
@@ -174,8 +208,9 @@ npm test
174
208
  ## Web REPL (VFS) Bundling
175
209
 
176
210
  When `repl.html` is generated (`npm run build:repl-html`):
177
- - Codecs (`int.mjs`, `json.mjs`, `utf8.mjs`, `unit.mjs`, `ieee.mjs`) are
178
- bundled into 100% self-contained ES modules with zero imports.
211
+ - Codecs (`int.mjs`, `json.mjs`, `djson.mjs`, `utf8.mjs`, `unit.mjs`,
212
+ `ieee.mjs`) are bundled into 100% self-contained ES modules with zero
213
+ imports.
179
214
  - `runCodecCLI` is shimmed as a no-op, and `Value` helpers are directly
180
215
  inlined.
181
216
  - In the browser, the VFS serves these self-contained modules directly to
@@ -192,11 +192,16 @@ runCodecCLI(import.meta.url, { parse, print, doc });
192
192
  pathToFileURL(process.argv[1]).href`). If imported as a module or
193
193
  running in a browser, it is a no-op.
194
194
  2. **Flag Parsing**: Handles `--parse`, `--print`, and `-h` / `--help`.
195
- 3. **Stream Piping**:
195
+ 3. **Stream Piping & Lazy Wire Decoding**:
196
196
  - For `--parse`: reads `stdin` to string, calls `parse(text)`, encodes
197
197
  enveloped value `(P, v)` to binary wire format on `stdout`.
198
- - For `--print`: decodes wire format from `stdin` into `(P, v)`, calls
199
- `print(value)`, writes formatted text to `stdout`.
198
+ - For `--print`: lazily decodes the wire format from `stdin` into
199
+ bitstream-backed `Value` proxies (`LazyGeneralProduct`,
200
+ `LazyGeneralVariant`) with precomputed fixed-width bit sizes and
201
+ static field offsets, calls `print(value)`, and writes formatted text
202
+ to `stdout`.
203
+ - Cleanly exits on `EPIPE` when piped into downstream consumers like
204
+ `head`.
200
205
 
201
206
  ---
202
207
 
@@ -207,7 +212,8 @@ To make `codecs/` intuitive and self-contained:
207
212
  ```text
208
213
  codecs/
209
214
  ├── int.mjs # Integer & list codec
210
- ├── json.mjs # JSON codec
215
+ ├── json.mjs # JSON codec (schema-specializing)
216
+ ├── djson.mjs # Dynamic JSON codec (singleton pattern)
211
217
  ├── utf8.mjs # UTF-8 string codec
212
218
  ├── utf16.mjs # UTF-16 string codec
213
219
  ├── ieee.mjs # IEEE 754 float codec
@@ -236,7 +242,8 @@ In the browser REPL (`repl.html`):
236
242
  imports**.
237
243
  - **No Runtime Files in VFS**: `codecs/runtime/*` is not included in the
238
244
  browser VFS. The VFS only contains verified, self-contained user-facing
239
- files (`int.mjs`, `json.mjs`, `utf8.mjs`, `unit.mjs`, `ieee.mjs`).
245
+ files (`int.mjs`, `json.mjs`, `djson.mjs`, `utf8.mjs`, `unit.mjs`,
246
+ `ieee.mjs`).
240
247
  - **Native Browser Loading**: In `repl-codecs.mjs`, the browser reads the
241
248
  codec source from VFS, wraps it in a Blob, and invokes native `await
242
249
  import(blobUrl)`. No regex manipulation, import stripping, or global
package/DOCS/REPL.md CHANGED
@@ -33,10 +33,11 @@ is complete.
33
33
  | Command | Meaning |
34
34
  | --- | --- |
35
35
  | `:help` | Show command summary |
36
- | `:engine [wasm|js]` | Display or switch evaluation engine (`wasm` or `js`) |
37
- | `:wasm` / `:js` | Shortcut to switch engine |
36
+ | `:engine [wasm\|pkir\|js]` | Display or switch evaluation engine (`wasm`, `pkir`, or `js`) |
37
+ | `:wasm` / `:pkir` / `:js` | Shortcut to switch engine |
38
38
  | `:rel name` | Show relation definition |
39
39
  | `:rels` | List relation aliases |
40
+ | `:limit [num\|none]` | Set maximum characters for value output (default: 4096) |
40
41
  | `:codec load file` | Load a codec from an ES module file (e.g. `:codec load codecs/int.mjs`) |
41
42
  | `:codec unload name` | Unload a registered codec |
42
43
  | `:codec list` | List loaded REPL codecs (alias: `:codecs`) |
@@ -231,11 +232,13 @@ The REPL supports two evaluation engines:
231
232
 
232
233
  ### CLI Options
233
234
 
234
- When starting `k-repl`, choose the engine with:
235
+ When starting `k-repl`, choose the engine or output limit with:
235
236
 
236
237
  ```bash
237
238
  k-repl --engine=js # or k-repl --js
239
+ k-repl --engine=pkir # or k-repl --pkir
238
240
  k-repl --engine=wasm # or k-repl --wasm (default)
241
+ k-repl --limit=8192 # set max characters for rendered output
239
242
  ```
240
243
 
241
244
  ## Timing and Profiling
@@ -297,6 +300,11 @@ Evaluated values print in k syntax together with the inferred envelope:
297
300
  {}|0|+1 ?<<{} 0, ...> +1, ...>
298
301
  ```
299
302
 
303
+ Large values are rendered iteratively and truncated at `maxChars`
304
+ (default: `4096` characters) so deeply nested or large values print
305
+ instantly without exhausting stack or terminal buffers. Use
306
+ `:limit <num>` or `:limit none` to adjust or disable the limit.
307
+
300
308
  `undefined` prints as:
301
309
 
302
310
  ```text
@@ -113,7 +113,7 @@ The bundled VFS and UI examples must include only verified useful files:
113
113
  #### 1. Curated Files Explorer
114
114
  In the Web REPL "Files" (VFS) explorer, the visible files are strictly curated to verified examples and codecs:
115
115
  - **k-codes**: `core.k`, `arithmetics.k`, `ieee.k`
116
- - **codecs**: `json.mjs`, `utf8.mjs`, `int.mjs`, `unit.mjs`, `ieee.mjs`
116
+ - **codecs**: `json.mjs`, `djson.mjs`, `utf8.mjs`, `int.mjs`, `unit.mjs`, `ieee.mjs`
117
117
  - User-created or uploaded files remain visible.
118
118
  - Internal runtime artifacts (`backends/wasm/runtime.wat`) and redundant directory prefixes (`Examples/`, `codecs/`) are kept accessible internally but hidden from the default file listing.
119
119
  - Clicking "Load" on a `.mjs` file automatically executes `:codec load <filename>`.
package/README.md CHANGED
@@ -223,7 +223,8 @@ For a detailed guide to all four formats, commands, and workflows, see
223
223
 
224
224
  | Command | Purpose |
225
225
  | --- | --- |
226
- | `k-json` | Convert JSON to/from the binary stream |
226
+ | `k-json` | Convert JSON to/from the binary stream (schema-specializing) |
227
+ | `k-djson` | Convert JSON to/from a universal dynamic JSON singleton pattern |
227
228
  | `k-int` | Convert decimal integers to/from the binary stream |
228
229
  | `k-ieee` | Convert float literals to/from the binary stream |
229
230
  | `k-unit` | Produce or validate the unit value |
package/Value.mjs CHANGED
@@ -193,15 +193,40 @@ class Value {
193
193
 
194
194
  toJSON() {
195
195
  if (isVariant(this)) {
196
- if (isProduct(this.value) && Object.keys(this.value.product).length === 0) {
196
+ if (
197
+ isProduct(this.value) &&
198
+ Object.keys(this.value.product).length === 0
199
+ ) {
197
200
  return this.tag;
198
201
  }
199
- return {[this.tag]: this.value.toJSON()};
202
+ const tags = [];
203
+ let curr = this;
204
+ while (isVariant(curr)) {
205
+ if (
206
+ isProduct(curr.value) &&
207
+ Object.keys(curr.value.product).length === 0
208
+ ) {
209
+ let res = curr.tag;
210
+ for (let i = tags.length - 1; i >= 0; i--) {
211
+ res = { [tags[i]]: res };
212
+ }
213
+ return res;
214
+ }
215
+ tags.push(curr.tag);
216
+ curr = curr.value;
217
+ }
218
+ let res = curr
219
+ ? (typeof curr.toJSON === "function" ? curr.toJSON() : curr)
220
+ : null;
221
+ for (let i = tags.length - 1; i >= 0; i--) {
222
+ res = { [tags[i]]: res };
223
+ }
224
+ return res;
200
225
  }
201
226
  if (!isProduct(this)) return { type: this.type };
202
227
  let vector = toVector(this.product);
203
228
  if (vector.length > 0) return vector;
204
- return this.product;
229
+ return this.product;
205
230
  }
206
231
  }
207
232
  export { Value, fromObject, cloneSubpattern, edgeSubpattern, composePattern, mergePatterns, withPattern, isProduct, isVariant, isValue };
package/backend-api.mjs CHANGED
@@ -1,6 +1,10 @@
1
1
  export { annotate } from "./index.mjs";
2
2
  export { KVM_FORMAT, KVM_VERSION, lowerKIRRelationToKVM, lowerKIRToKVM, lowerToKVM, executeKVM, objectToKVMArtifact, specializeKVM } from "./kvm.mjs";
3
- export { decodeWire, encodeToWire } from "./codecs/runtime/prefix-codec.mjs";
3
+ export {
4
+ decodeWire,
5
+ decodeWireLazy,
6
+ encodeToWire
7
+ } from "./codecs/runtime/prefix-codec.mjs";
4
8
  export { exportPatternGraph, NODE_KIND } from "./codecs/runtime/codec.mjs";
5
9
  export { patternToPropertyList, propertyListToPattern } from "./codecs/runtime/pattern-json.mjs";
6
10
  export { Value, fromObject, isProduct, isVariant, isValue } from "./Value.mjs";
@@ -101,7 +101,9 @@ export function renderValueNode(val, currentDepth = 1, depthLimit = 3) {
101
101
  const populateChildren = () => {
102
102
  if (populated) return;
103
103
  populated = true;
104
- for (const k of keys) {
104
+ const maxFields = 100;
105
+ const displayKeys = keys.slice(0, maxFields);
106
+ for (const k of displayKeys) {
105
107
  const row = document.createElement("div");
106
108
  row.className = "k-tree-row";
107
109
 
@@ -113,12 +115,25 @@ export function renderValueNode(val, currentDepth = 1, depthLimit = 3) {
113
115
  const childContainer = document.createElement("div");
114
116
  childContainer.className = "k-tree-child-val";
115
117
  childContainer.appendChild(
116
- renderValueNode(val.product[k], isOpen ? currentDepth + 1 : 1, depthLimit)
118
+ renderValueNode(
119
+ val.product[k],
120
+ isOpen ? currentDepth + 1 : 1,
121
+ depthLimit
122
+ )
117
123
  );
118
124
  row.appendChild(childContainer);
119
125
 
120
126
  childrenContainer.appendChild(row);
121
127
  }
128
+ if (keys.length > maxFields) {
129
+ const moreRow = document.createElement("div");
130
+ moreRow.className = "k-tree-row k-tree-more";
131
+ moreRow.style.color = "var(--color-muted)";
132
+ moreRow.style.paddingLeft = "1.2rem";
133
+ moreRow.textContent =
134
+ `... and ${keys.length - maxFields} more fields`;
135
+ childrenContainer.appendChild(moreRow);
136
+ }
122
137
  };
123
138
 
124
139
  if (isOpen) {
@@ -169,13 +184,15 @@ export function renderValueNode(val, currentDepth = 1, depthLimit = 3) {
169
184
  if (isVariant(val.value)) {
170
185
  let chainCount = 0;
171
186
  let curr = val.value;
172
- while (isVariant(curr)) {
187
+ while (isVariant(curr) && chainCount < 20) {
173
188
  chainCount++;
174
189
  curr = curr.value;
175
190
  }
176
191
  const badge = document.createElement("span");
177
192
  badge.className = "k-tree-badge";
178
- badge.textContent = `+${chainCount} tags`;
193
+ badge.textContent = isVariant(curr)
194
+ ? "+20+ tags"
195
+ : `+${chainCount} tags`;
179
196
  summary.appendChild(badge);
180
197
  }
181
198
 
@@ -486,7 +503,15 @@ export function createOutputEntryElement({
486
503
 
487
504
  const valSummary = document.createElement("summary");
488
505
  valSummary.className = "k-tree-section-summary";
489
- valSummary.innerHTML = `<span class="k-tree-arrow">▶</span> <span class="k-section-title">🌲 Value</span> <span class="k-section-preview">${escapeHtml(valueToK(value))}</span>`;
506
+ const rawValPreview = valueToK(value, 120);
507
+ const valPreview = rawValPreview.length > 120
508
+ ? `${rawValPreview.slice(0, 120)}...`
509
+ : rawValPreview;
510
+ const valEscaped = escapeHtml(valPreview);
511
+ valSummary.innerHTML =
512
+ `<span class="k-tree-arrow">▶</span> ` +
513
+ `<span class="k-section-title">🌲 Value</span> ` +
514
+ `<span class="k-section-preview">${valEscaped}</span>`;
490
515
  valSection.appendChild(valSummary);
491
516
 
492
517
  const valBody = document.createElement("div");
@@ -504,8 +529,15 @@ export function createOutputEntryElement({
504
529
 
505
530
  const patSummary = document.createElement("summary");
506
531
  patSummary.className = "k-tree-section-summary";
507
- const patPreview = propertyListToFilter(value.pattern);
508
- patSummary.innerHTML = `<span class="k-tree-arrow">▶</span> <span class="k-section-title">🌿 Pattern</span> <span class="k-section-preview">?${escapeHtml(patPreview)}</span>`;
532
+ const rawPatPreview = propertyListToFilter(value.pattern, "X", 120);
533
+ const patPreview = rawPatPreview.length > 120
534
+ ? `${rawPatPreview.slice(0, 120)}...`
535
+ : rawPatPreview;
536
+ const patEscaped = escapeHtml(patPreview);
537
+ patSummary.innerHTML =
538
+ `<span class="k-tree-arrow">▶</span> ` +
539
+ `<span class="k-section-title">🌿 Pattern</span> ` +
540
+ `<span class="k-section-preview">?${patEscaped}</span>`;
509
541
  patSection.appendChild(patSummary);
510
542
 
511
543
  patBody = document.createElement("div");
@@ -545,7 +577,14 @@ export function createOutputEntryElement({
545
577
 
546
578
  const rawPre = document.createElement("pre");
547
579
  rawPre.className = "k-raw-pre";
548
- rawPre.textContent = rawText;
580
+ const MAX_RAW_PRE_CHARS = 100000;
581
+ if (rawText.length > MAX_RAW_PRE_CHARS) {
582
+ rawPre.textContent =
583
+ rawText.slice(0, MAX_RAW_PRE_CHARS) +
584
+ `\n\n... [truncated, ${rawText.length - MAX_RAW_PRE_CHARS} more characters]`;
585
+ } else {
586
+ rawPre.textContent = rawText;
587
+ }
549
588
  rawWrapper.appendChild(rawPre);
550
589
 
551
590
  container.appendChild(rawWrapper);