@fraczak/k 6.8.0 → 6.10.1
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 +40 -0
- package/CLAUDE.md +1 -1
- package/DOCS/BIG_PICTURE.md +3 -5
- package/DOCS/CODECS.md +80 -181
- package/DOCS/CODEC_ARCHITECTURE.md +194 -0
- package/DOCS/REPL.md +69 -51
- package/Examples/arithmetics.k +1 -0
- package/Examples/poly.k +46 -0
- package/backends/llvm/runtime/krt.c +4 -0
- package/backends/llvm/src/kvm2llvm.mjs +2 -2
- package/backends/llvm/tests/test-llvm.mjs +19 -0
- package/backends/wasm/src/kvm2wasm.mjs +4 -0
- package/backends/wasm/tests/test-wasm.mjs +4 -0
- package/browser/repl-browser.mjs +131 -779
- 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 +177 -281
- package/scripts/build-repl-html.mjs +114 -385
- 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 +163 -68
- package/tests/test-repl.mjs +210 -156
- 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/repl-codecs.mjs
CHANGED
|
@@ -6,27 +6,7 @@ import { encodeToWire, decodeWire } from "./codecs/runtime/prefix-codec.mjs";
|
|
|
6
6
|
import { deriveClosedPattern } from "./codecs/runtime/codec.mjs";
|
|
7
7
|
import { patternToPropertyList } from "./codecs/runtime/pattern-json.mjs";
|
|
8
8
|
import { finalize } from "./codes.mjs";
|
|
9
|
-
import * as intCodec from "./codecs/int.mjs";
|
|
10
|
-
import * as utf8Codec from "./codecs/utf8.mjs";
|
|
11
|
-
import * as jsonCodec from "./codecs/json.mjs";
|
|
12
|
-
import * as ieeeCodec from "./codecs/ieee.mjs";
|
|
13
|
-
import * as unitCodec from "./codecs/unit.mjs";
|
|
14
|
-
import { patternFromJsonValue } from "./codecs/json-codec.mjs";
|
|
15
9
|
|
|
16
|
-
const BUILTIN_CODECS = {
|
|
17
|
-
int: intCodec,
|
|
18
|
-
utf8: utf8Codec,
|
|
19
|
-
json: jsonCodec,
|
|
20
|
-
ieee: ieeeCodec,
|
|
21
|
-
unit: unitCodec
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const UNIVERSAL_CODE = "*";
|
|
25
|
-
const NAME_RE = /^[a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*$/;
|
|
26
|
-
|
|
27
|
-
function isCanonicalCodeName(name) {
|
|
28
|
-
return typeof name === "string" && name.startsWith("@");
|
|
29
|
-
}
|
|
30
10
|
|
|
31
11
|
function closedPatternToCodeHash(pattern) {
|
|
32
12
|
if (!Array.isArray(pattern) || pattern.length === 0) {
|
|
@@ -73,17 +53,10 @@ function codeHashToPattern(codeHash, findCode) {
|
|
|
73
53
|
return patternToPropertyList(deriveClosedPattern(codeHash, code, resolveType));
|
|
74
54
|
}
|
|
75
55
|
|
|
76
|
-
function valueForPattern(value, pattern = null
|
|
56
|
+
function valueForPattern(value, pattern = null) {
|
|
77
57
|
if (pattern) {
|
|
78
58
|
return decodeWire(encodeToWire(value, pattern)).value;
|
|
79
59
|
}
|
|
80
|
-
if (codecName === "json" && rawInput != null) {
|
|
81
|
-
try {
|
|
82
|
-
const json = JSON.parse(rawInput);
|
|
83
|
-
const jsonPattern = patternFromJsonValue(json);
|
|
84
|
-
return decodeWire(encodeToWire(value, jsonPattern)).value;
|
|
85
|
-
} catch {}
|
|
86
|
-
}
|
|
87
60
|
return decodeWire(encodeToWire(value)).value;
|
|
88
61
|
}
|
|
89
62
|
|
|
@@ -92,35 +65,27 @@ function valueForCode(value, codeHash, findCode) {
|
|
|
92
65
|
return valueForPattern(value, pattern);
|
|
93
66
|
}
|
|
94
67
|
|
|
95
|
-
function
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const codeHash = closedPatternToCodeHash(pattern);
|
|
103
|
-
if (!codeHash) throw new Error(`Codec '${name}' pattern ${index} must be a closed property-list pattern`);
|
|
104
|
-
return codeHash;
|
|
105
|
-
});
|
|
106
|
-
if (!name || typeof name !== "string") {
|
|
107
|
-
throw new Error("Codec module must export a string name");
|
|
108
|
-
}
|
|
109
|
-
if (!universal && (!Array.isArray(codes) || codes.length === 0)) {
|
|
110
|
-
throw new Error(`Codec '${name}' must export a non-empty codes array`);
|
|
111
|
-
}
|
|
112
|
-
if (codec.parse != null && typeof codec.parse !== "function") {
|
|
113
|
-
throw new Error(`Codec '${name}' parse export must be a function`);
|
|
114
|
-
}
|
|
115
|
-
if (codec.print != null && typeof codec.print !== "function") {
|
|
116
|
-
throw new Error(`Codec '${name}' print export must be a function`);
|
|
68
|
+
function ensureEnveloped(value, codec = null) {
|
|
69
|
+
if (!value || typeof value !== "object") return value;
|
|
70
|
+
if (value.pattern) return value;
|
|
71
|
+
if (codec?.pattern) {
|
|
72
|
+
try {
|
|
73
|
+
return decodeWire(encodeToWire(value, codec.pattern)).value;
|
|
74
|
+
} catch {}
|
|
117
75
|
}
|
|
76
|
+
try {
|
|
77
|
+
return decodeWire(encodeToWire(value)).value;
|
|
78
|
+
} catch {}
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function normalizeCodecModule(mod, name) {
|
|
83
|
+
const codec = mod.replCodec || mod.codec || mod.default || mod;
|
|
118
84
|
return {
|
|
119
85
|
name,
|
|
120
|
-
codes: universal ? [UNIVERSAL_CODE] : codes,
|
|
121
|
-
universal,
|
|
122
86
|
parse: codec.parse,
|
|
123
|
-
print: codec.print
|
|
87
|
+
print: codec.print,
|
|
88
|
+
pattern: codec.pattern
|
|
124
89
|
};
|
|
125
90
|
}
|
|
126
91
|
|
|
@@ -129,168 +94,77 @@ function codecStore(state) {
|
|
|
129
94
|
return state.codecs;
|
|
130
95
|
}
|
|
131
96
|
|
|
132
|
-
function
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
97
|
+
function registerCodec(state, rawCodec, filePath) {
|
|
98
|
+
const name = path.basename(filePath);
|
|
99
|
+
const codec = normalizeCodecModule(rawCodec, name);
|
|
100
|
+
const store = codecStore(state);
|
|
101
|
+
store[codec.name] = {
|
|
102
|
+
...codec,
|
|
103
|
+
source: filePath
|
|
104
|
+
};
|
|
105
|
+
return [{ name: codec.name, source: filePath }];
|
|
140
106
|
}
|
|
141
107
|
|
|
142
|
-
function
|
|
143
|
-
const registered = [];
|
|
108
|
+
function unregisterCodec(state, codecName) {
|
|
144
109
|
const store = codecStore(state);
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if (!universal && !isCanonicalCodeName(codeHash)) {
|
|
149
|
-
throw new Error(`Codec '${codec.name}' has non-canonical code '${codeHash}'`);
|
|
150
|
-
}
|
|
151
|
-
if (!store[codeHash]) store[codeHash] = [];
|
|
152
|
-
const entry = {
|
|
153
|
-
name: codec.name,
|
|
154
|
-
source,
|
|
155
|
-
universal,
|
|
156
|
-
parse: codec.parse,
|
|
157
|
-
print: codec.print
|
|
158
|
-
};
|
|
159
|
-
const existingIndex = store[codeHash].findIndex((existing) =>
|
|
160
|
-
existing.name === entry.name && existing.source === entry.source
|
|
161
|
-
);
|
|
162
|
-
if (existingIndex === -1) {
|
|
163
|
-
store[codeHash].push(entry);
|
|
164
|
-
} else {
|
|
165
|
-
store[codeHash][existingIndex] = entry;
|
|
166
|
-
}
|
|
167
|
-
if (!universal && isCanonicalCodeName(codeHash) && state?.typeAliases && codec.name && NAME_RE.test(codec.name)) {
|
|
168
|
-
state.typeAliases[codec.name] = codeHash;
|
|
169
|
-
}
|
|
170
|
-
registered.push({ codeHash, ...entry });
|
|
171
|
-
}
|
|
172
|
-
return registered;
|
|
110
|
+
if (!store[codecName]) return 0;
|
|
111
|
+
delete store[codecName];
|
|
112
|
+
return 1;
|
|
173
113
|
}
|
|
174
114
|
|
|
175
|
-
function
|
|
115
|
+
function listCodecs(state) {
|
|
176
116
|
const store = codecStore(state);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (remaining.length !== codecs.length) {
|
|
181
|
-
removedCount += codecs.length - remaining.length;
|
|
182
|
-
if (remaining.length === 0) {
|
|
183
|
-
delete store[codeHash];
|
|
184
|
-
} else {
|
|
185
|
-
store[codeHash] = remaining;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
return removedCount;
|
|
117
|
+
const entries = Object.values(store).sort((a, b) => a.name.localeCompare(b.name));
|
|
118
|
+
if (entries.length === 0) return "(none)";
|
|
119
|
+
return entries.map((c) => `${c.name}${c.source ? ` (${c.source})` : ""}`).join("\n");
|
|
190
120
|
}
|
|
191
121
|
|
|
192
|
-
function
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
if (BUILTIN_CODECS[base]) return { name: base, mod: BUILTIN_CODECS[base] };
|
|
196
|
-
return null;
|
|
122
|
+
function codecNames(state) {
|
|
123
|
+
const store = codecStore(state);
|
|
124
|
+
return Object.keys(store).sort();
|
|
197
125
|
}
|
|
198
126
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const codec = normalizeCodecModule(builtin.mod, builtin.name);
|
|
203
|
-
return registerCodec(state, codec, "built-in");
|
|
127
|
+
function resolveCodec(state, codecName, capability = null) {
|
|
128
|
+
if (!codecName) {
|
|
129
|
+
throw new Error("Codec name is required");
|
|
204
130
|
}
|
|
131
|
+
const store = codecStore(state);
|
|
132
|
+
const codec = store[codecName];
|
|
133
|
+
if (!codec) {
|
|
134
|
+
throw new Error(`Codec '${codecName}' is not loaded`);
|
|
135
|
+
}
|
|
136
|
+
if (capability && typeof codec[capability] !== "function") {
|
|
137
|
+
throw new Error(`Codec '${codecName}' does not support ${capability}`);
|
|
138
|
+
}
|
|
139
|
+
return codec;
|
|
140
|
+
}
|
|
205
141
|
|
|
206
|
-
|
|
207
|
-
let
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
} catch {}
|
|
213
|
-
|
|
214
|
-
const resolved = path.resolve(filePath);
|
|
215
|
-
if (!source && fs.existsSync(resolved)) {
|
|
142
|
+
async function loadCodecModule(state, filePath) {
|
|
143
|
+
let mod;
|
|
144
|
+
if (typeof process !== "undefined" && process?.versions?.node) {
|
|
145
|
+
const fullPath = path.resolve(filePath);
|
|
146
|
+
const url = pathToFileURL(fullPath);
|
|
216
147
|
try {
|
|
217
|
-
|
|
148
|
+
const stats = fs.statSync(fullPath);
|
|
149
|
+
if (stats?.mtimeMs) url.searchParams.set("mtime", String(stats.mtimeMs));
|
|
218
150
|
} catch {}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
151
|
+
mod = await import(url.href);
|
|
152
|
+
} else {
|
|
153
|
+
const rawSource = fs.readFileSync(filePath, "utf8");
|
|
154
|
+
const source = rawSource.replace(/^#![^\n]*\n/, "");
|
|
155
|
+
const blob = new Blob([source], { type: "text/javascript" });
|
|
156
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
223
157
|
try {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (stats?.mtimeMs) url.searchParams.set("mtime", String(stats.mtimeMs));
|
|
228
|
-
} catch {}
|
|
229
|
-
const mod = await import(url.href);
|
|
230
|
-
const codec = normalizeCodecModule(mod, path.basename(resolved, path.extname(resolved)));
|
|
231
|
-
return registerCodec(state, codec, resolved);
|
|
232
|
-
} catch (nodeErr) {
|
|
233
|
-
if (!source) throw nodeErr;
|
|
158
|
+
mod = await import(blobUrl);
|
|
159
|
+
} finally {
|
|
160
|
+
URL.revokeObjectURL(blobUrl);
|
|
234
161
|
}
|
|
235
162
|
}
|
|
236
163
|
|
|
237
|
-
|
|
238
|
-
throw new Error(`Cannot find codec file '${filePath}'`);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Universal data URL loader (works in browser & Node, and in VFS)
|
|
242
|
-
const sanitized = source.replace(
|
|
243
|
-
/import\s+[^;]*from\s+['"][^'"]*['"];?/g,
|
|
244
|
-
"const { Value, isProduct, isVariant, NODE_KIND } = globalThis;"
|
|
245
|
-
);
|
|
246
|
-
const dataUrl = "data:text/javascript;charset=utf-8," + encodeURIComponent(sanitized);
|
|
247
|
-
const mod = await import(dataUrl);
|
|
248
|
-
const codec = normalizeCodecModule(mod, path.basename(filePath, path.extname(filePath)));
|
|
249
|
-
return registerCodec(state, codec, filePath);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function listCodecs(state) {
|
|
253
|
-
const entries = Object.entries(codecStore(state))
|
|
254
|
-
.flatMap(([codeHash, codecs]) => codecs.map((codec) => ({ codeHash, ...codec })))
|
|
255
|
-
.sort((a, b) => a.name.localeCompare(b.name) || a.codeHash.localeCompare(b.codeHash));
|
|
256
|
-
if (entries.length === 0) return "(none)";
|
|
257
|
-
return entries.map((entry) => `${entry.name} ${entry.codeHash === UNIVERSAL_CODE ? "all" : entry.codeHash}${entry.source ? ` (${entry.source})` : ""}`).join("\n");
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function codecNames(state, codeHash = null) {
|
|
261
|
-
const store = codecStore(state);
|
|
262
|
-
const codecs = codeHash
|
|
263
|
-
? [...(store[codeHash] || []), ...(store[UNIVERSAL_CODE] || [])]
|
|
264
|
-
: Object.values(store).flat();
|
|
265
|
-
return [...new Set(codecs.map((codec) => codec.name))].sort();
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function resolveCodec(state, codeHash, codecName = null, capability = null) {
|
|
269
|
-
const store = codecStore(state);
|
|
270
|
-
const matchesCodec = (codec) =>
|
|
271
|
-
(codecName == null || codec.name === codecName) &&
|
|
272
|
-
(capability == null || typeof codec[capability] === "function");
|
|
273
|
-
const exactMatches = (codeHash ? store[codeHash] || [] : []).filter(matchesCodec);
|
|
274
|
-
const universalMatches = (store[UNIVERSAL_CODE] || []).filter(matchesCodec);
|
|
275
|
-
const matches = codecName == null && exactMatches.length > 0
|
|
276
|
-
? exactMatches
|
|
277
|
-
: [...exactMatches, ...universalMatches];
|
|
278
|
-
if (matches.length === 0) {
|
|
279
|
-
if (codecName) {
|
|
280
|
-
const namedMatches = Object.values(store).flat().filter(matchesCodec);
|
|
281
|
-
if (namedMatches.length > 0) return namedMatches[0];
|
|
282
|
-
}
|
|
283
|
-
const suffix = codecName ? ` named '${codecName}'` : "";
|
|
284
|
-
throw new Error(`No ${capability || "usable"} codec${suffix}${codeHash ? ` for ${codeHash}` : ""}`);
|
|
285
|
-
}
|
|
286
|
-
if (matches.length > 1 && codecName == null) {
|
|
287
|
-
throw new Error(`Multiple codecs for ${codeHash || "target"}; specify one of: ${matches.map((codec) => codec.name).join(", ")}`);
|
|
288
|
-
}
|
|
289
|
-
return matches[0];
|
|
164
|
+
return registerCodec(state, mod, filePath);
|
|
290
165
|
}
|
|
291
166
|
|
|
292
167
|
export {
|
|
293
|
-
BUILTIN_CODECS,
|
|
294
168
|
codecNames,
|
|
295
169
|
codeHashToPattern,
|
|
296
170
|
listCodecs,
|
|
@@ -300,8 +174,7 @@ export {
|
|
|
300
174
|
resolveCodec,
|
|
301
175
|
unregisterCodec,
|
|
302
176
|
closedPatternToCodeHash,
|
|
303
|
-
UNIVERSAL_CODE,
|
|
304
177
|
valueForCode,
|
|
305
178
|
valueForPattern,
|
|
306
|
-
|
|
179
|
+
ensureEnveloped
|
|
307
180
|
};
|