@mark1russell7/coeval 0.20.1 → 0.21.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/build/index.d.ts +318 -236
- package/build/index.d.ts.map +1 -1
- package/build/index.js +609 -76
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/conform.ts +13 -4
- package/src/deref.ts +32 -2
- package/src/effects.ts +4 -2
- package/src/evaluate.ts +2 -0
- package/src/facts.ts +3 -2
- package/src/formula.ts +31 -5
- package/src/index.ts +4 -0
- package/src/instances.ts +50 -1
- package/src/parts.ts +30 -1
- package/src/providers.ts +209 -29
- package/src/reference.ts +52 -2
- package/src/static.ts +79 -19
- package/src/stdlib.ts +88 -2
- package/src/types.ts +12 -4
- package/src/world.ts +50 -0
package/src/stdlib.ts
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
import { Failure } from "./errors.ts";
|
|
19
19
|
import { type Cap, type HostThing, type Thing, type Walker, THING, isHostThing, valueOfThing } from "./deref.ts";
|
|
20
20
|
import type { World } from "./ladder.ts";
|
|
21
|
-
import { type AttributeDeclaration, type CircularRule, type TreeContract, circularOf, equationType, lookupKind } from "./world.ts";
|
|
21
|
+
import { type AttributeDeclaration, type CircularRule, type TreeContract, circularOf, collectOf, equationType, lookupKind } from "./world.ts";
|
|
22
22
|
import { STD_DOMAINS } from "./types.ts";
|
|
23
23
|
import type { SyntaxProfile } from "./syntax.ts";
|
|
24
24
|
|
|
@@ -67,6 +67,15 @@ export const STD_WORLD: World = {
|
|
|
67
67
|
pure: true,
|
|
68
68
|
summary: "A step of a path: it follows a key again and again, until it reaches a node of a type.",
|
|
69
69
|
},
|
|
70
|
+
tree: {
|
|
71
|
+
kinds: {
|
|
72
|
+
named: {
|
|
73
|
+
verbs: { step: "walk.named", view: "view.card" },
|
|
74
|
+
pure: true,
|
|
75
|
+
summary: "A step of a path: it goes up to the nearest registry of names, and it gives the node with the name (TREE.NAMED).",
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
70
79
|
panel: {
|
|
71
80
|
verbs: { eval: "tree.node", view: "view.op" },
|
|
72
81
|
parts: { shape: "record", fields: ["width", "height", "children"], of: "number" },
|
|
@@ -167,6 +176,7 @@ export const WORLD_SYNTAX: SyntaxProfile = /* @__PURE__ */ Object.freeze({
|
|
|
167
176
|
{ name: "panel", kind: "panel", args: "named", fields: ["width", "height", "children"] },
|
|
168
177
|
{ name: "box", kind: "box", args: "named", fields: ["width", "height", "children"] },
|
|
169
178
|
{ name: "nearest", kind: "nearest", args: "named", fields: ["type", "follow", "self"] },
|
|
179
|
+
{ name: "tree.named", kind: "tree.named", args: "named", fields: ["name", "in"] },
|
|
170
180
|
// An operator document with fewer terms than its operator takes, for example add(5) (K163, D65).
|
|
171
181
|
{ name: "add", kind: "add" },
|
|
172
182
|
{ name: "multiply", kind: "multiply" },
|
|
@@ -272,6 +282,8 @@ export interface AttributeTrace {
|
|
|
272
282
|
/** The state of a node behind its brand. */
|
|
273
283
|
interface NodeState extends LoopNode {
|
|
274
284
|
readonly build: TreeBuild;
|
|
285
|
+
/** The binding name from the header of the document of the node, for the registry of names (TREE.NAMED). */
|
|
286
|
+
readonly name: string | undefined;
|
|
275
287
|
/**
|
|
276
288
|
* This function gives an attribute through its ladder (TREE.LADDER).
|
|
277
289
|
*
|
|
@@ -853,6 +865,57 @@ function plainOf(value: unknown, path: Set<object>): Result {
|
|
|
853
865
|
}
|
|
854
866
|
}
|
|
855
867
|
|
|
868
|
+
/**
|
|
869
|
+
* This function gives the collection of an attribute of a node (TREE.COLLECT, D122). It visits the node, then each node
|
|
870
|
+
* of the same tree in each list `children`, in the order of the lists. Each node is visited one time.
|
|
871
|
+
* The registry of names (TREE.NAMED) gives a record from each binding name to its node.
|
|
872
|
+
*/
|
|
873
|
+
function collected(root: HostThing, key: string, declared: unknown): Result {
|
|
874
|
+
const top = stateOf(root) as NodeState;
|
|
875
|
+
const rule = collectOf(declared);
|
|
876
|
+
if (!rule.ok) return fail("#VALUE!", `The attribute ${attributeText(key, top.type, top.place)} has a bad declaration collect. ${rule.reason}`);
|
|
877
|
+
const registry: Record<string, unknown> = {};
|
|
878
|
+
const owners = new Map<string, NodeState>();
|
|
879
|
+
let acc = rule.names ? ok(registry) : top.equation(rule.unit, NO_NAMES);
|
|
880
|
+
if (!acc.ok) return acc;
|
|
881
|
+
const seen = new Set<NodeState>();
|
|
882
|
+
const stack: unknown[] = [root];
|
|
883
|
+
while (stack.length > 0) {
|
|
884
|
+
const thing = stack.pop();
|
|
885
|
+
const at = stateOf(thing);
|
|
886
|
+
// An item that is not a node of this tree does not count, and a node counts one time.
|
|
887
|
+
if (at === undefined || at.build !== top.build || seen.has(at)) continue;
|
|
888
|
+
seen.add(at);
|
|
889
|
+
if (rule.names) {
|
|
890
|
+
const name = at.name;
|
|
891
|
+
const earlier = name === undefined ? undefined : owners.get(name);
|
|
892
|
+
if (earlier !== undefined) {
|
|
893
|
+
const both = `${earlier.type} at [${earlier.place.join(", ")}] and ${at.type} at [${at.place.join(", ")}]`;
|
|
894
|
+
return fail("#VALUE!", `Two nodes have the name ${name} in the registry ${attributeText(key, top.type, top.place)}: ${both}.`);
|
|
895
|
+
}
|
|
896
|
+
if (name !== undefined) {
|
|
897
|
+
owners.set(name, at);
|
|
898
|
+
putField(registry, name, thing);
|
|
899
|
+
}
|
|
900
|
+
} else {
|
|
901
|
+
const term = entryOf(at.tree?.contributes, key);
|
|
902
|
+
if (term !== undefined) {
|
|
903
|
+
const item = at.equation(term, NO_NAMES);
|
|
904
|
+
if (!item.ok) return item;
|
|
905
|
+
acc = top.equation(rule.combine, { acc: acc.value, item: item.value });
|
|
906
|
+
if (!acc.ok) return acc;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
const children = at.read("children");
|
|
910
|
+
if (children === undefined) continue;
|
|
911
|
+
if (!children.ok) return children;
|
|
912
|
+
if (!Array.isArray(children.value)) return fail("#VALUE!", `The collection of ${key} needs a list in children of ${at.type} at [${at.place.join(", ")}].`);
|
|
913
|
+
const list = children.value as readonly unknown[];
|
|
914
|
+
for (let index = list.length - 1; index >= 0; index -= 1) stack.push(list[index]);
|
|
915
|
+
}
|
|
916
|
+
return acc;
|
|
917
|
+
}
|
|
918
|
+
|
|
856
919
|
/**
|
|
857
920
|
* This function makes a node of the decorated tree. A step `parent` gives the self of the context of the node, and a
|
|
858
921
|
* step `type` gives its type. Another step reads an attribute through its ladder (TREE.LADDER). The rungs are a part of
|
|
@@ -916,6 +979,7 @@ function treeThing(input: EvalInput, place: readonly Key[], build: TreeBuild, ou
|
|
|
916
979
|
if (copied !== undefined) return { result: copied, by: "copy" };
|
|
917
980
|
}
|
|
918
981
|
if (declared?.fold !== undefined) return { result: fold(key, declared.fold), by: "default" };
|
|
982
|
+
if (declared?.collect !== undefined) return { result: collected(node, key, declared.collect), by: "default" };
|
|
919
983
|
if (declared?.default !== undefined) return { result: state.equation(declared.default, NO_NAMES), by: "default" };
|
|
920
984
|
return undefined;
|
|
921
985
|
};
|
|
@@ -984,6 +1048,7 @@ function treeThing(input: EvalInput, place: readonly Key[], build: TreeBuild, ou
|
|
|
984
1048
|
|
|
985
1049
|
const state: NodeState = {
|
|
986
1050
|
build,
|
|
1051
|
+
name: input.doc.name,
|
|
987
1052
|
type,
|
|
988
1053
|
place,
|
|
989
1054
|
tree,
|
|
@@ -1170,8 +1235,29 @@ export function nearestWalker(options: NearestOptions = {}): Walker {
|
|
|
1170
1235
|
|
|
1171
1236
|
const nearest: Walker = /* @__PURE__ */ nearestWalker();
|
|
1172
1237
|
|
|
1238
|
+
/**
|
|
1239
|
+
* The walker of the step `tree.named` (TREE.NAMED, D122). It goes up from the thing before the step through the key
|
|
1240
|
+
* `parent`, to the first thing that answers the registry. The field `in` names the registry, and the default is
|
|
1241
|
+
* `names`. The walker gives the node with the field `name` in that registry.
|
|
1242
|
+
*/
|
|
1243
|
+
const named: Walker = ({ data, here, deref }) => {
|
|
1244
|
+
const name = isRecord(data) ? field(data, "name") : undefined;
|
|
1245
|
+
if (typeof name !== "string") return { ok: false, code: "#VALUE!", reason: "The step tree.named needs a text name." };
|
|
1246
|
+
const registry = isRecord(data) && typeof field(data, "in") === "string" ? String(field(data, "in")) : "names";
|
|
1247
|
+
let at: Thing | undefined = here;
|
|
1248
|
+
for (let hops = 0; hops < 256 && at !== undefined && at !== null; hops += 1) {
|
|
1249
|
+
const found = deref(at, registry);
|
|
1250
|
+
if (found !== undefined) {
|
|
1251
|
+
if (!isRecord(found) || !Object.hasOwn(found, name)) return { ok: false, reason: `The registry ${registry} has no node with the name ${name}.` };
|
|
1252
|
+
return { ok: true, thing: field(found, name) as Thing };
|
|
1253
|
+
}
|
|
1254
|
+
at = deref(at, "parent");
|
|
1255
|
+
}
|
|
1256
|
+
return { ok: false, reason: `No registry ${registry} is on the way.` };
|
|
1257
|
+
};
|
|
1258
|
+
|
|
1173
1259
|
/** The walkers of the standard world, by name. */
|
|
1174
|
-
export const STD_WALKERS: Readonly<Record<string, Walker>> = { "walk.nearest": nearest };
|
|
1260
|
+
export const STD_WALKERS: Readonly<Record<string, Walker>> = { "walk.nearest": nearest, "walk.named": named };
|
|
1175
1261
|
|
|
1176
1262
|
/** This function evaluates parts one after the other, and stops at the first error. */
|
|
1177
1263
|
function* inOrder(parts: readonly RunPart[]): Generator<Promise<unknown>, { readonly values: readonly unknown[] } | { readonly error: Result }, unknown> {
|
package/src/types.ts
CHANGED
|
@@ -1144,8 +1144,10 @@ function valueStep(value: unknown, key: Key): { readonly found: unknown } | { re
|
|
|
1144
1144
|
*
|
|
1145
1145
|
* - A record gives the type of its field. A closed record without the field has no such step. An open record gives
|
|
1146
1146
|
* `unknown`.
|
|
1147
|
-
* - A list gives the type of its items for an index, and `number` for `length`.
|
|
1148
|
-
* - A base type reads the steps of its domain, then of each parent (`Domain.steps`).
|
|
1147
|
+
* - A list gives the type of its items for an index, and `number` for `length`. Another key has no such step.
|
|
1148
|
+
* - A base type reads the steps of its domain, then of each parent (`Domain.steps`). A base type of the kernel
|
|
1149
|
+
* without such a step has no step, because its values are not objects (TYPE.NO-STEP, D120).
|
|
1150
|
+
* - A function type has no step, because a step into a function reaches nothing.
|
|
1149
1151
|
* - A union joins the steps of its types. `one` gives the values that the step reaches.
|
|
1150
1152
|
* - Each other step gives `unknown`, and `never` gives `never`.
|
|
1151
1153
|
*
|
|
@@ -1171,10 +1173,13 @@ export function stepType(
|
|
|
1171
1173
|
const found = steps[name] as ValueType;
|
|
1172
1174
|
return { type: examine === undefined ? found : examine(found, `type of the step ${name} of the domain ${each}`) };
|
|
1173
1175
|
}
|
|
1174
|
-
|
|
1176
|
+
// A value of a base type of the kernel is not an object, thus a step into it reaches nothing (TYPE.NO-STEP).
|
|
1177
|
+
return Object.hasOwn(STD_DOMAINS, form.name) ? NO_STEP : { type: "unknown" };
|
|
1178
|
+
case "fn":
|
|
1179
|
+
return NO_STEP;
|
|
1175
1180
|
case "list":
|
|
1176
1181
|
if (key === "length") return { type: "number" };
|
|
1177
|
-
return
|
|
1182
|
+
return indexOf(key) >= 0 ? { type: form.item } : NO_STEP;
|
|
1178
1183
|
case "record":
|
|
1179
1184
|
if (Object.hasOwn(form.fields, name)) return { type: form.fields[name] as ValueType };
|
|
1180
1185
|
return form.closed ? { missing: Object.keys(form.fields) } : { type: "unknown" };
|
|
@@ -1204,6 +1209,9 @@ export function stepType(
|
|
|
1204
1209
|
}
|
|
1205
1210
|
}
|
|
1206
1211
|
|
|
1212
|
+
/** The answer of `stepType` for a type whose members have no step with the key, and no fields to name. */
|
|
1213
|
+
const NO_STEP: StepType = { missing: [] };
|
|
1214
|
+
|
|
1207
1215
|
/** A term that `typeIssues` reads later: the term, its keys from the root and its depth. */
|
|
1208
1216
|
interface Pending {
|
|
1209
1217
|
readonly type: unknown;
|
package/src/world.ts
CHANGED
|
@@ -116,12 +116,20 @@ export interface LazyRule {
|
|
|
116
116
|
* - `lazy`: the parts that an evaluation can skip and still give a value.
|
|
117
117
|
* - `reads`: the roots that the code of the kind resolves as a whole, with the path of one root. The static facts
|
|
118
118
|
* give a read of each root at each document of the kind (D44). The tree node reads `self` this way.
|
|
119
|
+
* - `effects`: the effects that the code of `eval` can cause, in the form of the field `effects` of a kind (D27). In
|
|
120
|
+
* the answer of the hook `facts`, it gives the effects of the code of a name. The contract of a kind does not
|
|
121
|
+
* describe an own entry for another evaluator, thus only the hook can give its effects (CAP.OWN-EFFECTS, D118).
|
|
122
|
+
* - `drives`: the parts that the code of a runner evaluates itself, without a wait, in the form of the lazy rules. A
|
|
123
|
+
* step under such a part cannot wait for the host. The check of a run then refuses a part that can wait there
|
|
124
|
+
* (RUN.DRIVES, D119). Without `drives`, the check assumes that the runner passes each wait of its parts through.
|
|
119
125
|
*/
|
|
120
126
|
export interface KindFacts {
|
|
121
127
|
readonly pure?: boolean;
|
|
122
128
|
readonly volatile?: boolean;
|
|
123
129
|
readonly lazy?: readonly LazyRule[];
|
|
124
130
|
readonly reads?: readonly Root[];
|
|
131
|
+
readonly effects?: "type" | readonly string[];
|
|
132
|
+
readonly drives?: readonly LazyRule[];
|
|
125
133
|
}
|
|
126
134
|
|
|
127
135
|
/**
|
|
@@ -136,14 +144,38 @@ export interface KindFacts {
|
|
|
136
144
|
* - `default`: the term that gives the value when no rung before it answers and the declaration has no fold.
|
|
137
145
|
* - `circular`: a cycle of this attribute gives its fixed point, not `#CYCLE!` (TREE.CIRCULAR, D113). Only a cycle
|
|
138
146
|
* whose attributes each declare `circular` gives a fixed point.
|
|
147
|
+
* - `collect`: the value of a node without an answer of the first four rungs and without a fold. The node combines
|
|
148
|
+
* the contributions of each node of its subtree (TREE.COLLECT, D122).
|
|
139
149
|
*/
|
|
140
150
|
export interface AttributeDeclaration {
|
|
141
151
|
readonly copy?: boolean;
|
|
142
152
|
readonly fold?: { readonly unit: Json; readonly combine: Json };
|
|
143
153
|
readonly default?: Json;
|
|
144
154
|
readonly circular?: CircularDeclaration;
|
|
155
|
+
readonly collect?: CollectDeclaration;
|
|
145
156
|
}
|
|
146
157
|
|
|
158
|
+
/**
|
|
159
|
+
* The declaration `collect` of an attribute (TREE.COLLECT, D122). The node of the attribute visits each node of its
|
|
160
|
+
* subtree. It visits the node itself first. Then it visits the nodes in its list `children`, in the order of the list.
|
|
161
|
+
*
|
|
162
|
+
* - `{ unit, combine }`: the collection starts with the term `unit`. The term `combine` then adds the contribution
|
|
163
|
+
* of each node, with the names `acc` and `item`. A node contributes when its kind has an equation for the
|
|
164
|
+
* attribute in `tree.contributes`.
|
|
165
|
+
* - `"names"`: the registry of binding names. Its value is a record from the header name of each node to its node
|
|
166
|
+
* (TREE.NAMED). Two nodes with one name give `#VALUE!` at the read of the registry.
|
|
167
|
+
*/
|
|
168
|
+
export type CollectDeclaration = { readonly unit: Json; readonly combine: Json } | "names";
|
|
169
|
+
|
|
170
|
+
/** A declaration `collect` that the kit can use, or the reason of a bad declaration (D122). */
|
|
171
|
+
export type CollectRule =
|
|
172
|
+
| { readonly ok: true; readonly names: true }
|
|
173
|
+
| { readonly ok: true; readonly names: false; readonly unit: Json; readonly combine: Json }
|
|
174
|
+
| { readonly ok: false; readonly reason: string };
|
|
175
|
+
|
|
176
|
+
/** The rule of the registry of binding names. */
|
|
177
|
+
const NAMES_RULE: CollectRule = /* @__PURE__ */ Object.freeze({ ok: true, names: true });
|
|
178
|
+
|
|
147
179
|
/**
|
|
148
180
|
* The declaration `circular` of an attribute (TREE.CIRCULAR, D113). The attribute that a cycle reaches first leads it.
|
|
149
181
|
* The leader evaluates again until no value of the cycle changes.
|
|
@@ -200,6 +232,21 @@ export function circularOf(declared: unknown): CircularRule {
|
|
|
200
232
|
return { ok: true, start: start as Json, limit };
|
|
201
233
|
}
|
|
202
234
|
|
|
235
|
+
/**
|
|
236
|
+
* This function reads the declaration `collect` of an attribute (D122). The tree kit, the reference, `check` and the
|
|
237
|
+
* graph of attribute instances read it with this one function.
|
|
238
|
+
*
|
|
239
|
+
* @param declared - The value of the field `collect`.
|
|
240
|
+
* @returns The rule of the collection, or the reason when the declaration has no correct form.
|
|
241
|
+
*/
|
|
242
|
+
export function collectOf(declared: unknown): CollectRule {
|
|
243
|
+
if (declared === "names") return NAMES_RULE;
|
|
244
|
+
if (!isRecord(declared) || !Object.hasOwn(declared, "unit") || !Object.hasOwn(declared, "combine")) {
|
|
245
|
+
return { ok: false, reason: 'The declaration collect needs the text "names" or a record with the fields unit and combine.' };
|
|
246
|
+
}
|
|
247
|
+
return { ok: true, names: false, unit: field(declared, "unit") as Json, combine: field(declared, "combine") as Json };
|
|
248
|
+
}
|
|
249
|
+
|
|
203
250
|
/**
|
|
204
251
|
* The tree contract of a kind (D42): the equations of the attributes of its nodes. Each equation is a document that
|
|
205
252
|
* evaluates with the node as `self` and without capabilities. A bare equation gets the type `of` of the parts of the
|
|
@@ -209,11 +256,14 @@ export function circularOf(declared: unknown): CircularRule {
|
|
|
209
256
|
* - `down`: the attributes of each child of the node. `self` is this node. An equation sees the names `child`,
|
|
210
257
|
* `index` and `prev` (TREE.DOWN).
|
|
211
258
|
* - `attributes`: the declarations of the attributes of the node, for the rungs `copy` and `default`.
|
|
259
|
+
* - `contributes`: the contribution of the node to each collection of an attribute (TREE.COLLECT, D122). An
|
|
260
|
+
* equation sees no names.
|
|
212
261
|
*/
|
|
213
262
|
export interface TreeContract {
|
|
214
263
|
readonly equations?: Readonly<Record<string, Json>>;
|
|
215
264
|
readonly down?: Readonly<Record<string, Json>>;
|
|
216
265
|
readonly attributes?: Readonly<Record<string, AttributeDeclaration>>;
|
|
266
|
+
readonly contributes?: Readonly<Record<string, Json>>;
|
|
217
267
|
}
|
|
218
268
|
|
|
219
269
|
/**
|