@mark1russell7/coeval 0.6.1 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/build/index.d.ts +189 -22
- package/build/index.d.ts.map +1 -1
- package/build/index.js +390 -105
- package/build/index.js.map +1 -1
- package/package.json +2 -1
- package/src/agree.ts +44 -55
- package/src/deref.ts +17 -6
- package/src/doc.ts +23 -2
- package/src/errors.ts +2 -1
- package/src/evaluate.ts +198 -27
- package/src/formula.ts +2 -2
- package/src/index.ts +6 -1
- package/src/merge.ts +2 -2
- package/src/parts.ts +1 -1
- package/src/reference.ts +86 -15
- package/src/replay.ts +159 -0
- package/src/same.ts +42 -0
- package/src/static.ts +1 -1
- package/src/stdlib.ts +10 -6
package/src/evaluate.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type Envelope, type Json, type Key, type TypeRef, isEnvelope, typeName
|
|
|
2
2
|
import { type Split, partsOf, pathMatches } from "./parts.ts";
|
|
3
3
|
import { ask, type Answer } from "./ladder.ts";
|
|
4
4
|
import type { CapsRule, Kind, Side, World } from "./world.ts";
|
|
5
|
-
import { type ErrorCode, type EvalError, type Result, fail, ok, placed, thrown, describe } from "./errors.ts";
|
|
5
|
+
import { type ErrorCode, type EvalError, type Result, Failure, fail, ok, placed, thrown, describe } from "./errors.ts";
|
|
6
6
|
import {
|
|
7
7
|
type Cap,
|
|
8
8
|
type Context,
|
|
@@ -75,10 +75,10 @@ export function narrow(parent: Context, next: Context): Context {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
/** No capabilities. One list serves each masked part. */
|
|
78
|
-
const NO_CAPS: readonly Cap[] = Object.freeze([]);
|
|
78
|
+
const NO_CAPS: readonly Cap[] = /* @__PURE__ */ Object.freeze([]);
|
|
79
79
|
|
|
80
80
|
/** No capability rules. One list serves each part that no rule narrows. */
|
|
81
|
-
const NO_RULES: readonly CapsRule[] = Object.freeze([]);
|
|
81
|
+
const NO_RULES: readonly CapsRule[] = /* @__PURE__ */ Object.freeze([]);
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
84
|
* This function gives the capabilities that two lists both permit. An effect is permitted by the result when each
|
|
@@ -135,6 +135,7 @@ export type Steps = Generator<Promise<unknown>, Result, unknown>;
|
|
|
135
135
|
|
|
136
136
|
/** An effect that an evaluation caused, in the order of the effects. */
|
|
137
137
|
export interface Effect {
|
|
138
|
+
/** The number of effects before this effect. Under `all`, this number follows the order of the host. */
|
|
138
139
|
readonly index: number;
|
|
139
140
|
/** The kind of the effect, as a text with dots. */
|
|
140
141
|
readonly kind: string;
|
|
@@ -149,11 +150,100 @@ export interface Effect {
|
|
|
149
150
|
readonly place: readonly Key[];
|
|
150
151
|
/** The context of the document that caused the effect: its names, its node and its capabilities. */
|
|
151
152
|
readonly context: Context;
|
|
153
|
+
/**
|
|
154
|
+
* The stable address of the effect (RUN.ADDRESS). The order of the host does not change it. The address has the
|
|
155
|
+
* keys of the place, a `#` and the count of the earlier effects at that place in its scope. An example is
|
|
156
|
+
* `1/bind/x#0`. Each item of `all` is a scope, and its prefix comes first: `all0.1/` is the item 1 of the first `all`
|
|
157
|
+
* of the scope. The kernel gives each effect its address.
|
|
158
|
+
*/
|
|
159
|
+
readonly address?: string;
|
|
160
|
+
/**
|
|
161
|
+
* The outcome of the effect: the result that it gave its document (RUN.OUTCOME). The kernel sets it when the answer
|
|
162
|
+
* comes. It stays `undefined` when the run stops first, for example at an abort.
|
|
163
|
+
*/
|
|
164
|
+
readonly outcome?: Result;
|
|
152
165
|
}
|
|
153
166
|
|
|
154
|
-
/**
|
|
167
|
+
/**
|
|
168
|
+
* 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
|
+
* `Failure` gives its own error value. In an evaluation, this function is a mock: it answers at once, and a promise
|
|
170
|
+
* from it gives `#CALC!` (RUN.MOCK).
|
|
171
|
+
*/
|
|
155
172
|
export type Perform = (effect: Effect) => unknown;
|
|
156
173
|
|
|
174
|
+
/** The key of the journal of a recorder or of a replayer, on its function. Two copies of the kernel share it. */
|
|
175
|
+
export const JOURNAL: unique symbol = /* @__PURE__ */ Symbol.for("coeval.journal") as never;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* The journal of a recorder or of a replayer, on its function `perform`. The kernel tells it the order in which the
|
|
179
|
+
* waits of each `all` settle. In a replay, the kernel asks the journal for that order (RUN.RECORD, RUN.REPLAY).
|
|
180
|
+
*/
|
|
181
|
+
export interface Journal {
|
|
182
|
+
/**
|
|
183
|
+
* This function gets the item whose wait settled.
|
|
184
|
+
*
|
|
185
|
+
* @param all - The address of the `all`, for example `all0` or `all0.1/all0`.
|
|
186
|
+
* @param item - The index of the item.
|
|
187
|
+
*/
|
|
188
|
+
readonly settled?: (all: string, item: number) => void;
|
|
189
|
+
/**
|
|
190
|
+
* This function gives the item whose wait settles next.
|
|
191
|
+
*
|
|
192
|
+
* @param all - The address of the `all`.
|
|
193
|
+
* @param waiting - The indices of the items that wait.
|
|
194
|
+
* @returns The index of an item that waits, or `undefined` for the order of the host.
|
|
195
|
+
*/
|
|
196
|
+
readonly next?: (all: string, waiting: readonly number[]) => number | undefined;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** This function gives the journal of a function `perform`, when it has one. */
|
|
200
|
+
function journalOf(perform: Perform | undefined): Journal | undefined {
|
|
201
|
+
return perform === undefined ? undefined : (perform as { readonly [JOURNAL]?: Journal })[JOURNAL];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* This function gives the outcome of an effect that the host failed. A `Failure` gives its own error value, for
|
|
206
|
+
* example the recorded error of a replay or `#REPLAY!`. Each other exception gives `#CALC!`, with the exception as the
|
|
207
|
+
* cause (RUN.CAUSE).
|
|
208
|
+
*/
|
|
209
|
+
function hostFailed(kind: string, place: readonly Key[], error: unknown): Result {
|
|
210
|
+
if (error instanceof Failure) return placed({ ok: false, error: error.error }, place);
|
|
211
|
+
return fail("#CALC!", `The host failed the effect ${kind}: ${describe(error)}`, { place, cause: error });
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* The scope of the addresses of effects (RUN.ADDRESS). The root of an evaluation is one scope, and each item of `all`
|
|
216
|
+
* is a scope. The code of one scope does not interleave with itself, thus its counts do not depend on the host.
|
|
217
|
+
*/
|
|
218
|
+
interface Scope {
|
|
219
|
+
/** The text before each address of the scope, for example `all0.1/`. */
|
|
220
|
+
readonly prefix: string;
|
|
221
|
+
/** The number of earlier effects at each place, by the text of the place. It is made at the first effect. */
|
|
222
|
+
counts: Map<string, number> | undefined;
|
|
223
|
+
/** The number of earlier uses of `all` in the scope. */
|
|
224
|
+
alls: number;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* This function gives the text of a place in an address: each key, with a slash between two keys. A text key with
|
|
229
|
+
* other characters than letters, digits, `_`, `$` and `-` gets quotes.
|
|
230
|
+
*
|
|
231
|
+
* @param place - The keys from the root document to a document.
|
|
232
|
+
* @returns The text of the place.
|
|
233
|
+
*/
|
|
234
|
+
export function placeText(place: readonly Key[]): string {
|
|
235
|
+
let text = "";
|
|
236
|
+
for (let index = 0; index < place.length; index += 1) {
|
|
237
|
+
const key = place[index] as Key;
|
|
238
|
+
if (index > 0) text += "/";
|
|
239
|
+
text += typeof key === "number" || /^[\w$-]+$/.test(key) ? String(key) : JSON.stringify(key);
|
|
240
|
+
}
|
|
241
|
+
return text;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** An effect while the kernel waits for its outcome. */
|
|
245
|
+
type Recorded = { -readonly [K in keyof Effect]: Effect[K] };
|
|
246
|
+
|
|
157
247
|
/**
|
|
158
248
|
* The runtime of one evaluation or run. Compiled code gets it with the context, and asks it to resolve paths and
|
|
159
249
|
* to cause effects.
|
|
@@ -179,7 +269,7 @@ export interface Runtime {
|
|
|
179
269
|
* @param kind - The kind of the effect.
|
|
180
270
|
* @param payload - The data of the effect.
|
|
181
271
|
* @param place - The place of the document.
|
|
182
|
-
* @returns The payload, or an error value.
|
|
272
|
+
* @returns The answer of the mock, the payload without a mock, or an error value.
|
|
183
273
|
*/
|
|
184
274
|
effect(ctx: Context, kind: Cap, payload: unknown, place: readonly Key[]): Result;
|
|
185
275
|
/**
|
|
@@ -316,7 +406,7 @@ export interface EvalInput {
|
|
|
316
406
|
*
|
|
317
407
|
* @param kind - The kind of the effect, for example `log`.
|
|
318
408
|
* @param payload - The data of the effect.
|
|
319
|
-
* @returns The payload, or
|
|
409
|
+
* @returns The answer of the mock, the payload without a mock, or an error value, for example `#CAP!`.
|
|
320
410
|
*/
|
|
321
411
|
effect(kind: Cap, payload: unknown): Result;
|
|
322
412
|
/**
|
|
@@ -360,7 +450,7 @@ export interface RunInput {
|
|
|
360
450
|
resolve(path: readonly unknown[], options?: ResolveOptions): Resolution;
|
|
361
451
|
/**
|
|
362
452
|
* This function causes an effect. In a run, the host does the effect, and the steps wait when the host waits. In
|
|
363
|
-
* an evaluation, the effect gives its payload. Take its result with `yield*`.
|
|
453
|
+
* an evaluation, a mock answers the effect at once, or the effect gives its payload. Take its result with `yield*`.
|
|
364
454
|
*
|
|
365
455
|
* @param kind - The kind of the effect, for example `log`.
|
|
366
456
|
* @param payload - The data of the effect.
|
|
@@ -547,11 +637,24 @@ export interface RunOptions {
|
|
|
547
637
|
export interface EvalOptions {
|
|
548
638
|
/** A list that gets each effect, in its order. */
|
|
549
639
|
readonly effects?: Effect[];
|
|
640
|
+
/**
|
|
641
|
+
* The mock of the host (RUN.MOCK). It answers each effect at once, and its answer is the result of the effect.
|
|
642
|
+
* Without it, an effect gives its payload (RUN.EVAL).
|
|
643
|
+
*/
|
|
644
|
+
readonly perform?: Perform;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/** The options of `evaluate`. */
|
|
648
|
+
export interface EvaluateOptions {
|
|
649
|
+
/** The type of a bare root value. */
|
|
650
|
+
readonly declaredType?: string;
|
|
651
|
+
/** The mock of the host (RUN.MOCK). Without it, an effect gives its payload (RUN.EVAL). */
|
|
652
|
+
readonly perform?: Perform;
|
|
550
653
|
}
|
|
551
654
|
|
|
552
655
|
/** The options of a resolution without a record of hops, and with one. One object of each serves each call. */
|
|
553
|
-
const WITHOUT_HOPS: ResolveOptions = Object.freeze({ hops: false });
|
|
554
|
-
const WITH_HOPS: ResolveOptions = Object.freeze({ hops: true });
|
|
656
|
+
const WITHOUT_HOPS: ResolveOptions = /* @__PURE__ */ Object.freeze({ hops: false });
|
|
657
|
+
const WITH_HOPS: ResolveOptions = /* @__PURE__ */ Object.freeze({ hops: true });
|
|
555
658
|
|
|
556
659
|
/** This class holds the state of one evaluation or run: the machine, the effects, the clock and the trace. */
|
|
557
660
|
class Session implements Runtime {
|
|
@@ -561,6 +664,11 @@ class Session implements Runtime {
|
|
|
561
664
|
nextId = 0;
|
|
562
665
|
current: Building | undefined;
|
|
563
666
|
readonly holder: Building | undefined;
|
|
667
|
+
/**
|
|
668
|
+
* The scope of the addresses of the code that runs at this time (RUN.ADDRESS). `all` changes it for each item. The
|
|
669
|
+
* root scope is made at the first effect or `all`, thus an evaluation without effects makes no scope.
|
|
670
|
+
*/
|
|
671
|
+
private scope: Scope | undefined;
|
|
564
672
|
|
|
565
673
|
constructor(
|
|
566
674
|
readonly machine: Machine,
|
|
@@ -589,45 +697,90 @@ class Session implements Runtime {
|
|
|
589
697
|
return this.machine.permits === undefined ? permits(ctx.caps, keys) : this.machine.permits(ctx.caps, keys, ctx);
|
|
590
698
|
}
|
|
591
699
|
|
|
592
|
-
|
|
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 {
|
|
593
702
|
const keys = typeof kind === "string" ? kind.split(".") : kind;
|
|
594
703
|
const text = keys.join(".");
|
|
595
704
|
if (!this.permitted(ctx, keys)) return fail("#CAP!", `The context has no capability for the effect ${text}.`, { place });
|
|
596
705
|
const node = this.current;
|
|
597
706
|
const list = this.effects;
|
|
598
|
-
const
|
|
707
|
+
const scope = (this.scope ??= { prefix: "", counts: undefined, alls: 0 });
|
|
708
|
+
const at = placeText(place);
|
|
709
|
+
const counts = (scope.counts ??= new Map<string, number>());
|
|
710
|
+
const count = counts.get(at) ?? 0;
|
|
711
|
+
counts.set(at, count + 1);
|
|
712
|
+
const effect: Recorded = {
|
|
713
|
+
index: list.length,
|
|
714
|
+
kind: text,
|
|
715
|
+
path: keys,
|
|
716
|
+
payload,
|
|
717
|
+
node: node?.id ?? -1,
|
|
718
|
+
at: this.tick,
|
|
719
|
+
place,
|
|
720
|
+
context: ctx,
|
|
721
|
+
address: `${scope.prefix}${at}#${count}`,
|
|
722
|
+
};
|
|
599
723
|
list.push(effect);
|
|
600
724
|
if (node !== undefined) node.effects.push(effect.index);
|
|
601
725
|
return effect;
|
|
602
726
|
}
|
|
603
727
|
|
|
728
|
+
/**
|
|
729
|
+
* This function answers an effect at once, without a wait: the mock gives the result, or the effect gives its
|
|
730
|
+
* payload without a mock (RUN.MOCK, RUN.EVAL). A promise from the mock gives `#CALC!`.
|
|
731
|
+
*/
|
|
732
|
+
private answer(effect: Recorded, place: readonly Key[]): Result {
|
|
733
|
+
const perform = this.perform;
|
|
734
|
+
if (perform === undefined) return (effect.outcome = ok(effect.payload));
|
|
735
|
+
let done: unknown;
|
|
736
|
+
try {
|
|
737
|
+
done = perform(effect);
|
|
738
|
+
} catch (error) {
|
|
739
|
+
return (effect.outcome = hostFailed(effect.kind, place, error));
|
|
740
|
+
}
|
|
741
|
+
if (!(done instanceof Promise)) return (effect.outcome = ok(done));
|
|
742
|
+
// The evaluation does not read the promise, thus its rejection must not be unhandled.
|
|
743
|
+
void done.catch(() => undefined);
|
|
744
|
+
return (effect.outcome = fail("#CALC!", `The mock gave a promise for the effect ${effect.kind}, and an evaluation does not wait.`, { place }));
|
|
745
|
+
}
|
|
746
|
+
|
|
604
747
|
effect(ctx: Context, kind: Cap, payload: unknown, place: readonly Key[]): Result {
|
|
605
748
|
const text = typeof kind === "string" ? kind : kind.join(".");
|
|
606
749
|
if (this.running) return fail("#CALC!", `The effect ${text} needs a runner: in a run, the host does each effect.`, { place });
|
|
607
750
|
const made = this.record(ctx, kind, payload, place);
|
|
608
|
-
return "ok" in made ? made :
|
|
751
|
+
return "ok" in made ? made : this.answer(made, place);
|
|
609
752
|
}
|
|
610
753
|
|
|
611
754
|
*runEffect(ctx: Context, kind: Cap, payload: unknown, place: readonly Key[]): Steps {
|
|
612
755
|
const made = this.record(ctx, kind, payload, place);
|
|
613
756
|
if ("ok" in made) return made;
|
|
614
757
|
const perform = this.perform;
|
|
615
|
-
if (!this.running || perform === undefined) return
|
|
616
|
-
const failed = (error: unknown): Result => fail("#CALC!", `The host failed the effect ${made.kind}: ${describe(error)}`, { place, cause: error });
|
|
758
|
+
if (!this.running || perform === undefined) return this.answer(made, place);
|
|
617
759
|
let done: unknown;
|
|
618
760
|
try {
|
|
619
761
|
done = perform(made);
|
|
620
762
|
} catch (error) {
|
|
621
|
-
return
|
|
763
|
+
return (made.outcome = hostFailed(made.kind, place, error));
|
|
622
764
|
}
|
|
623
|
-
if (!(done instanceof Promise)) return ok(done);
|
|
624
|
-
|
|
765
|
+
if (!(done instanceof Promise)) return (made.outcome = ok(done));
|
|
766
|
+
const result = (yield done.then(
|
|
767
|
+
(value: unknown) => ok(value),
|
|
768
|
+
(error: unknown) => hostFailed(made.kind, place, error),
|
|
769
|
+
)) as Result;
|
|
770
|
+
// A run that stops at this wait does not reach this line, thus the effect keeps no outcome (RUN.OUTCOME).
|
|
771
|
+
return (made.outcome = result);
|
|
625
772
|
}
|
|
626
773
|
|
|
627
774
|
*all(list: readonly (() => Steps)[], options: AllOptions = {}): Generator<Promise<unknown>, readonly Result[], unknown> {
|
|
628
775
|
const results: Result[] = new Array<Result>(list.length);
|
|
629
776
|
const limit = Math.max(1, options.concurrency ?? list.length);
|
|
630
777
|
const failFast = options.failFast === true;
|
|
778
|
+
// The address of this call, and a scope for each item (RUN.ADDRESS). The journal of a replay reads the address.
|
|
779
|
+
const outer = (this.scope ??= { prefix: "", counts: undefined, alls: 0 });
|
|
780
|
+
const address = `${outer.prefix}all${outer.alls}`;
|
|
781
|
+
outer.alls += 1;
|
|
782
|
+
const scopes: Scope[] = new Array<Scope>(list.length);
|
|
783
|
+
const journal = journalOf(this.perform);
|
|
631
784
|
let failed = false;
|
|
632
785
|
interface Active {
|
|
633
786
|
readonly index: number;
|
|
@@ -642,11 +795,15 @@ class Session implements Runtime {
|
|
|
642
795
|
};
|
|
643
796
|
const advance = (index: number, steps: Steps, input: { readonly value?: unknown; readonly error?: unknown; readonly threw: boolean }): void => {
|
|
644
797
|
let step: IteratorResult<Promise<unknown>, Result>;
|
|
798
|
+
const before = this.scope;
|
|
799
|
+
this.scope = scopes[index] as Scope;
|
|
645
800
|
try {
|
|
646
801
|
step = input.threw ? steps.throw(input.error) : steps.next(input.value);
|
|
647
802
|
} catch (error) {
|
|
648
803
|
settle(index, thrown(error, "a part"));
|
|
649
804
|
return;
|
|
805
|
+
} finally {
|
|
806
|
+
this.scope = before;
|
|
650
807
|
}
|
|
651
808
|
if (step.done === true) {
|
|
652
809
|
settle(index, step.value);
|
|
@@ -663,25 +820,35 @@ class Session implements Runtime {
|
|
|
663
820
|
while (!failed && active.length < limit && next < list.length) {
|
|
664
821
|
const index = next;
|
|
665
822
|
next += 1;
|
|
823
|
+
const scope: Scope = { prefix: `${address}.${index}/`, counts: undefined, alls: 0 };
|
|
824
|
+
scopes[index] = scope;
|
|
666
825
|
const begin = list[index] as () => Steps;
|
|
667
826
|
let steps: Steps;
|
|
827
|
+
const before = this.scope;
|
|
828
|
+
this.scope = scope;
|
|
668
829
|
try {
|
|
669
830
|
steps = begin();
|
|
670
831
|
} catch (error) {
|
|
671
832
|
settle(index, thrown(error, "a part"));
|
|
672
833
|
continue;
|
|
834
|
+
} finally {
|
|
835
|
+
this.scope = before;
|
|
673
836
|
}
|
|
674
837
|
advance(index, steps, { threw: false });
|
|
675
838
|
}
|
|
676
839
|
};
|
|
677
840
|
start();
|
|
678
841
|
while (active.length > 0 && !failed) {
|
|
679
|
-
|
|
842
|
+
// A replay settles the waits in the recorded order. Each other run takes the first wait that settles.
|
|
843
|
+
const chosen = journal?.next?.(address, active.map((item) => item.index));
|
|
844
|
+
const pick = chosen === undefined ? undefined : active.find((item) => item.index === chosen);
|
|
845
|
+
const settled = (yield pick === undefined ? Promise.race(active.map((item) => item.pending)) : pick.pending) as {
|
|
680
846
|
readonly item: Active;
|
|
681
847
|
readonly value?: unknown;
|
|
682
848
|
readonly error?: unknown;
|
|
683
849
|
readonly threw: boolean;
|
|
684
850
|
};
|
|
851
|
+
journal?.settled?.(address, settled.item.index);
|
|
685
852
|
active.splice(active.indexOf(settled.item), 1);
|
|
686
853
|
advance(settled.item.index, settled.item.steps, settled);
|
|
687
854
|
start();
|
|
@@ -759,7 +926,7 @@ function drive(steps: Steps): Result {
|
|
|
759
926
|
return fail("#CALC!", "A part waited, and its parent cannot wait.");
|
|
760
927
|
}
|
|
761
928
|
|
|
762
|
-
const ABORTED = Symbol("aborted");
|
|
929
|
+
const ABORTED = /* @__PURE__ */ Symbol("aborted");
|
|
763
930
|
|
|
764
931
|
/** This function drives steps that can wait. The steps before the first wait happen at once, in the call. */
|
|
765
932
|
async function driveWaiting(steps: Steps, signal: StopSignal | undefined): Promise<Result> {
|
|
@@ -819,10 +986,11 @@ export interface Plan {
|
|
|
819
986
|
/** The compiled root document. */
|
|
820
987
|
readonly root: CompiledPart;
|
|
821
988
|
/**
|
|
822
|
-
* This function evaluates the document. It does not wait, and the host does no effect.
|
|
989
|
+
* This function evaluates the document. It does not wait, and the host does no effect. A mock can answer each
|
|
990
|
+
* effect at once (RUN.MOCK).
|
|
823
991
|
*
|
|
824
992
|
* @param context - The names and the capabilities at the root.
|
|
825
|
-
* @param options - A list that gets the effects.
|
|
993
|
+
* @param options - A list that gets the effects, and the mock of the host.
|
|
826
994
|
* @returns The result.
|
|
827
995
|
*/
|
|
828
996
|
evaluate(context?: Context, options?: EvalOptions): Result;
|
|
@@ -916,7 +1084,7 @@ function evaluatorOf(world: World, envelope: Envelope | undefined, kind: Kind |
|
|
|
916
1084
|
}
|
|
917
1085
|
|
|
918
1086
|
/** The meta of a node of a compilation without a trace. Only a trace reads the meta of a node. */
|
|
919
|
-
const NO_META: Meta = Object.freeze({ path: [], doc: { type: "?", value: null }, declared: false, answer: NO_ANSWER, side: "derive" });
|
|
1087
|
+
const NO_META: Meta = /* @__PURE__ */ Object.freeze({ path: [], doc: { type: "?", value: null }, declared: false, answer: NO_ANSWER, side: "derive" });
|
|
920
1088
|
|
|
921
1089
|
function compileNode(c: Compiler, value: unknown, place: readonly Key[], declared: string | undefined, depth: number, address = false): { readonly code: Code; readonly run: RunCode } {
|
|
922
1090
|
const world = c.machine.world;
|
|
@@ -1161,7 +1329,7 @@ function planOf(machine: Machine, traced: boolean, root: CompiledPart): Plan {
|
|
|
1161
1329
|
traced,
|
|
1162
1330
|
root,
|
|
1163
1331
|
evaluate(context: Context = makeContext(), evalOptions?: EvalOptions): Result {
|
|
1164
|
-
const s = new Session(machine, false, traced,
|
|
1332
|
+
const s = new Session(machine, false, traced, evalOptions?.perform, undefined, evalOptions?.effects);
|
|
1165
1333
|
return root.code(rootContext(machine, context), s);
|
|
1166
1334
|
},
|
|
1167
1335
|
run(context: Context = makeContext(), runOptions: RunOptions = {}): Promise<Result> {
|
|
@@ -1212,17 +1380,20 @@ function record(s: Session, result: Result): Run {
|
|
|
1212
1380
|
*
|
|
1213
1381
|
* The verb `eval` goes up the ladder for each document. The kind of the document tells where its parts are. The
|
|
1214
1382
|
* evaluator gets each part sealed: it can evaluate the part, and it cannot read its value. A failure gives an error
|
|
1215
|
-
* value, and the function does not throw. An effect is recorded in its order, and
|
|
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).
|
|
1216
1385
|
*
|
|
1217
1386
|
* @param machine - The world and the code of the evaluators.
|
|
1218
1387
|
* @param doc - The document, as JSON.
|
|
1219
1388
|
* @param context - The names and the capabilities at the root.
|
|
1220
|
-
* @param
|
|
1389
|
+
* @param options - The type of a bare root value, or the options: that type and the mock of the host.
|
|
1221
1390
|
* @returns The result, the trace and the effects in their order.
|
|
1222
1391
|
*/
|
|
1223
|
-
export function evaluate(machine: Machine, doc: unknown, context: Context = makeContext(),
|
|
1392
|
+
export function evaluate(machine: Machine, doc: unknown, context: Context = makeContext(), options?: string | EvaluateOptions): Run {
|
|
1393
|
+
const given: EvaluateOptions = typeof options === "string" ? { declaredType: options } : (options ?? {});
|
|
1394
|
+
const declaredType = given.declaredType;
|
|
1224
1395
|
const plan = prepare(machine, doc, { trace: true, ...(declaredType === undefined ? {} : { declaredType }) });
|
|
1225
|
-
const s = new Session(machine, false, true,
|
|
1396
|
+
const s = new Session(machine, false, true, given.perform, undefined, undefined);
|
|
1226
1397
|
const result = plan.root.code(rootContext(machine, context), s);
|
|
1227
1398
|
return record(s, result);
|
|
1228
1399
|
}
|
package/src/formula.ts
CHANGED
|
@@ -48,14 +48,14 @@ export interface FormulaProfile {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/** The mark of a field that the profile leaves out of a record. */
|
|
51
|
-
const SKIPPED: unique symbol = Symbol("skipped");
|
|
51
|
+
const SKIPPED: unique symbol = /* @__PURE__ */ Symbol("skipped");
|
|
52
52
|
|
|
53
53
|
/** The value of a name whose binding failed. A reference to the name gives the error. */
|
|
54
54
|
export class Held {
|
|
55
55
|
constructor(readonly error: EvalError) {}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const CLOSURE: unique symbol = Symbol.for("coeval.closure") as never;
|
|
58
|
+
const CLOSURE: unique symbol = /* @__PURE__ */ Symbol.for("coeval.closure") as never;
|
|
59
59
|
|
|
60
60
|
/** A function that the construct `fn` made. It throws a `Failure` when its body fails. */
|
|
61
61
|
export type Closure = ((...args: readonly unknown[]) => unknown) & { readonly [CLOSURE]: { readonly params: readonly string[] } };
|
package/src/index.ts
CHANGED
|
@@ -118,7 +118,9 @@ export {
|
|
|
118
118
|
type EvalFn,
|
|
119
119
|
type EvalInput,
|
|
120
120
|
type EvalOptions,
|
|
121
|
+
type EvaluateOptions,
|
|
121
122
|
type Evaluator,
|
|
123
|
+
type Journal,
|
|
122
124
|
type Machine,
|
|
123
125
|
type Part,
|
|
124
126
|
type Perform,
|
|
@@ -135,6 +137,7 @@ export {
|
|
|
135
137
|
type Steps,
|
|
136
138
|
type StopSignal,
|
|
137
139
|
type TraceNode,
|
|
140
|
+
JOURNAL,
|
|
138
141
|
capKeys,
|
|
139
142
|
evaluate,
|
|
140
143
|
failures,
|
|
@@ -198,5 +201,7 @@ export {
|
|
|
198
201
|
jsTruthy,
|
|
199
202
|
} from "./formula.ts";
|
|
200
203
|
export { type Definitions, type ReferenceOptions, STD_DEFINITIONS, formulaDefinitions, reference } from "./reference.ts";
|
|
201
|
-
export { type AgreeOptions, type Agreement, type Tier, type TierResult, agree, sameValue } from "./agree.ts";
|
|
204
|
+
export { type AgreeOptions, type Agreement, type Tier, type TierInput, type TierResult, agree, sameValue } from "./agree.ts";
|
|
205
|
+
export { sameResult } from "./same.ts";
|
|
206
|
+
export { type Recorder, type ReplayEntry, type ReplayLog, type ReplayOptions, type ReplaySettle, recorder, replayer } from "./replay.ts";
|
|
202
207
|
export { type GenerateOptions, type Random, generate, random, typesOf } from "./generate.ts";
|
package/src/merge.ts
CHANGED
|
@@ -55,7 +55,7 @@ export class MergeError extends Error {
|
|
|
55
55
|
|
|
56
56
|
type Known = NonNullable<Machine["known"]>;
|
|
57
57
|
|
|
58
|
-
const NONE: ReadonlySet<string> = new Set();
|
|
58
|
+
const NONE: ReadonlySet<string> = /* @__PURE__ */ new Set();
|
|
59
59
|
|
|
60
60
|
function keyOf(path: MergePath): string {
|
|
61
61
|
return JSON.stringify(path.map(String));
|
|
@@ -246,7 +246,7 @@ export function mergeWorlds(...parts: readonly (WorldPart | ((prev: World) => Wo
|
|
|
246
246
|
}
|
|
247
247
|
|
|
248
248
|
/** The fields of a machine that merge by their own rules. Each other field is one value, or a conflict. */
|
|
249
|
-
const TABLES = new Set(["world", "evaluators", "runners", "derefs", "walkers", "known", "replaces"]);
|
|
249
|
+
const TABLES = /* @__PURE__ */ new Set(["world", "evaluators", "runners", "derefs", "walkers", "known", "replaces"]);
|
|
250
250
|
|
|
251
251
|
interface MachineDraft {
|
|
252
252
|
readonly world: WorldDraft;
|
package/src/parts.ts
CHANGED
|
@@ -50,7 +50,7 @@ function setOwn(target: Record<string, unknown>, key: string, value: unknown): v
|
|
|
50
50
|
else target[key] = value;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
const NO_SLOTS: readonly PartSlot[] = Object.freeze([]);
|
|
53
|
+
const NO_SLOTS: readonly PartSlot[] = /* @__PURE__ */ Object.freeze([]);
|
|
54
54
|
|
|
55
55
|
function hasSlots(template: Template): boolean {
|
|
56
56
|
return template.t !== "data";
|