@mark1russell7/coeval 0.4.3 → 0.6.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.js CHANGED
@@ -299,7 +299,7 @@ var Failure = class extends Error {
299
299
  */
300
300
  function isStackOverflow(error) {
301
301
  try {
302
- return error instanceof RangeError && /call stack/i.test(error.message);
302
+ return (error instanceof RangeError || error instanceof SyntaxError) && error.message.includes("call stack");
303
303
  } catch {
304
304
  return true;
305
305
  }
@@ -508,6 +508,338 @@ function looksLikeKind(value) {
508
508
  return isRecord(value) && isRecord(field(value, "verbs"));
509
509
  }
510
510
  //#endregion
511
+ //#region src/merge.ts
512
+ /** The exception of a merge with conflicts. It lists each conflict of the merge, not only the first. */
513
+ var MergeError = class extends Error {
514
+ conflicts;
515
+ /**
516
+ * This constructor makes the exception.
517
+ *
518
+ * @param conflicts - The conflicts, in the order that the merge found them.
519
+ */
520
+ constructor(conflicts) {
521
+ const lines = conflicts.map((item) => `At ${JSON.stringify(item.path)}, the parts ${item.parts[0]} and ${item.parts[1]}: ${item.reason}`);
522
+ super(`The merge has ${conflicts.length === 1 ? "1 conflict" : `${conflicts.length} conflicts`}. ${lines.join(" ")}`);
523
+ this.name = "MergeError";
524
+ this.conflicts = conflicts;
525
+ }
526
+ };
527
+ const NONE$1 = /* @__PURE__ */ new Set();
528
+ function keyOf(path) {
529
+ return JSON.stringify(path.map(String));
530
+ }
531
+ /** This function sets an own data property. An assignment to the key `__proto__` changes the prototype. */
532
+ function setOwn$2(target, key, value) {
533
+ if (key === "__proto__") Object.defineProperty(target, key, {
534
+ value,
535
+ enumerable: true,
536
+ writable: true,
537
+ configurable: true
538
+ });
539
+ else target[key] = value;
540
+ }
541
+ function own$1(table, key) {
542
+ return table !== void 0 && Object.hasOwn(table, key) ? table[key] : void 0;
543
+ }
544
+ /** This function gives a copy of a JSON value without the fields whose value is `undefined`, as JSON writes it. */
545
+ function defined(value) {
546
+ if (Array.isArray(value)) return value.map(defined);
547
+ if (!isRecord(value)) return value;
548
+ const out = {};
549
+ for (const key of Object.keys(value)) if (value[key] !== void 0) setOwn$2(out, key, defined(value[key]));
550
+ return out;
551
+ }
552
+ /** Two values are the same when they are equal by `docEquals`. A function and a host value are equal only to themselves. */
553
+ function same(a, b) {
554
+ return Object.is(a, b) || docEquals(defined(a), defined(b));
555
+ }
556
+ /** Two lists of roots are the same when they hold the same roots, in any order. */
557
+ function sameRoots(a, b) {
558
+ if (!Array.isArray(a) || !Array.isArray(b)) return same(a, b);
559
+ return same([...new Set(a.map(String))].sort(), [...new Set(b.map(String))].sort());
560
+ }
561
+ /** The record of a merge: the conflicts, and the part that gave each entry. */
562
+ var Merger = class {
563
+ conflicts = [];
564
+ sources = /* @__PURE__ */ new Map();
565
+ /** This method gives the part that gave the entry at a path, or the entry nearest above it. */
566
+ sourceOf(path) {
567
+ for (let length = path.length; length >= 0; length -= 1) {
568
+ const found = this.sources.get(keyOf(path.slice(0, length)));
569
+ if (found !== void 0) return found;
570
+ }
571
+ return 0;
572
+ }
573
+ /** This method records the part that gives a new entry. */
574
+ give(path, part) {
575
+ this.sources.set(keyOf(path), part);
576
+ }
577
+ /** This method records the part that replaces an entry, and forgets the parts of each entry below it. */
578
+ replace(path, part) {
579
+ const below = `${keyOf(path).slice(0, -1)},`;
580
+ for (const key of [...this.sources.keys()]) if (key.startsWith(below)) this.sources.delete(key);
581
+ this.give(path, part);
582
+ }
583
+ clash(path, part, reason) {
584
+ this.conflicts.push({
585
+ path,
586
+ parts: [this.sourceOf(path), part],
587
+ reason
588
+ });
589
+ }
590
+ };
591
+ /**
592
+ * This function merges one entry of a table into the table. A new entry joins the table. A path in `replaces` gives
593
+ * the later entry. Two entries that are the same give one entry. Each other pair is a conflict.
594
+ */
595
+ function mergeLeaf(m, table, key, later, path, part, replaces, equal, reason) {
596
+ if (later === void 0) return;
597
+ const earlier = own$1(table, key);
598
+ if (earlier === void 0) {
599
+ setOwn$2(table, key, later);
600
+ m.give(path, part);
601
+ } else if (replaces.has(keyOf(path))) {
602
+ setOwn$2(table, key, later);
603
+ m.replace(path, part);
604
+ } else if (!equal(earlier, later)) m.clash(path, part, reason);
605
+ }
606
+ function what(entry) {
607
+ if (isKind(entry)) return "a kind";
608
+ if (isAlias(entry)) return "an alias";
609
+ return isNamespace(entry) ? "a namespace" : "an entry";
610
+ }
611
+ function mergeFallback(m, earlier, later, path, part, replaces) {
612
+ const table = earlier === void 0 ? {} : { fallback: earlier };
613
+ mergeLeaf(m, table, "fallback", later, path, part, replaces, same, "Two parts give two different fallback kinds.");
614
+ return table["fallback"];
615
+ }
616
+ function mergeEntries(m, into, from, path, part, replaces) {
617
+ for (const key of Object.keys(from)) {
618
+ const later = from[key];
619
+ if (later === void 0) continue;
620
+ const at = [...path, key];
621
+ const earlier = own$1(into, key);
622
+ if (earlier === void 0) {
623
+ setOwn$2(into, key, later);
624
+ m.give(at, part);
625
+ } else setOwn$2(into, key, mergeEntry(m, earlier, later, at, part, replaces));
626
+ }
627
+ }
628
+ /** This function merges two entries at one path. Two namespaces merge key by key, and their fallbacks merge too. */
629
+ function mergeEntry(m, earlier, later, path, part, replaces) {
630
+ if (replaces.has(keyOf(path))) {
631
+ m.replace(path, part);
632
+ return later;
633
+ }
634
+ if (same(earlier, later)) return earlier;
635
+ if (isNamespace(earlier) && isNamespace(later)) {
636
+ const kinds = { ...earlier.kinds };
637
+ mergeEntries(m, kinds, later.kinds, [...path, "kinds"], part, replaces);
638
+ const fallback = mergeFallback(m, earlier.fallback, later.fallback, [...path, "fallback"], part, replaces);
639
+ return fallback === void 0 ? { kinds } : {
640
+ kinds,
641
+ fallback
642
+ };
643
+ }
644
+ const a = what(earlier);
645
+ const b = what(later);
646
+ m.clash(path, part, a === b ? `Two parts give two different ${a === "an alias" ? "aliases" : "kinds"}.` : `One part gives ${a}, and the other part gives ${b}.`);
647
+ return earlier;
648
+ }
649
+ function worldOf(draft) {
650
+ const world = {
651
+ kinds: { ...draft.kinds },
652
+ defaults: { ...draft.defaults }
653
+ };
654
+ return draft.fallback === void 0 ? world : {
655
+ ...world,
656
+ fallback: draft.fallback
657
+ };
658
+ }
659
+ function mergeWorldInto(m, draft, part, index, replaces, base) {
660
+ if (part === void 0) return;
661
+ if (part.kinds !== void 0) mergeEntries(m, draft.kinds, part.kinds, [...base, "kinds"], index, replaces);
662
+ const defaults = part.defaults ?? {};
663
+ for (const verb of Object.keys(defaults)) mergeLeaf(m, draft.defaults, verb, defaults[verb], [
664
+ ...base,
665
+ "defaults",
666
+ verb
667
+ ], index, replaces, Object.is, "Two parts give two different defaults for this verb.");
668
+ const fallback = mergeFallback(m, draft.fallback, part.fallback, [...base, "fallback"], index, replaces);
669
+ if (fallback !== void 0) draft.fallback = fallback;
670
+ }
671
+ /**
672
+ * This function merges worlds into one world, in the order of the parts. It does not change a part.
673
+ *
674
+ * - Two entries that are equal by `docEquals` give one entry.
675
+ * - Two namespaces merge key by key, at each depth. Their fallbacks merge as each other entry.
676
+ * - The defaults merge verb by verb, and the world keeps one fallback.
677
+ * - Each other pair of entries at one path is a conflict, unless the later part names the path in `replaces`.
678
+ * Then the later entry wins, with no merge below it.
679
+ *
680
+ * A part can be a function of the merge of the parts before it (`prev`, as an overlay of Nix). Thus a part can read
681
+ * the entry that it replaces.
682
+ *
683
+ * @param parts - The worlds, or functions that give a world from the merge before them.
684
+ * @returns The world. It has no `replaces`.
685
+ * @throws {MergeError} The merge has a conflict. The error lists each conflict.
686
+ */
687
+ function mergeWorlds(...parts) {
688
+ const m = new Merger();
689
+ const draft = {
690
+ kinds: {},
691
+ defaults: {}
692
+ };
693
+ parts.forEach((input, index) => {
694
+ const part = typeof input === "function" ? input(worldOf(draft)) : input;
695
+ mergeWorldInto(m, draft, part, index, new Set((part.replaces ?? []).map(keyOf)), []);
696
+ });
697
+ if (m.conflicts.length > 0) throw new MergeError(m.conflicts);
698
+ return worldOf(draft);
699
+ }
700
+ /** The fields of a machine that merge by their own rules. Each other field is one value, or a conflict. */
701
+ const TABLES = /* @__PURE__ */ new Set([
702
+ "world",
703
+ "evaluators",
704
+ "runners",
705
+ "derefs",
706
+ "walkers",
707
+ "known",
708
+ "replaces"
709
+ ]);
710
+ /**
711
+ * This function composes the hooks `known` in the order of their parts. The first answer that is not `true` wins. A
712
+ * hook does not answer for an evaluator name whose code a later part replaces.
713
+ */
714
+ function composeKnown(hooks, replaced) {
715
+ const first = hooks[0];
716
+ if (first === void 0) return void 0;
717
+ if (hooks.length === 1 && replaced.size === 0) return first.known;
718
+ const list = [...hooks];
719
+ const last = new Map(replaced);
720
+ return (path, evaluator) => {
721
+ const after = last.get(evaluator);
722
+ for (const hook of list) {
723
+ if (after !== void 0 && hook.part < after) continue;
724
+ const answer = hook.known(path, evaluator);
725
+ if (answer !== true) return answer;
726
+ }
727
+ return true;
728
+ };
729
+ }
730
+ function machineOf(draft) {
731
+ const known = composeKnown(draft.hooks, draft.replaced);
732
+ return {
733
+ ...draft.rest,
734
+ world: worldOf(draft.world),
735
+ evaluators: { ...draft.evaluators },
736
+ ...draft.runners === void 0 ? {} : { runners: { ...draft.runners } },
737
+ ...draft.derefs === void 0 ? {} : { derefs: { ...draft.derefs } },
738
+ ...draft.walkers === void 0 ? {} : { walkers: { ...draft.walkers } },
739
+ ...known === void 0 ? {} : { known }
740
+ };
741
+ }
742
+ /** This function merges the evaluators and the runners of a part. The evaluator and the runner of a name are its code. */
743
+ function mergeCode(m, draft, part, index, replaces) {
744
+ if (part.runners !== void 0) draft.runners ??= {};
745
+ const names = /* @__PURE__ */ new Set([...Object.keys(part.evaluators ?? {}), ...Object.keys(part.runners ?? {})]);
746
+ for (const name of names) {
747
+ const evaluator = own$1(part.evaluators, name);
748
+ const runner = own$1(part.runners, name);
749
+ if (evaluator === void 0 && runner === void 0) continue;
750
+ const evaluatorPath = ["evaluators", name];
751
+ const runnerPath = ["runners", name];
752
+ if (replaces.has(keyOf(evaluatorPath)) || replaces.has(keyOf(runnerPath))) {
753
+ if (evaluator === void 0) delete draft.evaluators[name];
754
+ else setOwn$2(draft.evaluators, name, evaluator);
755
+ if (runner !== void 0) setOwn$2(draft.runners ??= {}, name, runner);
756
+ else if (draft.runners !== void 0) delete draft.runners[name];
757
+ m.replace(evaluatorPath, index);
758
+ m.replace(runnerPath, index);
759
+ draft.replaced.set(name, index);
760
+ continue;
761
+ }
762
+ mergeLeaf(m, draft.evaluators, name, evaluator, evaluatorPath, index, NONE$1, Object.is, "Two parts give two different evaluators for this name.");
763
+ if (runner !== void 0) mergeLeaf(m, draft.runners ??= {}, name, runner, runnerPath, index, NONE$1, Object.is, "Two parts give two different runners for this name.");
764
+ }
765
+ }
766
+ /** A runner hides the evaluator of its name. Thus a name with a runner and an evaluator needs one part that gives both. */
767
+ function checkHidden(m, draft) {
768
+ const runners = draft.runners ?? {};
769
+ for (const name of Object.keys(runners)) {
770
+ const runner = runners[name];
771
+ const evaluator = own$1(draft.evaluators, name);
772
+ if (runner === void 0 || evaluator === void 0) continue;
773
+ if (draft.parts.some((part) => own$1(part.evaluators, name) === evaluator && own$1(part.runners, name) === runner)) continue;
774
+ const a = m.sourceOf(["evaluators", name]);
775
+ const b = m.sourceOf(["runners", name]);
776
+ m.conflicts.push({
777
+ path: ["runners", name],
778
+ parts: [Math.min(a, b), Math.max(a, b)],
779
+ reason: "The runner of one part hides the evaluator of the other part, because a runner wins over an evaluator of its name."
780
+ });
781
+ }
782
+ }
783
+ /**
784
+ * This function merges machines into one machine, in the order of the parts. It does not change a part.
785
+ *
786
+ * - The worlds merge with the rules of `mergeWorlds`. The paths of their conflicts start with `"world"`.
787
+ * - The tables `evaluators`, `runners`, `derefs` and `walkers` merge name by name. A function is equal only to itself.
788
+ * - The evaluator and the runner of a name are its code. A replacement of the name replaces both.
789
+ * - A runner hides the evaluator of its name. Thus one part must give both, or the merge gives a conflict.
790
+ * - The hooks `known` compose in the order of the parts. A hook does not answer for a name that a later part replaces.
791
+ * - Each other field, for example `permits`, `adopt`, `roots`, `plain` or `maxDepth`, is one value. Two different
792
+ * values are a conflict. An absent field has no value.
793
+ *
794
+ * A conflict stops the merge, unless the later part names its path in `replaces`. A part can be a function of the
795
+ * merge of the parts before it (`prev`, as an overlay of Nix). Thus a part can wrap the code that it replaces.
796
+ *
797
+ * @param parts - The machines, or functions that give a machine from the merge before them.
798
+ * @returns The machine.
799
+ * @throws {MergeError} The merge has a conflict. The error lists each conflict.
800
+ */
801
+ function mergeMachines(...parts) {
802
+ const m = new Merger();
803
+ const draft = {
804
+ world: {
805
+ kinds: {},
806
+ defaults: {}
807
+ },
808
+ evaluators: {},
809
+ hooks: [],
810
+ replaced: /* @__PURE__ */ new Map(),
811
+ rest: {},
812
+ parts: []
813
+ };
814
+ parts.forEach((input, index) => {
815
+ const part = typeof input === "function" ? input(machineOf(draft)) : input;
816
+ draft.parts.push(part);
817
+ const replaces = new Set([...part.replaces ?? [], ...(part.world?.replaces ?? []).map((path) => ["world", ...path])].map(keyOf));
818
+ mergeWorldInto(m, draft.world, part.world, index, replaces, ["world"]);
819
+ mergeCode(m, draft, part, index, replaces);
820
+ if (part.derefs !== void 0) {
821
+ const derefs = draft.derefs ??= {};
822
+ for (const name of Object.keys(part.derefs)) mergeLeaf(m, derefs, name, part.derefs[name], ["derefs", name], index, replaces, Object.is, "Two parts give two different deref codes for this name.");
823
+ }
824
+ if (part.walkers !== void 0) {
825
+ const walkers = draft.walkers ??= {};
826
+ for (const name of Object.keys(part.walkers)) mergeLeaf(m, walkers, name, part.walkers[name], ["walkers", name], index, replaces, Object.is, "Two parts give two different walkers for this name.");
827
+ }
828
+ if (part.known !== void 0 && !draft.hooks.some((hook) => hook.known === part.known)) draft.hooks.push({
829
+ part: index,
830
+ known: part.known
831
+ });
832
+ const fields = part;
833
+ for (const key of Object.keys(fields)) {
834
+ if (TABLES.has(key)) continue;
835
+ mergeLeaf(m, draft.rest, key, fields[key], [key], index, replaces, key === "roots" ? sameRoots : same, `Two parts give two different values for ${key}.`);
836
+ }
837
+ });
838
+ checkHidden(m, draft);
839
+ if (m.conflicts.length > 0) throw new MergeError(m.conflicts);
840
+ return machineOf(draft);
841
+ }
842
+ //#endregion
511
843
  //#region src/ladder.ts
512
844
  /** The rungs of the ladder, in the order that a question visits them. */
513
845
  const RUNGS = [
@@ -2257,7 +2589,8 @@ const STD_WORLD = {
2257
2589
  ],
2258
2590
  of: "number"
2259
2591
  },
2260
- summary: "A node of a tree. Its parts see it as self, and each node links to its parent."
2592
+ caps: [{ rule: "mask" }],
2593
+ summary: "A node of a tree. Each part is an attribute that evaluates on demand, one time, with the node as self."
2261
2594
  },
2262
2595
  box: {
2263
2596
  verbs: {
@@ -2273,7 +2606,8 @@ const STD_WORLD = {
2273
2606
  ],
2274
2607
  of: "number"
2275
2608
  },
2276
- summary: "A node of a tree, as panel. A formula in a box can read the parent of the box."
2609
+ caps: [{ rule: "mask" }],
2610
+ summary: "A node of a tree, as panel. A formula in a box can read its parent and its siblings."
2277
2611
  },
2278
2612
  math: { kinds: {
2279
2613
  sum: {
@@ -2390,53 +2724,167 @@ const STD_EVALUATORS = {
2390
2724
  const found = resolve(data);
2391
2725
  return found.ok ? ok(valueOfThing(found.thing)) : fail(found.code, found.reason);
2392
2726
  },
2393
- "tree.node": (input) => treeNode(input.parts, input.context, input.kind)
2727
+ "tree.node": (input) => treeNode(input)
2394
2728
  };
2729
+ /** The brand of a node of the decorated tree. Its value is the state that the kit reads. */
2730
+ const TREE_NODE = Symbol.for("coeval.tree.node");
2731
+ const BUSY = Object.freeze({ busy: true });
2732
+ /** The attributes in evaluation, in the order of their demands. An evaluation does not wait, thus one list serves. */
2733
+ const demands = [];
2734
+ function attributeText(key, type, place) {
2735
+ return `${key} of ${type} at [${place.join(", ")}]`;
2736
+ }
2737
+ /** This function gives the `#CYCLE!` of a demand that reached an attribute in evaluation, with the chain of demands. */
2738
+ function cycleAt(cells, key, text) {
2739
+ const first = demands.findIndex((demand) => demand.cells === cells && demand.key === key);
2740
+ const chain = first === -1 ? [text] : demands.slice(first).map((demand) => demand.text);
2741
+ chain.push(text);
2742
+ return fail("#CYCLE!", `The attributes form a cycle: ${chain.join(" -> ")}.`);
2743
+ }
2744
+ function stateOf(thing) {
2745
+ return typeof thing === "object" && thing !== null ? thing[TREE_NODE] : void 0;
2746
+ }
2747
+ /** This function sets an own field, also the field `__proto__` (SHAPE.PROTO). */
2748
+ function putField(target, key, value) {
2749
+ Object.defineProperty(target, key, {
2750
+ value,
2751
+ enumerable: true,
2752
+ writable: true,
2753
+ configurable: true
2754
+ });
2755
+ }
2756
+ /**
2757
+ * This function gives a value with the JSON of each node of a tree in it. A list or a plain record is new only when
2758
+ * it holds a node. A container that holds itself stays as it is.
2759
+ */
2760
+ function plainOf(value, path) {
2761
+ const state = stateOf(value);
2762
+ if (state !== void 0) {
2763
+ if (path.has(value)) return fail("#CYCLE!", `The JSON of the node ${state.type} at [${state.place.join(", ")}] holds the node itself.`);
2764
+ return state.json(path);
2765
+ }
2766
+ if (typeof value !== "object" || value === null || path.has(value) || isHostThing(value)) return ok(value);
2767
+ if (!Array.isArray(value) && !isRecord(value)) return ok(value);
2768
+ path.add(value);
2769
+ try {
2770
+ if (Array.isArray(value)) {
2771
+ let list;
2772
+ for (let index = 0; index < value.length; index += 1) {
2773
+ const item = plainOf(value[index], path);
2774
+ if (!item.ok) return item;
2775
+ if (item.value !== value[index]) (list ??= [...value])[index] = item.value;
2776
+ }
2777
+ return ok(list ?? value);
2778
+ }
2779
+ const record = value;
2780
+ let out;
2781
+ for (const key of Object.keys(record)) {
2782
+ const item = plainOf(record[key], path);
2783
+ if (!item.ok) return item;
2784
+ if (item.value === record[key]) continue;
2785
+ if (out === void 0) {
2786
+ out = {};
2787
+ for (const other of Object.keys(record)) putField(out, other, record[other]);
2788
+ }
2789
+ putField(out, key, item.value);
2790
+ }
2791
+ return ok(out ?? value);
2792
+ } finally {
2793
+ path.delete(value);
2794
+ }
2795
+ }
2395
2796
  /**
2396
- * This function evaluates a node of a tree. The node is a host thing. A step `parent` reaches the node that holds
2397
- * it. A step with the name of a part evaluates that part on demand, one time. Each part sees the node as self.
2797
+ * This function makes a node of the decorated tree. A step `parent` gives the self of the context of the node, and a
2798
+ * step `type` gives its type. A step with the key of a part reads that attribute. A failed attribute throws `Failure`
2799
+ * with its own error, thus a reference to it gives that error.
2398
2800
  */
2399
- function treeNode(parts, context, type) {
2400
- const memo = /* @__PURE__ */ new Map();
2801
+ function treeThing(parts, context, type, place, build) {
2802
+ const cells = /* @__PURE__ */ new Map();
2401
2803
  const read = (key) => {
2402
- const known = memo.get(key);
2403
- if (known !== void 0) return known;
2804
+ const cell = cells.get(key);
2805
+ if (cell !== void 0) return cell.busy ? cycleAt(cells, key, attributeText(key, type, place)) : cell.result;
2404
2806
  const part = parts.find((item) => item.key === key);
2405
2807
  if (part === void 0) return void 0;
2406
- const result = part.evaluate({
2407
- ...context,
2408
- self: me
2808
+ cells.set(key, BUSY);
2809
+ demands.push({
2810
+ cells,
2811
+ key,
2812
+ text: attributeText(key, type, place)
2813
+ });
2814
+ let result;
2815
+ try {
2816
+ result = part.evaluate({
2817
+ ...context,
2818
+ self: node
2819
+ });
2820
+ } catch (error) {
2821
+ cells.delete(key);
2822
+ throw error;
2823
+ } finally {
2824
+ demands.pop();
2825
+ }
2826
+ cells.set(key, {
2827
+ busy: false,
2828
+ result
2409
2829
  });
2410
- memo.set(key, result);
2411
2830
  return result;
2412
2831
  };
2413
- const me = {
2832
+ const state = {
2833
+ build,
2834
+ type,
2835
+ place,
2836
+ json(path) {
2837
+ path.add(node);
2838
+ try {
2839
+ const out = {};
2840
+ for (const part of parts) {
2841
+ const result = read(part.key);
2842
+ if (!result.ok) return result;
2843
+ const value = plainOf(result.value, path);
2844
+ if (!value.ok) return value;
2845
+ putField(out, part.key, value.value);
2846
+ }
2847
+ return ok(out);
2848
+ } finally {
2849
+ path.delete(node);
2850
+ }
2851
+ }
2852
+ };
2853
+ const node = {
2414
2854
  [THING]: true,
2855
+ [TREE_NODE]: state,
2415
2856
  type,
2416
2857
  label: `node ${type}`,
2417
2858
  deref(key) {
2418
2859
  if (key === "parent") return context.self;
2419
2860
  if (key === "type") return type;
2420
2861
  const result = read(String(key));
2421
- return result?.ok === true ? result.value : void 0;
2862
+ if (result === void 0) return void 0;
2863
+ if (!result.ok) throw new Failure(result.error);
2864
+ return result.value;
2422
2865
  },
2423
2866
  toJson() {
2424
- const out = {};
2425
- for (const part of parts) {
2426
- const result = read(part.key);
2427
- out[part.key] = result?.ok === true ? result.value : null;
2428
- }
2429
- return out;
2867
+ const result = state.json(/* @__PURE__ */ new Set());
2868
+ if (!result.ok) throw new Failure(result.error);
2869
+ return result.value;
2430
2870
  }
2431
2871
  };
2432
- const out = {};
2433
- for (const part of parts) {
2434
- const result = read(part.key);
2435
- if (result === void 0) continue;
2436
- if (!result.ok) return result;
2437
- out[part.key] = result.value;
2872
+ return node;
2873
+ }
2874
+ /**
2875
+ * This function evaluates a node of the decorated tree. Inside an attribute of an open tree, the node gives its host
2876
+ * thing, and no attribute evaluates. Else the node is the root of a new tree, and it gives the JSON of the tree.
2877
+ */
2878
+ function treeNode({ parts, context, kind, place }) {
2879
+ const above = stateOf(context.self);
2880
+ if (above !== void 0 && above.build.open) return ok(treeThing(parts, context, kind, place, above.build));
2881
+ const build = { open: true };
2882
+ const root = treeThing(parts, context, kind, place, build);
2883
+ try {
2884
+ return stateOf(root).json(/* @__PURE__ */ new Set());
2885
+ } finally {
2886
+ build.open = false;
2438
2887
  }
2439
- return ok(out);
2440
2888
  }
2441
2889
  function hostType(node, wanted) {
2442
2890
  return isHostThing(node) && node.type === wanted;
@@ -3906,44 +4354,36 @@ function formulaKnown(profile = {}) {
3906
4354
  };
3907
4355
  }
3908
4356
  /**
3909
- * This function makes a machine for a formula language. The machine has the kinds and the evaluators of the
3910
- * formula kit. It also has the kinds, the evaluators and the walkers that the language adds. Its hook `known` comes
3911
- * from the profile (`formulaKnown`). A hook `known` of the language answers each name that the kit knows.
4357
+ * This function makes a machine for a formula language. It merges the kit and the parts that the language adds with
4358
+ * `mergeMachines`. Thus a kind `ops.*` or a namespace `ops` of the language extends the namespace of the kit, and
4359
+ * the fallback of the kit stays. The hook `known` of the kit comes from the profile (`formulaKnown`). A hook `known`
4360
+ * of the language answers after it.
4361
+ *
4362
+ * A kind or an evaluator of the language that differs from the entry of the kit with its name is a conflict. The
4363
+ * language names the path in `replaces` to replace the entry, for example `["world", "kinds", "ref"]` or
4364
+ * `["evaluators", "formula.op"]`. Then the hook of the kit does not answer for a replaced evaluator.
3912
4365
  *
3913
4366
  * @param profile - The operations and the rules of the language.
3914
- * @param more - The parts of a machine that the language adds. Its kinds win over the kinds of the kit.
4367
+ * @param more - The parts of a machine that the language adds, with the paths of the entries that it replaces.
3915
4368
  * @returns The machine.
4369
+ * @throws {MergeError} The language and the kit have a conflict.
3916
4370
  */
3917
4371
  function formulaMachine(profile = {}, more = {}) {
3918
- const kit = formulaKnown(profile);
3919
- const replaced = (name) => more.evaluators !== void 0 && Object.hasOwn(more.evaluators, name) || more.runners !== void 0 && Object.hasOwn(more.runners, name);
3920
- const theirs = more.known;
3921
- const known = (path, evaluator) => {
3922
- const answer = replaced(evaluator) ? true : kit(path, evaluator);
3923
- return answer === true && theirs !== void 0 ? theirs(path, evaluator) : answer;
3924
- };
3925
- return {
3926
- ...more,
3927
- world: {
3928
- kinds: {
3929
- ...formulaKinds(),
3930
- ...more.world?.kinds
3931
- },
3932
- defaults: { ...more.world?.defaults }
3933
- },
3934
- evaluators: {
3935
- ...formulaEvaluators(profile),
3936
- ...more.evaluators
3937
- },
3938
- known
3939
- };
4372
+ return mergeMachines({
4373
+ world: { kinds: formulaKinds() },
4374
+ evaluators: formulaEvaluators(profile),
4375
+ known: formulaKnown(profile)
4376
+ }, more);
3940
4377
  }
3941
4378
  //#endregion
3942
4379
  //#region src/reference.ts
3943
4380
  function own(table, name) {
3944
4381
  return table !== void 0 && Object.hasOwn(table, name) ? table[name] : void 0;
3945
4382
  }
3946
- /** This function finds the code of an evaluator name. A runner wins over an evaluator of the same name. */
4383
+ /**
4384
+ * This function finds the code of an evaluator name. A runner wins over an evaluator of the same name. A standard
4385
+ * evaluator gets the definition of the reference, unless the options give another definition (REF.TREE).
4386
+ */
3947
4387
  function choose(env, name) {
3948
4388
  const runner = own(env.machine.runners, name);
3949
4389
  if (runner !== void 0) {
@@ -3953,7 +4393,8 @@ function choose(env, name) {
3953
4393
  }
3954
4394
  const evaluator = own(env.machine.evaluators, name);
3955
4395
  if (evaluator === void 0) return void 0;
3956
- const defined = own(env.definitions.evaluators, name);
4396
+ const standard = evaluator === own(STD_EVALUATORS, name) ? own(STD_DEFINITIONS.evaluators, name) : void 0;
4397
+ const defined = own(env.definitions.evaluators, name) ?? standard;
3957
4398
  if (defined !== void 0) return { evaluator: defined };
3958
4399
  return typeof evaluator === "function" ? { evaluator } : { gap: `The reference has no definition of the compiled evaluator ${name}.` };
3959
4400
  }
@@ -4218,6 +4659,126 @@ function reference(machine, doc, context = makeContext(), options = {}) {
4218
4659
  return placed(thrown(error, "the reference"), []);
4219
4660
  }
4220
4661
  }
4662
+ const refNodes = /* @__PURE__ */ new WeakMap();
4663
+ /** The attributes in evaluation in the reference, in the order of their demands (TREE.CYCLE). */
4664
+ const inEvaluation = [];
4665
+ function nameOfAttribute(node, name) {
4666
+ return `${name} of ${node.kind} at [${node.place.join(", ")}]`;
4667
+ }
4668
+ function refNodeOf(thing) {
4669
+ return typeof thing === "object" && thing !== null ? refNodes.get(thing) : void 0;
4670
+ }
4671
+ /** TREE.ONCE, TREE.MEMO and TREE.CYCLE: the result of one attribute, or `undefined` when the node has no such part. */
4672
+ function attributeOf(node, self, name) {
4673
+ const done = node.values.get(name);
4674
+ if (done !== void 0) return done;
4675
+ if (node.pending.has(name)) {
4676
+ const from = inEvaluation.findIndex((entry) => entry.node === node && entry.name === name);
4677
+ return fail("#CYCLE!", `The attributes form a cycle: ${[...from < 0 ? [nameOfAttribute(node, name)] : inEvaluation.slice(from).map((entry) => nameOfAttribute(entry.node, entry.name)), nameOfAttribute(node, name)].join(" -> ")}.`);
4678
+ }
4679
+ const part = node.parts.find((item) => item.key === name);
4680
+ if (part === void 0) return void 0;
4681
+ node.pending.add(name);
4682
+ inEvaluation.push({
4683
+ node,
4684
+ name
4685
+ });
4686
+ try {
4687
+ const result = part.evaluate({
4688
+ ...node.ctx,
4689
+ self
4690
+ });
4691
+ node.values.set(name, result);
4692
+ return result;
4693
+ } finally {
4694
+ node.pending.delete(name);
4695
+ inEvaluation.pop();
4696
+ }
4697
+ }
4698
+ /** TREE.RESULT: the JSON of a node. `open` holds the nodes and the containers whose JSON is in progress. */
4699
+ function jsonOfNode(node, self, open) {
4700
+ const record = {};
4701
+ for (const part of node.parts) {
4702
+ const result = attributeOf(node, self, part.key);
4703
+ if (!result.ok) return result;
4704
+ const value = plainValue(result.value, [...open, self]);
4705
+ if (!value.ok) return value;
4706
+ setField(record, part.key, value.value);
4707
+ }
4708
+ return ok(record);
4709
+ }
4710
+ /** This function gives a copy of a value with the JSON of each node in it. */
4711
+ function plainValue(value, open) {
4712
+ const node = refNodeOf(value);
4713
+ if (node !== void 0) {
4714
+ if (open.includes(value)) return fail("#CYCLE!", `The JSON of the node ${node.kind} at [${node.place.join(", ")}] holds the node itself.`);
4715
+ return jsonOfNode(node, value, open);
4716
+ }
4717
+ if (typeof value !== "object" || value === null || isHostThing(value) || open.includes(value)) return ok(value);
4718
+ if (Array.isArray(value)) {
4719
+ const items = [];
4720
+ for (const item of value) {
4721
+ const plain = plainValue(item, [...open, value]);
4722
+ if (!plain.ok) return plain;
4723
+ items.push(plain.value);
4724
+ }
4725
+ return ok(items);
4726
+ }
4727
+ if (!isRecord(value)) return ok(value);
4728
+ const copy = {};
4729
+ for (const key of Object.keys(value)) {
4730
+ const plain = plainValue(value[key], [...open, value]);
4731
+ if (!plain.ok) return plain;
4732
+ setField(copy, key, plain.value);
4733
+ }
4734
+ return ok(copy);
4735
+ }
4736
+ /**
4737
+ * TREE.LAZY, TREE.PARENT, TREE.SIBLING and TREE.ERROR: a node of an open tree gives its thing, and the root of a new
4738
+ * tree gives its JSON. A step on the thing reads an attribute, and a failed attribute throws its own error.
4739
+ */
4740
+ const treeDefinition = ({ parts, context, kind, place }) => {
4741
+ const outer = refNodeOf(context.self);
4742
+ const nested = outer !== void 0 && outer.tree.open;
4743
+ const node = {
4744
+ tree: nested ? outer.tree : { open: true },
4745
+ kind,
4746
+ place,
4747
+ parts,
4748
+ ctx: context,
4749
+ values: /* @__PURE__ */ new Map(),
4750
+ pending: /* @__PURE__ */ new Set()
4751
+ };
4752
+ const self = {
4753
+ [THING]: true,
4754
+ type: kind,
4755
+ label: `node ${kind}`,
4756
+ deref: (key) => {
4757
+ if (key === "parent") return context.self;
4758
+ if (key === "type") return kind;
4759
+ const result = attributeOf(node, self, String(key));
4760
+ if (result === void 0 || result.ok) return result?.ok === true ? result.value : void 0;
4761
+ throw new Failure(result.error);
4762
+ },
4763
+ toJson: () => {
4764
+ const result = jsonOfNode(node, self, []);
4765
+ if (result.ok) return result.value;
4766
+ throw new Failure(result.error);
4767
+ }
4768
+ };
4769
+ refNodes.set(self, node);
4770
+ if (nested) return ok(self);
4771
+ try {
4772
+ return jsonOfNode(node, self, []);
4773
+ } finally {
4774
+ node.tree.open = false;
4775
+ }
4776
+ };
4777
+ /**
4778
+ * The definitions of the reference for the evaluators of the standard machine that need their own reading. The
4779
+ * reference uses them for the standard evaluators, also without the option `definitions`. It holds `tree.node`.
4780
+ */
4781
+ const STD_DEFINITIONS = { evaluators: { "tree.node": treeDefinition } };
4221
4782
  const CLOSURE$1 = Symbol.for("coeval.closure");
4222
4783
  /** This function sets an own field, also the field `__proto__` (SHAPE.PROTO). */
4223
4784
  function setField(target, key, value) {
@@ -4697,6 +5258,8 @@ function generate(world, options) {
4697
5258
  const maxSize = options.maxSize ?? 24;
4698
5259
  const malformed = options.malformed ?? .05;
4699
5260
  const keys = options.keys ?? KEYS;
5261
+ const steps = options.steps ?? 2;
5262
+ const roots = options.roots ?? ROOTS;
4700
5263
  const stepTypes = types.filter((type) => {
4701
5264
  const found = lookupKind(world, type);
4702
5265
  return found.ok && Object.hasOwn(found.kind.verbs, "step");
@@ -4774,11 +5337,12 @@ function generate(world, options) {
4774
5337
  { names: "a" },
4775
5338
  ["nowhere", "a"]
4776
5339
  ]);
4777
- const root = pos.names.length > 0 && rng.chance(.75) ? "names" : rng.pick(ROOTS.filter((name) => name !== "names"));
5340
+ const others = roots.filter((name) => name !== "names");
5341
+ const root = roots.includes("names") && pos.names.length > 0 && (others.length === 0 || rng.chance(.75)) || others.length === 0 ? "names" : rng.pick(others);
4778
5342
  const path = [root];
4779
5343
  if (root === "names") path.push(pickName(pos.names));
4780
5344
  if (root === "world") path.push(String(rng.pick(types)[0]));
4781
- for (let index = rng.int(3); index > 0; index -= 1) if (stepTypes.length > 0 && rng.chance(.2)) {
5345
+ for (let index = rng.int(steps + 1); index > 0; index -= 1) if (stepTypes.length > 0 && rng.chance(.2)) {
4782
5346
  const type = rng.pick(stepTypes);
4783
5347
  const kind = kindOf(type);
4784
5348
  const value = refKind?.parts?.shape === "deep" && kind?.parts !== void 0 ? valueOf(kind.parts, [], {
@@ -5000,6 +5564,6 @@ function generate(world, options) {
5000
5564
  });
5001
5565
  }
5002
5566
  //#endregion
5003
- export { CONSTRUCTS, ENVELOPE_KEYS, Failure, Held, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_EVALUATORS, STD_EXT, STD_MACHINE, STD_RUNNERS, STD_WALKERS, STD_WORLD, THING, agree, ask, capKeys, check, checkSteps, classify, context, dataEdges, derefStep, describe, docEquals, effectEdges, entriesAsJson, evaluate, fail, failures, field, flatten, formulaDefinitions, formulaEvaluators, formulaKinds, formulaKnown, formulaMachine, fromJson, generate, intersectCaps, isAlias, isAllowedOrder, isClosure, isEnvelope, isHostThing, isKey, isKind, isNamespace, isRecord, isRef, isStackOverflow, isTypeRef, jsTruthy, kindDocument, kindNamed, kindOf, layers, looksLikeKind, lookupKind, makeContext, mapRefs, narrow, nearestWalker, nodeAt, ok, partCaps, partsOf, pathMatches, permits, placed, planner, prepare, random, ref, reference, refsOf, resolve, run, sameValue, splitParts, stored, tag, thrown, toJson, typeName, typePath, typed, typesOf, valueOfThing, walk, worldThing };
5567
+ export { CONSTRUCTS, ENVELOPE_KEYS, Failure, Held, MergeError, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_DEFINITIONS, STD_EVALUATORS, STD_EXT, STD_MACHINE, STD_RUNNERS, STD_WALKERS, STD_WORLD, THING, agree, ask, capKeys, check, checkSteps, classify, context, dataEdges, derefStep, describe, docEquals, effectEdges, entriesAsJson, evaluate, fail, failures, field, flatten, formulaDefinitions, formulaEvaluators, formulaKinds, formulaKnown, formulaMachine, fromJson, generate, intersectCaps, isAlias, isAllowedOrder, isClosure, isEnvelope, isHostThing, isKey, isKind, isNamespace, isRecord, isRef, isStackOverflow, isTypeRef, jsTruthy, kindDocument, kindNamed, kindOf, layers, looksLikeKind, lookupKind, makeContext, mapRefs, mergeMachines, mergeWorlds, narrow, nearestWalker, nodeAt, ok, partCaps, partsOf, pathMatches, permits, placed, planner, prepare, random, ref, reference, refsOf, resolve, run, sameValue, splitParts, stored, tag, thrown, toJson, typeName, typePath, typed, typesOf, valueOfThing, walk, worldThing };
5004
5568
 
5005
5569
  //# sourceMappingURL=index.js.map