@fraczak/k 5.1.2 → 6.0.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/.codex +0 -0
- package/Code-derivation-tests/blowup.mjs +3 -3
- package/Code-derivation-tests/bnat.mjs +2 -2
- package/Code-derivation-tests/codec-witness-pattern.mjs +50 -0
- package/Code-derivation-tests/convergence-strategy.mjs +24 -0
- package/Code-derivation-tests/dot.mjs +12 -9
- package/Code-derivation-tests/filter-syntax.mjs +21 -0
- package/Code-derivation-tests/loop.mjs +8 -2
- package/Code-derivation-tests/nat.mjs +1 -1
- package/Code-derivation-tests/not-not.mjs +3 -3
- package/Code-derivation-tests/pattern-envelope.mjs +68 -0
- package/Code-derivation-tests/rlz.mjs +2 -2
- package/{BIG_PICTURE.md → DOCS/BIG_PICTURE.md} +103 -50
- package/DOCS/REPL.md +191 -0
- package/DOCS/a.html +398 -0
- package/DOCS/book/01-introduction.md +51 -0
- package/DOCS/book/02-syntax-and-values.md +111 -0
- package/DOCS/book/03-types-as-finite-automata.md +156 -0
- package/DOCS/book/04-partial-functions-and-composition.md +146 -0
- package/DOCS/book/05-typing-filters-and-normalization.md +111 -0
- package/DOCS/book-combined.html +3798 -0
- package/DOCS/book-combined.md +3745 -0
- package/DOCS/book-combined.pdf +0 -0
- package/DOCS/book-combined.tex +5682 -0
- package/DOCS/book.html +65 -0
- package/DOCS/book.md +54 -0
- package/DOCS/book.pdf +0 -0
- package/DOCS/book.tex +150 -0
- package/NAT.k~ +452 -0
- package/README.md +204 -508
- package/TypeGraph.mjs +43 -0
- package/TypePatternGraph.mjs +251 -0
- package/Value.mjs +192 -0
- package/augmentation.mjs +198 -0
- package/codecs/BINARY_FORMAT.html +471 -0
- package/codecs/BINARY_FORMAT.md +53 -0
- package/codecs/POLYMORPHIC_BINARY_FORMAT.html +355 -0
- package/codecs/POLYMORPHIC_BINARY_FORMAT.md +303 -0
- package/codecs/README.md +215 -0
- package/codecs/example-pipeline.mjs +149 -0
- package/codecs/ieee.mjs +140 -0
- package/codecs/int.mjs +104 -0
- package/codecs/json-codec.mjs +215 -0
- package/codecs/json.mjs +40 -0
- package/codecs/k-parse.mjs +90 -0
- package/codecs/k-pattern.mjs +75 -0
- package/codecs/k-print.mjs +46 -0
- package/codecs/runtime/codec.mjs +768 -0
- package/codecs/runtime/ieee-pattern.mjs +30 -0
- package/codecs/runtime/pattern-json.mjs +96 -0
- package/codecs/runtime/prefix-codec.mjs +425 -0
- package/codecs/runtime/show-value.mjs +68 -0
- package/codecs/runtime/typeFromValue.mjs +34 -0
- package/codecs/runtime/unicode-string.mjs +220 -0
- package/codecs/show.mjs +41 -0
- package/codecs/string-codec.mjs +80 -0
- package/codecs/unit.mjs +51 -0
- package/codecs/utf16.mjs +66 -0
- package/codecs/utf8.mjs +37 -0
- package/codes.mjs +146 -44
- package/compiler.mjs +111 -0
- package/convergence.mjs +197 -0
- package/core.k +197 -0
- package/export.mjs +22 -32
- package/hash.mjs +29 -31
- package/ieee.klib +0 -0
- package/index.mjs +41 -15
- package/k-vscode/.vscode/launch.json +14 -0
- package/k-vscode/README.md +65 -0
- package/k-vscode/language-configuration.json +42 -0
- package/k-vscode/package-lock.json +4176 -0
- package/k-vscode/package.json +57 -0
- package/k-vscode/snippets/k.json +92 -0
- package/k-vscode/syntaxes/k.tmLanguage.json +243 -0
- package/k.mjs +56 -54
- package/object.mjs +517 -0
- package/objects/README.md +38 -0
- package/objects/compile-lib.mjs +42 -0
- package/objects/compile.mjs +42 -0
- package/objects/decompile.mjs +32 -0
- package/package.json +13 -6
- package/parser.jison +27 -46
- package/parser.mjs +68 -100
- package/pretty.mjs +21 -40
- package/repl2.mjs +662 -0
- package/run.mjs +264 -231
- package/symbol-table.mjs +3 -17
- package/test-fingerprint.mjs +7 -7
- package/test-hash-fuzz.mjs +107 -0
- package/test-hash-normalization.mjs +134 -0
- package/test-k-object.mjs +74 -0
- package/test-repl2.mjs +106 -0
- package/test.mjs +45 -70
- package/tests.sh +10 -1
- package/type_registry/registry.mjs~ +105 -0
- package/type_registry/typeParser.jison~ +132 -0
- package/typing.mjs +129 -676
- package/unification.mjs +157 -0
- package/valueIO.mjs +54 -0
- package/valueParser.jison +143 -0
- package/valueParser.mjs +713 -0
- package/Code-derivation-tests/built-in.mjs +0 -19
- package/Code-derivation-tests/vector.mjs +0 -19
- package/Filters/bug.k +0 -3
- package/Filters/composing.k +0 -15
- package/Filters/filter.k +0 -9
- package/TODO.md +0 -53
- package/TYPING.md +0 -365
- package/k-repl.cast +0 -1913
- package/k.cast +0 -618
- package/my-objects.json +0 -6
- package/patterns.mjs +0 -543
- package/repl.mjs +0 -213
- package/test-filters.mjs +0 -45
- package/typing-test.mjs +0 -40
package/object.mjs
ADDED
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
import { annotate } from "./index.mjs";
|
|
2
|
+
import run from "./run.mjs";
|
|
3
|
+
import codes from "./codes.mjs";
|
|
4
|
+
import { TypePatternGraph } from "./TypePatternGraph.mjs";
|
|
5
|
+
import { patterns2filters, prettyRel } from "./pretty.mjs";
|
|
6
|
+
|
|
7
|
+
const MAGIC = Buffer.from([0x4b, 0x4f, 0x42, 0x4a, 0x00, 0x01, 0x0a]);
|
|
8
|
+
const LIB_MAGIC = Buffer.from([0x4b, 0x4c, 0x49, 0x42, 0x00, 0x01, 0x0a]);
|
|
9
|
+
|
|
10
|
+
function serializeTypePatternGraph(typePatternGraph) {
|
|
11
|
+
return {
|
|
12
|
+
patterns: {
|
|
13
|
+
nodes: typePatternGraph.patterns.nodes,
|
|
14
|
+
parent: typePatternGraph.patterns.parent.map((parent) => parent ?? null)
|
|
15
|
+
},
|
|
16
|
+
edges: typePatternGraph.edges,
|
|
17
|
+
codeId: typePatternGraph.codeId
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function hydrateTypePatternGraph(serialized) {
|
|
22
|
+
const graph = new TypePatternGraph(codes.register, codes.find);
|
|
23
|
+
graph.patterns.nodes = serialized.patterns.nodes;
|
|
24
|
+
graph.patterns.parent = serialized.patterns.parent.map((parent) => parent ?? undefined);
|
|
25
|
+
graph.edges = serialized.edges;
|
|
26
|
+
graph.codeId = serialized.codeId;
|
|
27
|
+
return graph;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function serializeRelation(rel) {
|
|
31
|
+
return {
|
|
32
|
+
...rel,
|
|
33
|
+
typePatternGraph: serializeTypePatternGraph(rel.typePatternGraph)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function hydrateRelation(rel) {
|
|
38
|
+
return {
|
|
39
|
+
...rel,
|
|
40
|
+
typePatternGraph: hydrateTypePatternGraph(rel.typePatternGraph)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function serializeAnnotated(annotated) {
|
|
45
|
+
return {
|
|
46
|
+
rels: Object.fromEntries(
|
|
47
|
+
Object.entries(annotated.rels).map(([name, rel]) => [name, serializeRelation(rel)])
|
|
48
|
+
),
|
|
49
|
+
representatives: annotated.representatives,
|
|
50
|
+
relAlias: annotated.relAlias,
|
|
51
|
+
compileStats: annotated.compileStats
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
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
|
+
function buildMeta(annotated, source) {
|
|
65
|
+
const meta = {};
|
|
66
|
+
const relAlias = annotated.relAlias || {};
|
|
67
|
+
const now = new Date().toISOString();
|
|
68
|
+
// Relation metadata: source name → canonical hash
|
|
69
|
+
for (const [name, hash] of Object.entries(relAlias)) {
|
|
70
|
+
if (name === "__main__") continue;
|
|
71
|
+
if (!meta[hash]) meta[hash] = { origins: [] };
|
|
72
|
+
meta[hash].origins.push({ source, name, compiledAt: now });
|
|
73
|
+
}
|
|
74
|
+
return meta;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function collectReachable(mainName, rels, allCodes) {
|
|
78
|
+
const reachableRels = new Set();
|
|
79
|
+
const reachableCodes = new Set();
|
|
80
|
+
|
|
81
|
+
function walkExp(exp) {
|
|
82
|
+
if (!exp) return;
|
|
83
|
+
switch (exp.op) {
|
|
84
|
+
case "ref":
|
|
85
|
+
if (exp.ref in rels && !reachableRels.has(exp.ref)) {
|
|
86
|
+
reachableRels.add(exp.ref);
|
|
87
|
+
walkExp(rels[exp.ref].def);
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
case "code":
|
|
91
|
+
walkCode(exp.code);
|
|
92
|
+
break;
|
|
93
|
+
case "filter":
|
|
94
|
+
walkFilter(exp.filter);
|
|
95
|
+
break;
|
|
96
|
+
case "comp":
|
|
97
|
+
exp.comp.forEach(walkExp);
|
|
98
|
+
break;
|
|
99
|
+
case "union":
|
|
100
|
+
exp.union.forEach(walkExp);
|
|
101
|
+
break;
|
|
102
|
+
case "product":
|
|
103
|
+
exp.product.forEach(({ exp: e }) => walkExp(e));
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function walkFilter(filter) {
|
|
109
|
+
if (!filter) return;
|
|
110
|
+
if (filter.type === "code") walkCode(filter.code);
|
|
111
|
+
if (filter.fields) Object.values(filter.fields).forEach(walkFilter);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function walkCode(hash) {
|
|
115
|
+
if (!hash || reachableCodes.has(hash)) return;
|
|
116
|
+
if (!(hash in allCodes)) return;
|
|
117
|
+
reachableCodes.add(hash);
|
|
118
|
+
const code = allCodes[hash];
|
|
119
|
+
const fields = code[code.code];
|
|
120
|
+
if (fields) Object.values(fields).forEach(walkCode);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Walk from main
|
|
124
|
+
reachableRels.add(mainName);
|
|
125
|
+
walkExp(rels[mainName].def);
|
|
126
|
+
|
|
127
|
+
// Also collect codes referenced in typePatternGraphs of reachable rels
|
|
128
|
+
for (const relName of reachableRels) {
|
|
129
|
+
const rel = rels[relName];
|
|
130
|
+
if (rel.typePatternGraph) {
|
|
131
|
+
const codeId = rel.typePatternGraph.codeId || (rel.typePatternGraph.codeId);
|
|
132
|
+
if (codeId) Object.values(codeId).forEach(walkCode);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return { reachableRels, reachableCodes };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function compileObject(script, options = {}) {
|
|
140
|
+
const annotated = annotate(script, options);
|
|
141
|
+
const allCodes = codes.dump();
|
|
142
|
+
const serialized = serializeAnnotated(annotated);
|
|
143
|
+
const meta = buildMeta(annotated, options.source || null);
|
|
144
|
+
|
|
145
|
+
// Prune to reachable from main
|
|
146
|
+
const { reachableRels, reachableCodes } = collectReachable("__main__", serialized.rels, allCodes);
|
|
147
|
+
const prunedCodes = Object.fromEntries(
|
|
148
|
+
Object.entries(allCodes).filter(([h]) => reachableCodes.has(h))
|
|
149
|
+
);
|
|
150
|
+
const prunedRels = Object.fromEntries(
|
|
151
|
+
Object.entries(serialized.rels).filter(([name]) => reachableRels.has(name))
|
|
152
|
+
);
|
|
153
|
+
const prunedMeta = Object.fromEntries(
|
|
154
|
+
Object.entries(meta).filter(([h]) => reachableRels.has(h) || reachableCodes.has(h))
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
format: "k-object",
|
|
159
|
+
version: 2,
|
|
160
|
+
codes: prunedCodes,
|
|
161
|
+
rels: prunedRels,
|
|
162
|
+
relAlias: serialized.relAlias,
|
|
163
|
+
compileStats: serialized.compileStats,
|
|
164
|
+
meta: prunedMeta,
|
|
165
|
+
main: "__main__"
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function rewriteRefsToCanonical(exp, relAlias) {
|
|
170
|
+
if (!exp) return exp;
|
|
171
|
+
const rewritten = { ...exp };
|
|
172
|
+
switch (exp.op) {
|
|
173
|
+
case "ref":
|
|
174
|
+
if (relAlias[exp.ref]) rewritten.ref = relAlias[exp.ref];
|
|
175
|
+
break;
|
|
176
|
+
case "comp":
|
|
177
|
+
rewritten.comp = exp.comp.map(e => rewriteRefsToCanonical(e, relAlias));
|
|
178
|
+
break;
|
|
179
|
+
case "union":
|
|
180
|
+
rewritten.union = exp.union.map(e => rewriteRefsToCanonical(e, relAlias));
|
|
181
|
+
break;
|
|
182
|
+
case "product":
|
|
183
|
+
rewritten.product = exp.product.map(({ label, exp: e }) => ({
|
|
184
|
+
label, exp: rewriteRefsToCanonical(e, relAlias)
|
|
185
|
+
}));
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
return rewritten;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function compileLibrary(script, options = {}) {
|
|
192
|
+
const annotated = annotate(script, options);
|
|
193
|
+
const serialized = serializeAnnotated(annotated);
|
|
194
|
+
const meta = buildMeta(annotated, options.source || null);
|
|
195
|
+
const relAlias = serialized.relAlias || {};
|
|
196
|
+
|
|
197
|
+
// Key rels by canonical hash, rewrite internal refs to canonical hashes
|
|
198
|
+
const libRels = {};
|
|
199
|
+
for (const [name, rel] of Object.entries(serialized.rels)) {
|
|
200
|
+
if (name === "__main__") continue;
|
|
201
|
+
const canonicalName = relAlias[name] || name;
|
|
202
|
+
const { varRefs, ...relWithoutVarRefs } = rel;
|
|
203
|
+
libRels[canonicalName] = {
|
|
204
|
+
...relWithoutVarRefs,
|
|
205
|
+
def: rewriteRefsToCanonical(rel.def, relAlias)
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
format: "k-object",
|
|
211
|
+
version: 2,
|
|
212
|
+
codes: codes.dump(),
|
|
213
|
+
rels: libRels,
|
|
214
|
+
relAlias: serialized.relAlias,
|
|
215
|
+
compileStats: serialized.compileStats,
|
|
216
|
+
meta,
|
|
217
|
+
main: null
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function hydrateObject(object) {
|
|
222
|
+
if (object?.format !== "k-object") {
|
|
223
|
+
throw new Error("Unsupported k object file");
|
|
224
|
+
}
|
|
225
|
+
// v1 compat: defs wrapper
|
|
226
|
+
if (object.version === 1) {
|
|
227
|
+
codes.load(object.codes);
|
|
228
|
+
return {
|
|
229
|
+
...object,
|
|
230
|
+
defs: hydrateAnnotated(object.defs)
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
// v2: flat rels
|
|
234
|
+
codes.load(object.codes);
|
|
235
|
+
const hydratedRels = Object.fromEntries(
|
|
236
|
+
Object.entries(object.rels).map(([name, rel]) => [name, hydrateRelation(rel)])
|
|
237
|
+
);
|
|
238
|
+
return { ...object, rels: hydratedRels };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function loadLibrary(object) {
|
|
242
|
+
const hydrated = isHydratedObject(object) ? object : hydrateObject(object);
|
|
243
|
+
if (hydrated.main != null) {
|
|
244
|
+
throw new Error("Expected a library (main: null), got an executable");
|
|
245
|
+
}
|
|
246
|
+
return hydrated;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function isHydratedObject(object) {
|
|
250
|
+
// v2 format
|
|
251
|
+
if (object?.rels) {
|
|
252
|
+
const firstRel = Object.values(object.rels)[0];
|
|
253
|
+
return firstRel?.typePatternGraph instanceof TypePatternGraph;
|
|
254
|
+
}
|
|
255
|
+
// v1 format
|
|
256
|
+
return object?.defs?.rels?.[object.main]?.typePatternGraph instanceof TypePatternGraph;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function encodeObject(object) {
|
|
260
|
+
const magic = object.main == null ? LIB_MAGIC : MAGIC;
|
|
261
|
+
const payload = Buffer.from(JSON.stringify(object), "utf8");
|
|
262
|
+
if (payload.length > 0xffffffff) {
|
|
263
|
+
throw new Error("k object payload is too large");
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const header = Buffer.alloc(magic.length + 4);
|
|
267
|
+
magic.copy(header, 0);
|
|
268
|
+
header.writeUInt32BE(payload.length, magic.length);
|
|
269
|
+
return Buffer.concat([header, payload]);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function decodeObject(buffer) {
|
|
273
|
+
if (!Buffer.isBuffer(buffer)) {
|
|
274
|
+
throw new Error("k object input must be a Buffer");
|
|
275
|
+
}
|
|
276
|
+
const isObj = buffer.length >= MAGIC.length && buffer.subarray(0, MAGIC.length).equals(MAGIC);
|
|
277
|
+
const isLib = buffer.length >= LIB_MAGIC.length && buffer.subarray(0, LIB_MAGIC.length).equals(LIB_MAGIC);
|
|
278
|
+
if (!isObj && !isLib) {
|
|
279
|
+
throw new Error("Invalid k object file header");
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const magic = isLib ? LIB_MAGIC : MAGIC;
|
|
283
|
+
const length = buffer.readUInt32BE(magic.length);
|
|
284
|
+
const payloadStart = magic.length + 4;
|
|
285
|
+
const payloadEnd = payloadStart + length;
|
|
286
|
+
if (payloadEnd !== buffer.length) {
|
|
287
|
+
throw new Error("Invalid k object file length");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return hydrateObject(JSON.parse(buffer.subarray(payloadStart, payloadEnd).toString("utf8")));
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function encodeLibrary(object) {
|
|
294
|
+
return encodeObject(object);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function decodeLibrary(buffer) {
|
|
298
|
+
const object = decodeObject(buffer);
|
|
299
|
+
if (object.main != null) {
|
|
300
|
+
throw new Error("Expected a library (main: null), got an executable");
|
|
301
|
+
}
|
|
302
|
+
return object;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function compileObjectBuffer(script, options = {}) {
|
|
306
|
+
return encodeObject(compileObject(script, options));
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function compileLibraryBuffer(script, options = {}) {
|
|
310
|
+
return encodeObject(compileLibrary(script, options));
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function getRels(hydrated) {
|
|
314
|
+
// v2: flat rels; v1: defs.rels
|
|
315
|
+
return hydrated.rels || hydrated.defs?.rels;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function runObject(object, value) {
|
|
319
|
+
const hydrated = isHydratedObject(object) ? object : hydrateObject(object);
|
|
320
|
+
const rels = getRels(hydrated);
|
|
321
|
+
run.defs = hydrated.defs || { rels };
|
|
322
|
+
const mainRel = rels[hydrated.main];
|
|
323
|
+
return run(codes.find, mainRel.def, value, mainRel.typePatternGraph);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function objectToFunction(object) {
|
|
327
|
+
const hydrated = isHydratedObject(object) ? object : hydrateObject(object);
|
|
328
|
+
return (value) => runObject(hydrated, value);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function hashAlias(hash) {
|
|
332
|
+
return hash.startsWith("@") ? hash.slice(1) : hash;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function shortestUniqueAliases(hashes, minimumLength = 4) {
|
|
336
|
+
const sorted = [...new Set(hashes)].sort();
|
|
337
|
+
const aliases = {};
|
|
338
|
+
|
|
339
|
+
for (const hash of sorted) {
|
|
340
|
+
const base = hashAlias(hash);
|
|
341
|
+
let length = Math.min(minimumLength, base.length);
|
|
342
|
+
while (
|
|
343
|
+
sorted.some((other) =>
|
|
344
|
+
other !== hash && hashAlias(other).startsWith(base.slice(0, length))
|
|
345
|
+
)
|
|
346
|
+
) {
|
|
347
|
+
length += 1;
|
|
348
|
+
if (length > base.length) {
|
|
349
|
+
throw new Error(`Cannot derive unique prefix alias for ${hash}`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
aliases[hash] = base.slice(0, length);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return aliases;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function rewriteCodesInFilter(filter, aliases) {
|
|
359
|
+
const rewritten = { ...filter };
|
|
360
|
+
if (rewritten.type === "code" && aliases[rewritten.code]) {
|
|
361
|
+
rewritten.code = aliases[rewritten.code];
|
|
362
|
+
}
|
|
363
|
+
if (rewritten.fields) {
|
|
364
|
+
rewritten.fields = Object.fromEntries(
|
|
365
|
+
Object.entries(rewritten.fields).map(([label, child]) => [label, rewriteCodesInFilter(child, aliases)])
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
return rewritten;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function rewriteRefsInExp(exp, aliases, relAliases) {
|
|
372
|
+
const rewritten = { ...exp };
|
|
373
|
+
switch (rewritten.op) {
|
|
374
|
+
case "filter":
|
|
375
|
+
rewritten.filter = rewriteCodesInFilter(rewritten.filter, aliases);
|
|
376
|
+
break;
|
|
377
|
+
case "code":
|
|
378
|
+
if (aliases[rewritten.code]) rewritten.code = aliases[rewritten.code];
|
|
379
|
+
break;
|
|
380
|
+
case "ref":
|
|
381
|
+
if (relAliases[rewritten.ref]) rewritten.ref = aliases[relAliases[rewritten.ref]] || relAliases[rewritten.ref];
|
|
382
|
+
else if (aliases[rewritten.ref]) rewritten.ref = aliases[rewritten.ref];
|
|
383
|
+
break;
|
|
384
|
+
case "comp":
|
|
385
|
+
rewritten.comp = rewritten.comp.map((child) => rewriteRefsInExp(child, aliases, relAliases));
|
|
386
|
+
break;
|
|
387
|
+
case "union":
|
|
388
|
+
rewritten.union = rewritten.union.map((child) => rewriteRefsInExp(child, aliases, relAliases));
|
|
389
|
+
break;
|
|
390
|
+
case "product":
|
|
391
|
+
rewritten.product = rewritten.product.map(({ label, exp: child }) => ({
|
|
392
|
+
label,
|
|
393
|
+
exp: rewriteRefsInExp(child, aliases, relAliases)
|
|
394
|
+
}));
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
397
|
+
return rewritten;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function codeToSource(code, aliases) {
|
|
401
|
+
const fields = code[code.code];
|
|
402
|
+
const body = Object.keys(fields)
|
|
403
|
+
.sort()
|
|
404
|
+
.map((label) => {
|
|
405
|
+
return `${aliases[fields[label]] || fields[label]} ${JSON.stringify(label)}`;
|
|
406
|
+
})
|
|
407
|
+
.join(", ");
|
|
408
|
+
return code.code === "product" ? `{${body}}` : `< ${body} >`;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function boundaryFilterSource(typePatternGraph, patternId, aliases) {
|
|
412
|
+
const [filter] = patterns2filters(typePatternGraph, patternId);
|
|
413
|
+
return prettyRel({ op: "filter", filter: rewriteCodesInFilter(filter, aliases) });
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function decompileObject(object) {
|
|
417
|
+
const hydrated = isHydratedObject(object) ? object : hydrateObject(object);
|
|
418
|
+
const rels = getRels(hydrated);
|
|
419
|
+
const relAliases = hydrated.defs?.relAlias || hydrated.relAlias || {};
|
|
420
|
+
const aliases = shortestUniqueAliases([
|
|
421
|
+
...Object.keys(hydrated.codes),
|
|
422
|
+
...Object.values(relAliases)
|
|
423
|
+
]);
|
|
424
|
+
|
|
425
|
+
const codeDefs = Object.keys(hydrated.codes)
|
|
426
|
+
.sort()
|
|
427
|
+
.map((name) => `$ ${aliases[name]} = ${codeToSource(hydrated.codes[name], aliases)};`);
|
|
428
|
+
const emittedRelAliases = new Set();
|
|
429
|
+
const relDefForName = (name) => {
|
|
430
|
+
const rel = rels[name];
|
|
431
|
+
if (!rel) return null;
|
|
432
|
+
const relName = relAliases[name] || name;
|
|
433
|
+
if (emittedRelAliases.has(relName)) return null;
|
|
434
|
+
emittedRelAliases.add(relName);
|
|
435
|
+
return `${aliases[relName] || relName} = ${prettyRel(rewriteRefsInExp(rel.def, aliases, relAliases))};`;
|
|
436
|
+
};
|
|
437
|
+
const compileStats = hydrated.defs?.compileStats || hydrated.compileStats;
|
|
438
|
+
const sccs = compileStats?.sccs || [];
|
|
439
|
+
const relGroups = sccs.map(({ members }) =>
|
|
440
|
+
members.map(relDefForName).filter((line) => line != null)
|
|
441
|
+
).filter((group) => group.length > 0);
|
|
442
|
+
const missingRelGroup = Object.keys(rels)
|
|
443
|
+
.filter((name) => !sccs.some(({ members }) => members.includes(name)))
|
|
444
|
+
.map(relDefForName)
|
|
445
|
+
.filter((line) => line != null);
|
|
446
|
+
if (missingRelGroup.length > 0) relGroups.push(missingRelGroup);
|
|
447
|
+
const relDefs = relGroups.flatMap((group, index) =>
|
|
448
|
+
index === 0 ? group : ["", ...group]
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
if (hydrated.main == null) {
|
|
452
|
+
// Library: no main section
|
|
453
|
+
return [
|
|
454
|
+
"----- codes -----",
|
|
455
|
+
...codeDefs,
|
|
456
|
+
"----- rels -----",
|
|
457
|
+
...relDefs,
|
|
458
|
+
""
|
|
459
|
+
].join("\n");
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const mainRel = relAliases[hydrated.main] || hydrated.main;
|
|
463
|
+
const mainRelDef = rels[hydrated.main];
|
|
464
|
+
const main = [
|
|
465
|
+
boundaryFilterSource(mainRelDef.typePatternGraph, mainRelDef.def.patterns[0], aliases),
|
|
466
|
+
aliases[mainRel] || mainRel,
|
|
467
|
+
boundaryFilterSource(mainRelDef.typePatternGraph, mainRelDef.def.patterns[1], aliases)
|
|
468
|
+
].join(" ");
|
|
469
|
+
|
|
470
|
+
return [
|
|
471
|
+
"----- codes -----",
|
|
472
|
+
...codeDefs,
|
|
473
|
+
"----- rels -----",
|
|
474
|
+
...relDefs,
|
|
475
|
+
"----- main -----",
|
|
476
|
+
main,
|
|
477
|
+
""
|
|
478
|
+
].join("\n");
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function decompileObjectBuffer(buffer) {
|
|
482
|
+
return decompileObject(decodeObject(buffer));
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
export {
|
|
486
|
+
compileObject,
|
|
487
|
+
compileObjectBuffer,
|
|
488
|
+
compileLibrary,
|
|
489
|
+
compileLibraryBuffer,
|
|
490
|
+
encodeObject,
|
|
491
|
+
encodeLibrary,
|
|
492
|
+
decodeObject,
|
|
493
|
+
decodeLibrary,
|
|
494
|
+
hydrateObject,
|
|
495
|
+
loadLibrary,
|
|
496
|
+
runObject,
|
|
497
|
+
objectToFunction,
|
|
498
|
+
decompileObject,
|
|
499
|
+
decompileObjectBuffer
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
export default {
|
|
503
|
+
compileObject,
|
|
504
|
+
compileObjectBuffer,
|
|
505
|
+
compileLibrary,
|
|
506
|
+
compileLibraryBuffer,
|
|
507
|
+
encodeObject,
|
|
508
|
+
encodeLibrary,
|
|
509
|
+
decodeObject,
|
|
510
|
+
decodeLibrary,
|
|
511
|
+
hydrateObject,
|
|
512
|
+
loadLibrary,
|
|
513
|
+
runObject,
|
|
514
|
+
objectToFunction,
|
|
515
|
+
decompileObject,
|
|
516
|
+
decompileObjectBuffer
|
|
517
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# objects/
|
|
2
|
+
|
|
3
|
+
CLI tools for compiling and inspecting k object files (`.ko`) and library files (`.klib`).
|
|
4
|
+
|
|
5
|
+
## Tools
|
|
6
|
+
|
|
7
|
+
### compile.mjs
|
|
8
|
+
|
|
9
|
+
Compiles a `.k` source file into an executable object file (`.ko`).
|
|
10
|
+
The output contains only the codes and relations reachable from `main`.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
./objects/compile.mjs [--lib lib-file]... [k-file [object-file]]
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### compile-lib.mjs
|
|
17
|
+
|
|
18
|
+
Compiles a `.k` source file into a library file (`.klib`).
|
|
19
|
+
The output contains all codes and relations defined in the source (no main entry point).
|
|
20
|
+
Use `--lib` to build on top of existing libraries.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
./objects/compile-lib.mjs [--lib lib-file]... [k-file [lib-file]]
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### decompile.mjs
|
|
27
|
+
|
|
28
|
+
Decompiles a `.ko` or `.klib` back into human-readable k source.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
./objects/decompile.mjs [object-file [k-file]]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Further reading
|
|
35
|
+
|
|
36
|
+
- [DOCS/OBJECT_FILE_AND_PATTERN.md](../DOCS/OBJECT_FILE_AND_PATTERN.md) — object file format and pattern encoding
|
|
37
|
+
- [DOCS/TYPE_DERIVATION.md](../DOCS/TYPE_DERIVATION.md) — type derivation (what compilation skips when loading a `.ko`/`.klib`)
|
|
38
|
+
- [DOCS/CONVERGENCE.md](../DOCS/CONVERGENCE.md) — convergence strategies for type inference
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import { argv, exit, stdin, stdout } from "node:process";
|
|
5
|
+
import { compileLibraryBuffer, decodeObject, loadLibrary } from "../object.mjs";
|
|
6
|
+
|
|
7
|
+
function usage() {
|
|
8
|
+
console.error(`Usage: ${argv[1]} [ --lib lib-file ]... [ k-file [ lib-file ] ]`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function readStdinText() {
|
|
12
|
+
const chunks = [];
|
|
13
|
+
for await (const chunk of stdin) {
|
|
14
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
15
|
+
}
|
|
16
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const args = argv.slice(2);
|
|
21
|
+
const libraries = [];
|
|
22
|
+
while (args.length > 0 && args[0] === "--lib") {
|
|
23
|
+
args.shift();
|
|
24
|
+
const libPath = args.shift();
|
|
25
|
+
if (!libPath) throw new Error("--lib requires a file argument");
|
|
26
|
+
libraries.push(loadLibrary(decodeObject(fs.readFileSync(libPath))));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const sourcePath = args.shift();
|
|
30
|
+
const outputPath = args.shift();
|
|
31
|
+
const source = sourcePath == null ? await readStdinText() : fs.readFileSync(sourcePath, "utf8");
|
|
32
|
+
const objectBuffer = compileLibraryBuffer(source, { source: sourcePath || "<stdin>", libraries });
|
|
33
|
+
if (outputPath == null) {
|
|
34
|
+
stdout.write(objectBuffer);
|
|
35
|
+
} else {
|
|
36
|
+
fs.writeFileSync(outputPath, objectBuffer);
|
|
37
|
+
}
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error(error);
|
|
40
|
+
usage();
|
|
41
|
+
exit(1);
|
|
42
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import { argv, exit, stdin, stdout } from "node:process";
|
|
5
|
+
import { compileObjectBuffer, decodeObject, loadLibrary } from "../object.mjs";
|
|
6
|
+
|
|
7
|
+
function usage() {
|
|
8
|
+
console.error(`Usage: ${argv[1]} [ --lib lib-file ]... [ k-file [ object-file ] ]`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function readStdinText() {
|
|
12
|
+
const chunks = [];
|
|
13
|
+
for await (const chunk of stdin) {
|
|
14
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
15
|
+
}
|
|
16
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const args = argv.slice(2);
|
|
21
|
+
const libraries = [];
|
|
22
|
+
while (args.length > 0 && args[0] === "--lib") {
|
|
23
|
+
args.shift();
|
|
24
|
+
const libPath = args.shift();
|
|
25
|
+
if (!libPath) throw new Error("--lib requires a file argument");
|
|
26
|
+
libraries.push(loadLibrary(decodeObject(fs.readFileSync(libPath))));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const sourcePath = args.shift();
|
|
30
|
+
const outputPath = args.shift();
|
|
31
|
+
const source = sourcePath == null ? await readStdinText() : fs.readFileSync(sourcePath, "utf8");
|
|
32
|
+
const objectBuffer = compileObjectBuffer(source, { source: sourcePath || "<stdin>", libraries });
|
|
33
|
+
if (outputPath == null) {
|
|
34
|
+
stdout.write(objectBuffer);
|
|
35
|
+
} else {
|
|
36
|
+
fs.writeFileSync(outputPath, objectBuffer);
|
|
37
|
+
}
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error(error);
|
|
40
|
+
usage();
|
|
41
|
+
exit(1);
|
|
42
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import { argv, exit, stdin, stdout } from "node:process";
|
|
5
|
+
import { decompileObjectBuffer } from "../object.mjs";
|
|
6
|
+
|
|
7
|
+
function usage() {
|
|
8
|
+
console.error(`Usage: ${argv[1]} [ object-file [ k-file ] ]`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function readStdinBuffer() {
|
|
12
|
+
const chunks = [];
|
|
13
|
+
for await (const chunk of stdin) {
|
|
14
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
15
|
+
}
|
|
16
|
+
return Buffer.concat(chunks);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const [, , objectPath, outputPath] = argv;
|
|
21
|
+
const objectBuffer = objectPath == null ? await readStdinBuffer() : fs.readFileSync(objectPath);
|
|
22
|
+
const source = decompileObjectBuffer(objectBuffer);
|
|
23
|
+
if (outputPath == null) {
|
|
24
|
+
stdout.write(source);
|
|
25
|
+
} else {
|
|
26
|
+
fs.writeFileSync(outputPath, source);
|
|
27
|
+
}
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error(error);
|
|
30
|
+
usage();
|
|
31
|
+
exit(1);
|
|
32
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fraczak/k",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "k
|
|
3
|
+
"version": "6.0.1",
|
|
4
|
+
"description": "k — a language for composing first-order partial functions over algebraic data types (products and tagged unions); includes runtime, type derivation, REPL, and codec toolchain",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"prepare": "jiwson -m es6 parser.jison -o parser.mjs",
|
|
8
|
-
"test": "node test.mjs; for f in Code-derivation-tests/*.mjs; do echo $f; node $f; done; node test-fingerprint.mjs; ./tests.sh"
|
|
7
|
+
"prepare": "jiwson -m es6 parser.jison -o parser.mjs; jiwson -m es6 valueParser.jison -o valueParser.mjs; ",
|
|
8
|
+
"test": "node test.mjs; for f in Code-derivation-tests/*.mjs; do echo $f; node $f; done; node test-fingerprint.mjs; node test-hash-fuzz.mjs; node test-hash-normalization.mjs; node test-k-object.mjs; node test-repl2.mjs; ./tests.sh",
|
|
9
|
+
"deps:graph": "node scripts/generate-dependency-graph.mjs"
|
|
9
10
|
},
|
|
10
11
|
"repository": {
|
|
11
12
|
"type": "git",
|
|
@@ -15,9 +16,15 @@
|
|
|
15
16
|
"license": "ISC",
|
|
16
17
|
"bin": {
|
|
17
18
|
"k": "./k.mjs",
|
|
18
|
-
"k-
|
|
19
|
+
"k-compile-object": "./objects/compile.mjs",
|
|
20
|
+
"k-decompile-object": "./objects/decompile.mjs",
|
|
21
|
+
"k-parse": "./codecs/k-parse.mjs",
|
|
22
|
+
"k-pattern": "./codecs/k-pattern.mjs",
|
|
23
|
+
"k-print": "./codecs/k-print.mjs",
|
|
24
|
+
"k-repl": "./repl2.mjs",
|
|
25
|
+
"k-repl2": "./repl2.mjs"
|
|
19
26
|
},
|
|
20
27
|
"devDependencies": {
|
|
21
|
-
"jiwson": "^0.1.
|
|
28
|
+
"jiwson": "^0.1.2"
|
|
22
29
|
}
|
|
23
30
|
}
|