@fraczak/k 7.1.0 → 8.0.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/DOCS/BIG_PICTURE.md +54 -7
  3. package/DOCS/CODECS.md +47 -2
  4. package/DOCS/CODEC_ARCHITECTURE.md +14 -5
  5. package/DOCS/DICTIONARY.md +13 -4
  6. package/DOCS/FILE_FORMATS.md +3 -2
  7. package/DOCS/JSON_APPROACHES_COMPARISON.md +306 -0
  8. package/DOCS/NAMED_PATTERNS.md +317 -0
  9. package/DOCS/PATTERNS.md +59 -11
  10. package/DOCS/REPL.md +24 -5
  11. package/DOCS/WEB_REPL_REQUIREMENTS.md +4 -2
  12. package/Examples/arithmetics.k +22 -17
  13. package/Examples/bnat.k +21 -11
  14. package/Examples/buda.k +37 -20
  15. package/Examples/byte.k +3 -1
  16. package/Examples/code.k +38 -39
  17. package/Examples/djson-btrie.k +414 -0
  18. package/Examples/djson-ptrie.k +663 -0
  19. package/Examples/djson.k +323 -0
  20. package/Examples/esc.k +1 -1
  21. package/Examples/karatsuba-mult.k +16 -13
  22. package/Examples/list.k +8 -4
  23. package/Examples/nat.k +17 -11
  24. package/Examples/poly.k +11 -3
  25. package/Examples/vhdl_to_k.k +43 -31
  26. package/README.md +42 -15
  27. package/Value.mjs +28 -3
  28. package/backend-api.mjs +5 -1
  29. package/browser/repl-browser.mjs +6 -0
  30. package/browser/tree-view.mjs +82 -24
  31. package/browser/vfs-data.mjs +21 -12
  32. package/codecs/README.md +3 -0
  33. package/codecs/djson-btrie.mjs +560 -0
  34. package/codecs/djson-ptrie.mjs +587 -0
  35. package/codecs/djson.mjs +270 -0
  36. package/codecs/int.mjs +2 -1
  37. package/codecs/json.mjs +7 -2
  38. package/codecs/k-parse.mjs +7 -1
  39. package/codecs/k-print.mjs +4 -0
  40. package/codecs/runtime/codec-sdk.mjs +16 -2
  41. package/codecs/runtime/pattern-json.mjs +80 -1
  42. package/codecs/runtime/prefix-codec.mjs +629 -67
  43. package/codecs/runtime/show-value.mjs +93 -51
  44. package/codecs/runtime/unicode-string.mjs +29 -6
  45. package/codecs/show.mjs +4 -0
  46. package/codecs/utf16.mjs +2 -1
  47. package/core.k +79 -115
  48. package/index.mjs +3 -0
  49. package/named-patterns.mjs +267 -0
  50. package/object.mjs +31 -3
  51. package/package.json +5 -1
  52. package/parser.jison +68 -5
  53. package/parser.mjs +111 -60
  54. package/pretty.mjs +5 -0
  55. package/repl.mjs +245 -28
  56. package/scripts/build-repl-html.mjs +76 -5
  57. package/scripts/perf-djson.mjs +1142 -0
  58. package/scripts/run-tests.mjs +5 -0
  59. package/symbol-table.mjs +20 -2
  60. package/tests/bench-djson-tries.mjs +340 -0
  61. package/tests/test-codecs.mjs +211 -0
  62. package/tests/test-named-patterns.mjs +189 -0
  63. package/tests/test-repl-html.mjs +52 -4
  64. package/tests/test-repl.mjs +223 -0
