@mark1russell7/coeval 0.7.0 → 0.8.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/src/stdlib.ts CHANGED
@@ -1,8 +1,21 @@
1
1
  import { type Json, type Key, field, isRecord } from "./doc.ts";
2
- import { type Context, type EvalInput, type Evaluator, type Machine, type Part, type Result, type RunPart, type Runner, type Steps, fail, ok } from "./evaluate.ts";
2
+ import {
3
+ type EvalInput,
4
+ type Evaluator,
5
+ type Machine,
6
+ type Plan,
7
+ type Result,
8
+ type RunPart,
9
+ type Runner,
10
+ type Steps,
11
+ fail,
12
+ ok,
13
+ planner,
14
+ } from "./evaluate.ts";
3
15
  import { Failure } from "./errors.ts";
4
- import { type HostThing, type Thing, type Walker, THING, isHostThing, valueOfThing } from "./deref.ts";
16
+ import { type Cap, type HostThing, type Thing, type Walker, THING, isHostThing, valueOfThing } from "./deref.ts";
5
17
  import type { World } from "./ladder.ts";
18
+ import { type AttributeDeclaration, type TreeContract, equationType, lookupKind } from "./world.ts";
6
19
  import { STD_DOMAINS } from "./types.ts";
7
20
 
8
21
  /**
@@ -188,11 +201,57 @@ interface TreeBuild {
188
201
  open: boolean;
189
202
  }
190
203
 
204
+ /** The rungs of the ladder of an attribute, in their order (TREE.LADDER, D42). */
205
+ export type AttributeRung = "own" | "kind" | "parent" | "copy" | "default";
206
+
207
+ /** The rungs of the ladder of an attribute, in their order (TREE.LADDER, D42). */
208
+ export const ATTRIBUTE_RUNGS: readonly AttributeRung[] = /* @__PURE__ */ Object.freeze(["own", "kind", "parent", "copy", "default"] as const);
209
+
210
+ /** The record of the demand of one attribute of a node (TREE.RUNGS). */
211
+ export interface AttributeTrace {
212
+ readonly attribute: string;
213
+ /** The rungs that the demand visited, in their order. The last one answered, unless no rung answered. */
214
+ readonly rungs: readonly AttributeRung[];
215
+ /** The rung that answered. It is `undefined` when no rung answered. */
216
+ readonly by?: AttributeRung;
217
+ }
218
+
191
219
  /** The state of a node behind its brand. */
