@mark1russell7/coeval 0.6.3 → 0.7.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
@@ -1,15 +1,21 @@
1
- import { type Envelope, type Json, type Key, type TypeRef, isEnvelope, typeName } from "./doc.ts";
1
+ import { type Envelope, type Json, type Key, type TypeRef, isEnvelope, isKey, typeName } from "./doc.ts";
2
2
  import { type Split, partsOf, pathMatches } from "./parts.ts";
3
- import { ask, type Answer } from "./ladder.ts";
4
- import type { CapsRule, Kind, Side, World } from "./world.ts";
3
+ import { answerOf, ask, type Answer } from "./ladder.ts";
4
+ import type { CapsRule, KindFacts, Side } from "./world.ts";
5
+ import type { Domain } from "./types.ts";
6
+ import type { TypeRule } from "./typing.ts";
5
7
  import { type ErrorCode, type EvalError, type Result, Failure, fail, ok, placed, thrown, describe } from "./errors.ts";
6
8
  import {
7
9
  type Cap,
10
+ type CompiledPath,
8
11
  type Context,
12
+ type Hop,
9
13
  type Navigator,
10
14
  type Resolution,
11
15
  type ResolveOptions,
16
+ type Root,
12
17
  type Thing,
18
+ ROOTS,
13
19
  context as makeRootContext,
14
20
  isHostThing,
15
21
  resolve,
@@ -126,6 +132,40 @@ export function partCaps(rules: readonly CapsRule[] | undefined, path: readonly
126
132
  return capsUnder(rulesFor(rules, path), caps);
127
133
  }
128
134
 
135
+ /**
136
+ * The key of the grant of a context (CAP.NARROW-CODE): the capabilities that the kernel gave the document of the
137
+ * context. A spread of a context keeps its grant. Thus compiled code passes the grant on in each context that it makes
138
+ * from the context of its document. Only the kernel writes a grant.
139
+ */
140
+ const GRANT = /* @__PURE__ */ Symbol("coeval.grant");
141
+
142
+ /** A context with the grant of the kernel. */
143
+ type Granted = Context & { readonly [GRANT]?: readonly Cap[] };
144
+
145
+ /**
146
+ * This function gives a context whose capabilities its grant permits (CAP.NARROW-CODE). A context whose grant is its
147
+ * own list of capabilities stays the same object. Each other context keeps only the capabilities that its grant
148
+ * permits, and a context without a grant keeps none. The result has the grant of its capabilities.
149
+ */
150
+ function granted(ctx: Context): Context {
151
+ const grant = (ctx as Granted)[GRANT];
152
+ if (grant === ctx.caps) return ctx;
153
+ const caps = grant === undefined ? NO_CAPS : ctx.caps.every((cap) => permits(grant, cap)) ? ctx.caps : ctx.caps.filter((cap) => permits(grant, cap));
154
+ const out: Granted = { ...ctx, caps, [GRANT]: caps };
155
+ return out;
156
+ }
157
+
158
+ /**
159
+ * This function gives the context of a part of a function evaluator. It is the context that the evaluator gives,
160
+ * narrowed by the context of the document (CAP.NARROW), with the grant of its capabilities.
161
+ */
162
+ function handed(parent: Context, next: Context): Context {
163
+ const ctx = narrow(parent, next);
164
+ if ((ctx as Granted)[GRANT] === ctx.caps) return ctx;
165
+ const out: Granted = { ...ctx, [GRANT]: ctx.caps };
166
+ return out;
167
+ }
168
+
129
169
  /**
130
170
  * The steps of an evaluation that can wait. It yields a promise only when the host makes it wait. A runner takes the
131
171
  * result of a part with `yield*`, thus a run goes on at once until an effect waits. A runner does not yield a
@@ -164,6 +204,86 @@ export interface Effect {
164
204
  readonly outcome?: Result;
165
205
  }
166
206
 
207
+ /**
208
+ * One read of an evaluation: a path that code resolved, and what the path reached (READS.RECORD, D26). A schedule
209
+ * keeps the list of the reads of an evaluation. `verifyReads` resolves each free read again in a new context, and it
210
+ * tells if the old result stays valid.
211
+ */
212
+ export interface Read {
213
+ /** The path that the code resolved: its root, and its steps with the value of each step document. */
214
+ readonly path: readonly unknown[];
215
+ /**
216
+ * What the path reached: the thing, or the error of the resolution. A host thing stays a thing. An exception of the
217
+ * host gives its error value.
218
+ */
219
+ readonly reached: Result;
220
+ /**
221
+ * `true` when the read reads the context of the evaluation (READS.FREE). A read of a name that a kind binds, for
222
+ * example a name of `let`, is not free. A read of a `self` that a kind sets is not free either, for example a node
223
+ * of a tree. A path without a root is not free. `verifyReads` resolves only the free reads again.
224
+ */
225
+ readonly free: boolean;
226
+ /**
227
+ * `true` when the kernel adds the read, because a value keeps a root of the context for later (READS.KEPT). Such a
228
+ * read has the whole root as its path.
229
+ */
230
+ readonly kept?: true;
231
+ /** The hops of the resolution, only when the code asked for them. `verifyReads` then compares each hop too. */
232
+ readonly hops?: readonly Hop[];
233
+ }
234
+
235
+ /** The text that names a resolution in the error of an exception of the host. */
236
+ const READ_FAILED = "the resolution of a path";
237
+
238
+ /**
239
+ * This function gives what a resolution reached as a result: the thing, or the error with its code and its reason.
240
+ * The record of a read and `verifyReads` share it.
241
+ *
242
+ * @param found - The resolution.
243
+ * @returns The result.
244
+ */
245
+ export function reachedOf(found: Resolution): Result {
246
+ return found.ok ? ok(found.thing) : fail(found.code, found.reason);
247
+ }
248
+
249
+ /**
250
+ * This function resolves the path of a read, and it gives an exception of the host as an error value. The record of
251
+ * a kept read and `verifyReads` use it.
252
+ *
253
+ * @param nav - The world and the code of the evaluators.
254
+ * @param ctx - The context.
255
+ * @param path - The path.
256
+ * @param hops - `true` records each hop.
257
+ * @returns What the path reached, and the hops when the resolution gave them.
258
+ */
259
+ export function readAgain(nav: Navigator, ctx: Context, path: readonly unknown[], hops: boolean): { readonly reached: Result; readonly hops?: readonly Hop[] } {
260
+ try {
261
+ const found = resolve(nav, ctx, path, hops ? WITH_HOPS : WITHOUT_HOPS);
262
+ return hops ? { reached: reachedOf(found), hops: found.hops } : { reached: reachedOf(found) };
263
+ } catch (error) {
264
+ return { reached: thrown(error, READ_FAILED) };
265
+ }
266
+ }
267
+
268
+ /**
269
+ * This function tells if a read reads the context at the root of the evaluation (READS.FREE). A kind can bind a name
270
+ * or set `self` for its parts, and a read of that binding is not free. A path without a root reads no context. A read
271
+ * of each other root is free. When a kind changes such a root, the read resolves again to another value, thus
272
+ * `verifyReads` only does more work.
273
+ */
274
+ function isFree(root: Context, ctx: Context, path: readonly unknown[]): boolean {
275
+ const head = path[0];
276
+ if (head === "self") return ctx === root || Object.is(ctx.self, root.self);
277
+ if (head !== "names") return typeof head === "string" && (ROOTS as readonly string[]).includes(head);
278
+ if (ctx === root || ctx.names === root.names) return true;
279
+ const name = path[1];
280
+ // A read of the whole record of the names reads each free name too.
281
+ if (!isKey(name)) return true;
282
+ const key = String(name);
283
+ const has = Object.hasOwn(ctx.names, key);
284
+ return has === Object.hasOwn(root.names, key) && (!has || Object.is(ctx.names[key], root.names[key]));
285
+ }
286
+
167
287
  /**
168
288
  * The host side of a run: it does one effect, and gives its result. A throw or a rejection is an error value, and a
169
289
  * `Failure` gives its own error value. In an evaluation, this function is a mock: it answers at once, and a promise
@@ -265,7 +385,7 @@ export interface Runtime {
265
385
  * This function causes an effect from an evaluator. In a run, an evaluator cannot cause an effect: the host does
266
386
  * each effect, thus only a runner can wait for it.
267
387
  *
268
- * @param ctx - The context of the document.
388
+ * @param ctx - The context of the document. It gives only the capabilities that the document has (CAP.NARROW-CODE).
269
389
  * @param kind - The kind of the effect.
270
390
  * @param payload - The data of the effect.
271
391
  * @param place - The place of the document.
@@ -276,7 +396,7 @@ export interface Runtime {
276
396
  * This function causes an effect from a runner. In a run, the host does it, and the steps wait when the host
277
397
  * waits.
278
398
  *
279
- * @param ctx - The context of the document.
399
+ * @param ctx - The context of the document. It gives only the capabilities that the document has (CAP.NARROW-CODE).
280
400
  * @param kind - The kind of the effect.
281
401
  * @param payload - The data of the effect.
282
402
  * @param place - The place of the document.
@@ -291,6 +411,12 @@ export interface Runtime {
291
411
  * @returns The steps that give each result, in the order of the list.
292
412
  */
293
413
  all(list: readonly (() => Steps)[], options?: AllOptions): Generator<Promise<unknown>, readonly Result[], unknown>;
414
+ /**
415
+ * This optional function tells the runtime that a value keeps the context for later, for example a closure of
416
+ * `fn`. A later use of the value reads that context, and no evaluation records these reads. Thus the list of reads
417
+ * gets a read of each root of the context (READS.KEPT).
418
+ */
419
+ keepsContext?(): void;
294
420
  }