@@ -0,0 +1,270 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Dynamic JSON (djson) codec with universal singleton pattern.
5
+ *
6
+ * Translates arbitrary JSON data to/from self-describing enveloped k
7
+ * values using a single recursive AST pattern:
8
+ *
9
+ * JSON = <
10
+ * float64 number,
11
+ * str string,
12
+ * <{} true, {} false> bool,
13
+ * {} null,
14
+ * <{} nil, {JSON car, LIST cdr} cons> = LIST list,
15
+ * <{} nil, {{str key, JSON val} car, OBJ cdr} cons> = OBJ object
16
+ * >
17
+ */
18
+
19
+ import {
20
+ Value,
21
+ isProduct,
22
+ isVariant,
23
+ withPattern,
24
+ patternFromFilter,
25
+ runCodecCLI
26
+ } from "./runtime/codec-sdk.mjs";
27
+ import { encodeNumberToValue, decodeValueToNumber } from "./ieee.mjs";
28
+ import {
29
+ textToStringValue,
30
+ stringValueToText
31
+ } from "./runtime/unicode-string.mjs";
32
+
33
+ export const doc = `
34
+ Dynamic JSON (djson) codec with universal singleton pattern.
35
+
36
+ Translates any JSON value to/from self-describing enveloped k values
37
+ conforming to the universal recursive JSON AST type:
38
+ JSON = <
39
+ float64 number,
40
+ str string,
41
+ <{} true, {} false> bool,
42
+ {} null,
43
+ <{} nil, {JSON car, LIST cdr} cons> = LIST list,
44
+ <{} nil, {{str key, JSON val} car, OBJ cdr} cons> = OBJ object
45
+ >
46
+ `;
47
+
48
+ export const DJSON_PATTERN = patternFromFilter(`
49
+ ? <
50
+ {
51
+ < {} "+", {} "-" > sign,
52
+ {
53
+ < {} 0, {} 1 >=Bit 0,
54
+ Bit 1, Bit 2, Bit 3, Bit 4, Bit 5,
55
+ Bit 6, Bit 7, Bit 8, Bit 9, Bit 10
56
+ } exponent,
57
+ {
58
+ Bit 0, Bit 1, Bit 2, Bit 3, Bit 4, Bit 5,
59
+ Bit 6, Bit 7, Bit 8, Bit 9, Bit 10, Bit 11,
60
+ Bit 12, Bit 13, Bit 14, Bit 15, Bit 16, Bit 17,
61
+ Bit 18, Bit 19, Bit 20, Bit 21, Bit 22, Bit 23,
62
+ Bit 24, Bit 25, Bit 26, Bit 27, Bit 28, Bit 29,
63
+ Bit 30, Bit 31, Bit 32, Bit 33, Bit 34, Bit 35,
64
+ Bit 36, Bit 37, Bit 38, Bit 39, Bit 40, Bit 41,
65
+ Bit 42, Bit 43, Bit 44, Bit 45, Bit 46, Bit 47,
66
+ Bit 48, Bit 49, Bit 50, Bit 51
67
+ } fraction
68
+ } number,
69
+
70
+ <
71
+ {} nil,
72
+ {
73
+ <
74
+ {
75
+ < {} 0, {} 1 >=Bit7 0,
76
+ Bit7 1, Bit7 2, Bit7 3, Bit7 4, Bit7 5, Bit7 6
77
+ }=Bits7 ascii,
78
+ {
79
+ <
80
+ { Bit7 0, Bit7 1, Bit7 2 }=Bits3 h08_0F,
81
+ Bits7 h10_7F,
82
+ Bits7 h80_CF,
83
+ Bits3 hD0_D7,
84
+ {} hF9,
85
+ { Bit7 0 }=Bits1 hFA_FB,
86
+ Bits1 hFC_FD,
87
+ Bits1 hFE_FF
88
+ > hi,
89
+ {
90
+ Bit7 0, Bit7 1, Bit7 2, Bit7 3,
91
+ Bit7 4, Bit7 5, Bit7 6, Bit7 7
92
+ }=Bits8 lo
93
+ } bmp_common,
94
+ {
95
+ <
96
+ { Bit7 0, Bit7 1, Bit7 2, Bit7 3 } hE0_EF,
97
+ Bits3 hF0_F7,
98
+ {} hF8
99
+ > hi,
100
+ Bits8 lo
101
+ } bmp_private_use,
102
+ {
103
+ Bit7 0, Bit7 1, Bit7 2, Bit7 3, Bit7 4, Bit7 5,
104
+ Bit7 6, Bit7 7, Bit7 8, Bit7 9, Bit7 10
105
+ } plane0,
106
+ { Bits8 mid, Bits8 lo } supplementary_plane1,
107
+ {
108
+ <
109
+ Bits1 p02_03,
110
+ { Bit7 0, Bit7 1 } p04_07,
111
+ Bits3 p08_0F,
112
+ {} p10
113
+ > plane,
114
+ Bits8 mid,
115
+ Bits8 lo
116
+ } supplementary_planes2_16
117
+ > car,
118
+ Str cdr
119
+ } cons
120
+ > = Str string,
121
+
122
+ < {} true, {} false > bool,
123
+ {} null,
124
+
125
+ < {} nil, { JSON car, LIST cdr } cons > = LIST list,
126
+
127
+ < {} nil, { { Str key, JSON val } car, OBJ cdr } cons > = OBJ object
128
+ > = JSON
129
+ `);
130
+
131
+ const UNIT = Value.product({});
132
+
133
+ function requireProduct(value, where) {
134
+ if (!isProduct(value)) {
135
+ throw new Error(`${where}: expected Product`);
136
+ }
137
+ return value.product;
138
+ }
139
+
140
+ function listToK(items) {
141
+ let result = Value.variant("nil", UNIT);
142
+ for (let i = items.length - 1; i >= 0; i--) {
143
+ result = Value.variant("cons", Value.product({
144
+ car: items[i],
145
+ cdr: result
146
+ }));
147
+ }
148
+ return result;
149
+ }
150
+
151
+ function listFromK(value, where) {
152
+ const result = [];
153
+ let current = value;
154
+ while (true) {
155
+ if (!isVariant(current)) {
156
+ throw new Error(`${where}: expected list variant (nil or cons)`);
157
+ }
158
+ if (current.tag === "nil") {
159
+ requireProduct(current.value, `${where}.nil`);
160
+ return result;
161
+ }
162
+ if (current.tag !== "cons") {
163
+ throw new Error(
164
+ `${where}: expected nil or cons, got ${current.tag}`
165
+ );
166
+ }
167
+ const pair = requireProduct(current.value, `${where}.cons`);
168
+ result.push(pair.car);
169
+ current = pair.cdr;
170
+ }
171
+ }
172
+
173
+ export function fromJsonValue(value) {
174
+ if (value === null) {
175
+ return Value.variant("null", UNIT);
176
+ }
177
+ if (typeof value === "boolean") {
178
+ return Value.variant(
179
+ "bool",
180
+ Value.variant(value ? "true" : "false", UNIT)
181
+ );
182
+ }
183
+ if (typeof value === "number") {
184
+ if (!Number.isFinite(value)) {
185
+ throw new Error("JSON numbers must be finite");
186
+ }
187
+ return Value.variant("number", encodeNumberToValue(value));
188
+ }
189
+ if (typeof value === "string") {
190
+ return Value.variant("string", textToStringValue(value));
191
+ }
192
+ if (Array.isArray(value)) {
193
+ return Value.variant("list", listToK(value.map(fromJsonValue)));
194
+ }
195
+ if (value && typeof value === "object") {
196
+ const keys = Object.keys(value).sort();
197
+ const entries = keys.map((k) =>
198
+ Value.product({
199
+ key: textToStringValue(k),
200
+ val: fromJsonValue(value[k])
201
+ })
202
+ );
203
+ return Value.variant("object", listToK(entries));
204
+ }
205
+ throw new Error(`Unsupported JSON value: ${value}`);
206
+ }
207
+
208
+ export function toJsonValue(value) {
209
+ if (!isVariant(value)) {
210
+ throw new Error("djson.value: expected top-level Variant");
211
+ }
212
+
213
+ if (value.tag === "null") {
214
+ requireProduct(value.value, "djson.null");
215
+ return null;
216
+ }
217
+
218
+ if (value.tag === "bool") {
219
+ if (!isVariant(value.value)) {
220
+ throw new Error("djson.bool: expected variant");
221
+ }
222
+ if (value.value.tag === "true") {
223
+ requireProduct(value.value.value, "djson.bool.true");
224
+ return true;
225
+ }
226
+ if (value.value.tag === "false") {
227
+ requireProduct(value.value.value, "djson.bool.false");
228
+ return false;
229
+ }
230
+ throw new Error(`djson.bool: invalid tag ${value.value.tag}`);
231
+ }
232
+
233
+ if (value.tag === "number") {
234
+ return decodeValueToNumber(value.value);
235
+ }
236
+
237
+ if (value.tag === "string") {
238
+ return stringValueToText(value.value);
239
+ }
240
+
241
+ if (value.tag === "list") {
242
+ const items = listFromK(value.value, "djson.list");
243
+ return items.map(toJsonValue);
244
+ }
245
+
246
+ if (value.tag === "object") {
247
+ const pairs = listFromK(value.value, "djson.object");
248
+ const result = {};
249
+ for (const pairValue of pairs) {
250
+ const pair = requireProduct(pairValue, "djson.object.pair");
251
+ const key = stringValueToText(pair.key);
252
+ result[key] = toJsonValue(pair.val);
253
+ }
254
+ return result;
255
+ }
256
+
257
+ throw new Error(`Unsupported djson tag: ${value.tag}`);
258
+ }
259
+
260
+ export function parse(text) {
261
+ const json = JSON.parse(text);
262
+ const value = fromJsonValue(json);
263
+ return withPattern(value, DJSON_PATTERN);
264
+ }
265
+
266
+ export function print(value) {
267
+ return JSON.stringify(toJsonValue(value));
268
+ }
269
+
270
+ runCodecCLI(import.meta.url, { parse, print, doc });
package/codecs/int.mjs CHANGED
@@ -14,7 +14,8 @@ export const doc = `
14
14
  Decimal integers and integer lists.
15
15
 
16
16
  Syntax:
17
- Decimal integer (e.g. 42, -15) or bracketed integer list (e.g. [0, 1, 2], []).
17
+ Decimal integer (e.g. 42, -15) or bracketed integer list
18
+ (e.g. [0, 1, 2], []).
18
19
  `;
