@fraczak/k 6.0.3 → 6.0.4
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/DOCS/KIR_SPECIALIZATION_DESIGN.md +458 -0
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +129 -43
- package/DOCS/REPL.md +6 -4
- package/README.md +8 -2
- 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/object.mjs +303 -62
- package/objects/README.md +18 -1
- 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 +1 -1
- package/repl2.mjs +26 -11
- package/run.mjs +172 -2
- package/test-k-object.mjs +58 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fraczak/k",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.4",
|
|
4
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": {
|
package/repl2.mjs
CHANGED
|
@@ -19,7 +19,8 @@ import {
|
|
|
19
19
|
encodeObject,
|
|
20
20
|
encodeLibrary,
|
|
21
21
|
hydrateObject,
|
|
22
|
-
loadLibrary
|
|
22
|
+
loadLibrary,
|
|
23
|
+
prettyRelation
|
|
23
24
|
} from "./object.mjs";
|
|
24
25
|
import { propertyListToFilter, valueToK, valueWithEnvelopeToK } from "./codecs/runtime/show-value.mjs";
|
|
25
26
|
|
|
@@ -58,7 +59,6 @@ function createState() {
|
|
|
58
59
|
function stateLibrary(state) {
|
|
59
60
|
return {
|
|
60
61
|
format: "k-object",
|
|
61
|
-
version: 2,
|
|
62
62
|
codes: state.codes,
|
|
63
63
|
rels: state.rels,
|
|
64
64
|
relAlias: state.relAliases,
|
|
@@ -149,11 +149,7 @@ function mergeMeta(state, meta = {}) {
|
|
|
149
149
|
const entryType = entry?.type;
|
|
150
150
|
if (!state.meta[hash]) state.meta[hash] = { type: entryType, origins: [] };
|
|
151
151
|
if (state.meta[hash].type == null && entryType != null) state.meta[hash].type = entryType;
|
|
152
|
-
state.meta[hash].origins.push(...origins.map((
|
|
153
|
-
source,
|
|
154
|
-
name,
|
|
155
|
-
...(compiledAt == null ? {} : { compiledAt })
|
|
156
|
-
})));
|
|
152
|
+
state.meta[hash].origins.push(...origins.map((origin) => ({ ...origin })));
|
|
157
153
|
}
|
|
158
154
|
}
|
|
159
155
|
|
|
@@ -220,7 +216,6 @@ function savedLibrary(state) {
|
|
|
220
216
|
}
|
|
221
217
|
return {
|
|
222
218
|
format: "k-object",
|
|
223
|
-
version: 2,
|
|
224
219
|
codes: state.codes,
|
|
225
220
|
rels: state.rels,
|
|
226
221
|
relAlias: state.relAliases,
|
|
@@ -289,6 +284,16 @@ function userDefinedNames(source) {
|
|
|
289
284
|
};
|
|
290
285
|
}
|
|
291
286
|
|
|
287
|
+
function originFromSourceNode(source, name, compiledAt, node) {
|
|
288
|
+
return {
|
|
289
|
+
source,
|
|
290
|
+
name,
|
|
291
|
+
compiledAt,
|
|
292
|
+
...(node?.start ? { start: node.start } : {}),
|
|
293
|
+
...(node?.end ? { end: node.end } : {})
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
292
297
|
function libraryOriginsFromSource(source, fullSource, lib, options) {
|
|
293
298
|
const { typeNames, relNames } = userDefinedNames(source);
|
|
294
299
|
const annotated = annotateWithOptionalIdentity(fullSource, options);
|
|
@@ -299,7 +304,12 @@ function libraryOriginsFromSource(source, fullSource, lib, options) {
|
|
|
299
304
|
const hash = annotated.representatives?.[name];
|
|
300
305
|
if (!hash) continue;
|
|
301
306
|
if (!meta[hash]) meta[hash] = { type: "code", origins: [] };
|
|
302
|
-
meta[hash].origins.push(
|
|
307
|
+
meta[hash].origins.push(originFromSourceNode(
|
|
308
|
+
options.source || null,
|
|
309
|
+
name,
|
|
310
|
+
now,
|
|
311
|
+
annotated.sourceDefs?.codes?.[name]
|
|
312
|
+
));
|
|
303
313
|
}
|
|
304
314
|
|
|
305
315
|
const relAlias = Object.fromEntries(
|
|
@@ -310,7 +320,12 @@ function libraryOriginsFromSource(source, fullSource, lib, options) {
|
|
|
310
320
|
|
|
311
321
|
for (const [name, hash] of Object.entries(relAlias)) {
|
|
312
322
|
if (!meta[hash]) meta[hash] = { type: "rel", origins: [] };
|
|
313
|
-
meta[hash].origins.push(
|
|
323
|
+
meta[hash].origins.push(originFromSourceNode(
|
|
324
|
+
options.source || null,
|
|
325
|
+
name,
|
|
326
|
+
now,
|
|
327
|
+
annotated.rels?.[name]?.def
|
|
328
|
+
));
|
|
314
329
|
}
|
|
315
330
|
|
|
316
331
|
return { relAlias, meta };
|
|
@@ -788,7 +803,7 @@ async function evaluateCommand(line, state) {
|
|
|
788
803
|
if (!arg) throw new Error(`${usagePrefix}d requires a relation name`);
|
|
789
804
|
const { hash, rel } = resolveRel(state, arg);
|
|
790
805
|
if (!rel) throw new Error(`Unknown relation '${arg}'`);
|
|
791
|
-
return [`${arg} = ${
|
|
806
|
+
return [`${arg} = ${prettyRelation(rel)}; -- ${hash}`];
|
|
792
807
|
}
|
|
793
808
|
case "C": {
|
|
794
809
|
if (!arg) throw new Error(`${usagePrefix}C requires a type name`);
|
package/run.mjs
CHANGED
|
@@ -166,7 +166,7 @@ function compiledExp(findCode, exp, typePatternGraph) {
|
|
|
166
166
|
{
|
|
167
167
|
const defn = run.defs.rels[exp.ref];
|
|
168
168
|
if (defn != undefined) {
|
|
169
|
-
const refFn =
|
|
169
|
+
const refFn = compiledRel(findCode, defn, exp.ref);
|
|
170
170
|
fn = (value) => {
|
|
171
171
|
return value === undefined ? undefined : refFn(value);
|
|
172
172
|
};
|
|
@@ -266,6 +266,161 @@ function compiledExp(findCode, exp, typePatternGraph) {
|
|
|
266
266
|
return compiled.fn;
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
+
function compiledRel(findCode, relDef, name = "<anonymous>") {
|
|
270
|
+
const cached = relDef._compiledRunRel;
|
|
271
|
+
if (cached?.findCode === findCode && cached?.defs === run.defs) {
|
|
272
|
+
return cached.fn;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const compiled = {
|
|
276
|
+
findCode,
|
|
277
|
+
defs: run.defs,
|
|
278
|
+
target: null,
|
|
279
|
+
fn(value) {
|
|
280
|
+
return compiled.target(value);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
relDef._compiledRunRel = compiled;
|
|
284
|
+
|
|
285
|
+
const exp = relDef.def;
|
|
286
|
+
const typePatternGraph = relDef.typePatternGraph || null;
|
|
287
|
+
const inputPattern = staticPattern(typePatternGraph, exp, 0);
|
|
288
|
+
const staticOutputPattern = outputPattern(typePatternGraph, exp);
|
|
289
|
+
const bodyFn = compiledExp(findCode, exp, typePatternGraph);
|
|
290
|
+
|
|
291
|
+
compiled.target = (value) => {
|
|
292
|
+
if (value === undefined) return undefined;
|
|
293
|
+
value = constrainWithPattern(value, inputPattern, exp);
|
|
294
|
+
const result = bodyFn(value);
|
|
295
|
+
if (result === undefined) return undefined;
|
|
296
|
+
try {
|
|
297
|
+
return constrainWithPattern(result, staticOutputPattern, exp);
|
|
298
|
+
} catch (error) {
|
|
299
|
+
error.message = `Type Error in output of '${name}'\n - ${error.message}`;
|
|
300
|
+
throw error;
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
return compiled.fn;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function assertConvergedRel(rel, name) {
|
|
307
|
+
const status = rel?.typeDerivation?.status || "unknown";
|
|
308
|
+
if (status !== "converged") {
|
|
309
|
+
throw new Error(`Cannot run '${name}' without envelopes: type derivation is ${status}`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function compiledConvergedExp(findCode, exp, typePatternGraph, options = {}) {
|
|
314
|
+
const requireConverged = options.requireConverged === true;
|
|
315
|
+
const cached = exp._compiledConvergedRun;
|
|
316
|
+
if (
|
|
317
|
+
cached?.findCode === findCode &&
|
|
318
|
+
cached?.typePatternGraph === typePatternGraph &&
|
|
319
|
+
cached?.defs === run_converged.defs &&
|
|
320
|
+
cached?.requireConverged === requireConverged
|
|
321
|
+
) {
|
|
322
|
+
return cached.fn;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const compiled = {
|
|
326
|
+
findCode,
|
|
327
|
+
typePatternGraph,
|
|
328
|
+
defs: run_converged.defs,
|
|
329
|
+
requireConverged,
|
|
330
|
+
target: null,
|
|
331
|
+
fn(value) {
|
|
332
|
+
return compiled.target(value);
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
exp._compiledConvergedRun = compiled;
|
|
336
|
+
|
|
337
|
+
let fn;
|
|
338
|
+
switch (exp.op) {
|
|
339
|
+
case "code":
|
|
340
|
+
case "filter":
|
|
341
|
+
case "identity":
|
|
342
|
+
fn = (value) => value;
|
|
343
|
+
break;
|
|
344
|
+
case "ref":
|
|
345
|
+
{
|
|
346
|
+
const defn = run_converged.defs?.rels?.[exp.ref];
|
|
347
|
+
if (defn != undefined) {
|
|
348
|
+
if (requireConverged) assertConvergedRel(defn, exp.ref);
|
|
349
|
+
const refFn = compiledConvergedExp(findCode, defn.def, defn.typePatternGraph, options);
|
|
350
|
+
fn = (value) => value === undefined ? undefined : refFn(value);
|
|
351
|
+
} else {
|
|
352
|
+
const builtin_func = builtin[exp.ref];
|
|
353
|
+
if (builtin_func != null) {
|
|
354
|
+
fn = (value) => value === undefined ? undefined : builtin_func(value);
|
|
355
|
+
} else {
|
|
356
|
+
fn = () => {
|
|
357
|
+
throw(`Unknown ref: '${exp.ref}'`);
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
break;
|
|
363
|
+
case "dot":
|
|
364
|
+
fn = (value) => {
|
|
365
|
+
if (value === undefined || !(value instanceof Product)) return undefined;
|
|
366
|
+
return Object.hasOwn(value.product, exp.dot) ? value.product[exp.dot] : undefined;
|
|
367
|
+
};
|
|
368
|
+
break;
|
|
369
|
+
case "div":
|
|
370
|
+
fn = (value) => {
|
|
371
|
+
if (value === undefined || !(value instanceof Variant) || value.tag !== exp.div) return undefined;
|
|
372
|
+
return value.value;
|
|
373
|
+
};
|
|
374
|
+
break;
|
|
375
|
+
case "comp": {
|
|
376
|
+
const parts = exp.comp.map((part) => compiledConvergedExp(findCode, part, typePatternGraph, options));
|
|
377
|
+
fn = (value) => {
|
|
378
|
+
for (let i = 0, len = parts.length; i < len; i++) {
|
|
379
|
+
value = parts[i](value);
|
|
380
|
+
if (value === undefined) return undefined;
|
|
381
|
+
}
|
|
382
|
+
return value;
|
|
383
|
+
};
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
case "union": {
|
|
387
|
+
const parts = exp.union.map((part) => compiledConvergedExp(findCode, part, typePatternGraph, options));
|
|
388
|
+
fn = (value) => {
|
|
389
|
+
if (value === undefined) return undefined;
|
|
390
|
+
for (let i = 0, len = parts.length; i < len; i++) {
|
|
391
|
+
const result = parts[i](value);
|
|
392
|
+
if (result !== undefined) return result;
|
|
393
|
+
}
|
|
394
|
+
return undefined;
|
|
395
|
+
};
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
case "product": {
|
|
399
|
+
const labels = exp.product.map(({ label }) => label);
|
|
400
|
+
const fieldFns = exp.product.map(({ exp: e }) => compiledConvergedExp(findCode, e, typePatternGraph, options));
|
|
401
|
+
fn = (value) => {
|
|
402
|
+
if (value === undefined) return undefined;
|
|
403
|
+
const result = {};
|
|
404
|
+
for (let i = 0, len = labels.length; i < len; i++) {
|
|
405
|
+
const r = fieldFns[i](value);
|
|
406
|
+
if (r === undefined) return undefined;
|
|
407
|
+
result[labels[i]] = r;
|
|
408
|
+
}
|
|
409
|
+
return new Product(result);
|
|
410
|
+
};
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
case "vid":
|
|
414
|
+
fn = (value) => value === undefined ? undefined : new Variant(exp.vid, value);
|
|
415
|
+
break;
|
|
416
|
+
default:
|
|
417
|
+
assert(false,`Unknown operation: '${exp.op}'`);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
compiled.target = fn;
|
|
421
|
+
return compiled.fn;
|
|
422
|
+
}
|
|
423
|
+
|
|
269
424
|
function verify(findCode, code, value) {
|
|
270
425
|
"use strict";
|
|
271
426
|
if (code == null) return false;
|
|
@@ -296,7 +451,22 @@ function run(findCode, exp, value, typePatternGraph) {
|
|
|
296
451
|
return compiledExp(findCode, exp, typePatternGraph || null)(value);
|
|
297
452
|
}
|
|
298
453
|
|
|
454
|
+
function run_rel(findCode, relDef, value, name) {
|
|
455
|
+
return compiledRel(findCode, relDef, name)(value);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function run_converged(findCode, exp, value, typePatternGraph, options = {}) {
|
|
459
|
+
const graph = typePatternGraph || null;
|
|
460
|
+
const result = compiledConvergedExp(findCode, exp, graph, options)(value);
|
|
461
|
+
if (result === undefined) return undefined;
|
|
462
|
+
return withPattern(result, outputPattern(graph, exp));
|
|
463
|
+
}
|
|
464
|
+
|
|
299
465
|
run.builtin = builtin;
|
|
466
|
+
run_converged.builtin = builtin;
|
|
467
|
+
run_converged.defs = null;
|
|
468
|
+
|
|
469
|
+
const run_coverged = run_converged;
|
|
300
470
|
|
|
301
471
|
export default run;
|
|
302
|
-
export { run
|
|
472
|
+
export { run, run_rel, run_converged, run_coverged };
|
package/test-k-object.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
compileLibraryBuffer,
|
|
12
12
|
decodeObject,
|
|
13
13
|
objectToFunction,
|
|
14
|
+
runConvergedObject,
|
|
14
15
|
decompileObjectBuffer,
|
|
15
16
|
extractAliasesFromObject
|
|
16
17
|
} from "./object.mjs";
|
|
@@ -54,10 +55,30 @@ const objectBuffer = compileObjectBuffer(`
|
|
|
54
55
|
$ int = < bits +, bits - >;
|
|
55
56
|
{}|_|0|1|+ $int
|
|
56
57
|
`);
|
|
57
|
-
|
|
58
|
+
assert.equal(objectBuffer.subarray(0, 5).toString("utf8"), "KOBJ\n");
|
|
59
|
+
const object = decodeObject(objectBuffer);
|
|
60
|
+
assert.equal("version" in object, false);
|
|
61
|
+
assert(Object.values(object.codes).every((code) => code.typeDerivation == null));
|
|
62
|
+
assert.equal(JSON.stringify(object.codes).includes('"start"'), false);
|
|
63
|
+
assert.equal(JSON.stringify(object.codes).includes('"end"'), false);
|
|
64
|
+
assert.equal(JSON.stringify(object.meta).includes('"spans"'), false);
|
|
65
|
+
assert(Object.values(object.rels).every((rel) =>
|
|
66
|
+
JSON.stringify(Object.keys(rel.typeDerivation || {}).sort()) === JSON.stringify(["status"]) &&
|
|
67
|
+
rel.typeDerivation.status === "converged"
|
|
68
|
+
));
|
|
69
|
+
assert.notEqual(object.rels.__main__.def.comp?.[0]?.op, "filter");
|
|
70
|
+
assert.equal(JSON.stringify(object.rels.__main__.def).includes('"start"'), false);
|
|
71
|
+
assert(object.meta[object.relAlias.__main__]?.origins?.some((origin) =>
|
|
72
|
+
origin.name === "__main__" && origin.start && origin.end
|
|
73
|
+
));
|
|
74
|
+
|
|
75
|
+
const objectFn = objectToFunction(object);
|
|
58
76
|
const objectResult = objectFn(new Product({}));
|
|
59
77
|
assert.deepEqual(objectResult.pattern, INT_PATTERN);
|
|
60
78
|
assert.deepEqual(objectResult.toJSON(), { "+": { "1": { "0": "_" } } });
|
|
79
|
+
const convergedObjectResult = runConvergedObject(object, new Product({}));
|
|
80
|
+
assert.deepEqual(convergedObjectResult.pattern, INT_PATTERN);
|
|
81
|
+
assert.deepEqual(convergedObjectResult.toJSON(), objectResult.toJSON());
|
|
61
82
|
|
|
62
83
|
const decompiledSource = decompileObjectBuffer(objectBuffer);
|
|
63
84
|
const decompiledResult = k.compile(decompiledSource)(new Product({}));
|
|
@@ -78,15 +99,28 @@ const sccSource = decompileObjectBuffer(compileObjectBuffer("c = {}; b = c |x; a
|
|
|
78
99
|
assert.match(sccSource, /----- rels -----\nPQgV = .+\n\naQAD = .+\n\nN9UH = /s);
|
|
79
100
|
assert.deepEqual(k.compile(sccSource)(new Product({})).toJSON(), { y: "x" });
|
|
80
101
|
|
|
81
|
-
const
|
|
102
|
+
const libraryBuffer = compileLibraryBuffer("$ nat = <{} zero, nat succ>;\nsucc = |succ;\n", { source: "defs-only.k" });
|
|
103
|
+
assert.equal(libraryBuffer.subarray(0, 1).toString("utf8"), "{");
|
|
104
|
+
const library = decodeObject(libraryBuffer);
|
|
105
|
+
assert.equal("version" in library, false);
|
|
82
106
|
assert.equal(library.main, null);
|
|
107
|
+
assert(Object.values(library.codes).every((code) => code.typeDerivation == null));
|
|
108
|
+
assert.equal(JSON.stringify(library.codes).includes('"start"'), false);
|
|
109
|
+
assert.equal(JSON.stringify(library.codes).includes('"end"'), false);
|
|
110
|
+
assert.equal(JSON.stringify(library.meta).includes('"spans"'), false);
|
|
111
|
+
assert(Object.values(library.rels).every((rel) =>
|
|
112
|
+
JSON.stringify(Object.keys(rel.typeDerivation || {}).sort()) === JSON.stringify(["status"]) &&
|
|
113
|
+
rel.typeDerivation.status === "converged"
|
|
114
|
+
));
|
|
115
|
+
assert(Object.values(library.rels).every((rel) => rel.def.op !== "comp" || rel.def.comp[0]?.op !== "filter"));
|
|
83
116
|
assert(Object.values(library.meta).some(({ type, origins }) =>
|
|
84
117
|
type === "code" &&
|
|
85
118
|
origins.some((origin) =>
|
|
86
119
|
origin.source === "defs-only.k" &&
|
|
87
120
|
origin.name === "nat" &&
|
|
88
|
-
|
|
89
|
-
|
|
121
|
+
typeof origin.compiledAt === "string" &&
|
|
122
|
+
origin.start &&
|
|
123
|
+
origin.end
|
|
90
124
|
)
|
|
91
125
|
));
|
|
92
126
|
assert(Object.values(library.meta).some(({ type, origins }) =>
|
|
@@ -94,8 +128,9 @@ assert(Object.values(library.meta).some(({ type, origins }) =>
|
|
|
94
128
|
origins.some((origin) =>
|
|
95
129
|
origin.source === "defs-only.k" &&
|
|
96
130
|
origin.name === "succ" &&
|
|
97
|
-
|
|
98
|
-
|
|
131
|
+
typeof origin.compiledAt === "string" &&
|
|
132
|
+
origin.start &&
|
|
133
|
+
origin.end
|
|
99
134
|
)
|
|
100
135
|
));
|
|
101
136
|
|
|
@@ -110,9 +145,25 @@ assert.match(derivedAliases, /^other = @/m);
|
|
|
110
145
|
assert(derivedAliases.indexOf("$ nat = @") < derivedAliases.indexOf("other = @"));
|
|
111
146
|
assert(derivedAliases.indexOf("other = @") < derivedAliases.indexOf("succ = @"));
|
|
112
147
|
|
|
148
|
+
{
|
|
149
|
+
const warn = console.warn;
|
|
150
|
+
console.warn = () => {};
|
|
151
|
+
try {
|
|
152
|
+
const nonConverged = decodeObject(compileObjectBuffer("f = .x f; f", {
|
|
153
|
+
convergence: { strategy: "fixed_point", maxIterations: 1 }
|
|
154
|
+
}));
|
|
155
|
+
assert.equal(nonConverged.rels.__main__.typeDerivation.status, "not-converged");
|
|
156
|
+
assert.throws(
|
|
157
|
+
() => runConvergedObject(nonConverged, new Product({ x: new Product({}) })),
|
|
158
|
+
/type derivation is not-converged/
|
|
159
|
+
);
|
|
160
|
+
} finally {
|
|
161
|
+
console.warn = warn;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
113
165
|
const aliasSnippet = extractAliasesFromObject({
|
|
114
166
|
format: "k-object",
|
|
115
|
-
version: 2,
|
|
116
167
|
codes: {},
|
|
117
168
|
rels: {},
|
|
118
169
|
meta: {
|