@mark1russell7/coeval 0.15.0 → 0.17.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/README.md +9 -4
- package/build/index.d.ts +828 -249
- package/build/index.d.ts.map +1 -1
- package/build/index.js +3138 -386
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/agree.ts +193 -21
- package/src/conform.ts +49 -5
- package/src/deref.ts +97 -14
- package/src/evaluate.ts +311 -35
- package/src/facts.ts +113 -78
- package/src/formula.ts +55 -10
- package/src/index.ts +33 -1
- package/src/meaning.ts +472 -0
- package/src/merge.ts +46 -14
- package/src/reads.ts +9 -2
- package/src/reference.ts +90 -14
- package/src/replay.ts +964 -18
- package/src/schedule.ts +14 -4
- package/src/static.ts +2 -2
- package/src/stdlib.ts +81 -6
- package/src/syntax.ts +357 -52
- package/src/types.ts +443 -16
package/src/reference.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type Envelope, type Json, type Key, isEnvelope, isRecord, typeName } from "./doc.ts";
|
|
1
|
+
import { type Envelope, type Json, type Key, isEnvelope, isKey, isRecord, typeName } from "./doc.ts";
|
|
2
2
|
import { ask } from "./ladder.ts";
|
|
3
3
|
import { type AttributeDeclaration, type CapsRule, type Kind, type TreeContract, equationType, lookupKind } from "./world.ts";
|
|
4
4
|
import { type PartSlot, type Split, partsOf, pathMatches } from "./parts.ts";
|
|
5
|
-
import { type Cap, type Context, type HostThing, type Resolution, type ResolveOptions, type Thing, THING, isHostThing, resolve, valueOfThing, worldThing } from "./deref.ts";
|
|
5
|
+
import { type Cap, type Context, type HostThing, type Resolution, type ResolveOptions, type Thing, ROOTS, THING, isHostThing, resolve, valueOfThing, worldThing } from "./deref.ts";
|
|
6
6
|
import { type AttributeRung, STD_EVALUATORS } from "./stdlib.ts";
|
|
7
7
|
import { type EvalError, type Failed, type Result, Failure, describe, fail, ok, placed, thrown } from "./errors.ts";
|
|
8
8
|
import {
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
type Machine,
|
|
15
15
|
type Part,
|
|
16
16
|
type Perform,
|
|
17
|
+
type Read,
|
|
17
18
|
type RunFn,
|
|
18
19
|
type RunInput,
|
|
19
20
|
type RunPart,
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
makeContext,
|
|
24
25
|
permits,
|
|
25
26
|
placeText,
|
|
27
|
+
reachedOf,
|
|
26
28
|
waitedReason,
|
|
27
29
|
} from "./evaluate.ts";
|
|
28
30
|
import { type FormulaProfile, type OpFn, Held, STD_EXT } from "./formula.ts";
|
|
@@ -57,6 +59,12 @@ export interface ReferenceOptions {
|
|
|
57
59
|
* each verb by the same rules (VERB.AGREE).
|
|
58
60
|
*/
|
|
59
61
|
readonly verb?: string;
|
|
62
|
+
/**
|
|
63
|
+
* A list that gets each read of a resolution, in its order (REF.READS). A read has the shape of a read of a plan:
|
|
64
|
+
* the path, what it reached and the mark `free`. The reads of a closure and of an equation of a tree go in the
|
|
65
|
+
* same list. The reference adds no kept reads, because it has no static reads of a closure.
|
|
66
|
+
*/
|
|
67
|
+
readonly reads?: Read[];
|
|
60
68
|
}
|
|
61
69
|
|
|
62
70
|
/** The options of a run of the reference: the options of an evaluation, with the host and a signal (REF.RUN). */
|
|
@@ -80,6 +88,13 @@ interface Scope {
|
|
|
80
88
|
alls: number;
|
|
81
89
|
}
|
|
82
90
|
|
|
91
|
+
/** The record of the reads of one evaluation of the reference (REF.READS). */
|
|
92
|
+
interface Reading {
|
|
93
|
+
readonly list: Read[];
|
|
94
|
+
/** The context at the root of the evaluation. A free read reads this context. */
|
|
95
|
+
readonly root: Context;
|
|
96
|
+
}
|
|
97
|
+
|
|
83
98
|
/** The state of one evaluation of the reference. */
|
|
84
99
|
interface Env {
|
|
85
100
|
readonly machine: Machine;
|
|
@@ -95,6 +110,8 @@ interface Env {
|
|
|
95
110
|
readonly verb: string;
|
|
96
111
|
/** The scope of the code that evaluates at this time. `all` changes it for each item. */
|
|
97
112
|
scope: Scope;
|
|
113
|
+
/** The record of the reads. A nested evaluation for an equation of a tree keeps the record of its tree. */
|
|
114
|
+
readonly reading: Reading | undefined;
|
|
98
115
|
}
|
|
99
116
|
|
|
100
117
|
/** The code that answers a document: a function evaluator, a runner, or a gap of the reference. */
|
|
@@ -221,6 +238,44 @@ function resolveFully(machine: Machine, ctx: Context, path: readonly unknown[],
|
|
|
221
238
|
return options?.hops === true ? found : { ...found, hops: [] };
|
|
222
239
|
}
|
|
223
240
|
|
|
241
|
+
/** The text that names a resolution in the error of an exception of the host (READS.RECORD). */
|
|
242
|
+
const READ_FAILED = "the resolution of a path";
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* This function resolves a path for a document, and records the read when the evaluation has a list (REF.READS,
|
|
246
|
+
* READS.RECORD). An exception of the host gives the error value of the read, and the exception goes on.
|
|
247
|
+
*/
|
|
248
|
+
function readAt(env: Env, ctx: Context, path: readonly unknown[], options?: ResolveOptions): Resolution {
|
|
249
|
+
const reading = env.reading;
|
|
250
|
+
if (reading === undefined) return resolveFully(env.machine, ctx, path, options);
|
|
251
|
+
const free = freeIn(reading.root, ctx, path);
|
|
252
|
+
let found: Resolution;
|
|
253
|
+
try {
|
|
254
|
+
found = resolveFully(env.machine, ctx, path, options);
|
|
255
|
+
} catch (error) {
|
|
256
|
+
reading.list.push({ path, reached: thrown(error, READ_FAILED), free });
|
|
257
|
+
throw error;
|
|
258
|
+
}
|
|
259
|
+
reading.list.push(options?.hops === true ? { path, reached: reachedOf(found), free, hops: found.hops } : { path, reached: reachedOf(found), free });
|
|
260
|
+
return found;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* This function tells if a read reads the context at the root of the evaluation (READS.FREE). The reference reads
|
|
265
|
+
* the rule from SPEC. A name that a kind binds, a `self` that a kind sets and a path without a root are not free. A
|
|
266
|
+
* name that a kind does not bind keeps its value from the root.
|
|
267
|
+
*/
|
|
268
|
+
function freeIn(root: Context, ctx: Context, path: readonly unknown[]): boolean {
|
|
269
|
+
const head = path[0];
|
|
270
|
+
if (head === "self") return Object.is(ctx.self, root.self);
|
|
271
|
+
if (head !== "names") return typeof head === "string" && (ROOTS as readonly string[]).includes(head);
|
|
272
|
+
const name = path[1];
|
|
273
|
+
if (ctx.names === root.names || !isKey(name)) return true;
|
|
274
|
+
const key = String(name);
|
|
275
|
+
const bound = Object.hasOwn(ctx.names, key);
|
|
276
|
+
return bound === Object.hasOwn(root.names, key) && (!bound || Object.is(ctx.names[key], root.names[key]));
|
|
277
|
+
}
|
|
278
|
+
|
|
224
279
|
/**
|
|
225
280
|
* This function causes an effect when the capabilities permit it (RUN.CAP, CAP.HOOK). It records the effect with its
|
|
226
281
|
* address (RUN.ADDRESS) and its outcome (RUN.OUTCOME). The mock answers the effect at once (RUN.MOCK). Without a
|
|
@@ -385,7 +440,7 @@ function evalInput(env: Env, seen: Seen, at: At): EvalInput {
|
|
|
385
440
|
place: at.place,
|
|
386
441
|
machine: env.machine,
|
|
387
442
|
...(at.address ? { address: true } : {}),
|
|
388
|
-
resolve: (path, options) =>
|
|
443
|
+
resolve: (path, options) => readAt(env, at.ctx, path, options),
|
|
389
444
|
effect: (kind, payload) => causeFromEvaluator(env, at.ctx, kind, payload, at.place),
|
|
390
445
|
fill,
|
|
391
446
|
};
|
|
@@ -413,7 +468,7 @@ function runInput(env: Env, seen: Seen, at: At): RunInput {
|
|
|
413
468
|
context: at.ctx,
|
|
414
469
|
place: at.place,
|
|
415
470
|
machine: env.machine,
|
|
416
|
-
resolve: (path, options) =>
|
|
471
|
+
resolve: (path, options) => readAt(env, at.ctx, path, options),
|
|
417
472
|
effect: (kind, payload) => causeAndWait(env, at.ctx, kind, payload, at.place),
|
|
418
473
|
all: (list, options) => together(env, list, options),
|
|
419
474
|
fill,
|
|
@@ -458,6 +513,7 @@ function* together(env: Env, list: readonly RunPart[], options: AllOptions = {})
|
|
|
458
513
|
outer.alls += 1;
|
|
459
514
|
let failed = false;
|
|
460
515
|
let started = 0;
|
|
516
|
+
let before = 0;
|
|
461
517
|
const end = (index: number, result: Result): void => {
|
|
462
518
|
results[index] = result;
|
|
463
519
|
if (failFast && !result.ok) failed = true;
|
|
@@ -508,10 +564,11 @@ function* together(env: Env, list: readonly RunPart[], options: AllOptions = {})
|
|
|
508
564
|
};
|
|
509
565
|
startMore();
|
|
510
566
|
while (waiting.length > 0 && !failed) {
|
|
511
|
-
const chosen = env.journal?.next?.(address, waiting.map((waiter) => waiter.index));
|
|
567
|
+
const chosen = env.journal?.next?.(address, waiting.map((waiter) => waiter.index), before);
|
|
512
568
|
const pick = chosen === undefined ? undefined : waiting.find((waiter) => waiter.index === chosen);
|
|
513
569
|
const resumed = (yield pick === undefined ? Promise.race(waiting.map((waiter) => waiter.next)) : pick.next) as Resumed;
|
|
514
570
|
env.journal?.settled?.(address, resumed.waiter.index);
|
|
571
|
+
before += 1;
|
|
515
572
|
waiting.splice(waiting.indexOf(resumed.waiter), 1);
|
|
516
573
|
goOn(resumed.waiter.index, resumed.waiter.steps, resumed);
|
|
517
574
|
startMore();
|
|
@@ -543,9 +600,18 @@ function* together(env: Env, list: readonly RunPart[], options: AllOptions = {})
|
|
|
543
600
|
* @returns The result.
|
|
544
601
|
*/
|
|
545
602
|
export function reference(machine: Machine, doc: unknown, context: Context = makeContext(), options: ReferenceOptions = {}): Result {
|
|
546
|
-
|
|
603
|
+
return evaluateRoot(machine, doc, context, options, undefined);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* This function evaluates a document from its root. An equation of a tree gives the record of its tree. Thus the
|
|
608
|
+
* reads of the equation go in the list of the outer evaluation (REF.READS).
|
|
609
|
+
*/
|
|
610
|
+
function evaluateRoot(machine: Machine, doc: unknown, context: Context, options: ReferenceOptions, outer: Reading | undefined): Result {
|
|
611
|
+
const at = rootAt(machine, doc, context, options);
|
|
612
|
+
const env = envOf(machine, options, false, at.ctx, outer);
|
|
547
613
|
try {
|
|
548
|
-
return within(env, () => evaluateAt(env,
|
|
614
|
+
return within(env, () => evaluateAt(env, at));
|
|
549
615
|
} catch (error) {
|
|
550
616
|
return placed(thrown(error, "the reference"), []);
|
|
551
617
|
}
|
|
@@ -567,13 +633,17 @@ export function reference(machine: Machine, doc: unknown, context: Context = mak
|
|
|
567
633
|
* @returns The result, when the run ends.
|
|
568
634
|
*/
|
|
569
635
|
export function referenceRun(machine: Machine, doc: unknown, context: Context = makeContext(), options: ReferenceRunOptions = {}): Promise<Result> {
|
|
570
|
-
const
|
|
571
|
-
const
|
|
636
|
+
const at = rootAt(machine, doc, context, options);
|
|
637
|
+
const env = envOf(machine, options, true, at.ctx, undefined);
|
|
638
|
+
const steps = stepsAt(env, at);
|
|
572
639
|
return driveRun(env, steps, options.signal).catch((error: unknown) => placed(thrown(error, "the reference"), []));
|
|
573
640
|
}
|
|
574
641
|
|
|
575
|
-
/**
|
|
576
|
-
function
|
|
642
|
+
/**
|
|
643
|
+
* This function makes the state of the reference for one evaluation or one run. The record of the reads comes from
|
|
644
|
+
* the evaluation around it, or from the list of the options.
|
|
645
|
+
*/
|
|
646
|
+
function envOf(machine: Machine, options: ReferenceOptions, running: boolean, root: Context, outer: Reading | undefined): Env {
|
|
577
647
|
const perform = options.perform;
|
|
578
648
|
return {
|
|
579
649
|
machine,
|
|
@@ -585,6 +655,7 @@ function envOf(machine: Machine, options: ReferenceOptions, running: boolean): E
|
|
|
585
655
|
running,
|
|
586
656
|
verb: options.verb ?? "eval",
|
|
587
657
|
scope: { prefix: "", counts: new Map(), alls: 0 },
|
|
658
|
+
reading: outer ?? (options.reads === undefined ? undefined : { list: options.reads, root }),
|
|
588
659
|
};
|
|
589
660
|
}
|
|
590
661
|
|
|
@@ -698,13 +769,14 @@ function refNodeOf(thing: unknown): RefNode | undefined {
|
|
|
698
769
|
|
|
699
770
|
/**
|
|
700
771
|
* TREE.LADDER: an equation of the kind of a node evaluates in the reference. It gets the node as `self`, the names of
|
|
701
|
-
* its rung and no capabilities. A bare equation gets the type `of` of the parts of the kind.
|
|
772
|
+
* its rung and no capabilities. A bare equation gets the type `of` of the parts of the kind. The reads of the
|
|
773
|
+
* equation go in the record of the evaluation that made the node (REF.READS).
|
|
702
774
|
*/
|
|
703
775
|
function equationOf(node: RefNode, term: Json, names: Readonly<Record<string, unknown>>): Result {
|
|
704
776
|
const env = node.env;
|
|
705
777
|
if (env === undefined) return fail("#NOEVAL", "The evaluation of an equation needs the machine.");
|
|
706
778
|
const ctx: Context = { ...node.ctx, names, caps: [], self: node.thing };
|
|
707
|
-
return
|
|
779
|
+
return evaluateRoot(env.machine, term, ctx, { definitions: env.definitions, ...(node.of === undefined ? {} : { declaredType: node.of }) }, env.reading);
|
|
708
780
|
}
|
|
709
781
|
|
|
710
782
|
function termIn<T>(table: Readonly<Record<string, T>> | undefined, name: string): T | undefined {
|
|
@@ -960,7 +1032,11 @@ export function formulaDefinitions(profile: FormulaProfile = {}): Definitions {
|
|
|
960
1032
|
// FORMULA.LET: a path through a name whose binding failed gives the error of the binding.
|
|
961
1033
|
const head = path[0] === "names" && typeof path[1] === "string" ? path[1] : undefined;
|
|
962
1034
|
const bound = head === undefined ? undefined : own(context.names, head);
|
|
963
|
-
if (bound instanceof Held)
|
|
1035
|
+
if (bound instanceof Held) {
|
|
1036
|
+
// The result comes from the binding of the name, thus the name is a read (READS.RECORD).
|
|
1037
|
+
walk(["names", head]);
|
|
1038
|
+
return { ok: false, error: bound.error };
|
|
1039
|
+
}
|
|
964
1040
|
const found = walk(steps);
|
|
965
1041
|
if (!found.ok) return fail(found.code, found.reason);
|
|
966
1042
|
// FORMULA.ADDRESS: an address gives what the path reaches.
|