19
20
 
20
21
  // Closed pattern for $ int = < bits '+', bits '-' >
package/codecs/json.mjs CHANGED
@@ -4,9 +4,14 @@ import { withPattern, runCodecCLI } from "./runtime/codec-sdk.mjs";
4
4
  import { fromJsonValue, toJsonValue, patternFromJsonValue } from "./json-codec.mjs";
5
5
 
6
6
  export const doc = `
7
- JSON codec (RFC 8259).
7
+ Schema-specializing JSON codec (RFC 8259).
8
8
 
9
- Translates JSON data to/from self-describing enveloped k values.
9
+ Translates JSON data to/from self-describing enveloped k values where
10
+ each JSON document derives a document-specific pattern:
11
+ - objects map to products { field: value, ... }
12
+ - arrays map to indexed products { "0": a0, "1": a1, ... }
13
+ - numbers, strings, booleans, and null map to canonical k values
14
+ (See djson.mjs for the universal singleton-pattern dynamic JSON codec.)
10
15
  `;
11
16
 
12
17
  export function parse(text) {
@@ -82,7 +82,13 @@ async function main() {
82
82
  return exit(1);
83
83
  }
84
84
 
85
- const inputBuffer = valueFile ? fs.readFileSync(valueFile) : await readAll(stdin);
85
+ stdout.on("error", (err) => {
86
+ if (err.code === "EPIPE") exit(0);
87
+ });
88
+
89
+ const inputBuffer = valueFile
90
+ ? fs.readFileSync(valueFile)
91
+ : await readAll(stdin);
86
92
  const inputText = inputBuffer.toString("utf8");
87
93
  const value = parseValue(inputText, null, null);
88
94
 
@@ -60,6 +60,10 @@ async function main() {
60
60
  }
61
61
  }
