@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/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@fraczak/k",
3
- "version": "6.0.3",
3
+ "version": "6.1.0",
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": {
7
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",
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-repl.mjs; ./tests.sh",
9
9
  "deps:graph": "node scripts/generate-dependency-graph.mjs"
10
10
  },
11
11
  "repository": {
@@ -16,14 +16,20 @@
16
16
  "license": "ISC",
17
17
  "bin": {
18
18
  "k": "./k.mjs",
19
- "k-compile-object": "./objects/compile.mjs",
20
- "k-decompile-object": "./objects/decompile.mjs",
19
+ "k-compile": "./objects/compile.mjs",
20
+ "k-compile-lib": "./objects/compile-lib.mjs",
21
+ "k-decompile": "./objects/decompile.mjs",
21
22
  "k-extract-aliases": "./objects/extract-aliases.mjs",
23
+ "k-ieee": "./codecs/ieee.mjs",
24
+ "k-int": "./codecs/int.mjs",
25
+ "k-json": "./codecs/json.mjs",
22
26
  "k-parse": "./codecs/k-parse.mjs",
23
- "k-pattern": "./patterns/from-k.mjs",
24
27
  "k-print": "./codecs/k-print.mjs",
25
- "k-repl": "./repl2.mjs",
26
- "k-repl2": "./repl2.mjs"
28
+ "k-repl": "./repl.mjs",
29
+ "k-show": "./codecs/show.mjs",
30
+ "k-unit": "./codecs/unit.mjs",
31
+ "k-utf16": "./codecs/utf16.mjs",
32
+ "k-utf8": "./codecs/utf8.mjs"
27
33
  },
28
34
  "devDependencies": {
29
35
  "jiwson": "^0.1.2"
@@ -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(({ source, name, compiledAt }) => ({
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({ source: options.source || null, name, compiledAt: now });
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({ source: options.source || null, name, compiledAt: now });
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} = ${prettyRel(rel.def)}; -- ${hash}`];
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`);
@@ -825,6 +840,16 @@ function helpText() {
825
840
  ].join("\n");
826
841
  }
827
842
 
843
+ function cliUsage() {
844
+ console.log("Usage: k-repl");
845
+ console.log(" k-repl -h");
846
+ console.log("");
847
+ console.log("Start the interactive k interpreter. Type :help inside the REPL for commands.");
848
+ console.log("");
849
+ console.log("Options:");
850
+ console.log(" -h, --help Show this help.");
851
+ }
852
+
828
853
  function startRepl() {
829
854
  const state = createState();
830
855
  const rl = readline.createInterface({
@@ -901,6 +926,15 @@ function startRepl() {
901
926
  }
902
927
 
903
928
  if (isMainEntrypoint()) {
929
+ const args = argv.slice(2);
930
+ if (args.includes("-h") || args.includes("--help")) {
931
+ cliUsage();
932
+ exit(0);
933
+ }
934
+ if (args.length > 0) {
935
+ cliUsage();
936
+ exit(1);
937
+ }
904
938
  startRepl();
905
939
  }
906
940
 
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 = compiledExp(findCode, defn.def, defn.typePatternGraph);
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
- const objectFn = objectToFunction(decodeObject(objectBuffer));
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 library = decodeObject(compileLibraryBuffer("$ nat = <{} zero, nat succ>;\nsucc = |succ;\n", { source: "defs-only.k" }));
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
- JSON.stringify(Object.keys(origin).sort()) === JSON.stringify(["compiledAt", "name", "source"]) &&
89
- typeof origin.compiledAt === "string"
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
- JSON.stringify(Object.keys(origin).sort()) === JSON.stringify(["compiledAt", "name", "source"]) &&
98
- typeof origin.compiledAt === "string"
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: {
@@ -15,7 +15,7 @@ import {
15
15
  isMainEntrypoint,
16
16
  lineHasExplicitContinuation,
17
17
  lineTerminatesSnippet
18
- } from "./repl2.mjs";
18
+ } from "./repl.mjs";
19
19
  import { decodeObject, objectToFunction } from "./object.mjs";
20
20
  import { Product } from "./Value.mjs";
21
21
 
@@ -78,11 +78,11 @@ await assert.rejects(
78
78
  /Type Error in 'filter' \(lines 1:1\.\.\.1:4\)/
79
79
  );
80
80
 
81
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "k-repl2-"));
81
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "k-repl-"));
82
82
  const libPath = path.join(tmpDir, "session.klib");
83
83
  const koPath = path.join(tmpDir, "succ.ko");
84
84
  const sourcePath = path.join(tmpDir, "session.k");
85
- const symlinkPath = path.resolve(".test-k-repl2-link");
85
+ const symlinkPath = path.resolve(".test-k-repl-link");
86
86
  output = await evaluateInput(`:klib ${libPath}`, state);
87
87
  assert.equal(output[0], `saved ${libPath}`);
88
88
 
@@ -106,7 +106,7 @@ assert.match(output[0], /^nat = @/m);
106
106
  output = await evaluateInput(":rels", loadedSource);
107
107
  assert.match(output[0], /^succ = @/m);
108
108
 
109
- const replPath = fileURLToPath(new URL("./repl2.mjs", import.meta.url));
109
+ const replPath = fileURLToPath(new URL("./repl.mjs", import.meta.url));
110
110
  try {
111
111
  fs.rmSync(symlinkPath, { force: true });
112
112
  } catch {}