@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/schedule.ts
CHANGED
|
@@ -166,12 +166,12 @@ const NO_KEYS: readonly Key[] = /* @__PURE__ */ Object.freeze([]);
|
|
|
166
166
|
const NO_EDGES: readonly NodeEdge[] = /* @__PURE__ */ Object.freeze([]);
|
|
167
167
|
|
|
168
168
|
/** This function gives the error of a node on a cycle. It names only the node, thus each order of demands gives the same error. */
|
|
169
|
-
function cycleOf(id: string): Failed {
|
|
169
|
+
export function cycleOf(id: string): Failed {
|
|
170
170
|
return fail("#CYCLE!", `The node ${id} is on a cycle of reads.`);
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
/** This function gives the error of a read of a node on the run side. */
|
|
174
|
-
function runSide(id: string): Failed {
|
|
174
|
+
export function runSide(id: string): Failed {
|
|
175
175
|
return fail("#REF!", `The node ${id} is on the run side, thus it has no value for a reader.`);
|
|
176
176
|
}
|
|
177
177
|
|
|
@@ -354,10 +354,12 @@ export function schedule(machine: Machine, options: ScheduleOptions = {}): Sched
|
|
|
354
354
|
|
|
355
355
|
/**
|
|
356
356
|
* This function gives the state of the current data epoch. Each data epoch has a new host thing at the root
|
|
357
|
-
* `world`. Thus a read that reaches the whole root changes at each change of data (READS.KEPT).
|
|
357
|
+
* `world`. Thus a read that reaches the whole root changes at each change of data (READS.KEPT). A kept read of a
|
|
358
|
+
* closure reads a node through `known`, without a demand (SCHED.CLOSURE).
|
|
358
359
|
*/
|
|
359
360
|
const current = (): Epoch => {
|
|
360
361
|
if (now !== undefined) return now;
|
|
362
|
+
const values = new Map<string, Result>();
|
|
361
363
|
const nodesThing: HostThing = {
|
|
362
364
|
[THING]: true,
|
|
363
365
|
label: "nodes",
|
|
@@ -371,10 +373,18 @@ export function schedule(machine: Machine, options: ScheduleOptions = {}): Sched
|
|
|
371
373
|
},
|
|
372
374
|
has: (key) => docs.has(String(key)),
|
|
373
375
|
toJson: record,
|
|
376
|
+
// A demand from a kept read can reach the node in evaluation, and that cycle is false (SCHED.ONE-MEANING).
|
|
377
|
+
// Thus only a node with its result in this epoch gives a value. Each such value is final (D31).
|
|
378
|
+
known(key) {
|
|
379
|
+
const result = values.get(String(key));
|
|
380
|
+
if (result === undefined) return undefined;
|
|
381
|
+
if (!result.ok) throw new Failure(result.error);
|
|
382
|
+
return { thing: result.value };
|
|
383
|
+
},
|
|
374
384
|
};
|
|
375
385
|
// The context has a world, thus `rootContext` only gives it its grant (CAP.NARROW-CODE). Each evaluation of the
|
|
376
386
|
// epoch then uses this object with no copy.
|
|
377
|
-
now = { context: rootContext(machine, { ...base, world: nodesThing }), values
|
|
387
|
+
now = { context: rootContext(machine, { ...base, world: nodesThing }), values };
|
|
378
388
|
return now;
|
|
379
389
|
};
|
|
380
390
|
|
package/src/static.ts
CHANGED
|
@@ -619,9 +619,9 @@ const FOLD_NAMES: readonly string[] = /* @__PURE__ */ Object.freeze(["acc", "ite
|
|
|
619
619
|
|
|
620
620
|
/**
|
|
621
621
|
* This function gives each equation of the tree contract of a kind, with the names that it sees (D42). An equation of
|
|
622
|
-
* `equations`, a `unit` and a `default` see no names.
|
|
622
|
+
* `equations`, a `unit` and a `default` see no names. `check` and `factsOf` read the equations with this one function.
|
|
623
623
|
*/
|
|
624
|
-
function equationsOf(kind: Kind): readonly { readonly label: string; readonly term: Json; readonly names: readonly string[] }[] {
|
|
624
|
+
export function equationsOf(kind: Kind): readonly { readonly label: string; readonly term: Json; readonly names: readonly string[] }[] {
|
|
625
625
|
const tree = kind.tree;
|
|
626
626
|
if (tree === undefined) return [];
|
|
627
627
|
const out: { readonly label: string; readonly term: Json; readonly names: readonly string[] }[] = [];
|
package/src/stdlib.ts
CHANGED
|
@@ -2,12 +2,15 @@ import { type Json, type Key, field, isRecord } from "./doc.ts";
|
|
|
2
2
|
import {
|
|
3
3
|
type EvalInput,
|
|
4
4
|
type Evaluator,
|
|
5
|
+
type InputWithRuntime,
|
|
5
6
|
type Machine,
|
|
6
7
|
type Plan,
|
|
7
8
|
type Result,
|
|
8
9
|
type RunPart,
|
|
9
10
|
type Runner,
|
|
10
11
|
type Steps,
|
|
12
|
+
RUNTIME,
|
|
13
|
+
evaluateWithin,
|
|
11
14
|
fail,
|
|
12
15
|
ok,
|
|
13
16
|
planner,
|
|
@@ -17,6 +20,7 @@ import { type Cap, type HostThing, type Thing, type Walker, THING, isHostThing,
|
|
|
17
20
|
import type { World } from "./ladder.ts";
|
|
18
21
|
import { type AttributeDeclaration, type TreeContract, equationType, lookupKind } from "./world.ts";
|
|
19
22
|
import { STD_DOMAINS } from "./types.ts";
|
|
23
|
+
import type { SyntaxProfile } from "./syntax.ts";
|
|
20
24
|
|
|
21
25
|
/**
|
|
22
26
|
* The world of the examples: small kinds for numbers, lists, conditions, names, sequences and effects.
|
|
@@ -127,6 +131,52 @@ export const STD_WORLD: World = {
|
|
|
127
131
|
defaults: { view: "view.card" },
|
|
128
132
|
};
|
|
129
133
|
|
|
134
|
+
/**
|
|
135
|
+
* The text syntax of the standard world (K162, D64). The syntax kit (D41) reads it, thus a host of `STD_WORLD` does
|
|
136
|
+
* not write its own profile. The operators `>`, `+` and `*` make `greater`, `add` and `multiply`. A call form makes
|
|
137
|
+
* each other kind, and `ref` uses the reference syntax of the kit.
|
|
138
|
+
*
|
|
139
|
+
* A literal in a place that takes a document gets its data envelope (K159, D65). Such a place is a part of `if`,
|
|
140
|
+
* `list`, `seq` or `pure`, the value of `write`, or the whole text. The document after a header name is such a place
|
|
141
|
+
* too. Thus `list(1, "a")` evaluates to `[1, "a"]`. The forms `add`, `multiply` and `greater` write an operator document that its operator
|
|
142
|
+
* does not write, for example `add(5)` (K163).
|
|
143
|
+
*
|
|
144
|
+
* A host with more kinds spreads this profile and adds its forms to `calls.forms`. `STD_SYNTAX` stays the profile of
|
|
145
|
+
* the formula kit.
|
|
146
|
+
*/
|
|
147
|
+
export const WORLD_SYNTAX: SyntaxProfile = /* @__PURE__ */ Object.freeze({
|
|
148
|
+
operators: [
|
|
149
|
+
{ symbol: ">", fixity: "infix", prec: 4, assoc: "none", kind: "greater" },
|
|
150
|
+
{ symbol: "+", fixity: "infix", prec: 5, assoc: "flat", kind: "add" },
|
|
151
|
+
{ symbol: "*", fixity: "infix", prec: 6, assoc: "flat", kind: "multiply" },
|
|
152
|
+
],
|
|
153
|
+
calls: {
|
|
154
|
+
ops: [],
|
|
155
|
+
forms: [
|
|
156
|
+
{ name: "if", kind: "if", fields: ["test", "then", "else"], min: 2, wrap: true },
|
|
157
|
+
{ name: "list", kind: "list", wrap: true },
|
|
158
|
+
{ name: "seq", kind: "seq", wrap: true },
|
|
159
|
+
{ name: "pure", kind: "pure", min: 1, wrap: true },
|
|
160
|
+
{ name: "log", kind: "log", min: 1 },
|
|
161
|
+
{ name: "write", kind: "write", fields: ["key", "value"], wrap: ["value"] },
|
|
162
|
+
{ name: "math.sum", kind: "math.sum" },
|
|
163
|
+
{ name: "math.product", kind: "math.product" },
|
|
164
|
+
{ name: "number", kind: "number", args: "literal" },
|
|
165
|
+
{ name: "text", kind: "text", args: "literal" },
|
|
166
|
+
{ name: "bool", kind: "bool", args: "literal" },
|
|
167
|
+
{ name: "panel", kind: "panel", args: "named", fields: ["width", "height", "children"] },
|
|
168
|
+
{ name: "box", kind: "box", args: "named", fields: ["width", "height", "children"] },
|
|
169
|
+
{ name: "nearest", kind: "nearest", args: "named", fields: ["type", "follow", "self"] },
|
|
170
|
+
// An operator document with fewer terms than its operator takes, for example add(5) (K163, D65).
|
|
171
|
+
{ name: "add", kind: "add" },
|
|
172
|
+
{ name: "multiply", kind: "multiply" },
|
|
173
|
+
{ name: "greater", kind: "greater" },
|
|
174
|
+
],
|
|
175
|
+
},
|
|
176
|
+
literals: { envelopes: { number: "number", text: "text", bool: "bool" } },
|
|
177
|
+
header: { bind: "=" },
|
|
178
|
+
} satisfies SyntaxProfile);
|
|
179
|
+
|
|
130
180
|
function isNumber(value: unknown): value is number {
|
|
131
181
|
return typeof value === "number" && Number.isFinite(value);
|
|
132
182
|
}
|
|
@@ -234,7 +284,8 @@ interface NodeState {
|
|
|
234
284
|
*/
|
|
235
285
|
read(key: string): Result | undefined;
|
|
236
286
|
/**
|
|
237
|
-
* This function evaluates an equation of the kind of the node, with the node as `self` and no capabilities.
|
|
287
|
+
* This function evaluates an equation of the kind of the node, with the node as `self` and no capabilities. Its
|
|
288
|
+
* reads go in the list of the evaluation that made the node (READS.EQUATION).
|
|
238
289
|
*
|
|
239
290
|
* @param term - The equation.
|
|
240
291
|
* @param names - The names that the equation sees.
|
|
@@ -403,6 +454,8 @@ function plainOf(value: unknown, path: Set<object>): Result {
|
|
|
403
454
|
*/
|
|
404
455
|
function treeThing(input: EvalInput, build: TreeBuild, outer: Thing | undefined): HostThing {
|
|
405
456
|
const { parts, context, kind: type, place, machine } = input;
|
|
457
|
+
// The equations evaluate in the evaluation that made the node, thus their reads go in its list (READS.EQUATION).
|
|
458
|
+
const runtime = (input as InputWithRuntime)[RUNTIME];
|
|
406
459
|
const { tree, of } = contractOf(machine, input.typePath);
|
|
407
460
|
const above = stateOf(outer);
|
|
408
461
|
// Only a node of the same tree is a parent for the rungs `parent` and `copy`.
|
|
@@ -481,7 +534,7 @@ function treeThing(input: EvalInput, build: TreeBuild, outer: Thing | undefined)
|
|
|
481
534
|
read,
|
|
482
535
|
equation(term, names) {
|
|
483
536
|
if (machine === undefined) return fail("#NOEVAL", "The evaluation of an equation needs the machine.");
|
|
484
|
-
return equationPlan(machine, of, term)
|
|
537
|
+
return evaluateWithin(equationPlan(machine, of, term), { ...context, names, caps: NO_CAPS, self: node }, runtime);
|
|
485
538
|
},
|
|
486
539
|
childNames(child) {
|
|
487
540
|
const children = read("children");
|
|
@@ -560,6 +613,18 @@ export function attributeTrace(node: Thing, attribute: string): AttributeTrace |
|
|
|
560
613
|
return stateOf(node)?.trace(attribute);
|
|
561
614
|
}
|
|
562
615
|
|
|
616
|
+
/**
|
|
617
|
+
* This function gives the place of a node of the decorated tree: the keys from the root document to the document of
|
|
618
|
+
* the node (HOP.PLACE). A view puts each hop of a resolution on its node with it. The node keeps its place already,
|
|
619
|
+
* thus the function adds no record.
|
|
620
|
+
*
|
|
621
|
+
* @param thing - A thing, for example the thing of a hop.
|
|
622
|
+
* @returns The keys of the place, or `undefined` for a thing that is not a node of the decorated tree.
|
|
623
|
+
*/
|
|
624
|
+
export function placeOf(thing: Thing): readonly Key[] | undefined {
|
|
625
|
+
return stateOf(thing)?.place;
|
|
626
|
+
}
|
|
627
|
+
|
|
563
628
|
/** The path of the root `self`. A node of the tree resolves it, thus the list of reads holds the read (C4). */
|
|
564
629
|
const SELF_PATH: readonly Json[] = /* @__PURE__ */ Object.freeze(["self"]);
|
|
565
630
|
|
|
@@ -597,6 +662,11 @@ export interface NearestOptions {
|
|
|
597
662
|
readonly matches?: (node: Thing, wanted: Json | undefined) => boolean;
|
|
598
663
|
/** The most hops of one walk. The default is 256. */
|
|
599
664
|
readonly maxHops?: number;
|
|
665
|
+
/**
|
|
666
|
+
* `true`: the walk records one hop for each node on the way before the node that it reaches (HOP.EACH-STEP). Each
|
|
667
|
+
* such hop has the followed key as its step. The default is `false`: one hop for the whole walk.
|
|
668
|
+
*/
|
|
669
|
+
readonly eachStep?: boolean;
|
|
600
670
|
}
|
|
601
671
|
|
|
602
672
|
function hostType(node: Thing, wanted: Json | undefined): boolean {
|
|
@@ -606,23 +676,28 @@ function hostType(node: Thing, wanted: Json | undefined): boolean {
|
|
|
606
676
|
/**
|
|
607
677
|
* This function makes a walker `nearest`. The walker follows a key (the default is `parent`) until the node that it
|
|
608
678
|
* reaches has the wanted type. With `self: true`, the node where the walk starts is the first candidate. A node
|
|
609
|
-
* without that key ends the walk with an error.
|
|
679
|
+
* without that key ends the walk with an error. With `eachStep`, a resolution with hops gets a hop for each node on the way.
|
|
610
680
|
*
|
|
611
|
-
* @param options - The test of a type and the
|
|
681
|
+
* @param options - The test of a type, the most hops and the record of each step.
|
|
612
682
|
* @returns The walker.
|
|
613
683
|
*/
|
|
614
684
|
export function nearestWalker(options: NearestOptions = {}): Walker {
|
|
615
685
|
const matches = options.matches ?? hostType;
|
|
616
686
|
const maxHops = options.maxHops ?? 256;
|
|
617
|
-
|
|
687
|
+
const eachStep = options.eachStep === true;
|
|
688
|
+
return ({ data, here, deref, visit }) => {
|
|
618
689
|
const follow = isRecord(data) && typeof field(data, "follow") === "string" ? String(field(data, "follow")) : "parent";
|
|
619
690
|
const wanted = isRecord(data) ? field(data, "type") : undefined;
|
|
620
691
|
const fromSelf = isRecord(data) && field(data, "self") === true;
|
|
692
|
+
const record = eachStep ? visit : undefined;
|
|
621
693
|
let at: Thing | undefined = here;
|
|
622
694
|
for (let hops = 0; hops < maxHops; hops += 1) {
|
|
623
|
-
|
|
695
|
+
const stepped = hops > 0 || !fromSelf;
|
|
696
|
+
if (stepped) at = at === undefined || at === null ? undefined : deref(at, follow);
|
|
624
697
|
if (at === undefined || at === null) return { ok: false, reason: `No node of the type ${String(wanted)} is on the way.` };
|
|
625
698
|
if (matches(at, wanted)) return { ok: true, thing: at };
|
|
699
|
+
// The hop of the whole walk records the node that the walk reaches. Each node before it gets its own hop.
|
|
700
|
+
if (stepped) record?.(follow, at);
|
|
626
701
|
}
|
|
627
702
|
return { ok: false, reason: "The walk is too long." };
|
|
628
703
|
};
|