@fraczak/k 6.8.0 → 6.9.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 +10 -0
- package/DOCS/CODECS.md +80 -181
- package/DOCS/CODEC_ARCHITECTURE.md +194 -0
- package/DOCS/REPL.md +41 -39
- package/Examples/arithmetics.k +1 -0
- package/Examples/poly.k +46 -0
- package/browser/repl-browser.mjs +109 -782
- package/browser/shims/process.mjs +7 -0
- package/browser/tree-view.mjs +5 -2
- package/browser/vfs-data.mjs +14 -14
- package/codecs/README.md +7 -0
- package/codecs/ieee.mjs +50 -75
- package/codecs/int.mjs +83 -62
- package/codecs/json-codec.mjs +5 -77
- package/codecs/json.mjs +12 -56
- package/codecs/k-parse.mjs +3 -19
- package/codecs/runtime/codec-sdk.mjs +94 -0
- package/codecs/runtime/pattern-k.mjs +45 -0
- package/codecs/string-codec.mjs +48 -74
- package/codecs/unit.mjs +13 -61
- package/codecs/utf16.mjs +13 -56
- package/codecs/utf8.mjs +11 -55
- package/package.json +1 -1
- package/patterns/from-k.mjs +2 -22
- package/repl-codecs.mjs +69 -196
- package/repl.mjs +63 -187
- package/scripts/build-repl-html.mjs +87 -379
- package/scripts/run-tests.mjs +1 -0
- package/tests/test-codecs.mjs +186 -0
- package/tests/test-poly.mjs +44 -0
- package/tests/test-repl-html.mjs +123 -64
- package/tests/test-repl.mjs +120 -134
- package/tests/test-structural-values.mjs +17 -0
- package/tests/test-tree-view.mjs +3 -0
- package/codecs/example-pipeline.mjs +0 -163
- package/codecs/runtime/ieee-pattern.mjs +0 -30
- package/codecs/runtime/test-codec.mjs +0 -89
- package/codecs/runtime/typeFromValue.mjs +0 -34
package/codecs/README.md
CHANGED
|
@@ -79,6 +79,8 @@ notation for k values. That notation is documented in
|
|
|
79
79
|
[`../DOCS/TEXTUAL_VALUES.md`](../DOCS/TEXTUAL_VALUES.md). It is a readable
|
|
80
80
|
notation for k values, not a separate JSON container format.
|
|
81
81
|
|
|
82
|
+
For the codec architecture and specification, see
|
|
83
|
+
[`../DOCS/CODEC_ARCHITECTURE.md`](../DOCS/CODEC_ARCHITECTURE.md).
|
|
82
84
|
For the contributor-facing guide to writing a new external codec module, see
|
|
83
85
|
[`../DOCS/CODECS.md`](../DOCS/CODECS.md).
|
|
84
86
|
|
|
@@ -238,6 +240,10 @@ The new codec work is split into layers:
|
|
|
238
240
|
|
|
239
241
|
## Files
|
|
240
242
|
|
|
243
|
+
- [`../DOCS/CODEC_ARCHITECTURE.md`](../DOCS/CODEC_ARCHITECTURE.md):
|
|
244
|
+
comprehensive architecture specification for k codecs.
|
|
245
|
+
- [`../DOCS/CODECS.md`](../DOCS/CODECS.md):
|
|
246
|
+
practical guide to writing, running, and testing codecs.
|
|
241
247
|
- [`POLYMORPHIC_BINARY_FORMAT.md`](./POLYMORPHIC_BINARY_FORMAT.md):
|
|
242
248
|
main design document for the new pattern-plus-prefix-tree codec.
|
|
243
249
|
- [`BINARY_FORMAT.md`](./BINARY_FORMAT.md):
|
|
@@ -247,3 +253,4 @@ The new codec work is split into layers:
|
|
|
247
253
|
|
|
248
254
|
The active command-line pipeline emits and consumes the self-hosted binary
|
|
249
255
|
pattern+value stream.
|
|
256
|
+
|
package/codecs/ieee.mjs
CHANGED
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import { Value, isProduct, isVariant, withPattern, patternFromFilter, runCodecCLI } from "./runtime/codec-sdk.mjs";
|
|
4
|
+
|
|
5
|
+
export const doc = `
|
|
6
|
+
IEEE 754 64-bit float codec.
|
|
7
|
+
|
|
8
|
+
Parses decimal floats, Infinity, -Infinity, NaN to 64-bit IEEE products.
|
|
9
|
+
`;
|
|
10
|
+
|
|
11
|
+
const FLOAT64_PATTERN = patternFromFilter(`
|
|
12
|
+
$bit = < {} 0, {} 1 >;
|
|
13
|
+
$sign = < {} "+", {} "-" >;
|
|
14
|
+
$exp11 = {bit 0, bit 1, bit 2, bit 3, bit 4, bit 5, bit 6, bit 7, bit 8, bit 9, bit 10};
|
|
15
|
+
$frac52 = {bit 0, bit 1, bit 2, bit 3, bit 4, bit 5, bit 6, bit 7, bit 8, bit 9,
|
|
16
|
+
bit 10, bit 11, bit 12, bit 13, bit 14, bit 15, bit 16, bit 17, bit 18, bit 19,
|
|
17
|
+
bit 20, bit 21, bit 22, bit 23, bit 24, bit 25, bit 26, bit 27, bit 28, bit 29,
|
|
18
|
+
bit 30, bit 31, bit 32, bit 33, bit 34, bit 35, bit 36, bit 37, bit 38, bit 39,
|
|
19
|
+
bit 40, bit 41, bit 42, bit 43, bit 44, bit 45, bit 46, bit 47, bit 48, bit 49,
|
|
20
|
+
bit 50, bit 51};
|
|
21
|
+
$float64 = {sign sign, exp11 exponent, frac52 fraction};
|
|
22
|
+
?$float64
|
|
23
|
+
`);
|
|
24
|
+
|
|
8
25
|
const UNIT = Value.product({});
|
|
9
|
-
const name = "ieee";
|
|
10
|
-
const patterns = [FLOAT64_PATTERN];
|
|
11
|
-
|
|
12
|
-
function usage(stream = console.error) {
|
|
13
|
-
stream(`Usage: ${argv[1]} --parse | --print`);
|
|
14
|
-
stream(" --parse Read a float literal from stdin, write binary pattern+value stream.");
|
|
15
|
-
stream(" --print Read binary pattern+value stream from stdin, write a float literal.");
|
|
16
|
-
stream(" -h, --help Show this help.");
|
|
17
|
-
}
|
|
18
26
|
|
|
19
27
|
function bitValue(bit) {
|
|
20
28
|
return Value.variant(bit === 0 ? "0" : "1", UNIT);
|
|
@@ -29,10 +37,11 @@ function bitsProduct(width, value) {
|
|
|
29
37
|
return Value.product(product);
|
|
30
38
|
}
|
|
31
39
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
40
|
+
const floatView = new DataView(new ArrayBuffer(8));
|
|
41
|
+
|
|
42
|
+
export function encodeNumberToValue(number) {
|
|
43
|
+
floatView.setFloat64(0, number, false);
|
|
44
|
+
const bits = floatView.getBigUint64(0, false);
|
|
36
45
|
const sign = Number((bits >> 63n) & 1n);
|
|
37
46
|
const exponent = Number((bits >> 52n) & 0x7ffn);
|
|
38
47
|
const fraction = bits & ((1n << 52n) - 1n);
|
|
@@ -71,7 +80,7 @@ function parseBitsProduct(value, maxBit, where) {
|
|
|
71
80
|
return n;
|
|
72
81
|
}
|
|
73
82
|
|
|
74
|
-
function decodeValueToNumber(value) {
|
|
83
|
+
export function decodeValueToNumber(value) {
|
|
75
84
|
const product = requireProduct(value, "float64");
|
|
76
85
|
const sign = requireVariant(product.sign, "float64.sign");
|
|
77
86
|
if (sign.tag !== "+" && sign.tag !== "-") {
|
|
@@ -85,9 +94,8 @@ function decodeValueToNumber(value) {
|
|
|
85
94
|
(exponent << 52n) |
|
|
86
95
|
fraction;
|
|
87
96
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return buf.readDoubleBE(0);
|
|
97
|
+
floatView.setBigUint64(0, bits, false);
|
|
98
|
+
return floatView.getFloat64(0, false);
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
function parseFloatText(text) {
|
|
@@ -98,68 +106,35 @@ function parseFloatText(text) {
|
|
|
98
106
|
|
|
99
107
|
if (/^[+-]?nan$/i.test(trimmed)) return NaN;
|
|
100
108
|
if (/^[+]?inf(inity)?$/i.test(trimmed)) return Infinity;
|
|
101
|
-
if (
|
|
109
|
+
if (/^[-](inf|infinity)$/i.test(trimmed)) return -Infinity;
|
|
102
110
|
|
|
103
|
-
const
|
|
104
|
-
if (Number.isNaN(
|
|
105
|
-
throw new Error(`Invalid
|
|
111
|
+
const n = Number(trimmed);
|
|
112
|
+
if (Number.isNaN(n)) {
|
|
113
|
+
throw new Error(`Invalid float literal ${JSON.stringify(trimmed)}`);
|
|
106
114
|
}
|
|
107
|
-
return
|
|
115
|
+
return n;
|
|
108
116
|
}
|
|
109
117
|
|
|
110
|
-
function printFloatText(
|
|
111
|
-
if (Number.isNaN(
|
|
112
|
-
if (
|
|
113
|
-
if (
|
|
114
|
-
if (Object.is(
|
|
115
|
-
return String(
|
|
118
|
+
function printFloatText(n) {
|
|
119
|
+
if (Number.isNaN(n)) return "NaN";
|
|
120
|
+
if (n === Infinity) return "Infinity";
|
|
121
|
+
if (n === -Infinity) return "-Infinity";
|
|
122
|
+
if (Object.is(n, -0)) return "-0";
|
|
123
|
+
return String(n);
|
|
116
124
|
}
|
|
117
125
|
|
|
118
|
-
function parse(text) {
|
|
119
|
-
|
|
126
|
+
export function parse(text) {
|
|
127
|
+
const n = parseFloatText(text);
|
|
128
|
+
const value = encodeNumberToValue(n);
|
|
129
|
+
return withPattern(value, FLOAT64_PATTERN);
|
|
120
130
|
}
|
|
121
131
|
|
|
122
|
-
function print(value) {
|
|
132
|
+
export function print(value) {
|
|
123
133
|
return printFloatText(decodeValueToNumber(value));
|
|
124
134
|
}
|
|
125
135
|
|
|
126
|
-
|
|
127
|
-
return new Promise((resolve, reject) => {
|
|
128
|
-
const chunks = [];
|
|
129
|
-
stream.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
|
|
130
|
-
stream.on("end", () => resolve(Buffer.concat(chunks)));
|
|
131
|
-
stream.on("error", reject);
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
async function main() {
|
|
136
|
-
const args = argv.slice(2);
|
|
137
|
-
if (args.includes("-h") || args.includes("--help")) {
|
|
138
|
-
usage(console.log);
|
|
139
|
-
exit(0);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (args.length !== 1 || (args[0] !== "--parse" && args[0] !== "--print")) {
|
|
143
|
-
usage();
|
|
144
|
-
exit(1);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const buf = await readAll(stdin);
|
|
148
|
-
|
|
149
|
-
if (args[0] === "--parse") {
|
|
150
|
-
const value = encodeNumberToValue(parseFloatText(buf.toString("utf8")));
|
|
151
|
-
stdout.write(encodeToWire(value, FLOAT64_PATTERN));
|
|
152
|
-
} else {
|
|
153
|
-
const { value } = decodeWire(buf);
|
|
154
|
-
stdout.write(`${printFloatText(decodeValueToNumber(value))}\n`);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (isMainEntrypoint(import.meta.url, argv[1])) {
|
|
159
|
-
main().catch((error) => {
|
|
160
|
-
console.error(error.message || String(error));
|
|
161
|
-
exit(1);
|
|
162
|
-
});
|
|
163
|
-
}
|
|
136
|
+
runCodecCLI(import.meta.url, { parse, print, doc });
|
|
164
137
|
|
|
165
|
-
export {
|
|
138
|
+
export {
|
|
139
|
+
FLOAT64_PATTERN
|
|
140
|
+
};
|
package/codecs/int.mjs
CHANGED
|
@@ -1,48 +1,31 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* int: parse/print integers
|
|
4
|
+
* int: parse/print integers and integer lists as enveloped k values.
|
|
5
5
|
*
|
|
6
6
|
* $ bits = < {} _, bits 0, bits 1 >;
|
|
7
7
|
* $ int = < bits '+', bits '-' >;
|
|
8
|
-
*
|
|
9
|
-
* Bits are stored MSB-outermost (remove_leading_zeros strips outermost 0s).
|
|
10
|
-
*
|
|
11
|
-
* Usage:
|
|
12
|
-
* echo "-21" | int.mjs --parse # decimal → binary pattern+value stream
|
|
13
|
-
* <wire> | int.mjs --print # binary pattern+value stream → decimal
|
|
8
|
+
* list = < {} nil, {int car, list cdr} cons >;
|
|
14
9
|
*/
|
|
15
10
|
|
|
16
|
-
import {
|
|
17
|
-
import { Value, isVariant } from "../Value.mjs";
|
|
18
|
-
import { isMainEntrypoint } from "./runtime/cli-entry.mjs";
|
|
19
|
-
import { decodeWire, encodeToWire } from "./runtime/prefix-codec.mjs";
|
|
11
|
+
import { Value, isProduct, isVariant, withPattern, patternFromFilter, runCodecCLI } from "./runtime/codec-sdk.mjs";
|
|
20
12
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
13
|
+
export const doc = `
|
|
14
|
+
Decimal integers and integer lists.
|
|
15
|
+
|
|
16
|
+
Syntax:
|
|
17
|
+
Decimal integer (e.g. 42, -15) or bracketed integer list (e.g. [0, 1, 2], []).
|
|
18
|
+
`;
|
|
27
19
|
|
|
28
20
|
// Closed pattern for $ int = < bits '+', bits '-' >
|
|
29
21
|
// with $ bits = < {} _, bits 0, bits 1 >
|
|
30
|
-
const INT_PATTERN =
|
|
31
|
-
["closed-union", [["+", 1], ["-", 1]]],
|
|
32
|
-
["closed-union", [["0", 1], ["1", 1], ["_", 2]]],
|
|
33
|
-
["closed-product", []]
|
|
34
|
-
];
|
|
35
|
-
const name = "int";
|
|
36
|
-
const patterns = [INT_PATTERN];
|
|
22
|
+
const INT_PATTERN = patternFromFilter('?< <bits 0, bits 1, {} _>=bits "+", bits "-">');
|
|
37
23
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
stream.on("error", reject);
|
|
44
|
-
});
|
|
45
|
-
}
|
|
24
|
+
// Closed pattern for list of int:
|
|
25
|
+
// list = < {} nil, {int car, list cdr} cons >
|
|
26
|
+
const INT_LIST_PATTERN = patternFromFilter(
|
|
27
|
+
'$ bits = < bits 0, bits 1, {} _ >; $ int = < bits "+", bits "-" >; ?< {} nil, { $int car, list cdr } cons > = list'
|
|
28
|
+
);
|
|
46
29
|
|
|
47
30
|
// Build bits Value (MSB outermost) from a non-negative BigInt.
|
|
48
31
|
function buildBits(n) {
|
|
@@ -54,7 +37,7 @@ function buildBits(n) {
|
|
|
54
37
|
return v;
|
|
55
38
|
}
|
|
56
39
|
|
|
57
|
-
// Parse a decimal integer string
|
|
40
|
+
// Parse a decimal integer string into a k int Value.
|
|
58
41
|
function parseIntStr(str) {
|
|
59
42
|
str = str.trim();
|
|
60
43
|
let sign = "+";
|
|
@@ -64,12 +47,44 @@ function parseIntStr(str) {
|
|
|
64
47
|
throw new Error(`Invalid integer syntax: ${JSON.stringify(str)}`);
|
|
65
48
|
}
|
|
66
49
|
const n = BigInt(str);
|
|
67
|
-
if (n === 0n) sign = "+";
|
|
50
|
+
if (n === 0n) sign = "+";
|
|
68
51
|
return Value.variant(sign, buildBits(n));
|
|
69
52
|
}
|
|
70
53
|
|
|
71
|
-
//
|
|
72
|
-
function
|
|
54
|
+
// Parse a list of decimal integers, e.g. "[0,1,2]" or "[]"
|
|
55
|
+
function parseIntListStr(str) {
|
|
56
|
+
str = str.trim();
|
|
57
|
+
if (!str.startsWith("[") || !str.endsWith("]")) {
|
|
58
|
+
throw new Error(`Invalid integer list syntax: ${JSON.stringify(str)}`);
|
|
59
|
+
}
|
|
60
|
+
const inner = str.slice(1, -1).trim();
|
|
61
|
+
if (inner === "") {
|
|
62
|
+
return Value.variant("nil", Value.product({}));
|
|
63
|
+
}
|
|
64
|
+
const parts = inner.split(",");
|
|
65
|
+
const items = parts.map((part) => parseIntStr(part));
|
|
66
|
+
let result = Value.variant("nil", Value.product({}));
|
|
67
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
68
|
+
result = Value.variant("cons", Value.product({
|
|
69
|
+
car: items[i],
|
|
70
|
+
cdr: result
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Parse a decimal integer string or integer list into an enveloped k Value.
|
|
77
|
+
export function parse(str) {
|
|
78
|
+
str = str.trim();
|
|
79
|
+
const raw = str.startsWith("[") ? parseIntListStr(str) : parseIntStr(str);
|
|
80
|
+
const pattern = isVariant(raw) && (raw.tag === "nil" || raw.tag === "cons")
|
|
81
|
+
? INT_LIST_PATTERN
|
|
82
|
+
: INT_PATTERN;
|
|
83
|
+
return withPattern(raw, pattern);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Walk a decoded int Value and return a decimal string.
|
|
87
|
+
function printSingleInt(value) {
|
|
73
88
|
if (!isVariant(value) || (value.tag !== "+" && value.tag !== "-")) {
|
|
74
89
|
throw new Error("Not a valid k int value");
|
|
75
90
|
}
|
|
@@ -80,42 +95,48 @@ function printIntValue(value) {
|
|
|
80
95
|
bits += node.tag;
|
|
81
96
|
node = node.value;
|
|
82
97
|
}
|
|
83
|
-
// bits is MSB-first binary string (may be empty for zero represented as "0")
|
|
84
98
|
const n = bits === "" ? 0n : BigInt("0b" + bits);
|
|
85
99
|
const digits = n.toString(10);
|
|
86
100
|
if (n === 0n) return "0";
|
|
87
101
|
return (sign === "-" ? "-" : "") + digits;
|
|
88
102
|
}
|
|
89
103
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
104
|
+
// Walk a decoded int list Value and return a bracketed string like "[0,1,2]".
|
|
105
|
+
function printIntList(value) {
|
|
106
|
+
const items = [];
|
|
107
|
+
let node = value;
|
|
108
|
+
while (isVariant(node) && node.tag === "cons") {
|
|
109
|
+
const product = isProduct(node.value) ? node.value.product : null;
|
|
110
|
+
if (!product || !product.car || !product.cdr) {
|
|
111
|
+
throw new Error("Invalid list cons node");
|
|
112
|
+
}
|
|
113
|
+
items.push(printSingleInt(product.car));
|
|
114
|
+
node = product.cdr;
|
|
95
115
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
usage();
|
|
99
|
-
exit(1);
|
|
116
|
+
if (!isVariant(node) || node.tag !== "nil") {
|
|
117
|
+
throw new Error("Not a valid k int list: does not terminate in nil");
|
|
100
118
|
}
|
|
119
|
+
return `[${items.join(",")}]`;
|
|
120
|
+
}
|
|
101
121
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
122
|
+
// Walk a decoded int or int list Value and return a formatted string.
|
|
123
|
+
export function print(value) {
|
|
124
|
+
if (isVariant(value)) {
|
|
125
|
+
if (value.tag === "nil" || value.tag === "cons") {
|
|
126
|
+
return printIntList(value);
|
|
127
|
+
}
|
|
128
|
+
if (value.tag === "+" || value.tag === "-") {
|
|
129
|
+
return printSingleInt(value);
|
|
130
|
+
}
|
|
111
131
|
}
|
|
132
|
+
throw new Error("Not a valid k int or int list value");
|
|
112
133
|
}
|
|
113
134
|
|
|
114
|
-
|
|
115
|
-
main().catch(err => {
|
|
116
|
-
console.error(err.message || String(err));
|
|
117
|
-
exit(1);
|
|
118
|
-
});
|
|
119
|
-
}
|
|
135
|
+
runCodecCLI(import.meta.url, { parse, print, doc });
|
|
120
136
|
|
|
121
|
-
export {
|
|
137
|
+
export {
|
|
138
|
+
INT_PATTERN,
|
|
139
|
+
INT_LIST_PATTERN,
|
|
140
|
+
parseIntStr,
|
|
141
|
+
parseIntListStr
|
|
142
|
+
};
|
package/codecs/json-codec.mjs
CHANGED
|
@@ -1,44 +1,11 @@
|
|
|
1
1
|
import { Value, isProduct, isVariant } from "../Value.mjs";
|
|
2
|
+
import { patternFromFilter } from "./runtime/codec-sdk.mjs";
|
|
2
3
|
import { STRING_PATTERN_PROPERTY_LIST, textToStringValue, stringValueToText } from "./string-codec.mjs";
|
|
3
|
-
import { FLOAT64_PATTERN } from "./
|
|
4
|
+
import { FLOAT64_PATTERN, encodeNumberToValue, decodeValueToNumber } from "./ieee.mjs";
|
|
4
5
|
|
|
5
6
|
const UNIT = Value.product({});
|
|
6
|
-
const BOOL_PATTERN =
|
|
7
|
-
|
|
8
|
-
["closed-product", []]
|
|
9
|
-
];
|
|
10
|
-
const NULL_PATTERN = [
|
|
11
|
-
["closed-union", [["null", 1]]],
|
|
12
|
-
["closed-product", []]
|
|
13
|
-
];
|
|
14
|
-
|
|
15
|
-
function bitValue(bit) {
|
|
16
|
-
return Value.variant(bit === 0 ? "0" : "1", UNIT);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function bitsProduct(width, value) {
|
|
20
|
-
const big = BigInt(value);
|
|
21
|
-
const product = {};
|
|
22
|
-
for (let i = width - 1; i >= 0; i--) {
|
|
23
|
-
product[String(i)] = bitValue(Number((big >> BigInt(i)) & 1n));
|
|
24
|
-
}
|
|
25
|
-
return Value.product(product);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function encodeNumberToValue(number) {
|
|
29
|
-
const buf = Buffer.alloc(8);
|
|
30
|
-
buf.writeDoubleBE(number, 0);
|
|
31
|
-
const bits = buf.readBigUInt64BE(0);
|
|
32
|
-
const sign = Number((bits >> 63n) & 1n);
|
|
33
|
-
const exponent = Number((bits >> 52n) & 0x7ffn);
|
|
34
|
-
const fraction = bits & ((1n << 52n) - 1n);
|
|
35
|
-
|
|
36
|
-
return Value.product({
|
|
37
|
-
sign: Value.variant(sign === 0 ? "+" : "-", UNIT),
|
|
38
|
-
exponent: bitsProduct(11, exponent),
|
|
39
|
-
fraction: bitsProduct(52, fraction)
|
|
40
|
-
});
|
|
41
|
-
}
|
|
7
|
+
const BOOL_PATTERN = patternFromFilter("?< {} false, {} true >");
|
|
8
|
+
const NULL_PATTERN = patternFromFilter("?< {} null >");
|
|
42
9
|
|
|
43
10
|
function requireProduct(value, where) {
|
|
44
11
|
if (!isProduct(value)) {
|
|
@@ -47,45 +14,6 @@ function requireProduct(value, where) {
|
|
|
47
14
|
return value.product;
|
|
48
15
|
}
|
|
49
16
|
|
|
50
|
-
function requireVariant(value, where) {
|
|
51
|
-
if (!isVariant(value)) {
|
|
52
|
-
throw new Error(`${where}: expected Variant`);
|
|
53
|
-
}
|
|
54
|
-
return value;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function parseBitsProduct(value, maxBit, where) {
|
|
58
|
-
const product = requireProduct(value, where);
|
|
59
|
-
let n = 0n;
|
|
60
|
-
for (let i = maxBit; i >= 0; i--) {
|
|
61
|
-
const entry = requireVariant(product[String(i)], `${where}.bit${i}`);
|
|
62
|
-
if (entry.tag !== "0" && entry.tag !== "1") {
|
|
63
|
-
throw new Error(`${where}.bit${i}: expected tag 0 or 1`);
|
|
64
|
-
}
|
|
65
|
-
n = (n << 1n) | BigInt(entry.tag === "1" ? 1 : 0);
|
|
66
|
-
}
|
|
67
|
-
return n;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function decodeValueToNumber(value) {
|
|
71
|
-
const product = requireProduct(value, "json.number");
|
|
72
|
-
const sign = requireVariant(product.sign, "json.number.sign");
|
|
73
|
-
if (sign.tag !== "+" && sign.tag !== "-") {
|
|
74
|
-
throw new Error(`json.number.sign: expected + or -, got ${sign.tag}`);
|
|
75
|
-
}
|
|
76
|
-
const exponent = parseBitsProduct(product.exponent, 10, "json.number.exponent");
|
|
77
|
-
const fraction = parseBitsProduct(product.fraction, 51, "json.number.fraction");
|
|
78
|
-
|
|
79
|
-
const bits =
|
|
80
|
-
(BigInt(sign.tag === "-" ? 1 : 0) << 63n) |
|
|
81
|
-
(exponent << 52n) |
|
|
82
|
-
fraction;
|
|
83
|
-
|
|
84
|
-
const buf = Buffer.alloc(8);
|
|
85
|
-
buf.writeBigUInt64BE(bits, 0);
|
|
86
|
-
return buf.readDoubleBE(0);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
17
|
function fromJsonValue(value) {
|
|
90
18
|
if (value === null) {
|
|
91
19
|
return Value.variant("null", UNIT);
|
|
@@ -153,7 +81,7 @@ function patternFromJsonValue(value) {
|
|
|
153
81
|
return composePattern(
|
|
154
82
|
"closed-product",
|
|
155
83
|
Object.keys(value)
|
|
156
|
-
.sort((a, b) =>
|
|
84
|
+
.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))
|
|
157
85
|
.map((key) => [key, patternFromJsonValue(value[key])])
|
|
158
86
|
);
|
|
159
87
|
}
|
package/codecs/json.mjs
CHANGED
|
@@ -1,67 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { decodeWire, encodeToWire } from "./runtime/prefix-codec.mjs";
|
|
5
|
-
import { isMainEntrypoint } from "./runtime/cli-entry.mjs";
|
|
3
|
+
import { withPattern, runCodecCLI } from "./runtime/codec-sdk.mjs";
|
|
6
4
|
import { fromJsonValue, toJsonValue, patternFromJsonValue } from "./json-codec.mjs";
|
|
7
5
|
|
|
8
|
-
const
|
|
9
|
-
|
|
6
|
+
export const doc = `
|
|
7
|
+
JSON codec (RFC 8259).
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
stream(" --parse Read JSON from stdin, write binary pattern+value stream.");
|
|
14
|
-
stream(" --print Read binary pattern+value stream from stdin, write JSON.");
|
|
15
|
-
stream(" -h, --help Show this help.");
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function readAll(stream) {
|
|
19
|
-
return new Promise((resolve, reject) => {
|
|
20
|
-
const chunks = [];
|
|
21
|
-
stream.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
|
|
22
|
-
stream.on("end", () => resolve(Buffer.concat(chunks)));
|
|
23
|
-
stream.on("error", reject);
|
|
24
|
-
});
|
|
25
|
-
}
|
|
9
|
+
Translates JSON data to/from self-describing enveloped k values.
|
|
10
|
+
`;
|
|
26
11
|
|
|
27
|
-
function parse(text) {
|
|
28
|
-
|
|
12
|
+
export function parse(text) {
|
|
13
|
+
const json = JSON.parse(text);
|
|
14
|
+
const value = fromJsonValue(json);
|
|
15
|
+
const pattern = patternFromJsonValue(json);
|
|
16
|
+
return withPattern(value, pattern);
|
|
29
17
|
}
|
|
30
18
|
|
|
31
|
-
function print(value) {
|
|
19
|
+
export function print(value) {
|
|
32
20
|
return JSON.stringify(toJsonValue(value));
|
|
33
21
|
}
|
|
34
22
|
|
|
35
|
-
|
|
36
|
-
const args = argv.slice(2);
|
|
37
|
-
if (args.includes("-h") || args.includes("--help")) {
|
|
38
|
-
usage(console.log);
|
|
39
|
-
exit(0);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (args.length !== 1 || (args[0] !== "--parse" && args[0] !== "--print")) {
|
|
43
|
-
usage();
|
|
44
|
-
exit(1);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const buf = await readAll(stdin);
|
|
48
|
-
|
|
49
|
-
if (args[0] === "--parse") {
|
|
50
|
-
const text = buf.toString("utf8");
|
|
51
|
-
const json = JSON.parse(text);
|
|
52
|
-
const value = fromJsonValue(json);
|
|
53
|
-
stdout.write(encodeToWire(value, patternFromJsonValue(json)));
|
|
54
|
-
} else {
|
|
55
|
-
const { value } = decodeWire(buf);
|
|
56
|
-
stdout.write(`${print(value)}\n`);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (isMainEntrypoint(import.meta.url, argv[1])) {
|
|
61
|
-
main().catch((error) => {
|
|
62
|
-
console.error(error.stack || error.message || String(error));
|
|
63
|
-
exit(1);
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export { name, universal, parse, print };
|
|
23
|
+
runCodecCLI(import.meta.url, { parse, print, doc });
|
package/codecs/k-parse.mjs
CHANGED
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import { argv, stdin, exit, stdout } from "node:process";
|
|
5
|
-
import k from "../index.mjs";
|
|
6
5
|
import { parseValue } from "../valueIO.mjs";
|
|
7
6
|
import { isMainEntrypoint } from "./runtime/cli-entry.mjs";
|
|
8
|
-
import {
|
|
9
|
-
import { patternToPropertyList } from "./runtime/pattern-json.mjs";
|
|
7
|
+
import { patternFromFilter } from "./runtime/pattern-k.mjs";
|
|
10
8
|
import { encodeToWire } from "./runtime/prefix-codec.mjs";
|
|
11
9
|
|
|
12
10
|
function usage(prog) {
|
|
@@ -32,20 +30,6 @@ function readAll(stream) {
|
|
|
32
30
|
});
|
|
33
31
|
}
|
|
34
32
|
|
|
35
|
-
function propertyListFromScript(script) {
|
|
36
|
-
const annotated = k.annotate(script);
|
|
37
|
-
const mainRel = annotated.rels.__main__;
|
|
38
|
-
if (!mainRel || !mainRel.typePatternGraph) {
|
|
39
|
-
throw new Error("Could not resolve __main__ relation");
|
|
40
|
-
}
|
|
41
|
-
if (mainRel.def.op !== "filter" && mainRel.def.op !== "code") {
|
|
42
|
-
throw new Error("Input script must end with a filter or a type name");
|
|
43
|
-
}
|
|
44
|
-
const rootPatternId = mainRel.typePatternGraph.find(mainRel.def.patterns[0]);
|
|
45
|
-
const pattern = exportPatternGraph(mainRel.typePatternGraph, rootPatternId);
|
|
46
|
-
return patternToPropertyList(pattern);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
33
|
async function main() {
|
|
50
34
|
const prog = argv[1];
|
|
51
35
|
const args = argv.slice(2);
|
|
@@ -82,8 +66,8 @@ async function main() {
|
|
|
82
66
|
const value = parseValue(inputText, null, null);
|
|
83
67
|
|
|
84
68
|
const propertyList = (() => {
|
|
85
|
-
if (inputTypeArg != null) return
|
|
86
|
-
if (inputPatternArg != null) return
|
|
69
|
+
if (inputTypeArg != null) return patternFromFilter(maybeReadFile(inputTypeArg));
|
|
70
|
+
if (inputPatternArg != null) return patternFromFilter(maybeReadFile(inputPatternArg));
|
|
87
71
|
return null;
|
|
88
72
|
})();
|
|
89
73
|
|