295
421
 
296
422
  /**
@@ -305,10 +431,17 @@ export interface AllOptions {
305
431
  readonly failFast?: boolean;
306
432
  }
307
433
 
308
- /** Compiled code: the evaluation of a document in a context. */
434
+ /**
435
+ * Compiled code: the evaluation of a document in a context. The code gives each part a context that it makes from
436
+ * its own context with a spread. The part then keeps only the capabilities of the document (CAP.NARROW-CODE). A
437
+ * context that the code makes without a spread gives the part no capabilities.
438
+ */
309
439
  export type Code = (ctx: Context, rt: Runtime) => Result;
310
440
 
311
- /** Compiled code that can wait: the run of a document in a context. */
441
+ /**
442
+ * Compiled code that can wait: the run of a document in a context. Its parts keep only the capabilities of the
443
+ * document, as for `Code`.
444
+ */
312
445
  export type RunCode = (ctx: Context, rt: Runtime) => Steps;
313
446
 
314
447
  /** A compiled part: its place in its parent, its binding name, and its code. It has no field with its value. */
@@ -488,6 +621,11 @@ export interface CompiledEvaluator {
488
621
  * @returns The code that evaluates the document.
489
622
  */
490
623
  readonly compile: (input: CompileInput) => Code;
624
+ /**
625
+ * The type rule of the evaluator: the value types that a check of types gives its documents and their parts
626
+ * (TYPE.RULE). Only a check reads it. An evaluator without a rule gives `unknown`.
627
+ */
628
+ readonly type?: TypeRule;
491
629
  }
492
630
 
493
631
  /** An evaluator: a function of its input, or a compiler. */
@@ -517,8 +655,11 @@ export type Runner = RunFn | CompiledRunner;
517
655
  * A machine: a world of kinds, and the code of each evaluator name, of each `deref` and of each walker.
518
656
  *
519
657
  * - `maxDepth`: the deepest nesting of documents. The default is 1000.
520
- * - `permits`: the rule of the host for an effect. The default is the function `permits` on the capabilities.
658
+ * - `permits`: the rule of the host for an effect. The default is the function `permits` on the capabilities. A run,
659
+ * `check` and `effectsOf` ask it (CAP.PERMITS). In a check, its context has only the capabilities and the world.
521
660
  * - `known`: the names that the evaluators know, for `check`. Without it, `check` does not check such names.
661
+ * - `domains`: the value domains of the language, by the name of their base type (D22).
662
+ * - `facts`: the static facts of the names that a fallback kind covers, for `factsOf` (D24).
522
663
  */
