@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/README.md +31 -0
- package/build/index.d.ts +399 -100
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1818 -172
- package/build/index.js.map +1 -1
- package/package.json +2 -2
- package/src/agree.ts +26 -5
- package/src/codec.ts +185 -0
- package/src/conform.ts +2 -3
- package/src/doc.ts +2 -2
- package/src/evaluate.ts +281 -9
- package/src/formula.ts +19 -8
- package/src/index.ts +36 -2
- package/src/parts.ts +70 -1
- package/src/reference.ts +143 -18
- package/src/static.ts +54 -74
- package/src/stdlib.ts +220 -18
- package/src/syntax.ts +846 -0
- package/src/types.ts +28 -1
- package/src/world.ts +50 -0
package/src/stdlib.ts
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import { type Json, type Key, field, isRecord } from "./doc.ts";
|
|
2
|
-
import {
|
|
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
|
-
/**
|
|
207
|
-
|
|
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.
|
|
286
|
-
*
|
|
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(
|
|
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
|
-
|
|
294
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
362
|
-
const above = stateOf(context.self);
|
|
363
|
-
if (above !== undefined && above.build.open) return ok(treeThing(
|
|
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(
|
|
567
|
+
const root = treeThing(input, build);
|
|
366
568
|
try {
|
|
367
569
|
return (stateOf(root) as NodeState).json(new Set());
|
|
368
570
|
} finally {
|