@mark1russell7/coeval 0.14.0 → 0.16.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/evaluate.ts CHANGED
@@ -4,6 +4,7 @@ import { answerOf, ask, type Answer } from "./ladder.ts";
4
4
  import type { CapsRule, Kind, KindFacts, Side } from "./world.ts";
5
5
  import type { Domain } from "./types.ts";
6
6
  import type { TypeRule } from "./typing.ts";
7
+ import type { StaticRead } from "./facts.ts";
7
8
  import { type ErrorCode, type EvalError, type Result, Failure, fail, ok, placed, thrown, describe } from "./errors.ts";
8
9
  import {
9
10
  type Cap,
@@ -17,6 +18,7 @@ import {
17
18
  type Thing,
18
19
  ROOTS,
19
20
  context as makeRootContext,
21
+ derefStep,
20
22
  isHostThing,
21
23
  isRef,
22
24
  resolve,
@@ -269,8 +271,8 @@ export interface Read {
269
271
  */
270
272
  readonly free: boolean;
271
273
  /**
272
- * `true` when the kernel adds the read, because a value keeps a root of the context for later (READS.KEPT). Such a
273
- * read has the whole root as its path.
274
+ * `true` when the kernel adds the read, because a value keeps the context for later (READS.KEPT). Such a read has
275
+ * a whole root as its path, or a path that a closure can read (READS.CLOSURE).
274
276
  */
275
277
  readonly kept?: true;
276
278
  /** The hops of the resolution, only when the code asked for them. `verifyReads` then compares each hop too. */
@@ -459,9 +461,13 @@ export interface Runtime {
459
461
  /**
460
462
  * This optional function tells the runtime that a value keeps the context for later, for example a closure of
461
463
  * `fn`. A later use of the value reads that context, and no evaluation records these reads. Thus the list of reads
462
- * gets a read of each root of the context (READS.KEPT).
464
+ * gets a kept read of each path that the value can read (READS.CLOSURE). Without the static reads, the list gets a
465
+ * read of each root of the context (READS.KEPT).
466
+ *
467
+ * @param context - The context that the value keeps.
468
+ * @param reads - This function gives the static reads of the value, or `undefined` when they are not known.
463
469
  */
464
- keepsContext?(): void;
470
+ keepsContext?(context?: Context, reads?: () => readonly StaticRead[] | undefined): void;
465
471
  }
466
472
 
467
473
  /**
@@ -894,6 +900,97 @@ interface Reading {
894
900
  kept: boolean;
895
901
  /** `true` after the kernel added the read of the root `self`. */
896
902
  self: boolean;
903
+ /** The paths of the kept reads of closures, as JSON texts (READS.CLOSURE). The set is made at the first such read. */
904
+ paths?: Set<string>;
905
+ }
906
+
907
+ /** The walk of a kept path: the path, and what it reached when a host thing answered a step with `known`. */
908
+ interface KeptWalk {
909
+ readonly path: readonly unknown[];
910
+ /** What the path reached. Without it, the read resolves its path with `readAgain`. */
911
+ readonly reached?: Result;
912
+ }
913
+
914
+ /**
915
+ * This function gives the path of a kept read (READS.CLOSURE). The evaluation did not ask for the read. Thus the read
916
+ * does not step into a host thing with `deref`. A host thing with `known` answers a step without new work
917
+ * (SCHED.CLOSURE). Each other host thing ends the path, and a host thing that does not know the step ends it too.
918
+ * A reference in the data reads the context of its reader, thus the function then gives `undefined`.
919
+ *
920
+ * @param nav - The world and the code of the evaluators.
921
+ * @param root - The context at the root of the evaluation.
922
+ * @param path - The root and the keys of the static read.
923
+ * @returns The path of the kept read with what it reached after a step of `known`, or `undefined` for a reference on the way.
924
+ */
925
+ function keptPath(nav: Navigator, root: Context, path: readonly unknown[]): KeptWalk | undefined {
926
+ const adopt = nav.adopt ?? ((value: unknown): Thing => value);
927
+ let here = adopt(root[path[0] as Root]);
928
+ // This mark is `true` after a step of `known`. Then the walk gives what the path reached, and no resolution follows.
929
+ let known = false;
930
+ const end = (index: number): KeptWalk => {
931
+ const kept = index >= path.length ? path : path.slice(0, index);
932
+ return known ? { path: kept, reached: ok(here) } : { path: kept };
933
+ };
934
+ for (let index = 1; ; index += 1) {
935
+ if (isHostThing(here)) {
936
+ if (index >= path.length || here.known === undefined) return end(index);
937
+ let step: { readonly thing: Thing } | undefined;
938
+ try {
939
+ step = here.known(path[index] as Key);
940
+ } catch (error) {
941
+ // A failed value gives its error to the read, as an exception in `readAgain` does.
942
+ return { path: path.slice(0, index + 1), reached: thrown(error, READ_FAILED) };
943
+ }
944
+ if (step === undefined) return end(index);
945
+ known = true;
946
+ here = adopt(step.thing);
947
+ continue;
948
+ }
949
+ if (nav.plain !== true && isRef(here)) return undefined;
950
+ if (index >= path.length) return end(index);
951
+ const reached = derefStep(nav, here, path[index] as Key);
952
+ // A step that reaches nothing fails, and the resolution of the path fails at the same step.
953
+ if (reached === undefined) return known ? end(index) : { path };
954
+ here = adopt(reached.thing);
955
+ }
956
+ }
957
+
958
+ /**
959
+ * This function resolves the path of a kept read again, for `verifyReads` (SCHED.CLOSURE). It walks the path as
960
+ * `keptPath` did. A host thing with `known` answers its step. A path without such a step resolves with `readAgain`.
961
+ *
962
+ * @param nav - The world and the code of the evaluators.
963
+ * @param ctx - The new context.
964
+ * @param path - The path of the kept read.
965
+ * @returns What the path reaches, or `undefined` when a host does not know a step yet. Then the read changed.
966
+ */
967
+ export function keptAgain(nav: Navigator, ctx: Context, path: readonly unknown[]): Result | undefined {
968
+ const adopt = nav.adopt ?? ((value: unknown): Thing => value);
969
+ let here = adopt(ctx[path[0] as Root]);
970
+ let known = false;
971
+ // Before a step of `known`, each other answer of the walk is the answer of `readAgain`, as before SCHED.CLOSURE.
972
+ const other = (): Result | undefined => (known ? undefined : readAgain(nav, ctx, path, false).reached);
973
+ for (let index = 1; index < path.length; index += 1) {
974
+ if (isHostThing(here)) {
975
+ if (here.known === undefined) return other();
976
+ let step: { readonly thing: Thing } | undefined;
977
+ try {
978
+ step = here.known(path[index] as Key);
979
+ } catch (error) {
980
+ return thrown(error, READ_FAILED);
981
+ }
982
+ if (step === undefined) return undefined;
983
+ known = true;
984
+ here = adopt(step.thing);
985
+ continue;
986
+ }
987
+ if (nav.plain !== true && isRef(here)) return other();
988
+ const reached = derefStep(nav, here, path[index] as Key);
989
+ if (reached === undefined) return other();
990
+ here = adopt(reached.thing);
991
+ }
992
+ if (!known) return other();
993
+ return nav.plain !== true && isRef(here) ? undefined : ok(here);
897
994
  }
898
995
 
899
996
  /** This function makes the record of the reads of an evaluation, when the host gives a list. */
@@ -995,13 +1092,46 @@ class Session implements Runtime {
995
1092
  return resolve(this.machine, ctx, path, hops ? WITH_HOPS : WITHOUT_HOPS);
996
1093
  }
997
1094
 
998
- keepsContext(): void {
1095
+ keepsContext(ctx?: Context, reads?: () => readonly StaticRead[] | undefined): void {
999
1096
  const reading = this.reading;
1000
1097
  if (reading === undefined || reading.kept) return;
1098
+ const known = ctx === undefined ? undefined : reads?.();
1099
+ // A value without its static reads can read each root later (READS.KEPT).
1100
+ if (ctx !== undefined && known !== undefined && known.every((read) => this.keepRead(reading, ctx, read))) return;
1001
1101
  reading.kept = true;
1002
1102
  this.keep(reading, reading.self ? ROOTS.filter((root) => root !== "self") : ROOTS);
1003
1103
  }
1004
1104
 
1105
+ /**
1106
+ * This function adds the kept read of one static read of a closure (READS.CLOSURE). The read resolves from the
1107
+ * context at the root, one time in an evaluation. A name that a kind binds in the kept context gives no read,
1108
+ * because the reads of its binding are already in the list.
1109
+ *
1110
+ * @returns `false` when the path meets a reference in the data. Then the closure keeps each whole root.
1111
+ */
1112
+ private keepRead(reading: Reading, ctx: Context, read: StaticRead): boolean {
1113
+ const root = read.root;
1114
+ const exact: readonly unknown[] = [root, ...read.keys];
1115
+ if (!isFree(reading.root, ctx, exact)) {
1116
+ // A kind that sets another self can give the old self on, as for a direct read (READS.KEPT).
1117
+ if (root === "self" && !reading.self) this.keep(reading, ["self"]);
1118
+ return true;
1119
+ }
1120
+ // A read of the whole root self already holds each path of self.
1121
+ if (root === "self" && reading.self) return true;
1122
+ // A step document can walk outside the keys of its path, thus such a read keeps the whole root.
1123
+ const walk = keptPath(this.machine, reading.root, read.exact ? exact : [root]);
1124
+ if (walk === undefined) return false;
1125
+ const path = walk.path;
1126
+ const paths = (reading.paths ??= new Set());
1127
+ const text = JSON.stringify(path);
1128
+ if (paths.has(text) || paths.has(JSON.stringify([root]))) return true;
1129
+ paths.add(text);
1130
+ if (path.length === 1 && root === "self") reading.self = true;
1131
+ reading.list.push({ path, reached: walk.reached ?? readAgain(this.machine, reading.root, path, false).reached, free: true, kept: true });
1132
+ return true;
1133
+ }
1134
+
1005
1135
  /**
1006
1136
  * This function adds a read of each whole root to the list, from the context at the root (READS.KEPT). An exception
1007
1137
  * of the host gives the error value of the read, because the evaluation did not ask for the read.
@@ -1338,6 +1468,15 @@ export interface PrepareOptions {
1338
1468
  * evaluates one time, when the plan compiles. Its result is one frozen value for each evaluation (PLAN.FOLD).
1339
1469
  */
1340
1470
  readonly fold?: boolean;
1471
+ /**
1472
+ * The values that the host declares fixed for each evaluation of the plan (PLAN.FIXED, D60). The field `names` gives
1473
+ * a value for each fixed name. A path from a fixed name then resolves one time, when the plan compiles, and a pure
1474
+ * part that reads only fixed names folds. Only a deeply frozen value of plain data fixes its name.
1475
+ *
1476
+ * Each folded part compares the value of its names with one own-key test and `Object.is`. A different value gives
1477
+ * the code without the fold. A plan without folds ignores this option.
1478
+ */
1479
+ readonly fixed?: { readonly names?: Readonly<Record<string, unknown>> };
1341
1480
  }
1342
1481
 
1343
1482
  /** A compiled document. It evaluates many times, and it does the work of the ladder one time. */
@@ -1376,6 +1515,8 @@ interface Compiler {
1376
1515
  readonly noAnswer: Answer;
1377
1516
  /** `true` when closed pure parts fold (PLAN.FOLD). A plan with a trace does not fold. */
1378
1517
  readonly fold: boolean;
1518
+ /** The fixed names and their values, or `undefined` when the plan has none (PLAN.FIXED). */
1519
+ readonly fixed: Readonly<Record<string, unknown>> | undefined;
1379
1520
  /** The runtime of the folds of this compilation. The first fold makes it. */
1380
1521
  probe?: Probe;
1381
1522
  }
@@ -1536,7 +1677,11 @@ function compileNode(c: Compiler, value: unknown, place: readonly Key[], declare
1536
1677
  if (frees === undefined || kind === undefined || !pureCode(c, envelope, kind, typePath, name, quick)) return done;
1537
1678
  const free = freeOf(doc, kind, split.parts, frees);
1538
1679
  if (free === undefined) return done;
1539
- return free.length === 0 ? folded(c, done) : { code: done.code, run: done.run, free };
1680
+ if (free.length === 0) return folded(c, done, undefined);
1681
+ // A part that reads only fixed names folds, and its code compares the values of these names (PLAN.FIXED).
1682
+ const fixed = c.fixed;
1683
+ if (fixed !== undefined && free.every((each) => Object.hasOwn(fixed, each))) return folded(c, done, free);
1684
+ return { code: done.code, run: done.run, free };
1540
1685
  }
1541
1686
 
1542
1687
  /*
@@ -1629,6 +1774,20 @@ class Probe implements Runtime {
1629
1774
  this.root = root;
1630
1775
  }
1631
1776
 
1777
+ /** The context of a part that folds with fixed names: the context of the probe, with the fixed names (PLAN.FIXED). */
1778
+ private withFixed: Context | undefined;
1779
+
1780
+ /**
1781
+ * This function gives the context of a part that folds with fixed names. Its root `names` holds only the fixed
1782
+ * names, thus the probe resolves each fixed name and refuses each other name.
1783
+ *
1784
+ * @param fixed - The fixed names and their values.
1785
+ * @returns The context.
1786
+ */
1787
+ fixedRoot(fixed: Readonly<Record<string, unknown>>): Context {
1788
+ return (this.withFixed ??= { ...this.root, names: fixed });
1789
+ }
1790
+
1632
1791
  resolve(ctx: Context, path: readonly unknown[]): Resolution {
1633
1792
  const name = path[1];
1634
1793
  if (path[0] === "names" && isKey(name) && Object.hasOwn(ctx.names, String(name))) {
@@ -1693,25 +1852,106 @@ function trap(refuse: () => void, copies: boolean): object {
1693
1852
  * one deeply frozen copy, and the code gives that copy at each evaluation (PLAN.SHARED). A fold that the probe cancels
1694
1853
  * gives the code without free names, thus no parent tries the fold again.
1695
1854
  */
1696
- function folded(c: Compiler, done: Compiled): Compiled {
1855
+ function folded(c: Compiler, done: Compiled, fixed: readonly string[] | undefined): Compiled {
1697
1856
  const probe = (c.probe ??= new Probe(c.machine));
1698
1857
  probe.refused = false;
1699
1858
  probe.reads = 0;
1700
1859
  let constant: Result | undefined;
1701
1860
  try {
1702
- const result = done.code(probe.root, probe);
1861
+ const root = fixed === undefined ? probe.root : probe.fixedRoot(c.fixed as Readonly<Record<string, unknown>>);
1862
+ const result = done.code(root, probe);
1703
1863
  if (!probe.refused && (result.ok || result.error.code !== "#DEPTH!")) constant = sharedCopy(result) as Result | undefined;
1704
1864
  } catch {
1705
1865
  constant = undefined;
1706
1866
  }
1707
1867
  if (constant === undefined) return { code: done.code, run: done.run };
1708
1868
  const value = constant;
1869
+ const original = done.code;
1870
+ if (fixed !== undefined) return { code: fixedCode(fixed, c.fixed as Readonly<Record<string, unknown>>, value, original), run: undefined, free: fixed };
1709
1871
  if (probe.reads === 0) return { code: () => value, run: undefined, free: CLOSED };
1710
1872
  // The part reads its own names. A list of reads gets each read, thus the code evaluates again (READS.RECORD).
1711
- const original = done.code;
1712
1873
  return { code: (ctx, rt) => (rt instanceof Session && !rt.traced && !rt.recording ? value : original(ctx, rt)), run: undefined, free: CLOSED };
1713
1874
  }
1714
1875
 
1876
+ /**
1877
+ * This function gives the code of a part that folded with fixed names (PLAN.FIXED, D60). The code gives the folded
1878
+ * value when the context has the fixed value of each name, by an own-key test and `Object.is`. A fixed value is
1879
+ * deeply frozen, thus the same object gives the same result. Each other context, a list of reads and another runtime
1880
+ * get the code without the fold. The probe of a parent fold gets the folded value, and it counts a read.
1881
+ */
1882
+ function fixedCode(names: readonly string[], fixed: Readonly<Record<string, unknown>>, value: Result, original: Code): Code {
1883
+ if (names.length === 1) {
1884
+ const name = names[0] as string;
1885
+ const want = fixed[name];
1886
+ return (ctx, rt) => {
1887
+ const given = ctx.names;
1888
+ return Object.hasOwn(given, name) && Object.is(given[name], want) && folds(rt) ? value : original(ctx, rt);
1889
+ };
1890
+ }
1891
+ const wants = names.map((name) => fixed[name]);
1892
+ return (ctx, rt) => {
1893
+ const given = ctx.names;
1894
+ for (let index = 0; index < names.length; index += 1) {
1895
+ const name = names[index] as string;
1896
+ if (!Object.hasOwn(given, name) || !Object.is(given[name], wants[index])) return original(ctx, rt);
1897
+ }
1898
+ return folds(rt) ? value : original(ctx, rt);
1899
+ };
1900
+ }
1901
+
1902
+ /**
1903
+ * This function tells if the code of a part with fixed names can give its folded value in a runtime. The probe of a
1904
+ * fold counts the read of the part, as the code without the fold reads the names.
1905
+ */
1906
+ function folds(rt: Runtime): boolean {
1907
+ if (rt instanceof Session) return !rt.recording;
1908
+ if (!(rt instanceof Probe)) return false;
1909
+ rt.reads += 1;
1910
+ return true;
1911
+ }
1912
+
1913
+ /**
1914
+ * This function gives the fixed names of the options of `prepare` (PLAN.FIXED). A name fixes only when its value is
1915
+ * deeply frozen plain data. Then no change in place can make the folded value stale.
1916
+ *
1917
+ * @returns A record without a prototype, or `undefined` when no name fixes.
1918
+ */
1919
+ function fixedNamesOf(given: Readonly<Record<string, unknown>> | undefined): Readonly<Record<string, unknown>> | undefined {
1920
+ if (given === undefined) return undefined;
1921
+ let out: Record<string, unknown> | undefined;
1922
+ for (const name of Object.keys(given)) {
1923
+ const value = given[name];
1924
+ if (!frozenPlain(value)) continue;
1925
+ out ??= Object.create(null) as Record<string, unknown>;
1926
+ Object.defineProperty(out, name, { value, enumerable: true });
1927
+ }
1928
+ return out;
1929
+ }
1930
+
1931
+ /** This function tells if a value is plain data, as `sharedCopy` reads it, and if each of its objects is frozen. */
1932
+ function frozenPlain(value: unknown): boolean {
1933
+ const seen = new Set<object>();
1934
+ const walk = (here: unknown): boolean => {
1935
+ if (typeof here === "function") return false;
1936
+ if (typeof here !== "object" || here === null || SHARED.has(here) || seen.has(here)) return true;
1937
+ if (!Object.isFrozen(here)) return false;
1938
+ seen.add(here);
1939
+ const proto: unknown = Object.getPrototypeOf(here);
1940
+ if (Array.isArray(here) ? proto !== Array.prototype : proto !== Object.prototype && proto !== null) return false;
1941
+ for (const key of Reflect.ownKeys(here)) {
1942
+ const field = Object.getOwnPropertyDescriptor(here, key);
1943
+ if (typeof key !== "string" || field === undefined || !("value" in field) || !walk(field.value)) return false;
1944
+ }
1945
+ return true;
1946
+ };
1947
+ try {
1948
+ return walk(value);
1949
+ } catch {
1950
+ // A very deep value overflows the stack of the host.
1951
+ return false;
1952
+ }
1953
+ }
1954
+
1715
1955
  /** The objects that a fold made. Each one is deeply frozen, thus a later fold uses it again and does not copy it. */
1716
1956
  const SHARED: WeakSet<object> = /* @__PURE__ */ new WeakSet();
1717
1957
 
@@ -1721,9 +1961,13 @@ const NOT_PLAIN: unique symbol = /* @__PURE__ */ Symbol("not plain");
1721
1961
  /**
1722
1962
  * This function gives a deeply frozen copy of plain data, or `undefined` for other values. Plain data is a primitive or
1723
1963
  * an array without holes and extra keys. It is also a record of enumerable data fields with the prototype of an object
1724
- * or no prototype. The copy keeps cycles and shared objects. The objects of the host do not change.
1964
+ * or no prototype. The copy keeps cycles and shared objects. The objects of the host do not change. The tier `fixed`
1965
+ * of `agree` uses it. The package does not export it.
1966
+ *
1967
+ * @param value - The value.
1968
+ * @returns The frozen copy, or `undefined`.
1725
1969
  */
1726
- function sharedCopy(value: unknown): unknown {
1970
+ export function sharedCopy(value: unknown): unknown {
1727
1971
  try {
1728
1972
  const out = copyPlain(value, new Map());
1729
1973
  return out === NOT_PLAIN ? undefined : out;
@@ -1978,7 +2222,9 @@ export function prepare(machine: Machine, doc: unknown, options: PrepareOptions
1978
2222
  const verb = options.verb ?? "eval";
1979
2223
  const noAnswer: Answer = verb === "eval" ? NO_ANSWER : { verb, error: "no-answer", steps: [] };
1980
2224
  const traced = options.trace === true;
1981
- const c: Compiler = { machine, traced, maxDepth: machine.maxDepth ?? 1000, verb, noAnswer, fold: !traced && options.fold !== false };
2225
+ const fold = !traced && options.fold !== false;
2226
+ const fixed = fold ? fixedNamesOf(options.fixed?.names) : undefined;
2227
+ const c: Compiler = { machine, traced, maxDepth: machine.maxDepth ?? 1000, verb, noAnswer, fold, fixed };
1982
2228
  let root: Compiled;
1983
2229
  try {
1984
2230
  root = compileNode(c, doc, [], options.declaredType, 0);
package/src/formula.ts CHANGED
@@ -7,6 +7,7 @@ import { type MachinePart, mergeMachines } from "./merge.ts";
7
7
  import { type Domain, type ValueType, HEADER_KEYS, STD_DOMAINS, callable, fieldOf, fits, fnOf, holdsEnvelope, isPrecise, isUnknown, itemOf, join, member, stepType, typeText } from "./types.ts";
8
8
  import type { TypeInput, TypeRule } from "./typing.ts";
9
9
  import { decodeDomain, domainTypeOf } from "./codec.ts";
10
+ import { type StaticRead, factsOf } from "./facts.ts";
10
11
 
11
12
  /** An operation of a formula language: a function of the values of its arguments. */
12
13
  export type OpFn = (...args: never[]) => unknown;
@@ -777,7 +778,7 @@ function formulaCode(profile: FormulaProfile): Readonly<Record<string, CompiledE
777
778
  };
778
779
  }),
779
780
 
780
- "formula.fn": compiled(({ value, data }) => {
781
+ "formula.fn": compiled(({ value, data, doc, machine }) => {
781
782
  const params = isRecord(data) ? data["params"] : undefined;
782
783
  if (!Array.isArray(params) || !params.every((param) => typeof param === "string") || new Set(params).size !== params.length) {
783
784
  const bad = fail("#VALUE!", "The params of fn are a list of different texts.");
@@ -785,9 +786,22 @@ function formulaCode(profile: FormulaProfile): Readonly<Record<string, CompiledE
785
786
  }
786
787
  const names = params as readonly string[];
787
788
  const body = partsOf(value)["body"] as CompiledPart;
789
+ // The static reads of the function come at the first closure of an evaluation with a list of reads.
790
+ let known: readonly StaticRead[] | undefined | null = null;
791
+ const reads = (): readonly StaticRead[] | undefined => {
792
+ if (known !== null) return known;
793
+ // Only a pure document reads its context through the runtime alone (FACTS.PURE). Each other closure keeps each root.
794
+ try {
795
+ const facts = factsOf(machine, doc);
796
+ known = facts.pure ? facts.reads : undefined;
797
+ } catch {
798
+ known = undefined;
799
+ }
800
+ return known;
801
+ };
788
802
  return (ctx, rt) => {
789
- // A closure keeps the context for each later use, thus the list of reads gets each root (READS.KEPT).
790
- rt.keepsContext?.();
803
+ // A closure keeps the context for each later use, thus the list of reads gets each path that it can read (READS.CLOSURE).
804
+ rt.keepsContext?.(ctx, reads);
791
805
  const closure = (...args: readonly unknown[]): unknown => {
792
806
  const scope: Record<string, unknown> = { ...ctx.names };
793
807
  names.forEach((name, index) => setOwn(scope, name, args[index]));
package/src/index.ts CHANGED
@@ -176,6 +176,7 @@ export {
176
176
  STD_WORLD,
177
177
  attributeTrace,
178
178
  nearestWalker,
179
+ placeOf,
179
180
  } from "./stdlib.ts";
180
181
  export {
181
182
  type Edge,
@@ -250,8 +251,8 @@ export {
250
251
  print,
251
252
  syntaxFormat,
252
253
  } from "./syntax.ts";
253
- export { type Definitions, type ReferenceOptions, STD_DEFINITIONS, formulaDefinitions, reference } from "./reference.ts";
254
- export { type AgreeOptions, type Agreement, type Tier, type TierInput, type TierResult, agree, sameValue } from "./agree.ts";
254
+ export { type Definitions, type ReferenceOptions, type ReferenceRunOptions, STD_DEFINITIONS, formulaDefinitions, reference, referenceRun } from "./reference.ts";
255
+ export { type AgreeOptions, type AgreeRunOptions, type Agreement, type Tier, type TierInput, type TierResult, agree, agreeRun, sameValue } from "./agree.ts";
255
256
  export { type ConformOptions, type ConformProbe, type Finding, type Obligation, conforms } from "./conform.ts";
256
257
  export { sameResult } from "./same.ts";
257
258
  export { type ReadsCheck, type VerifyOptions, verifyReads } from "./reads.ts";
@@ -266,6 +267,15 @@ export {
266
267
  edgesOf,
267
268
  schedule,
268
269
  } from "./schedule.ts";
270
+ export {
271
+ type AgreeScheduleOptions,
272
+ type ScheduleAgreement,
273
+ type ScheduleAgreementCounts,
274
+ type ScheduleEngine,
275
+ type ScheduleEngineFactory,
276
+ type ScheduleEngineStart,
277
+ agreeSchedule,
278
+ } from "./meaning.ts";
269
279
  export { type GraphCycle, type GraphEdge, type GraphNode, type GraphOptions, type NodeGraph, components, cycles, heights, necessary, nodeLayers } from "./graph.ts";
270
280
  export { type Recorder, type ReplayEntry, type ReplayLog, type ReplayOptions, type ReplaySettle, recorder, replayer } from "./replay.ts";
271
281
  export { type GenerateOptions, type Random, generate, random, typesOf } from "./generate.ts";