523
664
  export interface Machine extends Navigator {
524
665
  readonly evaluators: Readonly<Record<string, Evaluator>>;
@@ -532,10 +673,26 @@ export interface Machine extends Navigator {
532
673
  * `check` asks it for each document with an evaluator. The function must be pure.
533
674
  *
534
675
  * @param path - The keys of the kind of the document, after each alias.
535
- * @param evaluator - The name of the evaluator that the ladder gives the document.
676
+ * @param evaluator - The name of the evaluator that the ladder of the verb of the check gives the document.
536
677
  * @returns `true` when the evaluator knows the name, or else the reason of the `#NAME?` that a run gives.
537
678
  */
538
679
  readonly known?: (path: readonly Key[], evaluator: string) => true | string;
680
+ /**
681
+ * The value domains of the language, by the name of their base type, for example a color or a unit of length. A
682
+ * check reads them through `fits` and `member`. A domain of the kernel that the table does not name comes from
683
+ * `STD_DOMAINS`.
684
+ */
685
+ readonly domains?: Readonly<Record<string, Domain>>;
686
+ /**
687
+ * This function gives the static facts of the code of a name, for `factsOf` (D24). An example is the operation
688
+ * `["ops", "now"]` of a formula language: a fallback kind names it, and only the language knows that it is volatile.
689
+ * Each field of the answer wins over the same field of the contract of the kind. The function must be pure.
690
+ *
691
+ * @param path - The keys of the kind of the document, after each alias.
692
+ * @param code - The name of the evaluator of the document, or of the walker of a step document.
693
+ * @returns The facts, or `undefined` for the facts of the contract.
694
+ */
695
+ readonly facts?: (path: readonly Key[], code: string) => KindFacts | undefined;
539
696
  }
540
697
 
541
698
  /** One evaluation in a trace. The children are the evaluations of its parts, in the order that they started. */
@@ -547,7 +704,7 @@ export interface TraceNode {
547
704
  readonly doc: Envelope;
548
705
  /** `true` when the type came from the container and not from an envelope. */
549
706
  readonly declared: boolean;
550
- /** The answer of the ladder to the verb `eval`. */
707
+ /** The answer of the ladder to the verb of the plan, `eval` by default. */
551
708
  readonly answer: Answer;
552
709
  readonly side: Side;
553
710
  /** The names that the document could read when its evaluation started. */
@@ -629,8 +786,18 @@ export interface RunOptions {
629
786
  readonly perform?: Perform;
630
787
  /** The type of a bare root value. */
631
788
  readonly declaredType?: string;
789
+ /**
790
+ * The verb of `run`, for example `format`. The default is `eval` (VERB.PREPARE). A plan keeps the verb of its
791
+ * `prepare`, thus `plan.run` does not read this option.
792
+ */
793
+ readonly verb?: string;
632
794
  /** A signal of the host. When it aborts, the run stops with `#ABORT`. */
633
795
  readonly signal?: StopSignal;
796
+ /**
797
+ * A list that gets each read, in the order of the resolutions (READS.RECORD). Under `all`, the order of the host
798
+ * decides the order of the reads of the items.
799
+ */
800
+ readonly reads?: Read[];
634
801
  }
635
802
 
636
803
  /** The options of an evaluation of a plan. */
@@ -642,20 +809,45 @@ export interface EvalOptions {
642
809
  * Without it, an effect gives its payload (RUN.EVAL).
643
810
  */
644
811
  readonly perform?: Perform;
812
+ /**
813
+ * A list that gets each read, in the order of the resolutions (READS.RECORD). Without it, the plan records no read,
814
+ * and each resolution costs one more test.
815
+ */
816
+ readonly reads?: Read[];
645
817
  }
646
818
 
647
819
  /** The options of `evaluate`. */
648
820
  export interface EvaluateOptions {
649
821
  /** The type of a bare root value. */
650
822
  readonly declaredType?: string;
823
+ /** The verb that goes up the ladder at each document, for example `format`. The default is `eval` (VERB.PREPARE). */
824
+ readonly verb?: string;
651
825
  /** The mock of the host (RUN.MOCK). Without it, an effect gives its payload (RUN.EVAL). */
652
826
  readonly perform?: Perform;
827
+ /** A list that gets each read, in the order of the resolutions (READS.RECORD). */
828
+ readonly reads?: Read[];
653
829
  }
654
830
 
655
831
  /** The options of a resolution without a record of hops, and with one. One object of each serves each call. */
656
832
  const WITHOUT_HOPS: ResolveOptions = /* @__PURE__ */ Object.freeze({ hops: false });
657
833
  const WITH_HOPS: ResolveOptions = /* @__PURE__ */ Object.freeze({ hops: true });
658
834
 
835
+ /** The record of the reads of one evaluation (READS.RECORD). */
836
+ interface Reading {
837
+ readonly list: Read[];
838
+ /** The context at the root of the evaluation. A free read has the binding of this context. */
839
+ readonly root: Context;
840
+ /** `true` after the kernel added a read of each root (READS.KEPT). */
841
+ kept: boolean;
842
+ /** `true` after the kernel added the read of the root `self`. */
843
+ self: boolean;
844
+ }
845
+
846
+ /** This function makes the record of the reads of an evaluation, when the host gives a list. */
847
+ function readingOf(list: Read[] | undefined, root: Context): Reading | undefined {
848
+ return list === undefined ? undefined : { list, root, kept: false, self: false };
849
+ }
850
+
659
851
  /** This class holds the state of one evaluation or run: the machine, the effects, the clock and the trace. */
660
852
  class Session implements Runtime {
661
853
  /** The list of the effects. It is made at the first effect, thus an evaluation without effects makes no list. */
@@ -677,6 +869,8 @@ class Session implements Runtime {
677
869
  readonly perform: Perform | undefined,
678
870
  readonly signal: StopSignal | undefined,
679
871
  effects: Effect[] | undefined,
872
+ /** The record of the reads. Without it, a resolution records nothing. */
873
+ private readonly reading: Reading | undefined = undefined,
680
874
  ) {
681
875
  this.list = effects;
682
876
  this.holder = traced ? holderNode() : undefined;
@@ -689,16 +883,77 @@ class Session implements Runtime {
689
883
  }
690
884
 
691
885
  resolve(ctx: Context, path: readonly unknown[], options?: ResolveOptions): Resolution {
886
+ return this.read(ctx, path, undefined, options);
887
+ }
888
+
889
+ /**
890
+ * This function resolves a compiled path (PLAN.PATH). Without a list of reads and without a trace, the compiled
891
+ * path answers at once. Each other resolution records its read, as `resolve` does.
892
+ */
893
+ resolvePath(ctx: Context, compiled: CompiledPath): Resolution {
894
+ if (this.reading === undefined && !this.traced && compiled.nav === this.machine) return compiled.resolve(ctx);
895
+ return this.read(ctx, compiled.path, compiled, undefined);
896
+ }
897
+
898
+ /** This function resolves a path, and records its read when the evaluation has a list of reads (READS.RECORD). */
899
+ private read(ctx: Context, path: readonly unknown[], compiled: CompiledPath | undefined, options: ResolveOptions | undefined): Resolution {
692
900
  const hops = options?.hops ?? this.traced;
901
+ const reading = this.reading;
902
+ if (reading === undefined) return this.find(ctx, path, compiled, hops);
903
+ const free = isFree(reading.root, ctx, path);
904
+ // A kind that sets another self can give the old self on, as the parent of a node of a tree does (READS.KEPT).
905
+ if (!free && path[0] === "self" && !reading.self) this.keep(reading, ["self"]);
906
+ let found: Resolution;
907
+ try {
908
+ found = this.find(ctx, path, compiled, hops);
909
+ } catch (error) {
910
+ reading.list.push({ path, reached: thrown(error, READ_FAILED), free });
911
+ throw error;
912
+ }
913
+ const asked = options?.hops === true;
914
+ reading.list.push(asked ? { path, reached: reachedOf(found), free, hops: found.hops } : { path, reached: reachedOf(found), free });
915
+ return found;
916
+ }
917
+
918
+ /**
919
+ * This function resolves the steps of a path. A compiled path of this machine answers a resolution without hops. A
920
+ * resolution with hops, and a path that compiled for another navigator, resolve the steps again.
921
+ */
922
+ private find(ctx: Context, path: readonly unknown[], compiled: CompiledPath | undefined, hops: boolean): Resolution {
923
+ if (compiled !== undefined && !hops && compiled.nav === this.machine) return compiled.resolve(ctx);
693
924
  return resolve(this.machine, ctx, path, hops ? WITH_HOPS : WITHOUT_HOPS);
694
925
  }
695
926
 
927
+ keepsContext(): void {
928
+ const reading = this.reading;
929
+ if (reading === undefined || reading.kept) return;
930
+ reading.kept = true;
931
+ this.keep(reading, reading.self ? ROOTS.filter((root) => root !== "self") : ROOTS);
932
+ }
933
+
934
+ /**
935
+ * This function adds a read of each whole root to the list, from the context at the root (READS.KEPT). An exception
936
+ * of the host gives the error value of the read, because the evaluation did not ask for the read.
937
+ */
938
+ private keep(reading: Reading, roots: readonly Root[]): void {
939
+ for (const root of roots) {
940
+ if (root === "self") reading.self = true;
941
+ const path = [root];
942
+ reading.list.push({ path, reached: readAgain(this.machine, reading.root, path, false).reached, free: true, kept: true });
943
+ }
944
+ }
945
+
696
946
  private permitted(ctx: Context, keys: readonly Key[]): boolean {
697
947
  return this.machine.permits === undefined ? permits(ctx.caps, keys) : this.machine.permits(ctx.caps, keys, ctx);
698
948
  }
699
949
 
700
- /** This function checks the capability of an effect, and records the effect with its address (RUN.ADDRESS). */
701
- private record(ctx: Context, kind: Cap, payload: unknown, place: readonly Key[]): Result | Recorded {
950
+ /**
951
+ * This function checks the capability of an effect, and records the effect with its address (RUN.ADDRESS). The
952
+ * context keeps only the capabilities of its grant, thus compiled code cannot widen the context of its own effect
953
+ * (CAP.NARROW-CODE).
954
+ */
955
+ private record(given: Context, kind: Cap, payload: unknown, place: readonly Key[]): Result | Recorded {
956
+ const ctx = granted(given);
702
957
  const keys = typeof kind === "string" ? kind.split(".") : kind;
703
958
  const text = keys.join(".");
704
959
  if (!this.permitted(ctx, keys)) return fail("#CAP!", `The context has no capability for the effect ${text}.`, { place });
@@ -897,6 +1152,19 @@ class Session implements Runtime {
897
1152
  }
898
1153
  }
899
1154
 
1155
+ /**
1156
+ * This function resolves a compiled path through a runtime (PLAN.PATH). The runtime of the kernel uses the compiled
1157
+ * path. Each other runtime gets the steps through `resolve`, thus a runtime that wraps another runtime sees each path.
1158
+ *
1159
+ * @param rt - The runtime.
1160
+ * @param ctx - The context.
1161
+ * @param compiled - The compiled path, from `compilePath`.
1162
+ * @returns What the path reached, or an error.
1163
+ */
1164
+ export function resolveCompiled(rt: Runtime, ctx: Context, compiled: CompiledPath): Resolution {
1165
+ return rt instanceof Session ? rt.resolvePath(ctx, compiled) : rt.resolve(ctx, compiled.path);
1166
+ }
1167
+
900
1168
  function holderNode(): Building {
901
1169
  return {
902
1170
  id: -1,
@@ -917,13 +1185,25 @@ function holderNode(): Building {
917
1185
  };
918
1186
  }
919
1187
 
920
- /** This function drives steps that cannot wait. */
921
- function drive(steps: Steps): Result {
1188
+ /**
1189
+ * This function gives the reason of a runner that waits where its parent cannot wait (RUN.WAIT). The plan, the
1190
+ * reference and `check` with the option `run` give the same reason.
1191
+ *
1192
+ * @param kindText - The text of the type of the runner document.
1193
+ * @returns The reason.
1194
+ */
1195
+ export function waitedReason(kindText: string): string {
1196
+ return `${kindText} waits for effects, and its parent cannot wait. Give the parent a runner.`;
1197
+ }
1198
+
1199
+ /** This function drives steps that cannot wait. A step that waits stops the steps, and gives `#CALC!`. */
1200
+ function drive(steps: Steps, kindText: string): Result {
922
1201
  const step = steps.next();
923
1202
  if (step.done === true) return step.value;
924
1203
  void step.value.catch(() => undefined);
925
- void steps.return(fail("#CALC!", "A part waited, and its parent cannot wait."));
926
- return fail("#CALC!", "A part waited, and its parent cannot wait.");
1204
+ const waited = fail("#CALC!", waitedReason(kindText));
1205
+ void steps.return(waited);
1206
+ return waited;
927
1207
  }
928
1208
 
929
1209
  const ABORTED = /* @__PURE__ */ Symbol("aborted");
@@ -976,6 +1256,11 @@ export interface PrepareOptions {
976
1256
  readonly declaredType?: string;
977
1257
  /** `true`: each evaluation records a trace. The default is `false`. */
978
1258
  readonly trace?: boolean;
1259
+ /**
1260
+ * The verb that goes up the ladder at each document, for example `format`. Each part evaluates with the same verb.
1261
+ * The default is `eval` (VERB.PREPARE).
1262
+ */
1263
+ readonly verb?: string;
979
1264
  }
980
1265
 
981
1266
  /** A compiled document. It evaluates many times, and it does the work of the ladder one time. */
@@ -990,7 +1275,7 @@ export interface Plan {
990
1275
  * effect at once (RUN.MOCK).
991
1276
  *
992
1277
  * @param context - The names and the capabilities at the root.
993
- * @param options - A list that gets the effects, and the mock of the host.
1278
+ * @param options - A list that gets the effects, the mock of the host and a list that gets the reads.
994
1279
  * @returns The result.
995
1280
  */
996
1281
  evaluate(context?: Context, options?: EvalOptions): Result;
@@ -998,7 +1283,7 @@ export interface Plan {
998
1283
  * This function runs the document: the host does each effect, and the run waits when the host waits.
999
1284
  *
1000
1285
  * @param context - The names and the capabilities at the root.
1001
- * @param options - The host code that does the effects, and a signal to stop the run.
1286
+ * @param options - The host code that does the effects, a signal to stop the run and a list that gets the reads.
1002
1287
  * @returns The result, when the run ends.
1003
1288
  */
1004
1289
  run(context?: Context, options?: RunOptions): Promise<Result>;
@@ -1008,23 +1293,31 @@ interface Compiler {
1008
1293
  readonly machine: Machine;
1009
1294
  readonly traced: boolean;
1010
1295
  readonly maxDepth: number;
1296
+ /** The verb that goes up the ladder at each document. */
1297
+ readonly verb: string;
1298
+ /** The answer of a document that the ladder did not see, in a trace. */
1299
+ readonly noAnswer: Answer;
1011
1300
  }
1012
1301
 
1013
- function failing(c: Compiler, meta: Meta, place: readonly Key[], code: ErrorCode, reason: string): { readonly code: Code; readonly run: RunCode } {
1302
+ function failing(c: Compiler, meta: Meta, place: readonly Key[], code: ErrorCode, reason: string): Compiled {
1014
1303
  return failingWith(c, meta, fail(code, reason, { place }));
1015
1304
  }
1016
1305
 
1017
1306
  /** This function gives the code of a document that fails with one result at each evaluation. */
1018
- function failingWith(c: Compiler, meta: Meta, result: Result): { readonly code: Code; readonly run: RunCode } {
1307
+ function failingWith(c: Compiler, meta: Meta, result: Result): Compiled {
1019
1308
  const plain: Code = () => result;
1020
- return finish(c, meta, plain, function* () {
1021
- return result;
1022
- });
1309
+ return finish(c, meta, plain, undefined);
1023
1310
  }
1024
1311
 
1025
- function finish(c: Compiler, meta: Meta, code: Code, run: RunCode): { readonly code: Code; readonly run: RunCode } {
1026
- if (!c.traced) return { code, run };
1027
- const tracedCode: Code = (ctx, rt) => {
1312
+ /**
1313
+ * This function gives the code of a document in a trace. Each node records the context that its grant permits
1314
+ * (CAP.NARROW-CODE), as the code of the document gets it.
1315
+ */
1316
+ function finish(c: Compiler, meta: Meta, code: Code, steps: RunCode | undefined): Compiled {
1317
+ if (!c.traced) return { code, run: steps };
1318
+ const run = steps ?? runOf(code);
1319
+ const tracedCode: Code = (given, rt) => {
1320
+ const ctx = granted(given);
1028
1321
  const s = rt as Session;
1029
1322
  const node = s.enter(meta, ctx);
1030
1323
  const before = s.current;
@@ -1038,7 +1331,8 @@ function finish(c: Compiler, meta: Meta, code: Code, run: RunCode): { readonly c
1038
1331
  s.exit(node, result);
1039
1332
  return result;
1040
1333
  };
1041
- const tracedRun: RunCode = function* (ctx, rt) {
1334
+ const tracedRun: RunCode = function* (given, rt) {
1335
+ const ctx = granted(given);
1042
1336
  const s = rt as Session;
1043
1337
  const node = s.enter(meta, ctx);
1044
1338
  const steps = run(ctx, rt);
@@ -1069,47 +1363,34 @@ function finish(c: Compiler, meta: Meta, code: Code, run: RunCode): { readonly c
1069
1363
  return { code: tracedCode, run: tracedRun };
1070
1364
  }
1071
1365
 
1072
- /**
1073
- * This function finds the evaluator of the verb `eval` without the record of the rungs, for a compilation without a
1074
- * trace. It gives the same evaluator as `ask`: the own entry, then the kind, then the default of the world.
1075
- */
1076
- function evaluatorOf(world: World, envelope: Envelope | undefined, kind: Kind | undefined): { readonly name?: string; readonly unknown: boolean } {
1077
- const own = envelope?.own;
1078
- const ownName = own !== undefined && Object.hasOwn(own, "eval") ? own["eval"] : undefined;
1079
- if (ownName !== undefined) return { name: ownName, unknown: false };
1080
- if (kind === undefined) return { unknown: true };
1081
- const verbs = kind.verbs;
1082
- const name = Object.hasOwn(verbs, "eval") ? verbs["eval"] : Object.hasOwn(world.defaults, "eval") ? world.defaults["eval"] : undefined;
1083
- return name === undefined ? { unknown: false } : { name, unknown: false };
1084
- }
1085
-
1086
1366
  /** The meta of a node of a compilation without a trace. Only a trace reads the meta of a node. */
1087
1367
  const NO_META: Meta = /* @__PURE__ */ Object.freeze({ path: [], doc: { type: "?", value: null }, declared: false, answer: NO_ANSWER, side: "derive" });
1088
1368
 
1089
- function compileNode(c: Compiler, value: unknown, place: readonly Key[], declared: string | undefined, depth: number, address = false): { readonly code: Code; readonly run: RunCode } {
1369
+ function compileNode(c: Compiler, value: unknown, place: readonly Key[], declared: string | undefined, depth: number, address = false): Compiled {
1090
1370
  const world = c.machine.world;
1091
1371
  const read = partsOf(world, value, declared);
1092
1372
  if (!read.ok) {
1093
- const meta: Meta = c.traced ? { path: place.map(String), doc: { type: "?", value: value as Json }, declared: true, answer: NO_ANSWER, side: "derive" } : NO_META;
1373
+ const meta: Meta = c.traced ? { path: place.map(String), doc: { type: "?", value: value as Json }, declared: true, answer: c.noAnswer, side: "derive" } : NO_META;
1094
1374
  return failing(c, meta, place, read.code, read.reason);
1095
1375
  }
1096
1376
  const { doc, kind, split } = read;
1097
1377
  const envelope = read.declared ? undefined : doc;
1098
- const meta0: Meta = c.traced ? { path: place.map(String), doc, declared: read.declared, answer: NO_ANSWER, side: "derive" } : NO_META;
1378
+ const meta0: Meta = c.traced ? { path: place.map(String), doc, declared: read.declared, answer: c.noAnswer, side: "derive" } : NO_META;
1099
1379
  if (depth > c.maxDepth) return failing(c, meta0, place, "#DEPTH!", `The nesting is deeper than ${c.maxDepth} documents.`);
1100
1380
 
1101
1381
  let meta = meta0;
1102
1382
  let name: string | undefined;
1103
1383
  let unknown: boolean;
1104
1384
  if (c.traced) {
1105
- const answer = ask(world, envelope ?? {}, "eval", declared === undefined ? {} : { declaredType: declared });
1385
+ const answer = ask(world, envelope ?? {}, c.verb, declared === undefined ? {} : { declaredType: declared });
1106
1386
  meta = { ...meta0, answer, side: kind?.side ?? "derive" };
1107
1387
  name = answer.evaluator;
1108
1388
  unknown = answer.error === "unknown-kind";
1109
1389
  } else {
1110
- const quick = evaluatorOf(world, envelope, kind);
1111
- name = quick.name;
1112
- unknown = quick.unknown;
1390
+ // A document has a type here, thus a kind that the read did not find is a kind that the world does not have.
1391
+ const quick = answerOf(world, envelope?.own, kind ?? null, c.verb);
1392
+ name = quick?.name;
1393
+ unknown = quick === undefined && kind === undefined;
1113
1394
  }
1114
1395
  const kindText = typeName(doc.type);
1115
1396
  if (unknown) return failing(c, meta, place, "#KIND?", `The world has no kind ${kindText}.`);
@@ -1129,9 +1410,10 @@ function compileNode(c: Compiler, value: unknown, place: readonly Key[], declare
1129
1410
  const compiled = rules.length === 0 ? own : gated(rules, own);
1130
1411
  // The binding name is in the header of an envelope. Most parts have no name, thus the cheap test comes first.
1131
1412
  const header = typeof (item as { readonly name?: unknown } | null)?.name === "string" && isEnvelope(item) ? (item as { readonly name: string }).name : undefined;
1413
+ const run = compiled.run ?? runOf(compiled.code);
1132
1414
  return header === undefined
1133
- ? { key: slot.key, path: slot.path, code: compiled.code, run: compiled.run }
1134
- : { key: slot.key, path: slot.path, name: header, code: compiled.code, run: compiled.run };
1415
+ ? { key: slot.key, path: slot.path, code: compiled.code, run }
1416
+ : { key: slot.key, path: slot.path, name: header, code: compiled.code, run };
1135
1417
  });
1136
1418
  const typePath = read.typePath ?? [];
1137
1419
  const input: CompileInput = {
@@ -1164,8 +1446,7 @@ function compileNode(c: Compiler, value: unknown, place: readonly Key[], declare
1164
1446
  } catch (error) {
1165
1447
  return failingWith(c, meta, placed(thrown(error, `the evaluator ${name}`), place));
1166
1448
  }
1167
- const code = placedCode(inner, place, name);
1168
- return finish(c, meta, code, runOf(code));
1449
+ return finish(c, meta, placedCode(inner, place, name), undefined);
1169
1450
  }
1170
1451
 
1171
1452
  /*
@@ -1174,11 +1455,14 @@ function compileNode(c: Compiler, value: unknown, place: readonly Key[], declare
1174
1455
  * the split of the value. Thus a plan with these helpers keeps only its code.
1175
1456
  */
1176
1457
 
1177
- /** This function gives the code of a document: the code of its evaluator, with the place of each error and of each exception. */
1458
+ /**
1459
+ * This function gives the code of a document: the code of its evaluator, with the place of each error and of each
1460
+ * exception. The code gets the context that its grant permits, thus a compiled parent cannot widen it (CAP.NARROW-CODE).
1461
+ */
1178
1462
  function placedCode(inner: Code, place: readonly Key[], name: string): Code {
1179
1463
  return (ctx, rt) => {
1180
1464
  try {
1181
- return placed(inner(ctx, rt), place);
1465
+ return placed(inner(granted(ctx), rt), place);
1182
1466
  } catch (error) {
1183
1467
  return placed(thrown(error, `the evaluator ${name}`), place);
1184
1468
  }
@@ -1187,23 +1471,31 @@ function placedCode(inner: Code, place: readonly Key[], name: string): Code {
1187
1471
 
1188
1472
  /**
1189
1473
  * This function gives the code of a part that capability rules cover. The code narrows the context of the part by
1190
- * the rules before the part starts.
1474
+ * the rules before the part starts. First the context keeps only the capabilities of its grant, thus a compiled
1475
+ * parent cannot widen the capabilities that the rules narrow (CAP.NARROW-CODE).
1191
1476
  */
1192
- function gated(rules: readonly CapsRule[], part: { readonly code: Code; readonly run: RunCode }): { readonly code: Code; readonly run: RunCode } {
1477
+ function gated(rules: readonly CapsRule[], part: Compiled): Compiled {
1193
1478
  const inner = part.code;
1194
1479
  const steps = part.run;
1195
- const under = (ctx: Context): Context => {
1480
+ const under = (given: Context): Context => {
1481
+ const ctx = granted(given);
1196
1482
  const caps = capsUnder(rules, ctx.caps);
1197
- return caps === ctx.caps || (caps.length === 0 && ctx.caps.length === 0) ? ctx : { ...ctx, caps };
1483
+ if (caps === ctx.caps || (caps.length === 0 && ctx.caps.length === 0)) return ctx;
1484
+ const out: Granted = { ...ctx, caps, [GRANT]: caps };
1485
+ return out;
1198
1486
  };
1199
- return { code: (ctx, rt) => inner(under(ctx), rt), run: (ctx, rt) => steps(under(ctx), rt) };
1487
+ // The steps of an evaluator are `runOf` of its code, thus `runOf` of this code narrows the context the same way.
1488
+ return { code: (ctx, rt) => inner(under(ctx), rt), run: steps === undefined ? undefined : (ctx, rt) => steps(under(ctx), rt) };
1200
1489
  }
1201
1490
 
1202
- /** This function gives the steps of a runner, with the place of each error and of each exception. */
1491
+ /**
1492
+ * This function gives the steps of a runner, with the place of each error and of each exception. The steps get the
1493
+ * context that its grant permits (CAP.NARROW-CODE).
1494
+ */
1203
1495
  function placedRun(inner: RunCode, place: readonly Key[], name: string): RunCode {
1204
1496
  return function* (ctx, rt) {
1205
1497
  try {
1206
- return placed(yield* inner(ctx, rt), place);
1498
+ return placed(yield* inner(granted(ctx), rt), place);
1207
1499
  } catch (error) {
1208
1500
  return placed(thrown(error, `the runner ${name}`), place);
1209
1501
  }
@@ -1211,14 +1503,21 @@ function placedRun(inner: RunCode, place: readonly Key[], name: string): RunCode
1211
1503
  }
1212
1504
 
1213
1505
  /**
1214
- * This function gives the code of a runner in an evaluation: its steps, which cannot wait. A step that waits gives
1215
- * `#CALC!` at the place of the runner, where the error started (RUN.WAIT).
1506
+ * This function gives the code of a runner under a parent that cannot wait: its steps, driven without a wait. An
1507
+ * evaluation and a run both drive them (RUN.DRIVE-NESTED). Only a step that really waits gives `#CALC!`, at the place
1508
+ * of the runner, where the error started (RUN.WAIT).
1216
1509
  */
1217
1510
  function runnerCode(run: RunCode, place: readonly Key[], kindText: string): Code {
1218
- return (ctx, rt) =>
1219
- rt.running
1220
- ? fail("#CALC!", `${kindText} waits for effects, and its parent cannot wait. Give the parent a runner.`, { place })
1221
- : placed(drive(run(ctx, rt)), place);
1511
+ return (ctx, rt) => placed(drive(run(ctx, rt), kindText), place);
1512
+ }
1513
+
1514
+ /**
1515
+ * The code of a compiled document. The steps of a run are `undefined` for an evaluator: they are `runOf(code)`, and a
1516
+ * plan makes them only for a run (PLAN.LEAN).
1517
+ */
1518
+ interface Compiled {
1519
+ readonly code: Code;
1520
+ readonly run: RunCode | undefined;
1222
1521
  }
1223
1522
 
1224
1523
  /** This function gives the steps of a run of an evaluator. The code runs at the first step, and it does not wait. */
@@ -1237,6 +1536,8 @@ function fillOf(split: Extract<Split, { readonly ok: true }>, doc: Envelope): <P
1237
1536
  function interpret(fn: EvalFn, input: CompileInput, fill: <P>(handles: readonly P[]) => unknown): Code {
1238
1537
  const { kind, typePath, doc, data, place } = input;
1239
1538
  const address = input.address === true ? { address: true } : {};
1539
+ // The code keeps the fill and not the whole input, thus it does not keep the compiled value of the document (PLAN.LEAN).
1540
+ const refill = input.fill;
1240
1541
  const compiled = input.parts;
1241
1542
  const hasParts = compiled.length > 0;
1242
1543
  return (ctx, rt) => {
@@ -1244,7 +1545,7 @@ function interpret(fn: EvalFn, input: CompileInput, fill: <P>(handles: readonly
1244
1545
  key: part.key,
1245
1546
  path: part.path,
1246
1547
  ...(part.name === undefined ? {} : { name: part.name }),
1247
- evaluate: (override?: Context) => part.code(override === undefined ? ctx : narrow(ctx, override), rt),
1548
+ evaluate: (override?: Context) => part.code(override === undefined ? ctx : handed(ctx, override), rt),
1248
1549
  }));
1249
1550
  return fn({
1250
1551
  kind,
@@ -1258,13 +1559,14 @@ function interpret(fn: EvalFn, input: CompileInput, fill: <P>(handles: readonly
1258
1559
  ...address,
1259
1560
  resolve: (path, options) => rt.resolve(ctx, path, options),
1260
1561
  effect: (effectKind, payload) => rt.effect(ctx, effectKind, payload, place),
1261
- fill: input.fill,
1562
+ fill: refill,
1262
1563
  });
1263
1564
  };
1264
1565
  }
1265
1566
 
1266
1567
  function interpretRun(fn: RunFn, input: CompileInput, fill: <P>(handles: readonly P[]) => unknown): RunCode {
1267
1568
  const { kind, typePath, doc, data, place } = input;
1569
+ const refill = input.fill;
1268
1570
  const compiled = input.parts;
1269
1571
  const hasParts = compiled.length > 0;
1270
1572
  return (ctx, rt) => {
@@ -1272,7 +1574,7 @@ function interpretRun(fn: RunFn, input: CompileInput, fill: <P>(handles: readonl
1272
1574
  key: part.key,
1273
1575
  path: part.path,
1274
1576
  ...(part.name === undefined ? {} : { name: part.name }),
1275
- evaluate: (override?: Context) => part.run(override === undefined ? ctx : narrow(ctx, override), rt),
1577
+ evaluate: (override?: Context) => part.run(override === undefined ? ctx : handed(ctx, override), rt),
1276
1578
  }));
1277
1579
  return fn({
1278
1580
  kind,
@@ -1286,57 +1588,102 @@ function interpretRun(fn: RunFn, input: CompileInput, fill: <P>(handles: readonl
1286
1588
  resolve: (path, options) => rt.resolve(ctx, path, options),
1287
1589
  effect: (effectKind, payload) => rt.runEffect(ctx, effectKind, payload, place),
1288
1590
  all: (list, options) => rt.all(list.map((part) => () => part.evaluate()), options),
1289
- fill: input.fill,
1591
+ fill: refill,
1290
1592
  });
1291
1593
  };
1292
1594
  }
1293
1595
 
1294
- function rootContext(machine: Machine, ctx: Context): Context {
1295
- return ctx.world === null ? { ...ctx, world: worldThing(machine.world) } : ctx;
1596
+ /**
1597
+ * This function gives the context at the root of an evaluation. It is the context of the host, with the world of
1598
+ * the machine when the context has none. The host gives the capabilities, thus the root has the grant of each one
1599
+ * (CAP.NARROW-CODE). An evaluation and `verifyReads` share it.
1600
+ *
1601
+ * @param machine - The machine.
1602
+ * @param ctx - The context of the host.
1603
+ * @returns The context at the root.
1604
+ */
1605
+ export function rootContext(machine: Machine, ctx: Context): Context {
1606
+ if (ctx.world !== null && (ctx as Granted)[GRANT] === ctx.caps) return ctx;
1607
+ const root: Granted = { ...ctx, world: ctx.world === null ? worldThing(machine.world) : ctx.world, [GRANT]: ctx.caps };
1608
+ return root;
1296
1609
  }
1297
1610
 
1298
1611
  /**
1299
1612
  * This function compiles a document into a plan. The ladder, the kinds and the parts are known after it. A plan
1300
1613
  * evaluates many times. A document that does not compile gives a plan that fails with the reason, when it evaluates.
1301
1614
  *
1615
+ * The verb of the options goes up the ladder at each document, and each part evaluates with the same verb. The
1616
+ * default verb is `eval`. A plan of another verb, for example `format`, evaluates, runs and traces as a plan of
1617
+ * `eval` does (VERB.PREPARE).
1618
+ *
1302
1619
  * @param machine - The world and the code of the evaluators.
1303
1620
  * @param doc - The document, as JSON.
1304
- * @param options - The type of a bare root value, and a trace.
1621
+ * @param options - The type of a bare root value, a trace and the verb.
1305
1622
  * @returns The plan.
1306
1623
  */
1307
1624
  export function prepare(machine: Machine, doc: unknown, options: PrepareOptions = {}): Plan {
1308
- const c: Compiler = { machine, traced: options.trace === true, maxDepth: machine.maxDepth ?? 1000 };
1309
- let root: { readonly code: Code; readonly run: RunCode };
1625
+ const verb = options.verb ?? "eval";
1626
+ const noAnswer: Answer = verb === "eval" ? NO_ANSWER : { verb, error: "no-answer", steps: [] };
1627
+ const c: Compiler = { machine, traced: options.trace === true, maxDepth: machine.maxDepth ?? 1000, verb, noAnswer };
1628
+ let root: Compiled;
1310
1629
  try {
1311
1630
  root = compileNode(c, doc, [], options.declaredType, 0);
1312
1631
  } catch (error) {
1313
1632
  const result = placed(thrown(error, "the compilation"), []);
1314
- root = {
1315
- code: () => result,
1316
- run: function* () {
1317
- return result;
1318
- },
1319
- };
1633
+ root = { code: () => result, run: undefined };
1320
1634
  }
1321
- const compiled: CompiledPart = { key: "", path: [], code: root.code, run: root.run };
1322
- return planOf(machine, c.traced, compiled);
1635
+ return new CompiledPlan(machine, c.traced, root.code, root.run);
1323
1636
  }
1324
1637
 
1325
- /** This function makes the object of a plan. Its closures keep only the machine and the compiled root. */
1326
- function planOf(machine: Machine, traced: boolean, root: CompiledPart): Plan {
1327
- return {
1328
- machine,
1329
- traced,
1330
- root,
1331
- evaluate(context: Context = makeContext(), evalOptions?: EvalOptions): Result {
1332
- const s = new Session(machine, false, traced, evalOptions?.perform, undefined, evalOptions?.effects);
1333
- return root.code(rootContext(machine, context), s);
1334
- },
1335
- run(context: Context = makeContext(), runOptions: RunOptions = {}): Promise<Result> {
1336
- const s = new Session(machine, true, traced, runOptions.perform, runOptions.signal, undefined);
1337
- return driveWaiting(root.run(rootContext(machine, context), s), runOptions.signal);
1338
- },
1339
- };
1638
+ /**
1639
+ * The object of a plan (PLAN.LEAN). The code of `evaluate` and `run` is on the class, thus a plan keeps only its
1640
+ * machine and its compiled code. A plan makes its compiled root and the steps of a run at their first use.
1641
+ */
1642
+ class CompiledPlan implements Plan {
1643
+ /*
1644
+ * The constructor writes each field one time, and the class declares the fields with `declare`. A field that a class
1645
+ * defines first as `undefined` and then writes again is not constant for V8, and the code of an evaluation then
1646
+ * loads it at each call.
1647
+ */
1648
+ declare readonly machine: Machine;
1649
+ declare readonly traced: boolean;
1650
+ /** The code of the root document. */
1651
+ declare readonly code: Code;
1652
+ /** The steps of a run of the root document. `undefined` gives `runOf(code)` at the first `run`. */
1653
+ declare private steps: RunCode | undefined;
1654
+ /** The compiled root, after the first read of `root`. */
1655
+ declare private part: CompiledPart | undefined;
1656
+
1657
+ constructor(machine: Machine, traced: boolean, code: Code, steps: RunCode | undefined) {
1658
+ this.machine = machine;
1659
+ this.traced = traced;
1660
+ this.code = code;
1661
+ this.steps = steps;
1662
+ this.part = undefined;
1663
+ }
1664
+
1665
+ get root(): CompiledPart {
1666
+ return (this.part ??= { key: "", path: [], code: this.code, run: this.runCode() });
1667
+ }
1668
+
1669
+ /** This function gives the steps of a run of the root document. */
1670
+ runCode(): RunCode {
1671
+ return (this.steps ??= runOf(this.code));
1672
+ }
1673
+
1674
+ evaluate(context: Context = makeContext(), evalOptions?: EvalOptions): Result {
1675
+ const machine = this.machine;
1676
+ const ctx = rootContext(machine, context);
1677
+ const s = new Session(machine, false, this.traced, evalOptions?.perform, undefined, evalOptions?.effects, readingOf(evalOptions?.reads, ctx));
1678
+ return this.code(ctx, s);
1679
+ }
1680
+
1681
+ run(context: Context = makeContext(), runOptions: RunOptions = {}): Promise<Result> {
1682
+ const machine = this.machine;
1683
+ const ctx = rootContext(machine, context);
1684
+ const s = new Session(machine, true, this.traced, runOptions.perform, runOptions.signal, undefined, readingOf(runOptions.reads, ctx));
1685
+ return driveWaiting(this.runCode()(ctx, s), runOptions.signal);
1686
+ }
1340
1687
  }
1341
1688
 
1342
1689
  /**
@@ -1359,6 +1706,8 @@ export function planner(machine: Machine, options: PrepareOptions = {}): (doc: u
1359
1706
  }
1360
1707
  return plan;
1361
1708
  }
1709
+ // A map treats -0 and 0 as one key, so -0 gets its own plan each time.
1710
+ if (Object.is(doc, -0)) return prepare(machine, doc, options);
1362
1711
  let plan = values.get(doc);
1363
1712
  if (plan === undefined) {
1364
1713
  plan = prepare(machine, doc, options);
@@ -1378,23 +1727,30 @@ function record(s: Session, result: Result): Run {
1378
1727
  /**
1379
1728
  * This function evaluates a document, and records each evaluation in a trace.
1380
1729
  *
1381
- * The verb `eval` goes up the ladder for each document. The kind of the document tells where its parts are. The
1382
- * evaluator gets each part sealed: it can evaluate the part, and it cannot read its value. A failure gives an error
1383
- * value, and the function does not throw. An effect is recorded in its order, and the host does not do it. A mock
1384
- * can answer each effect at once (RUN.MOCK).
1730
+ * The verb of the options goes up the ladder for each document. The default verb is `eval`. The kind of the document
1731
+ * tells where its parts are. The evaluator gets each part sealed: it can evaluate the part, and it cannot read its
1732
+ * value.
1733
+ *
1734
+ * A failure gives an error value, and the function does not throw. An effect is recorded in its order, and the host
1735
+ * does not do it. A mock can answer each effect at once (RUN.MOCK).
1385
1736
  *
1386
1737
  * @param machine - The world and the code of the evaluators.
1387
1738
  * @param doc - The document, as JSON.
1388
1739
  * @param context - The names and the capabilities at the root.
1389
- * @param options - The type of a bare root value, or the options: that type and the mock of the host.
1740
+ * @param options - The type of a bare root value, or the options: that type, the mock of the host, the verb and a list for the reads.
1390
1741
  * @returns The result, the trace and the effects in their order.
1391
1742
  */
1392
1743
  export function evaluate(machine: Machine, doc: unknown, context: Context = makeContext(), options?: string | EvaluateOptions): Run {
1393
1744
  const given: EvaluateOptions = typeof options === "string" ? { declaredType: options } : (options ?? {});
1394
1745
  const declaredType = given.declaredType;
1395
- const plan = prepare(machine, doc, { trace: true, ...(declaredType === undefined ? {} : { declaredType }) });
1396
- const s = new Session(machine, false, true, given.perform, undefined, undefined);
1397
- const result = plan.root.code(rootContext(machine, context), s);
1746
+ const plan = prepare(machine, doc, {
1747
+ trace: true,
1748
+ ...(declaredType === undefined ? {} : { declaredType }),
1749
+ ...(given.verb === undefined ? {} : { verb: given.verb }),
1750
+ });
1751
+ const ctx = rootContext(machine, context);
1752
+ const s = new Session(machine, false, true, given.perform, undefined, undefined, readingOf(given.reads, ctx));
1753
+ const result = (plan as CompiledPlan).code(ctx, s);
1398
1754
  return record(s, result);
1399
1755
  }
1400
1756
 
@@ -1406,13 +1762,18 @@ export function evaluate(machine: Machine, doc: unknown, context: Context = make
1406
1762
  * @param machine - The world, the evaluators and the runners.
1407
1763
  * @param doc - The document, as JSON.
1408
1764
  * @param context - The names and the capabilities at the root.
1409
- * @param options - The host code that does the effects, the type of a bare root value, and a signal.
1765
+ * @param options - The host code that does the effects, the type of a bare root value, a signal, the verb and a list for the reads.
1410
1766
  * @returns The result, the trace and the effects in their order, when the run ends.
1411
1767
  */
1412
1768
  export async function run(machine: Machine, doc: unknown, context: Context = makeContext(), options: RunOptions = {}): Promise<Run> {
1413
- const plan = prepare(machine, doc, { trace: true, ...(options.declaredType === undefined ? {} : { declaredType: options.declaredType }) });
1414
- const s = new Session(machine, true, true, options.perform, options.signal, undefined);
1415
- const result = await driveWaiting(plan.root.run(rootContext(machine, context), s), options.signal);
1769
+ const plan = prepare(machine, doc, {
1770
+ trace: true,
1771
+ ...(options.declaredType === undefined ? {} : { declaredType: options.declaredType }),
1772
+ ...(options.verb === undefined ? {} : { verb: options.verb }),
1773
+ });
1774
+ const ctx = rootContext(machine, context);
1775
+ const s = new Session(machine, true, true, options.perform, options.signal, undefined, readingOf(options.reads, ctx));
1776
+ const result = await driveWaiting((plan as CompiledPlan).runCode()(ctx, s), options.signal);
1416
1777
  return record(s, result);
1417
1778
  }
1418
1779