62
62
 
63
+ stdout.on("error", (err) => {
64
+ if (err.code === "EPIPE") process.exit(0);
65
+ });
66
+
63
67
  const input = fileArg ? fs.createReadStream(fileArg) : stdin;
64
68
  const buffer = await readAll(input);
65
69
  const { pattern, value } = decodeWire(buffer);
@@ -3,8 +3,17 @@
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { stdin, stdout, stderr, exit } from "node:process";
6
- import { Value, isProduct, isVariant, withPattern } from "../../Value.mjs";
7
- import { decodeWire, encodeToWire } from "./prefix-codec.mjs";
6
+ import {
7
+ Value,
8
+ isProduct,
9
+ isVariant,
10
+ withPattern
11
+ } from "../../Value.mjs";
12
+ import {
13
+ decodeWire,
14
+ decodeWireLazy,
15
+ encodeToWire
16
+ } from "./prefix-codec.mjs";
8
17
  import { isMainEntrypoint } from "./cli-entry.mjs";
9
18
  import { patternFromFilter } from "./pattern-k.mjs";
10
19
 
@@ -53,6 +62,10 @@ function runCodecCLI(metaUrl, codec) {
53
62
 
54
63
  const mode = args[0];
55
64
 
65
+ stdout.on("error", (err) => {
66
+ if (err.code === "EPIPE") exit(0);
67
+ });
68
+
56
69
  (async () => {
57
70
  if (mode === "--parse") {
58
71
  let parsed;
@@ -92,6 +105,7 @@ export {
92
105
  withPattern,
93
106
  encodeToWire,
94
107
  decodeWire,
108
+ decodeWireLazy,
95
109
  isMainEntrypoint,
96
110
  runCodecCLI,
97
111
  patternFromFilter
@@ -80,17 +80,96 @@ function propertyListToPattern(propertyList) {
80
80
  throw new Error("Pattern node 'any' cannot have outgoing edges");
81
81
  }
82
82
 
83
+ const tagIndex = Object.create(null);
84
+ for (let i = 0; i < edges.length; i++) {
85
+ tagIndex[edges[i].label] = i;
86
+ }
87
+ const choiceBits = edges.length <= 1
88
+ ? 0
89
+ : Math.ceil(Math.log2(edges.length));
83
90
  return {
84
91
  kind,
85
- edges: edges.map((edge) => ({ ...edge, symbolId: symbolIds.get(edge.label) }))
92
+ edges: edges.map((edge) => ({
93
+ ...edge,
94
+ symbolId: symbolIds.get(edge.label)
95
+ })),
96
+ tagIndex,
97
+ choiceBits
86
98
  };
87
99
  });
88
100
 
101
+ computePatternBitWidths(nodes);
102
+
89
103
  return {
90
104
  dictionary,
91
105
  nodes
92
106
  };
93
107
  }
94
108
 
109
+ function computePatternBitWidths(nodes) {
110
+ let changed = true;
111
+ while (changed) {
112
+ changed = false;
113
+ for (let i = 0; i < nodes.length; i++) {
114
+ const node = nodes[i];
115
+ if (node.fixedBitWidth !== undefined) continue;
116
+
117
+ if (node.kind === NAME_TO_NODE_KIND["closed-product"]) {
118
+ if (node.edges.length === 0) {
119
+ node.fixedBitWidth = 0;
120
+ node.staticOffsets = [];
121
+ changed = true;
122
+ } else {
123
+ let allKnown = true;
124
+ let sum = 0;
125
+ const offsets = new Array(node.edges.length);
126
+ for (let j = 0; j < node.edges.length; j++) {
127
+ const target = nodes[node.edges[j].target];
128
+ if (target.fixedBitWidth === undefined) {
129
+ allKnown = false;
130
+ break;
131
+ }
132
+ offsets[j] = sum;
133
+ sum += target.fixedBitWidth;
134
+ }
135
+ if (allKnown) {
136
+ node.fixedBitWidth = sum;
137
+ node.staticOffsets = offsets;
138
+ changed = true;
139
+ }
140
+ }
141
+ } else if (node.kind === NAME_TO_NODE_KIND["closed-union"]) {
142
+ if (node.edges.length > 0) {
143
+ const firstTarget = nodes[node.edges[0].target];
144
+ const w = firstTarget.fixedBitWidth;
145
+ if (w !== undefined) {
146
+ let allSame = true;
147
+ for (let j = 1; j < node.edges.length; j++) {
148
+ if (nodes[node.edges[j].target].fixedBitWidth !== w) {
149
+ allSame = false;
150
+ break;
151
+ }
152
+ }
153
+ if (allSame) {
154
+ node.fixedBitWidth = node.choiceBits + w;
155
+ changed = true;
156
+ }
157
+ }
158
+ }
159
+ }
160
+ }
161
+ }
162
+
163
+ for (let i = 0; i < nodes.length; i++) {
164
+ const edges = nodes[i].edges;
165
+ for (let j = 0; j < edges.length; j++) {
166
+ const target = nodes[edges[j].target];
167
+ if (target.fixedBitWidth !== undefined) {
168
+ edges[j].fixedTargetBitWidth = target.fixedBitWidth;
169
+ }
170
+ }
171
+ }
172
+ }
173
+
95
174
  export { patternToPropertyList, propertyListToPattern, NODE_KIND_TO_NAME, NAME_TO_NODE_KIND };
96
175
  export default { patternToPropertyList, propertyListToPattern, NODE_KIND_TO_NAME, NAME_TO_NODE_KIND };