192
220
  interface NodeState {
193
221
  readonly build: TreeBuild;
194
222
  readonly type: string;
195
223
  readonly place: readonly Key[];
224
+ /** The tree contract of the kind of the node (D42). */
225
+ readonly tree: TreeContract | undefined;
226
+ /**
227
+ * This function gives an attribute through its ladder (TREE.LADDER).
228
+ *
229
+ * @param key - The name of the attribute.
230
+ * @returns The result, or `undefined` when no rung answers.
231
+ */
232
+ read(key: string): Result | undefined;
233
+ /**
234
+ * This function evaluates an equation of the kind of the node, with the node as `self` and no capabilities.
235
+ *
236
+ * @param term - The equation.
237
+ * @param names - The names that the equation sees.
238
+ * @returns The result.
239
+ */
240
+ equation(term: Json, names: Readonly<Record<string, unknown>>): Result;
241
+ /**
242
+ * This function gives the names of a child for an equation of `down`: `child`, `index` and `prev` (TREE.DOWN).
243
+ *
244
+ * @param child - The thing of the child.
245
+ * @returns The names, or the error of the list `children`.
246
+ */
247
+ childNames(child: HostThing): Result;
248
+ /**
249
+ * This function gives the record of the demand of an attribute that ended (TREE.RUNGS).
250
+ *
251
+ * @param key - The name of the attribute.
252
+ * @returns The record, or `undefined` before the end of the demand.
253
+ */
254
+ trace(key: string): AttributeTrace | undefined;
196
255
  /**
197
256
  * This function gives the JSON of the node: each attribute in the order of the parts, with the JSON of each node in
198
257
  * its value.
@@ -203,8 +262,60 @@ interface NodeState {
203
262
  json(path: Set<object>): Result;
204
263
  }
205
264
 
206
- /** The memo cell of one attribute: in evaluation, or done with its result. */
207
- type Cell = { readonly busy: true } | { readonly busy: false; readonly result: Result };
265
+ /**
266
+ * The memo cell of one attribute: in evaluation, or done with its result and the rung that answered. A done cell
267
+ * without a result tells that no rung answered.
268
+ */
269
+ type Cell = { readonly busy: true } | { readonly busy: false; readonly result: Result | undefined; readonly by: AttributeRung | undefined };
270
+
271
+ /** The capabilities of an equation: none (CAP.PURE). */
272
+ const NO_CAPS: readonly Cap[] = /* @__PURE__ */ Object.freeze([]);
273
+
274
+ /** The names of an equation of `equations`, of a `unit` and of a `default`. */
275
+ const NO_NAMES: Readonly<Record<string, unknown>> = /* @__PURE__ */ Object.freeze({});
276
+
277
+ /** The planners of equations, for each machine and each type of a bare equation (TREE.PLAN). */
278
+ const equationPlanners = /* @__PURE__ */ new WeakMap<Machine, Map<string, (doc: unknown) => Plan>>();
279
+
280
+ /**
281
+ * This function gives the plan of an equation. Each equation compiles one time for each machine (PLAN.CACHE). The
282
+ * plan of the document does not keep it (PLAN.LEAN).
283
+ */
284
+ function equationPlan(machine: Machine, of: string | undefined, term: Json): Plan {
285
+ let byType = equationPlanners.get(machine);
286
+ if (byType === undefined) {
287
+ byType = new Map();
288
+ equationPlanners.set(machine, byType);
289
+ }
290
+ const key = of ?? "";
291
+ let plans = byType.get(key);
292
+ if (plans === undefined) {
293
+ plans = planner(machine, of === undefined ? {} : { declaredType: of });
294
+ byType.set(key, plans);
295
+ }
296
+ return plans(term);
297
+ }
298
+
299
+ /** This function gives the own entry of a table of a tree contract, or `undefined`. */
300
+ function entryOf<T>(table: Readonly<Record<string, T>> | undefined, key: string): T | undefined {
301
+ return table !== undefined && Object.hasOwn(table, key) ? table[key] : undefined;
302
+ }
303
+
304
+ /** The contract of the kind of a node: its tree contract and the type of a bare equation. */
305
+ interface NodeContract {
306
+ readonly tree: TreeContract | undefined;
307
+ readonly of: string | undefined;
308
+ }
309
+
310
+ const NO_CONTRACT: NodeContract = /* @__PURE__ */ Object.freeze({ tree: undefined, of: undefined });
311
+
312
+ /** This function finds the tree contract of the kind of a node in the world of the machine. */
313
+ function contractOf(machine: Machine | undefined, typePath: readonly Key[]): NodeContract {
314
+ if (machine === undefined || typePath.length === 0) return NO_CONTRACT;
315
+ const found = lookupKind(machine.world, typePath);
316
+ if (!found.ok || found.kind.tree === undefined) return NO_CONTRACT;
317
+ return { tree: found.kind.tree, of: equationType(found.kind) };
318
+ }
208
319
 
209
320
  const BUSY: Cell = /* @__PURE__ */ Object.freeze({ busy: true });
210
321
 
@@ -282,21 +393,71 @@ function plainOf(value: unknown, path: Set<object>): Result {
282
393
 
283
394
  /**
284
395
  * This function makes a node of the decorated tree. A step `parent` gives the self of the context of the node, and a
285
- * step `type` gives its type. A step with the key of a part reads that attribute. A failed attribute throws `Failure`
286
- * with its own error, thus a reference to it gives that error.
396
+ * step `type` gives its type. Another step reads an attribute through its ladder (TREE.LADDER). The rungs are a part of
397
+ * the node, an equation of its kind and an equation `down` of the kind of its parent. A copy from the parent, then a
398
+ * fold or a default, come last. A failed attribute throws `Failure` with its own error, thus a reference to it gives that error.
287
399
  */
288
- function treeThing(parts: readonly Part[], context: Context, type: string, place: readonly Key[], build: TreeBuild): HostThing {
400
+ function treeThing(input: EvalInput, build: TreeBuild): HostThing {
401
+ const { parts, context, kind: type, place, machine } = input;
402
+ const { tree, of } = contractOf(machine, input.typePath);
403
+ const above = stateOf(context.self);
404
+ // Only a node of the same tree is a parent for the rungs `parent` and `copy`.
405
+ const up = above !== undefined && above.build === build ? above : undefined;
289
406
  const cells = new Map<string, Cell>();
407
+ /** The positions of the items of the list `children`, for the names of the children. */
408
+ let positions: { readonly list: readonly unknown[]; readonly at: Map<unknown, number> } | undefined;
409
+
410
+ const fold = (key: string, declared: NonNullable<AttributeDeclaration["fold"]>): Result => {
411
+ let acc = state.equation(declared.unit, NO_NAMES);
412
+ if (!acc.ok) return acc;
413
+ const children = read("children");
414
+ if (children === undefined) return acc;
415
+ if (!children.ok) return children;
416
+ if (!Array.isArray(children.value)) return fail("#VALUE!", `The fold of ${key} needs a list in children.`);
417
+ for (const item of children.value as readonly unknown[]) {
418
+ const child = stateOf(item);
419
+ // An item that is not a node of this tree does not count.
420
+ if (child === undefined || child.build !== build) continue;
421
+ const value = child.read(key);
422
+ if (value === undefined) return fail("#N/A", `No rung gives ${attributeText(key, child.type, child.place)}.`);
423
+ if (!value.ok) return value;
424
+ acc = state.equation(declared.combine, { acc: acc.value, item: value.value });
425
+ if (!acc.ok) return acc;
426
+ }
427
+ return acc;
428
+ };
429
+
430
+ /** This function visits the rungs of the ladder of an attribute in their order, until one answers. */
431
+ const climb = (key: string): { readonly result: Result; readonly by: AttributeRung } | undefined => {
432
+ const part = parts.find((item) => item.key === key);
433
+ if (part !== undefined) return { result: part.evaluate({ ...context, self: node }), by: "own" };
434
+ const equation = entryOf(tree?.equations, key);
435
+ if (equation !== undefined) return { result: state.equation(equation, NO_NAMES), by: "kind" };
436
+ const down = up === undefined ? undefined : entryOf(up.tree?.down, key);
437
+ if (up !== undefined && down !== undefined) {
438
+ const names = up.childNames(node);
439
+ return { result: names.ok ? up.equation(down, names.value as Readonly<Record<string, unknown>>) : names, by: "parent" };
440
+ }
441
+ const declared = entryOf(tree?.attributes, key);
442
+ if (declared?.copy === true && up !== undefined) {
443
+ const copied = up.read(key);
444
+ if (copied !== undefined) return { result: copied, by: "copy" };
445
+ }
446
+ if (declared?.fold !== undefined) return { result: fold(key, declared.fold), by: "default" };
447
+ if (declared?.default !== undefined) return { result: state.equation(declared.default, NO_NAMES), by: "default" };
448
+ return undefined;
449
+ };
450
+
290
451
  const read = (key: string): Result | undefined => {
291
452
  const cell = cells.get(key);
292
453
  if (cell !== undefined) return cell.busy ? cycleAt(cells, key, attributeText(key, type, place)) : cell.result;
293
- const part = parts.find((item) => item.key === key);
294
- if (part === undefined) return undefined;
454
+ // A node without a contract has only its parts, as before D42. Thus a missing part needs no cell.
455
+ if (tree === undefined && up?.tree === undefined && !parts.some((item) => item.key === key)) return undefined;
295
456
  cells.set(key, BUSY);
296
457
  demands.push({ cells, key, text: attributeText(key, type, place) });
297
- let result: Result;
458
+ let answer: { readonly result: Result; readonly by: AttributeRung } | undefined;
298
459
  try {
299
- result = part.evaluate({ ...context, self: node });
460
+ answer = climb(key);
300
461
  } catch (error) {
301
462
  // An exception of the host, for example an overflow of the stack, leaves the attribute without a result.
302
463
  cells.delete(key);
@@ -304,13 +465,40 @@ function treeThing(parts: readonly Part[], context: Context, type: string, place
304
465
  } finally {
305
466
  demands.pop();
306
467
  }
307
- cells.set(key, { busy: false, result });
308
- return result;
468
+ cells.set(key, { busy: false, result: answer?.result, by: answer?.by });
469
+ return answer?.result;
309
470
  };
471
+
310
472
  const state: NodeState = {
311
473
  build,
312
474
  type,
313
475
  place,
476
+ tree,
477
+ read,
478
+ equation(term, names) {
479
+ if (machine === undefined) return fail("#NOEVAL", "The evaluation of an equation needs the machine.");
480
+ return equationPlan(machine, of, term).evaluate({ ...context, names, caps: NO_CAPS, self: node });
481
+ },
482
+ childNames(child) {
483
+ const children = read("children");
484
+ if (children !== undefined && !children.ok) return children;
485
+ const list = children !== undefined && Array.isArray(children.value) ? (children.value as readonly unknown[]) : [];
486
+ if (positions?.list !== list) {
487
+ const at = new Map<unknown, number>();
488
+ list.forEach((item, index) => {
489
+ if (!at.has(item)) at.set(item, index);
490
+ });
491
+ positions = { list, at };
492
+ }
493
+ const index = positions.at.get(child);
494
+ return ok({ child, index: index ?? null, prev: index === undefined || index === 0 ? null : list[index - 1] });
495
+ },
496
+ trace(key) {
497
+ const cell = cells.get(key);
498
+ if (cell === undefined || cell.busy) return undefined;
499
+ const rungs = cell.by === undefined ? ATTRIBUTE_RUNGS : ATTRIBUTE_RUNGS.slice(0, ATTRIBUTE_RUNGS.indexOf(cell.by) + 1);
500
+ return cell.by === undefined ? { attribute: key, rungs } : { attribute: key, rungs, by: cell.by };
501
+ },
314
502
  json(path) {
315
503
  path.add(node);
316
504
  try {
@@ -343,7 +531,9 @@ function treeThing(parts: readonly Part[], context: Context, type: string, place
343
531
  },
344
532
  has(key: Key): boolean {
345
533
  // DEREF.HOST-UNDEFINED: an attribute with the value undefined is a field, not a missing step.
346
- return key === "parent" || key === "type" || parts.some((part) => part.key === String(key));
534
+ if (key === "parent" || key === "type" || parts.some((part) => part.key === String(key))) return true;
535
+ const cell = cells.get(String(key));
536
+ return cell !== undefined && !cell.busy && cell.result !== undefined;
347
537
  },
348
538
  toJson(): unknown {
349
539
  const result = state.json(new Set());
@@ -354,15 +544,27 @@ function treeThing(parts: readonly Part[], context: Context, type: string, place
354
544
  return node;
355
545
  }
356
546
 
547
+ /**
548
+ * This function gives the record of the demand of an attribute of a node of the decorated tree (TREE.RUNGS). The
549
+ * record holds the rungs that the demand visited, and the rung that answered. An explorer draws the ladder with it.
550
+ *
551
+ * @param node - The thing of a node.
552
+ * @param attribute - The name of the attribute.
553
+ * @returns The record, or `undefined` for a thing that is not a node or an attribute that no demand reached.
554
+ */
555
+ export function attributeTrace(node: Thing, attribute: string): AttributeTrace | undefined {
556
+ return stateOf(node)?.trace(attribute);
557
+ }
558
+
357
559
  /**
358
560
  * This function evaluates a node of the decorated tree. Inside an attribute of an open tree, the node gives its host
359
561
  * thing, and no attribute evaluates. Else the node is the root of a new tree, and it gives the JSON of the tree.
360
562
  */
361
- function treeNode({ parts, context, kind, place }: EvalInput): Result {
362
- const above = stateOf(context.self);
363
- if (above !== undefined && above.build.open) return ok(treeThing(parts, context, kind, place, above.build));
563
+ function treeNode(input: EvalInput): Result {
564
+ const above = stateOf(input.context.self);
565
+ if (above !== undefined && above.build.open) return ok(treeThing(input, above.build));
364
566
  const build: TreeBuild = { open: true };
365
- const root = treeThing(parts, context, kind, place, build);
567
+ const root = treeThing(input, build);
366
568
  try {
367
569
  return (stateOf(root) as NodeState).json(new Set());
368
570
  } finally {