@fraczak/k 6.0.3 → 6.1.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/AGENTS.md +2 -2
- package/CHANGELOG.md +22 -5
- package/CLAUDE.md +4 -4
- package/DOCS/BIG_PICTURE.md +5 -5
- package/DOCS/BINARY_CODEC_STATUS_AND_TODO.md +3 -3
- package/DOCS/KIR_SPECIALIZATION_DESIGN.md +458 -0
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +135 -49
- package/DOCS/PATTERNS.md +6 -5
- package/DOCS/REPL.md +8 -6
- package/DOCS/book/06-values-in-memory.md +1 -1
- package/DOCS/book.md +20 -24
- package/README.md +200 -268
- package/codecs/README.md +21 -2
- package/codecs/example-pipeline.mjs +14 -0
- package/codecs/ieee.mjs +13 -3
- package/codecs/int.mjs +13 -3
- package/codecs/json.mjs +13 -3
- package/codecs/k-parse.mjs +5 -0
- package/codecs/k-print.mjs +16 -1
- package/codecs/show.mjs +15 -1
- package/codecs/unit.mjs +13 -3
- package/codecs/utf16.mjs +13 -3
- package/codecs/utf8.mjs +13 -3
- package/core.json +1 -0
- package/core.klib +0 -0
- package/export.mjs +1 -1
- package/ieee.klib +0 -0
- package/index.mjs +1 -1
- package/k-vscode/README.md +3 -2
- package/k.mjs +9 -1
- package/object.mjs +303 -62
- package/objects/README.md +29 -9
- package/objects/compile-lib.mjs +3 -2
- package/objects/compile.mjs +1 -0
- package/objects/decompile.mjs +1 -1
- package/objects/extract-aliases.mjs +1 -1
- package/p.json +5475 -0
- package/package.json +13 -7
- package/{repl2.mjs → repl.mjs} +45 -11
- package/run.mjs +172 -2
- package/test-k-object.mjs +58 -7
- package/{test-repl2.mjs → test-repl.mjs} +4 -4
package/object.mjs
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { annotate } from "./index.mjs";
|
|
2
|
-
import run from "./run.mjs";
|
|
2
|
+
import run, { run_rel, run_converged } from "./run.mjs";
|
|
3
3
|
import codes from "./codes.mjs";
|
|
4
4
|
import { TypePatternGraph } from "./TypePatternGraph.mjs";
|
|
5
5
|
import { patterns2filters, prettyRel } from "./pretty.mjs";
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
const LIB_MAGIC = Buffer.from([0x4b, 0x4c, 0x49, 0x42, 0x00, 0x01, 0x0a]);
|
|
7
|
+
const OBJECT_MAGIC = Buffer.from("KOBJ\n");
|
|
9
8
|
|
|
10
9
|
function serializeTypePatternGraph(typePatternGraph) {
|
|
11
10
|
return {
|
|
@@ -27,9 +26,11 @@ function hydrateTypePatternGraph(serialized) {
|
|
|
27
26
|
return graph;
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
function serializeRelation(rel) {
|
|
29
|
+
function serializeRelation(rel, typeDerivation = rel.typeDerivation) {
|
|
30
|
+
const def = stripDebugFieldsFromExp(stripRelationBoundaryFilters(rel.def, rel.typePatternGraph));
|
|
31
31
|
return {
|
|
32
|
-
|
|
32
|
+
def,
|
|
33
|
+
...(typeDerivation ? { typeDerivation } : {}),
|
|
33
34
|
typePatternGraph: serializeTypePatternGraph(rel.typePatternGraph)
|
|
34
35
|
};
|
|
35
36
|
}
|
|
@@ -42,9 +43,13 @@ function hydrateRelation(rel) {
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
function serializeAnnotated(annotated) {
|
|
46
|
+
const typeDerivations = buildRelTypeDerivations(annotated.rels, annotated.compileStats);
|
|
45
47
|
return {
|
|
46
48
|
rels: Object.fromEntries(
|
|
47
|
-
Object.entries(annotated.rels).map(([name, rel]) => [
|
|
49
|
+
Object.entries(annotated.rels).map(([name, rel]) => [
|
|
50
|
+
name,
|
|
51
|
+
serializeRelation(rel, typeDerivations[name])
|
|
52
|
+
])
|
|
48
53
|
),
|
|
49
54
|
representatives: annotated.representatives,
|
|
50
55
|
relAlias: annotated.relAlias,
|
|
@@ -52,15 +57,6 @@ function serializeAnnotated(annotated) {
|
|
|
52
57
|
};
|
|
53
58
|
}
|
|
54
59
|
|
|
55
|
-
function hydrateAnnotated(serialized) {
|
|
56
|
-
return {
|
|
57
|
-
...serialized,
|
|
58
|
-
rels: Object.fromEntries(
|
|
59
|
-
Object.entries(serialized.rels).map(([name, rel]) => [name, hydrateRelation(rel)])
|
|
60
|
-
)
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
60
|
const NAME_RE = /^[a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*$/;
|
|
65
61
|
|
|
66
62
|
function isSourceName(name) {
|
|
@@ -86,6 +82,207 @@ function addOrigin(meta, hash, type, origin) {
|
|
|
86
82
|
meta[hash].origins.push(origin);
|
|
87
83
|
}
|
|
88
84
|
|
|
85
|
+
const TYPE_DERIVATION_STATUSES = new Set(["converged", "not-converged", "unknown"]);
|
|
86
|
+
|
|
87
|
+
function validTypeDerivationStatus(status) {
|
|
88
|
+
return TYPE_DERIVATION_STATUSES.has(status) ? status : "unknown";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function compileStatsByRelName(compileStats = {}) {
|
|
92
|
+
const byName = {};
|
|
93
|
+
for (const scc of compileStats.sccs || []) {
|
|
94
|
+
for (const member of scc.members || []) {
|
|
95
|
+
byName[member] = scc;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return byName;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function samePatternId(typePatternGraph, left, right) {
|
|
102
|
+
return typePatternGraph.find(left) === typePatternGraph.find(right);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function isBoundaryFilter(exp, typePatternGraph, patternId) {
|
|
106
|
+
return exp?.op === "filter" &&
|
|
107
|
+
exp.patterns?.length === 2 &&
|
|
108
|
+
samePatternId(typePatternGraph, exp.patterns[0], patternId) &&
|
|
109
|
+
samePatternId(typePatternGraph, exp.patterns[1], patternId);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function stripRelationBoundaryFilters(exp, typePatternGraph = null) {
|
|
113
|
+
if (!exp) return exp;
|
|
114
|
+
if (typePatternGraph == null) {
|
|
115
|
+
return exp;
|
|
116
|
+
}
|
|
117
|
+
const [inputPattern, outputPattern] = exp.patterns || [];
|
|
118
|
+
if (inputPattern == null || outputPattern == null) return exp;
|
|
119
|
+
|
|
120
|
+
if (isBoundaryFilter(exp, typePatternGraph, inputPattern) && samePatternId(typePatternGraph, inputPattern, outputPattern)) {
|
|
121
|
+
return {
|
|
122
|
+
op: "identity",
|
|
123
|
+
patterns: [...exp.patterns],
|
|
124
|
+
...(exp.start ? { start: exp.start } : {}),
|
|
125
|
+
...(exp.end ? { end: exp.end } : {})
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (exp.op !== "comp") return exp;
|
|
130
|
+
let parts = exp.comp || [];
|
|
131
|
+
if (parts.length > 0 && isBoundaryFilter(parts[0], typePatternGraph, inputPattern)) {
|
|
132
|
+
parts = parts.slice(1);
|
|
133
|
+
}
|
|
134
|
+
if (parts.length > 0 && isBoundaryFilter(parts[parts.length - 1], typePatternGraph, outputPattern)) {
|
|
135
|
+
parts = parts.slice(0, -1);
|
|
136
|
+
}
|
|
137
|
+
if (parts.length === (exp.comp || []).length) return exp;
|
|
138
|
+
if (parts.length === 0) {
|
|
139
|
+
return {
|
|
140
|
+
op: "identity",
|
|
141
|
+
patterns: [...exp.patterns],
|
|
142
|
+
...(exp.start ? { start: exp.start } : {}),
|
|
143
|
+
...(exp.end ? { end: exp.end } : {})
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if (parts.length === 1) return { ...parts[0], patterns: [...exp.patterns] };
|
|
147
|
+
return { ...exp, comp: parts };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function stripDebugFieldsFromFilter(filter) {
|
|
151
|
+
if (!filter) return filter;
|
|
152
|
+
const { start, end, fields, ...rest } = filter;
|
|
153
|
+
if (!fields) return rest;
|
|
154
|
+
return {
|
|
155
|
+
...rest,
|
|
156
|
+
fields: Object.fromEntries(
|
|
157
|
+
Object.entries(fields).map(([label, child]) => [label, stripDebugFieldsFromFilter(child)])
|
|
158
|
+
)
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function stripDebugFieldsFromExp(exp) {
|
|
163
|
+
if (!exp) return exp;
|
|
164
|
+
const { start, end, ...rest } = exp;
|
|
165
|
+
switch (rest.op) {
|
|
166
|
+
case "filter":
|
|
167
|
+
return { ...rest, filter: stripDebugFieldsFromFilter(rest.filter) };
|
|
168
|
+
case "comp":
|
|
169
|
+
return { ...rest, comp: rest.comp.map(stripDebugFieldsFromExp) };
|
|
170
|
+
case "union":
|
|
171
|
+
return { ...rest, union: rest.union.map(stripDebugFieldsFromExp) };
|
|
172
|
+
case "product":
|
|
173
|
+
return {
|
|
174
|
+
...rest,
|
|
175
|
+
product: rest.product.map(({ label, exp: child }) => ({
|
|
176
|
+
label,
|
|
177
|
+
exp: stripDebugFieldsFromExp(child)
|
|
178
|
+
}))
|
|
179
|
+
};
|
|
180
|
+
default:
|
|
181
|
+
return rest;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function stripDebugFieldsFromCode(code) {
|
|
186
|
+
if (!code) return code;
|
|
187
|
+
const { start, end, ...rest } = code;
|
|
188
|
+
return rest;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function stripDebugFieldsFromCodes(codeTable) {
|
|
192
|
+
return Object.fromEntries(
|
|
193
|
+
Object.entries(codeTable || {}).map(([hash, code]) => [hash, stripDebugFieldsFromCode(code)])
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function firstFilterSourceRange(filter) {
|
|
198
|
+
if (!filter) return null;
|
|
199
|
+
if (filter.start || filter.end) {
|
|
200
|
+
return {
|
|
201
|
+
...(filter.start ? { start: filter.start } : {}),
|
|
202
|
+
...(filter.end ? { end: filter.end } : {})
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
for (const child of Object.values(filter.fields || {})) {
|
|
206
|
+
const range = firstFilterSourceRange(child);
|
|
207
|
+
if (range) return range;
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function firstExpSourceRange(exp) {
|
|
213
|
+
if (!exp) return null;
|
|
214
|
+
if (exp.start || exp.end) {
|
|
215
|
+
return {
|
|
216
|
+
...(exp.start ? { start: exp.start } : {}),
|
|
217
|
+
...(exp.end ? { end: exp.end } : {})
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
switch (exp.op) {
|
|
221
|
+
case "filter":
|
|
222
|
+
return firstFilterSourceRange(exp.filter);
|
|
223
|
+
case "comp":
|
|
224
|
+
for (const child of exp.comp) {
|
|
225
|
+
const range = firstExpSourceRange(child);
|
|
226
|
+
if (range) return range;
|
|
227
|
+
}
|
|
228
|
+
return null;
|
|
229
|
+
case "union":
|
|
230
|
+
for (const child of exp.union) {
|
|
231
|
+
const range = firstExpSourceRange(child);
|
|
232
|
+
if (range) return range;
|
|
233
|
+
}
|
|
234
|
+
return null;
|
|
235
|
+
case "product":
|
|
236
|
+
for (const { exp: child } of exp.product) {
|
|
237
|
+
const range = firstExpSourceRange(child);
|
|
238
|
+
if (range) return range;
|
|
239
|
+
}
|
|
240
|
+
return null;
|
|
241
|
+
default:
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function originFromSourceNode(source, name, compiledAt, node) {
|
|
247
|
+
return {
|
|
248
|
+
source,
|
|
249
|
+
name,
|
|
250
|
+
compiledAt,
|
|
251
|
+
...(node?.start ? { start: node.start } : {}),
|
|
252
|
+
...(node?.end ? { end: node.end } : {})
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function relationOrigin(source, name, compiledAt, rel) {
|
|
257
|
+
const printableDef = stripRelationBoundaryFilters(rel.def, rel.typePatternGraph);
|
|
258
|
+
const range = firstExpSourceRange(printableDef);
|
|
259
|
+
return {
|
|
260
|
+
source,
|
|
261
|
+
name,
|
|
262
|
+
compiledAt,
|
|
263
|
+
...(range?.start ? { start: range.start } : {}),
|
|
264
|
+
...(range?.end ? { end: range.end } : {})
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function buildRelTypeDerivation(rel, sccStats) {
|
|
269
|
+
const existing = rel?.typeDerivation || {};
|
|
270
|
+
const status = sccStats
|
|
271
|
+
? (sccStats.converged ? "converged" : "not-converged")
|
|
272
|
+
: validTypeDerivationStatus(existing.status);
|
|
273
|
+
return { status };
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function buildRelTypeDerivations(rels, compileStats) {
|
|
277
|
+
const statsByName = compileStatsByRelName(compileStats);
|
|
278
|
+
return Object.fromEntries(
|
|
279
|
+
Object.entries(rels || {}).map(([name, rel]) => [
|
|
280
|
+
name,
|
|
281
|
+
buildRelTypeDerivation(rel, statsByName[name])
|
|
282
|
+
])
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
89
286
|
function mergeMetaEntries(...metas) {
|
|
90
287
|
const merged = {};
|
|
91
288
|
for (const meta of metas) {
|
|
@@ -94,11 +291,7 @@ function mergeMetaEntries(...metas) {
|
|
|
94
291
|
if (type !== "code" && type !== "rel") continue;
|
|
95
292
|
for (const origin of entry.origins || []) {
|
|
96
293
|
if (!origin?.name) continue;
|
|
97
|
-
addOrigin(merged, hash, type, {
|
|
98
|
-
...(origin.source == null ? {} : { source: origin.source }),
|
|
99
|
-
name: origin.name,
|
|
100
|
-
...(origin.compiledAt == null ? {} : { compiledAt: origin.compiledAt })
|
|
101
|
-
});
|
|
294
|
+
addOrigin(merged, hash, type, { ...origin });
|
|
102
295
|
}
|
|
103
296
|
}
|
|
104
297
|
}
|
|
@@ -113,13 +306,20 @@ function buildMeta(annotated, source) {
|
|
|
113
306
|
|
|
114
307
|
for (const [name, hash] of Object.entries(representatives)) {
|
|
115
308
|
if (!isSourceName(name)) continue;
|
|
116
|
-
addOrigin(meta, hash, "code",
|
|
309
|
+
addOrigin(meta, hash, "code", originFromSourceNode(
|
|
310
|
+
source,
|
|
311
|
+
name,
|
|
312
|
+
now,
|
|
313
|
+
annotated.sourceDefs?.codes?.[name]
|
|
314
|
+
));
|
|
117
315
|
}
|
|
118
316
|
|
|
119
317
|
for (const [name, hash] of Object.entries(relAlias)) {
|
|
120
|
-
|
|
121
|
-
if (!
|
|
122
|
-
|
|
318
|
+
const rel = annotated.rels?.[name];
|
|
319
|
+
if (!rel) continue;
|
|
320
|
+
if (name === "__main__" || isSourceName(name)) {
|
|
321
|
+
addOrigin(meta, hash, "rel", relationOrigin(source, name, now, rel));
|
|
322
|
+
}
|
|
123
323
|
}
|
|
124
324
|
return meta;
|
|
125
325
|
}
|
|
@@ -196,7 +396,7 @@ function collectReachable(mainName, rels, allCodes) {
|
|
|
196
396
|
const rel = rels[relName];
|
|
197
397
|
if (rel.typePatternGraph) {
|
|
198
398
|
const codeId = rel.typePatternGraph.codeId || (rel.typePatternGraph.codeId);
|
|
199
|
-
if (codeId) Object.
|
|
399
|
+
if (codeId) Object.keys(codeId).forEach(walkCode);
|
|
200
400
|
}
|
|
201
401
|
}
|
|
202
402
|
|
|
@@ -205,7 +405,7 @@ function collectReachable(mainName, rels, allCodes) {
|
|
|
205
405
|
|
|
206
406
|
function compileObject(script, options = {}) {
|
|
207
407
|
const annotated = annotate(script, options);
|
|
208
|
-
const allCodes = codes.dump();
|
|
408
|
+
const allCodes = stripDebugFieldsFromCodes(codes.dump());
|
|
209
409
|
const serialized = serializeAnnotated(annotated);
|
|
210
410
|
const meta = mergeMetaEntries(
|
|
211
411
|
importedLibraryMeta(options.libraries),
|
|
@@ -220,13 +420,15 @@ function compileObject(script, options = {}) {
|
|
|
220
420
|
const prunedRels = Object.fromEntries(
|
|
221
421
|
Object.entries(serialized.rels).filter(([name]) => reachableRels.has(name))
|
|
222
422
|
);
|
|
423
|
+
const reachableRelHashes = new Set(
|
|
424
|
+
[...reachableRels].map((name) => serialized.relAlias?.[name] || name)
|
|
425
|
+
);
|
|
223
426
|
const prunedMeta = Object.fromEntries(
|
|
224
|
-
Object.entries(meta).filter(([h]) => reachableRels.has(h) || reachableCodes.has(h))
|
|
427
|
+
Object.entries(meta).filter(([h]) => reachableRels.has(h) || reachableRelHashes.has(h) || reachableCodes.has(h))
|
|
225
428
|
);
|
|
226
429
|
|
|
227
430
|
return {
|
|
228
431
|
format: "k-object",
|
|
229
|
-
version: 2,
|
|
230
432
|
codes: prunedCodes,
|
|
231
433
|
rels: prunedRels,
|
|
232
434
|
relAlias: serialized.relAlias,
|
|
@@ -260,6 +462,7 @@ function rewriteRefsToCanonical(exp, relAlias) {
|
|
|
260
462
|
|
|
261
463
|
function compileLibrary(script, options = {}) {
|
|
262
464
|
const annotated = annotateLibrary(script, options);
|
|
465
|
+
const allCodes = stripDebugFieldsFromCodes(codes.dump());
|
|
263
466
|
const serialized = serializeAnnotated(annotated);
|
|
264
467
|
const meta = mergeMetaEntries(
|
|
265
468
|
importedLibraryMeta(options.libraries),
|
|
@@ -278,15 +481,21 @@ function compileLibrary(script, options = {}) {
|
|
|
278
481
|
def: rewriteRefsToCanonical(rel.def, relAlias)
|
|
279
482
|
};
|
|
280
483
|
}
|
|
484
|
+
const libraryMetaHashes = new Set([
|
|
485
|
+
...Object.keys(allCodes),
|
|
486
|
+
...Object.keys(libRels)
|
|
487
|
+
]);
|
|
488
|
+
const libMeta = Object.fromEntries(
|
|
489
|
+
Object.entries(meta).filter(([hash]) => libraryMetaHashes.has(hash))
|
|
490
|
+
);
|
|
281
491
|
|
|
282
492
|
return {
|
|
283
493
|
format: "k-object",
|
|
284
|
-
|
|
285
|
-
codes: codes.dump(),
|
|
494
|
+
codes: allCodes,
|
|
286
495
|
rels: libRels,
|
|
287
496
|
relAlias: serialized.relAlias,
|
|
288
497
|
compileStats: serialized.compileStats,
|
|
289
|
-
meta,
|
|
498
|
+
meta: libMeta,
|
|
290
499
|
main: null
|
|
291
500
|
};
|
|
292
501
|
}
|
|
@@ -295,15 +504,6 @@ function hydrateObject(object) {
|
|
|
295
504
|
if (object?.format !== "k-object") {
|
|
296
505
|
throw new Error("Unsupported k object file");
|
|
297
506
|
}
|
|
298
|
-
// v1 compat: defs wrapper
|
|
299
|
-
if (object.version === 1) {
|
|
300
|
-
codes.load(object.codes);
|
|
301
|
-
return {
|
|
302
|
-
...object,
|
|
303
|
-
defs: hydrateAnnotated(object.defs)
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
// v2: flat rels
|
|
307
507
|
codes.load(object.codes);
|
|
308
508
|
const hydratedRels = Object.fromEntries(
|
|
309
509
|
Object.entries(object.rels).map(([name, rel]) => [name, hydrateRelation(rel)])
|
|
@@ -320,25 +520,23 @@ function loadLibrary(object) {
|
|
|
320
520
|
}
|
|
321
521
|
|
|
322
522
|
function isHydratedObject(object) {
|
|
323
|
-
// v2 format
|
|
324
523
|
if (object?.rels) {
|
|
325
524
|
const firstRel = Object.values(object.rels)[0];
|
|
326
525
|
return firstRel?.typePatternGraph instanceof TypePatternGraph;
|
|
327
526
|
}
|
|
328
|
-
|
|
329
|
-
return object?.defs?.rels?.[object.main]?.typePatternGraph instanceof TypePatternGraph;
|
|
527
|
+
return false;
|
|
330
528
|
}
|
|
331
529
|
|
|
332
530
|
function encodeObject(object) {
|
|
333
|
-
|
|
531
|
+
if (object.main == null) return encodeLibrary(object);
|
|
334
532
|
const payload = Buffer.from(JSON.stringify(object), "utf8");
|
|
335
533
|
if (payload.length > 0xffffffff) {
|
|
336
534
|
throw new Error("k object payload is too large");
|
|
337
535
|
}
|
|
338
536
|
|
|
339
|
-
const header = Buffer.alloc(
|
|
340
|
-
|
|
341
|
-
header.writeUInt32BE(payload.length,
|
|
537
|
+
const header = Buffer.alloc(OBJECT_MAGIC.length + 4);
|
|
538
|
+
OBJECT_MAGIC.copy(header, 0);
|
|
539
|
+
header.writeUInt32BE(payload.length, OBJECT_MAGIC.length);
|
|
342
540
|
return Buffer.concat([header, payload]);
|
|
343
541
|
}
|
|
344
542
|
|
|
@@ -346,15 +544,16 @@ function decodeObject(buffer) {
|
|
|
346
544
|
if (!Buffer.isBuffer(buffer)) {
|
|
347
545
|
throw new Error("k object input must be a Buffer");
|
|
348
546
|
}
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
547
|
+
const trimmed = buffer.toString("utf8").trimStart();
|
|
548
|
+
if (trimmed.startsWith("{")) {
|
|
549
|
+
return hydrateObject(JSON.parse(trimmed));
|
|
550
|
+
}
|
|
551
|
+
if (buffer.length < OBJECT_MAGIC.length || !buffer.subarray(0, OBJECT_MAGIC.length).equals(OBJECT_MAGIC)) {
|
|
352
552
|
throw new Error("Invalid k object file header");
|
|
353
553
|
}
|
|
354
554
|
|
|
355
|
-
const
|
|
356
|
-
const
|
|
357
|
-
const payloadStart = magic.length + 4;
|
|
555
|
+
const length = buffer.readUInt32BE(OBJECT_MAGIC.length);
|
|
556
|
+
const payloadStart = OBJECT_MAGIC.length + 4;
|
|
358
557
|
const payloadEnd = payloadStart + length;
|
|
359
558
|
if (payloadEnd !== buffer.length) {
|
|
360
559
|
throw new Error("Invalid k object file length");
|
|
@@ -364,7 +563,7 @@ function decodeObject(buffer) {
|
|
|
364
563
|
}
|
|
365
564
|
|
|
366
565
|
function encodeLibrary(object) {
|
|
367
|
-
return
|
|
566
|
+
return Buffer.from(JSON.stringify(object), "utf8");
|
|
368
567
|
}
|
|
369
568
|
|
|
370
569
|
function decodeLibrary(buffer) {
|
|
@@ -380,20 +579,19 @@ function compileObjectBuffer(script, options = {}) {
|
|
|
380
579
|
}
|
|
381
580
|
|
|
382
581
|
function compileLibraryBuffer(script, options = {}) {
|
|
383
|
-
return
|
|
582
|
+
return encodeLibrary(compileLibrary(script, options));
|
|
384
583
|
}
|
|
385
584
|
|
|
386
585
|
function getRels(hydrated) {
|
|
387
|
-
|
|
388
|
-
return hydrated.rels || hydrated.defs?.rels;
|
|
586
|
+
return hydrated.rels;
|
|
389
587
|
}
|
|
390
588
|
|
|
391
589
|
function runObject(object, value) {
|
|
392
590
|
const hydrated = isHydratedObject(object) ? object : hydrateObject(object);
|
|
393
591
|
const rels = getRels(hydrated);
|
|
394
|
-
run.defs =
|
|
592
|
+
run.defs = { rels };
|
|
395
593
|
const mainRel = rels[hydrated.main];
|
|
396
|
-
return
|
|
594
|
+
return run_rel(codes.find, mainRel, value, hydrated.main);
|
|
397
595
|
}
|
|
398
596
|
|
|
399
597
|
function objectToFunction(object) {
|
|
@@ -401,6 +599,29 @@ function objectToFunction(object) {
|
|
|
401
599
|
return (value) => runObject(hydrated, value);
|
|
402
600
|
}
|
|
403
601
|
|
|
602
|
+
function assertRelConverged(rel, name) {
|
|
603
|
+
const status = rel?.typeDerivation?.status || "unknown";
|
|
604
|
+
if (status !== "converged") {
|
|
605
|
+
throw new Error(`Cannot run '${name}' without envelopes: type derivation is ${status}`);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function runConvergedObject(object, value) {
|
|
610
|
+
const hydrated = isHydratedObject(object) ? object : hydrateObject(object);
|
|
611
|
+
const rels = getRels(hydrated);
|
|
612
|
+
const mainRel = rels[hydrated.main];
|
|
613
|
+
assertRelConverged(mainRel, hydrated.main);
|
|
614
|
+
run_converged.defs = { rels };
|
|
615
|
+
return run_converged(codes.find, mainRel.def, value, mainRel.typePatternGraph, {
|
|
616
|
+
requireConverged: true
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function objectToConvergedFunction(object) {
|
|
621
|
+
const hydrated = isHydratedObject(object) ? object : hydrateObject(object);
|
|
622
|
+
return (value) => runConvergedObject(hydrated, value);
|
|
623
|
+
}
|
|
624
|
+
|
|
404
625
|
function hashAlias(hash) {
|
|
405
626
|
return hash.startsWith("@") ? hash.slice(1) : hash;
|
|
406
627
|
}
|
|
@@ -486,10 +707,23 @@ function boundaryFilterSource(typePatternGraph, patternId, aliases) {
|
|
|
486
707
|
return prettyRel({ op: "filter", filter: rewriteCodesInFilter(filter, aliases) });
|
|
487
708
|
}
|
|
488
709
|
|
|
710
|
+
function prettyRelation(rel, aliases = {}, relAliases = {}) {
|
|
711
|
+
const body = rewriteRefsInExp(
|
|
712
|
+
stripRelationBoundaryFilters(stripDebugFieldsFromExp(rel.def), rel.typePatternGraph),
|
|
713
|
+
aliases,
|
|
714
|
+
relAliases
|
|
715
|
+
);
|
|
716
|
+
return [
|
|
717
|
+
boundaryFilterSource(rel.typePatternGraph, rel.def.patterns[0], aliases),
|
|
718
|
+
prettyRel(body),
|
|
719
|
+
boundaryFilterSource(rel.typePatternGraph, rel.def.patterns[1], aliases)
|
|
720
|
+
].join(" ");
|
|
721
|
+
}
|
|
722
|
+
|
|
489
723
|
function decompileObject(object) {
|
|
490
724
|
const hydrated = isHydratedObject(object) ? object : hydrateObject(object);
|
|
491
725
|
const rels = getRels(hydrated);
|
|
492
|
-
const relAliases = hydrated.
|
|
726
|
+
const relAliases = hydrated.relAlias || {};
|
|
493
727
|
const aliases = shortestUniqueAliases([
|
|
494
728
|
...Object.keys(hydrated.codes),
|
|
495
729
|
...Object.values(relAliases)
|
|
@@ -505,9 +739,9 @@ function decompileObject(object) {
|
|
|
505
739
|
const relName = relAliases[name] || name;
|
|
506
740
|
if (emittedRelAliases.has(relName)) return null;
|
|
507
741
|
emittedRelAliases.add(relName);
|
|
508
|
-
return `${aliases[relName] || relName} = ${
|
|
742
|
+
return `${aliases[relName] || relName} = ${prettyRelation(rel, aliases, relAliases)};`;
|
|
509
743
|
};
|
|
510
|
-
const compileStats = hydrated.
|
|
744
|
+
const compileStats = hydrated.compileStats;
|
|
511
745
|
const sccs = compileStats?.sccs || [];
|
|
512
746
|
const relGroups = sccs.map(({ members }) =>
|
|
513
747
|
members.map(relDefForName).filter((line) => line != null)
|
|
@@ -592,6 +826,7 @@ function extractAliasesFromObject(object) {
|
|
|
592
826
|
if (type !== "code" && type !== "rel") continue;
|
|
593
827
|
const typeOrder = type === "code" ? 0 : 1;
|
|
594
828
|
for (const origin of entry.origins || []) {
|
|
829
|
+
if (origin?.name === "__main__") continue;
|
|
595
830
|
if (!origin?.name || !isSourceName(origin.name)) continue;
|
|
596
831
|
entries.push({
|
|
597
832
|
type,
|
|
@@ -644,7 +879,10 @@ export {
|
|
|
644
879
|
hydrateObject,
|
|
645
880
|
loadLibrary,
|
|
646
881
|
runObject,
|
|
882
|
+
runConvergedObject,
|
|
647
883
|
objectToFunction,
|
|
884
|
+
objectToConvergedFunction,
|
|
885
|
+
prettyRelation,
|
|
648
886
|
decompileObject,
|
|
649
887
|
decompileObjectBuffer,
|
|
650
888
|
extractAliasesFromObject,
|
|
@@ -663,7 +901,10 @@ export default {
|
|
|
663
901
|
hydrateObject,
|
|
664
902
|
loadLibrary,
|
|
665
903
|
runObject,
|
|
904
|
+
runConvergedObject,
|
|
666
905
|
objectToFunction,
|
|
906
|
+
objectToConvergedFunction,
|
|
907
|
+
prettyRelation,
|
|
667
908
|
decompileObject,
|
|
668
909
|
decompileObjectBuffer,
|
|
669
910
|
extractAliasesFromObject,
|
package/objects/README.md
CHANGED
|
@@ -4,43 +4,63 @@ CLI tools for compiling and inspecting k object files (`.ko`) and library files
|
|
|
4
4
|
|
|
5
5
|
## Tools
|
|
6
6
|
|
|
7
|
-
### compile
|
|
7
|
+
### `k-compile`
|
|
8
8
|
|
|
9
9
|
Compiles a `.k` source file into an executable object file (`.ko`).
|
|
10
10
|
The output contains only the codes and relations reachable from `main`.
|
|
11
|
+
Each stored relation includes `typeDerivation.status` for backend eligibility.
|
|
11
12
|
|
|
12
13
|
```bash
|
|
13
|
-
|
|
14
|
+
k-compile [--lib lib-file]... [k-file [object-file]]
|
|
14
15
|
```
|
|
15
16
|
|
|
16
|
-
### compile-lib
|
|
17
|
+
### `k-compile-lib`
|
|
17
18
|
|
|
18
19
|
Compiles a `.k` source file into a library file (`.klib`).
|
|
19
|
-
The output
|
|
20
|
+
The output is plain JSON with no binary header. It contains the library closure
|
|
21
|
+
formed from loaded `--lib` dependencies plus the compiled source definitions,
|
|
22
|
+
with `main: null`.
|
|
20
23
|
Use `--lib` to build on top of existing libraries.
|
|
24
|
+
Each stored relation includes `typeDerivation.status`; libraries may contain a
|
|
25
|
+
mix of converged and non-converged relations.
|
|
21
26
|
|
|
22
27
|
```bash
|
|
23
|
-
|
|
28
|
+
k-compile-lib [--lib lib-file]... [k-file [lib-file]]
|
|
24
29
|
```
|
|
25
30
|
|
|
26
|
-
### decompile
|
|
31
|
+
### `k-decompile`
|
|
27
32
|
|
|
28
33
|
Decompiles a `.ko` or `.klib` back into human-readable k source.
|
|
29
34
|
|
|
30
35
|
```bash
|
|
31
|
-
|
|
36
|
+
k-decompile [object-file [k-file]]
|
|
32
37
|
```
|
|
33
38
|
|
|
34
|
-
### extract-aliases
|
|
39
|
+
### `k-extract-aliases`
|
|
35
40
|
|
|
36
41
|
Extracts metadata aliases from a `.ko` or `.klib` as a valid k definition
|
|
37
42
|
snippet. Output is grouped by metadata type (`code`, then `rel`), then sorted by
|
|
38
43
|
alias name and `compiledAt`.
|
|
39
44
|
|
|
40
45
|
```bash
|
|
41
|
-
|
|
46
|
+
k-extract-aliases [object-file [k-file]]
|
|
42
47
|
```
|
|
43
48
|
|
|
49
|
+
The installed names are `k-` plus the source basename without `.mjs`. Every
|
|
50
|
+
installed object binary supports `-h` and `--help`.
|
|
51
|
+
|
|
52
|
+
## Current Format Notes
|
|
53
|
+
|
|
54
|
+
- `.klib` files are plain UTF-8 JSON, not binary containers.
|
|
55
|
+
- `.ko` files use the `KOBJ\n` binary header followed by a JSON payload.
|
|
56
|
+
- There is no object payload version field in either format.
|
|
57
|
+
- `typeDerivation` belongs only to relations and currently stores only
|
|
58
|
+
`status`.
|
|
59
|
+
- Source `start` / `end` ranges live on `meta[hash].origins[]` entries.
|
|
60
|
+
- Origin entries do not have `kind`; the metadata entry has `type: "code"` or
|
|
61
|
+
`type: "rel"`.
|
|
62
|
+
- Stored relation bodies do not include generated input/output boundary filters.
|
|
63
|
+
|
|
44
64
|
## Further reading
|
|
45
65
|
|
|
46
66
|
- [DOCS/OBJECT_FILE_AND_PATTERN.md](../DOCS/OBJECT_FILE_AND_PATTERN.md) — object file format and pattern encoding
|
package/objects/compile-lib.mjs
CHANGED
|
@@ -6,13 +6,14 @@ import { compileLibraryBuffer, decodeObject, loadLibrary } from "../object.mjs";
|
|
|
6
6
|
|
|
7
7
|
function helpText() {
|
|
8
8
|
return [
|
|
9
|
-
"Compile a k source file into a .klib library object.",
|
|
9
|
+
"Compile a k source file into a plain-JSON .klib library object.",
|
|
10
|
+
"The library records type-derivation convergence for each stored relation.",
|
|
10
11
|
"",
|
|
11
12
|
`Usage: ${argv[1]} [ --lib lib-file ]... [ k-file [ lib-file ] ]`,
|
|
12
13
|
"",
|
|
13
14
|
"Arguments:",
|
|
14
15
|
" k-file Source .k file to compile. Reads UTF-8 source from stdin when omitted.",
|
|
15
|
-
" lib-file Output .klib path. Writes
|
|
16
|
+
" lib-file Output .klib path. Writes plain JSON library data to stdout when omitted.",
|
|
16
17
|
"",
|
|
17
18
|
"Options:",
|
|
18
19
|
" --lib file Load a .klib dependency before compiling. May be repeated.",
|
package/objects/compile.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import { compileObjectBuffer, decodeObject, loadLibrary } from "../object.mjs";
|
|
|
7
7
|
function helpText() {
|
|
8
8
|
return [
|
|
9
9
|
"Compile a k source file into an executable .ko object.",
|
|
10
|
+
"The object records type-derivation convergence for each stored relation.",
|
|
10
11
|
"",
|
|
11
12
|
`Usage: ${argv[1]} [ --lib lib-file ]... [ k-file [ object-file ] ]`,
|
|
12
13
|
"",
|
package/objects/decompile.mjs
CHANGED
|
@@ -11,7 +11,7 @@ function helpText() {
|
|
|
11
11
|
`Usage: ${argv[1]} [ object-file [ k-file ] ]`,
|
|
12
12
|
"",
|
|
13
13
|
"Arguments:",
|
|
14
|
-
" object-file Input .ko or .klib path. Reads
|
|
14
|
+
" object-file Input .ko or .klib path. Reads object data from stdin when omitted.",
|
|
15
15
|
" k-file Output .k path. Writes source to stdout when omitted.",
|
|
16
16
|
"",
|
|
17
17
|
"Options:",
|
|
@@ -15,7 +15,7 @@ function helpText() {
|
|
|
15
15
|
"out so only the latest one remains active.",
|
|
16
16
|
"",
|
|
17
17
|
"Arguments:",
|
|
18
|
-
" object-file Input .ko or .klib path. Reads
|
|
18
|
+
" object-file Input .ko or .klib path. Reads object data from stdin when omitted.",
|
|
19
19
|
" k-file Output .k path. Writes source to stdout when omitted.",
|
|
20
20
|
"",
|
|
21
21
|
"Options:",
|