@mark1russell7/coeval 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.d.ts +157 -104
- package/build/index.d.ts.map +1 -1
- package/build/index.js +533 -173
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/agree.ts +110 -2
- package/src/evaluate.ts +114 -22
- package/src/formula.ts +17 -3
- package/src/index.ts +2 -2
- package/src/reference.ts +247 -42
package/build/index.js
CHANGED
|
@@ -2905,6 +2905,12 @@ function partCaps(rules, path, caps) {
|
|
|
2905
2905
|
*/
|
|
2906
2906
|
const GRANT = /* @__PURE__ */ Symbol("coeval.grant");
|
|
2907
2907
|
/**
|
|
2908
|
+
* The key of the epoch of a grant (CAP.NARROW-KEPT). The epoch is the list of capabilities that the host gave the root
|
|
2909
|
+
* of the evaluation of the grant. A spread of a context keeps its epoch, as it keeps its grant. An
|
|
2910
|
+
* effect and a resolution read a context of another epoch as a context without a grant.
|
|
2911
|
+
*/
|
|
2912
|
+
const EPOCH = /* @__PURE__ */ Symbol("coeval.epoch");
|
|
2913
|
+
/**
|
|
2908
2914
|
* This function gives the capabilities that a context and its grant both permit, the meet `intersectCaps` (CAP.MEET).
|
|
2909
2915
|
* A context whose grant is its own list gives that list after one comparison. A context without a grant gives none.
|
|
2910
2916
|
*/
|
|
@@ -2928,12 +2934,27 @@ function granted(ctx) {
|
|
|
2928
2934
|
};
|
|
2929
2935
|
}
|
|
2930
2936
|
/**
|
|
2937
|
+
* This function gives a context of another epoch the epoch of the evaluation, without capabilities (CAP.NARROW-KEPT).
|
|
2938
|
+
* Thus the context reads as a context without a grant. The kernel cannot know the document of the context in this
|
|
2939
|
+
* evaluation.
|
|
2940
|
+
*/
|
|
2941
|
+
function ungranted(ctx, epoch) {
|
|
2942
|
+
return {
|
|
2943
|
+
...ctx,
|
|
2944
|
+
caps: NO_CAPS$2,
|
|
2945
|
+
[GRANT]: NO_CAPS$2,
|
|
2946
|
+
[EPOCH]: epoch
|
|
2947
|
+
};
|
|
2948
|
+
}
|
|
2949
|
+
/**
|
|
2931
2950
|
* This function gives the context in which the runtime resolves a path of compiled code (CAP.CAPS-GRANT, CAP.MEET).
|
|
2932
2951
|
* The context has the meet of its list and its grant, and a context without a grant has none. Thus a read of the root
|
|
2933
2952
|
* `caps` and a walker of a step document get the capabilities of the document from a widened context.
|
|
2934
2953
|
* A context that the code narrowed keeps its own list. A context whose list does not change stays the same object.
|
|
2954
|
+
* A context of another epoch has no capabilities (CAP.NARROW-KEPT).
|
|
2935
2955
|
*/
|
|
2936
|
-
function readable(ctx) {
|
|
2956
|
+
function readable(ctx, epoch) {
|
|
2957
|
+
if (ctx[EPOCH] !== epoch) return ctx.caps.length === 0 ? ctx : ungranted(ctx, epoch);
|
|
2937
2958
|
const caps = grantedCaps(ctx);
|
|
2938
2959
|
if (caps === ctx.caps || caps.length === 0 && ctx.caps.length === 0) return ctx;
|
|
2939
2960
|
return {
|
|
@@ -2944,14 +2965,17 @@ function readable(ctx) {
|
|
|
2944
2965
|
}
|
|
2945
2966
|
/**
|
|
2946
2967
|
* This function gives the context of a part of a function evaluator. It is the context that the evaluator gives,
|
|
2947
|
-
* narrowed by the context of the document (CAP.NARROW), with the grant of its capabilities.
|
|
2968
|
+
* narrowed by the context of the document (CAP.NARROW), with the grant of its capabilities. The grant has the epoch
|
|
2969
|
+
* of the document, because the document permits each of its capabilities (CAP.NARROW-KEPT).
|
|
2948
2970
|
*/
|
|
2949
2971
|
function handed(parent, next) {
|
|
2950
2972
|
const ctx = narrow(parent, next);
|
|
2951
|
-
|
|
2973
|
+
const epoch = parent[EPOCH];
|
|
2974
|
+
if (ctx[GRANT] === ctx.caps && ctx[EPOCH] === epoch) return ctx;
|
|
2952
2975
|
return {
|
|
2953
2976
|
...ctx,
|
|
2954
|
-
[GRANT]: ctx.caps
|
|
2977
|
+
[GRANT]: ctx.caps,
|
|
2978
|
+
[EPOCH]: epoch
|
|
2955
2979
|
};
|
|
2956
2980
|
}
|
|
2957
2981
|
/** The text that names a resolution in the error of an exception of the host. */
|
|
@@ -3058,6 +3082,28 @@ const NO_ANSWER = {
|
|
|
3058
3082
|
/** The options of a resolution without a record of hops, and with one. One object of each serves each call. */
|
|
3059
3083
|
const WITHOUT_HOPS = /* @__PURE__ */ Object.freeze({ hops: false });
|
|
3060
3084
|
const WITH_HOPS = /* @__PURE__ */ Object.freeze({ hops: true });
|
|
3085
|
+
/**
|
|
3086
|
+
* This function gives the path of a kept read (READS.CLOSURE). The evaluation did not ask for the read. Thus the read
|
|
3087
|
+
* does not step into a host thing, and the path ends at the first host thing. The host gives a new thing for a new state.
|
|
3088
|
+
* A reference in the data reads the context of its reader, thus the function then gives `undefined`.
|
|
3089
|
+
*
|
|
3090
|
+
* @param nav - The world and the code of the evaluators.
|
|
3091
|
+
* @param root - The context at the root of the evaluation.
|
|
3092
|
+
* @param path - The root and the keys of the static read.
|
|
3093
|
+
* @returns The path of the kept read, or `undefined` for a reference on the way.
|
|
3094
|
+
*/
|
|
3095
|
+
function keptPath(nav, root, path) {
|
|
3096
|
+
const adopt = nav.adopt ?? ((value) => value);
|
|
3097
|
+
let here = adopt(root[path[0]]);
|
|
3098
|
+
for (let index = 1;; index += 1) {
|
|
3099
|
+
if (isHostThing(here)) return index === path.length ? path : path.slice(0, index);
|
|
3100
|
+
if (nav.plain !== true && isRef(here)) return void 0;
|
|
3101
|
+
if (index >= path.length) return path;
|
|
3102
|
+
const reached = derefStep(nav, here, path[index]);
|
|
3103
|
+
if (reached === void 0) return path;
|
|
3104
|
+
here = adopt(reached.thing);
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3061
3107
|
/** This function makes the record of the reads of an evaluation, when the host gives a list. */
|
|
3062
3108
|
function readingOf(list, root) {
|
|
3063
3109
|
return list === void 0 ? void 0 : {
|
|
@@ -3070,6 +3116,7 @@ function readingOf(list, root) {
|
|
|
3070
3116
|
/** This class holds the state of one evaluation or run: the machine, the effects, the clock and the trace. */
|
|
3071
3117
|
var Session = class {
|
|
3072
3118
|
machine;
|
|
3119
|
+
epoch;
|
|
3073
3120
|
running;
|
|
3074
3121
|
traced;
|
|
3075
3122
|
perform;
|
|
@@ -3086,8 +3133,9 @@ var Session = class {
|
|
|
3086
3133
|
* root scope is made at the first effect or `all`, thus an evaluation without effects makes no scope.
|
|
3087
3134
|
*/
|
|
3088
3135
|
scope;
|
|
3089
|
-
constructor(machine, running, traced, perform, signal, effects, reading = void 0) {
|
|
3136
|
+
constructor(machine, epoch, running, traced, perform, signal, effects, reading = void 0) {
|
|
3090
3137
|
this.machine = machine;
|
|
3138
|
+
this.epoch = epoch;
|
|
3091
3139
|
this.running = running;
|
|
3092
3140
|
this.traced = traced;
|
|
3093
3141
|
this.perform = perform;
|
|
@@ -3110,7 +3158,7 @@ var Session = class {
|
|
|
3110
3158
|
* `caps` and a walker of a step document get the capabilities of the document.
|
|
3111
3159
|
*/
|
|
3112
3160
|
resolve(ctx, path, options) {
|
|
3113
|
-
return this.read(readable(ctx), path, void 0, options);
|
|
3161
|
+
return this.read(readable(ctx, this.epoch), path, void 0, options);
|
|
3114
3162
|
}
|
|
3115
3163
|
/**
|
|
3116
3164
|
* This function resolves a compiled path (PLAN.PATH). Without a list of reads and without a trace, the compiled
|
|
@@ -3118,7 +3166,7 @@ var Session = class {
|
|
|
3118
3166
|
* that the grant permits (CAP.CAPS-GRANT, CAP.MEET).
|
|
3119
3167
|
*/
|
|
3120
3168
|
resolvePath(given, compiled) {
|
|
3121
|
-
const ctx = readable(given);
|
|
3169
|
+
const ctx = readable(given, this.epoch);
|
|
3122
3170
|
if (this.reading === void 0 && !this.traced && compiled.nav === this.machine) return compiled.resolve(ctx);
|
|
3123
3171
|
return this.read(ctx, compiled.path, compiled, void 0);
|
|
3124
3172
|
}
|
|
@@ -3162,13 +3210,45 @@ var Session = class {
|
|
|
3162
3210
|
if (compiled !== void 0 && !hops && compiled.nav === this.machine) return compiled.resolve(ctx);
|
|
3163
3211
|
return resolve(this.machine, ctx, path, hops ? WITH_HOPS : WITHOUT_HOPS);
|
|
3164
3212
|
}
|
|
3165
|
-
keepsContext() {
|
|
3213
|
+
keepsContext(ctx, reads) {
|
|
3166
3214
|
const reading = this.reading;
|
|
3167
3215
|
if (reading === void 0 || reading.kept) return;
|
|
3216
|
+
const known = ctx === void 0 ? void 0 : reads?.();
|
|
3217
|
+
if (ctx !== void 0 && known !== void 0 && known.every((read) => this.keepRead(reading, ctx, read))) return;
|
|
3168
3218
|
reading.kept = true;
|
|
3169
3219
|
this.keep(reading, reading.self ? ROOTS.filter((root) => root !== "self") : ROOTS);
|
|
3170
3220
|
}
|
|
3171
3221
|
/**
|
|
3222
|
+
* This function adds the kept read of one static read of a closure (READS.CLOSURE). The read resolves from the
|
|
3223
|
+
* context at the root, one time in an evaluation. A name that a kind binds in the kept context gives no read,
|
|
3224
|
+
* because the reads of its binding are already in the list.
|
|
3225
|
+
*
|
|
3226
|
+
* @returns `false` when the path meets a reference in the data. Then the closure keeps each whole root.
|
|
3227
|
+
*/
|
|
3228
|
+
keepRead(reading, ctx, read) {
|
|
3229
|
+
const root = read.root;
|
|
3230
|
+
const exact = [root, ...read.keys];
|
|
3231
|
+
if (!isFree(reading.root, ctx, exact)) {
|
|
3232
|
+
if (root === "self" && !reading.self) this.keep(reading, ["self"]);
|
|
3233
|
+
return true;
|
|
3234
|
+
}
|
|
3235
|
+
if (root === "self" && reading.self) return true;
|
|
3236
|
+
const path = keptPath(this.machine, reading.root, read.exact ? exact : [root]);
|
|
3237
|
+
if (path === void 0) return false;
|
|
3238
|
+
const paths = reading.paths ??= /* @__PURE__ */ new Set();
|
|
3239
|
+
const text = JSON.stringify(path);
|
|
3240
|
+
if (paths.has(text) || paths.has(JSON.stringify([root]))) return true;
|
|
3241
|
+
paths.add(text);
|
|
3242
|
+
if (path.length === 1 && root === "self") reading.self = true;
|
|
3243
|
+
reading.list.push({
|
|
3244
|
+
path,
|
|
3245
|
+
reached: readAgain(this.machine, reading.root, path, false).reached,
|
|
3246
|
+
free: true,
|
|
3247
|
+
kept: true
|
|
3248
|
+
});
|
|
3249
|
+
return true;
|
|
3250
|
+
}
|
|
3251
|
+
/**
|
|
3172
3252
|
* This function adds a read of each whole root to the list, from the context at the root (READS.KEPT). An exception
|
|
3173
3253
|
* of the host gives the error value of the read, because the evaluation did not ask for the read.
|
|
3174
3254
|
*/
|
|
@@ -3190,10 +3270,11 @@ var Session = class {
|
|
|
3190
3270
|
/**
|
|
3191
3271
|
* This function checks the capability of an effect, and records the effect with its address (RUN.ADDRESS). The
|
|
3192
3272
|
* context keeps only the capabilities of its grant, thus compiled code cannot widen the context of its own effect
|
|
3193
|
-
* (CAP.NARROW-CODE).
|
|
3273
|
+
* (CAP.NARROW-CODE). A context of another epoch has no capabilities, thus code cannot keep a context for a later
|
|
3274
|
+
* evaluation with fewer capabilities (CAP.NARROW-KEPT).
|
|
3194
3275
|
*/
|
|
3195
3276
|
record(given, kind, payload, place) {
|
|
3196
|
-
const ctx = granted(given);
|
|
3277
|
+
const ctx = given[EPOCH] === this.epoch ? granted(given) : ungranted(given, this.epoch);
|
|
3197
3278
|
const keys = typeof kind === "string" ? kind.split(".") : kind;
|
|
3198
3279
|
const text = keys.join(".");
|
|
3199
3280
|
if (!this.permitted(ctx, keys)) return fail("#CAP!", `The context has no capability for the effect ${text}.`, { place });
|
|
@@ -4027,18 +4108,20 @@ function interpretRun(fn, input, fill) {
|
|
|
4027
4108
|
/**
|
|
4028
4109
|
* This function gives the context at the root of an evaluation. It is the context of the host, with the world of
|
|
4029
4110
|
* the machine when the context has none. The host gives the capabilities, thus the root has the grant of each one
|
|
4030
|
-
* (CAP.NARROW-CODE).
|
|
4111
|
+
* (CAP.NARROW-CODE). The list of the host is also the epoch of the grant (CAP.NARROW-KEPT). An evaluation and
|
|
4112
|
+
* `verifyReads` share it.
|
|
4031
4113
|
*
|
|
4032
4114
|
* @param machine - The machine.
|
|
4033
4115
|
* @param ctx - The context of the host.
|
|
4034
4116
|
* @returns The context at the root.
|
|
4035
4117
|
*/
|
|
4036
4118
|
function rootContext(machine, ctx) {
|
|
4037
|
-
if (ctx.world !== null && ctx[GRANT] === ctx.caps) return ctx;
|
|
4119
|
+
if (ctx.world !== null && ctx[GRANT] === ctx.caps && ctx[EPOCH] === ctx.caps) return ctx;
|
|
4038
4120
|
return {
|
|
4039
4121
|
...ctx,
|
|
4040
4122
|
world: ctx.world === null ? worldThing(machine.world) : ctx.world,
|
|
4041
|
-
[GRANT]: ctx.caps
|
|
4123
|
+
[GRANT]: ctx.caps,
|
|
4124
|
+
[EPOCH]: ctx.caps
|
|
4042
4125
|
};
|
|
4043
4126
|
}
|
|
4044
4127
|
/**
|
|
@@ -4109,13 +4192,13 @@ var CompiledPlan = class {
|
|
|
4109
4192
|
evaluate(context = makeContext(), evalOptions) {
|
|
4110
4193
|
const machine = this.machine;
|
|
4111
4194
|
const ctx = rootContext(machine, context);
|
|
4112
|
-
const s = new Session(machine, false, this.traced, evalOptions?.perform, void 0, evalOptions?.effects, readingOf(evalOptions?.reads, ctx));
|
|
4195
|
+
const s = new Session(machine, ctx.caps, false, this.traced, evalOptions?.perform, void 0, evalOptions?.effects, readingOf(evalOptions?.reads, ctx));
|
|
4113
4196
|
return this.code(ctx, s);
|
|
4114
4197
|
}
|
|
4115
4198
|
run(context = makeContext(), runOptions = {}) {
|
|
4116
4199
|
const machine = this.machine;
|
|
4117
4200
|
const ctx = rootContext(machine, context);
|
|
4118
|
-
const s = new Session(machine, true, this.traced, runOptions.perform, runOptions.signal, void 0, readingOf(runOptions.reads, ctx));
|
|
4201
|
+
const s = new Session(machine, ctx.caps, true, this.traced, runOptions.perform, runOptions.signal, void 0, readingOf(runOptions.reads, ctx));
|
|
4119
4202
|
return driveWaiting(this.runCode()(ctx, s), runOptions.signal);
|
|
4120
4203
|
}
|
|
4121
4204
|
};
|
|
@@ -4184,7 +4267,7 @@ function evaluate(machine, doc, context = makeContext(), options) {
|
|
|
4184
4267
|
...given.verb === void 0 ? {} : { verb: given.verb }
|
|
4185
4268
|
});
|
|
4186
4269
|
const ctx = rootContext(machine, context);
|
|
4187
|
-
const s = new Session(machine, false, true, given.perform, void 0, void 0, readingOf(given.reads, ctx));
|
|
4270
|
+
const s = new Session(machine, ctx.caps, false, true, given.perform, void 0, void 0, readingOf(given.reads, ctx));
|
|
4188
4271
|
return record(s, plan.code(ctx, s));
|
|
4189
4272
|
}
|
|
4190
4273
|
/**
|
|
@@ -4205,7 +4288,7 @@ async function run(machine, doc, context = makeContext(), options = {}) {
|
|
|
4205
4288
|
...options.verb === void 0 ? {} : { verb: options.verb }
|
|
4206
4289
|
});
|
|
4207
4290
|
const ctx = rootContext(machine, context);
|
|
4208
|
-
const s = new Session(machine, true, true, options.perform, options.signal, void 0, readingOf(options.reads, ctx));
|
|
4291
|
+
const s = new Session(machine, ctx.caps, true, true, options.perform, options.signal, void 0, readingOf(options.reads, ctx));
|
|
4209
4292
|
return record(s, await driveWaiting(plan.runCode()(ctx, s), options.signal));
|
|
4210
4293
|
}
|
|
4211
4294
|
/**
|
|
@@ -7510,7 +7593,7 @@ function formulaCode(profile) {
|
|
|
7510
7593
|
}, rt);
|
|
7511
7594
|
};
|
|
7512
7595
|
}),
|
|
7513
|
-
"formula.fn": compiled(({ value, data }) => {
|
|
7596
|
+
"formula.fn": compiled(({ value, data, doc, machine }) => {
|
|
7514
7597
|
const params = isRecord(data) ? data["params"] : void 0;
|
|
7515
7598
|
if (!Array.isArray(params) || !params.every((param) => typeof param === "string") || new Set(params).size !== params.length) {
|
|
7516
7599
|
const bad = fail("#VALUE!", "The params of fn are a list of different texts.");
|
|
@@ -7518,8 +7601,19 @@ function formulaCode(profile) {
|
|
|
7518
7601
|
}
|
|
7519
7602
|
const names = params;
|
|
7520
7603
|
const body = partsOf$1(value)["body"];
|
|
7604
|
+
let known = null;
|
|
7605
|
+
const reads = () => {
|
|
7606
|
+
if (known !== null) return known;
|
|
7607
|
+
try {
|
|
7608
|
+
const facts = factsOf(machine, doc);
|
|
7609
|
+
known = facts.pure ? facts.reads : void 0;
|
|
7610
|
+
} catch {
|
|
7611
|
+
known = void 0;
|
|
7612
|
+
}
|
|
7613
|
+
return known;
|
|
7614
|
+
};
|
|
7521
7615
|
return (ctx, rt) => {
|
|
7522
|
-
rt.keepsContext?.();
|
|
7616
|
+
rt.keepsContext?.(ctx, reads);
|
|
7523
7617
|
const closure = (...args) => {
|
|
7524
7618
|
const scope = { ...ctx.names };
|
|
7525
7619
|
names.forEach((name, index) => setOwn$1(scope, name, args[index]));
|
|
@@ -8733,6 +8827,42 @@ function resolveFully(machine, ctx, path, options) {
|
|
|
8733
8827
|
* mock, the effect gives its payload (RUN.EVAL).
|
|
8734
8828
|
*/
|
|
8735
8829
|
function cause(env, ctx, kind, payload, place) {
|
|
8830
|
+
const effect = recordEffect(env, ctx, kind, payload, place);
|
|
8831
|
+
if ("ok" in effect) return effect;
|
|
8832
|
+
effect.outcome = answer(env.perform, effect, place);
|
|
8833
|
+
return effect.outcome;
|
|
8834
|
+
}
|
|
8835
|
+
/**
|
|
8836
|
+
* This function causes the effect of an evaluator. In a run, the host does each effect, thus only a runner can cause
|
|
8837
|
+
* one. The effect then gives `#CALC!` before the test of its capability (RUN.NEEDS-RUNNER).
|
|
8838
|
+
*/
|
|
8839
|
+
function causeFromEvaluator(env, ctx, kind, payload, place) {
|
|
8840
|
+
const text = typeof kind === "string" ? kind : kind.join(".");
|
|
8841
|
+
if (env.running) return fail("#CALC!", `The effect ${text} needs a runner: in a run, the host does each effect.`, { place });
|
|
8842
|
+
return cause(env, ctx, kind, payload, place);
|
|
8843
|
+
}
|
|
8844
|
+
/**
|
|
8845
|
+
* This function causes the effect of a runner. In an evaluation, the mock answers it at once. In a run, the host does
|
|
8846
|
+
* it, and a promise from the host makes the steps wait (RUN.SEQ). An effect whose wait a stop ends keeps no outcome
|
|
8847
|
+
* (RUN.OUTCOME).
|
|
8848
|
+
*/
|
|
8849
|
+
function* causeAndWait(env, ctx, kind, payload, place) {
|
|
8850
|
+
if (!env.running) return cause(env, ctx, kind, payload, place);
|
|
8851
|
+
const effect = recordEffect(env, ctx, kind, payload, place);
|
|
8852
|
+
if ("ok" in effect) return effect;
|
|
8853
|
+
const perform = env.perform;
|
|
8854
|
+
if (perform === void 0) return effect.outcome = ok(payload);
|
|
8855
|
+
let given;
|
|
8856
|
+
try {
|
|
8857
|
+
given = perform(effect);
|
|
8858
|
+
} catch (error) {
|
|
8859
|
+
return effect.outcome = hostError(effect, place, error);
|
|
8860
|
+
}
|
|
8861
|
+
if (!(given instanceof Promise)) return effect.outcome = ok(given);
|
|
8862
|
+
return effect.outcome = yield given.then((value) => ok(value), (error) => hostError(effect, place, error));
|
|
8863
|
+
}
|
|
8864
|
+
/** This function checks the capability of an effect, and records the effect with its address (RUN.CAP, RUN.ADDRESS). */
|
|
8865
|
+
function recordEffect(env, ctx, kind, payload, place) {
|
|
8736
8866
|
const keys = typeof kind === "string" ? kind.split(".") : kind;
|
|
8737
8867
|
const text = keys.join(".");
|
|
8738
8868
|
const machine = env.machine;
|
|
@@ -8752,8 +8882,18 @@ function cause(env, ctx, kind, payload, place) {
|
|
|
8752
8882
|
address: `${env.scope.prefix}${at}#${count}`
|
|
8753
8883
|
};
|
|
8754
8884
|
env.effects.push(effect);
|
|
8755
|
-
|
|
8756
|
-
|
|
8885
|
+
return effect;
|
|
8886
|
+
}
|
|
8887
|
+
/** This function gives the outcome of an effect that the host failed. A `Failure` gives its own error value (RUN.CAUSE). */
|
|
8888
|
+
function hostError(effect, place, error) {
|
|
8889
|
+
if (error instanceof Failure) return placed({
|
|
8890
|
+
ok: false,
|
|
8891
|
+
error: error.error
|
|
8892
|
+
}, place);
|
|
8893
|
+
return fail("#CALC!", `The host failed the effect ${effect.kind}: ${describe(error)}`, {
|
|
8894
|
+
place,
|
|
8895
|
+
cause: error
|
|
8896
|
+
});
|
|
8757
8897
|
}
|
|
8758
8898
|
/**
|
|
8759
8899
|
* This function gives the answer of the mock to an effect. A throw gives `#CALC!` with the cause, and a `Failure`
|
|
@@ -8765,14 +8905,7 @@ function answer(perform, effect, place) {
|
|
|
8765
8905
|
try {
|
|
8766
8906
|
given = perform(effect);
|
|
8767
8907
|
} catch (error) {
|
|
8768
|
-
|
|
8769
|
-
ok: false,
|
|
8770
|
-
error: error.error
|
|
8771
|
-
}, place);
|
|
8772
|
-
return fail("#CALC!", `The host failed the effect ${effect.kind}: ${describe(error)}`, {
|
|
8773
|
-
place,
|
|
8774
|
-
cause: error
|
|
8775
|
-
});
|
|
8908
|
+
return hostError(effect, place, error);
|
|
8776
8909
|
}
|
|
8777
8910
|
if (!(given instanceof Promise)) return ok(given);
|
|
8778
8911
|
given.catch(() => void 0);
|
|
@@ -8843,7 +8976,7 @@ function evalInput(env, seen, at) {
|
|
|
8843
8976
|
machine: env.machine,
|
|
8844
8977
|
...at.address ? { address: true } : {},
|
|
8845
8978
|
resolve: (path, options) => resolveFully(env.machine, at.ctx, path, options),
|
|
8846
|
-
effect: (kind, payload) =>
|
|
8979
|
+
effect: (kind, payload) => causeFromEvaluator(env, at.ctx, kind, payload, at.place),
|
|
8847
8980
|
fill
|
|
8848
8981
|
};
|
|
8849
8982
|
}
|
|
@@ -8870,67 +9003,116 @@ function runInput(env, seen, at) {
|
|
|
8870
9003
|
place: at.place,
|
|
8871
9004
|
machine: env.machine,
|
|
8872
9005
|
resolve: (path, options) => resolveFully(env.machine, at.ctx, path, options),
|
|
8873
|
-
effect:
|
|
8874
|
-
return cause(env, at.ctx, kind, payload, at.place);
|
|
8875
|
-
},
|
|
9006
|
+
effect: (kind, payload) => causeAndWait(env, at.ctx, kind, payload, at.place),
|
|
8876
9007
|
all: (list, options) => together(env, list, options),
|
|
8877
9008
|
fill
|
|
8878
9009
|
};
|
|
8879
9010
|
}
|
|
8880
9011
|
/**
|
|
8881
9012
|
* This function evaluates parts for `all` (RUN.ALL). The parts start in their order, up to the limit, and each part
|
|
8882
|
-
* goes on until it ends or waits.
|
|
8883
|
-
*
|
|
8884
|
-
*
|
|
9013
|
+
* goes on until it ends or waits. A part that waits keeps its place in the limit. With `failFast`, the first failure
|
|
9014
|
+
* stops the parts that wait, and the later parts do not start (RUN.FAIL-FAST).
|
|
9015
|
+
*
|
|
9016
|
+
* When a part waits, the steps wait for the next part that settles. The journal of the host can choose that part
|
|
9017
|
+
* (RUN.REPLAY), and it gets each part that settled (RUN.RECORD). In an evaluation, the drive stops at the first wait
|
|
9018
|
+
* and gives `#CALC!`. In a run, the host settles the wait, and the part goes on in its own scope.
|
|
8885
9019
|
*
|
|
8886
9020
|
* Each part has its own scope of addresses. Its prefix names the use of `all` in the scope around it, and the part
|
|
8887
9021
|
* (RUN.ADDRESS).
|
|
8888
9022
|
*/
|
|
8889
9023
|
function* together(env, list, options = {}) {
|
|
8890
9024
|
const limit = Math.max(1, options.concurrency ?? list.length);
|
|
9025
|
+
const failFast = options.failFast === true;
|
|
8891
9026
|
const results = list.map(() => void 0);
|
|
9027
|
+
const scopes = [];
|
|
8892
9028
|
const waiting = [];
|
|
8893
9029
|
const outer = env.scope;
|
|
8894
9030
|
const address = `${outer.prefix}all${outer.alls}`;
|
|
8895
9031
|
outer.alls += 1;
|
|
8896
9032
|
let failed = false;
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
8900
|
-
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
|
|
9033
|
+
let started = 0;
|
|
9034
|
+
const end = (index, result) => {
|
|
9035
|
+
results[index] = result;
|
|
9036
|
+
if (failFast && !result.ok) failed = true;
|
|
9037
|
+
};
|
|
9038
|
+
/** This function gives a part its input in its own scope, and keeps it in the list when it waits again. */
|
|
9039
|
+
const goOn = (index, steps, input) => {
|
|
9040
|
+
const around = env.scope;
|
|
9041
|
+
env.scope = scopes[index];
|
|
9042
|
+
let step;
|
|
8904
9043
|
try {
|
|
8905
|
-
|
|
8906
|
-
const step = steps.next();
|
|
8907
|
-
if (step.done === true) result = step.value;
|
|
8908
|
-
else waiting.push({
|
|
8909
|
-
steps,
|
|
8910
|
-
promise: step.value
|
|
8911
|
-
});
|
|
9044
|
+
step = input.threw ? steps.throw(input.error) : steps.next(input.value);
|
|
8912
9045
|
} catch (error) {
|
|
8913
|
-
|
|
9046
|
+
end(index, thrown(error, "a part"));
|
|
9047
|
+
return;
|
|
8914
9048
|
} finally {
|
|
8915
|
-
env.scope =
|
|
9049
|
+
env.scope = around;
|
|
8916
9050
|
}
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
const
|
|
8922
|
-
|
|
8923
|
-
|
|
8924
|
-
|
|
8925
|
-
|
|
9051
|
+
if (step.done === true) {
|
|
9052
|
+
end(index, step.value);
|
|
9053
|
+
return;
|
|
9054
|
+
}
|
|
9055
|
+
const waiter = {
|
|
9056
|
+
index,
|
|
9057
|
+
steps,
|
|
9058
|
+
next: Promise.resolve()
|
|
9059
|
+
};
|
|
9060
|
+
waiter.next = step.value.then((value) => ({
|
|
9061
|
+
waiter,
|
|
9062
|
+
value,
|
|
9063
|
+
threw: false
|
|
9064
|
+
}), (error) => ({
|
|
9065
|
+
waiter,
|
|
9066
|
+
error,
|
|
9067
|
+
threw: true
|
|
9068
|
+
}));
|
|
9069
|
+
waiting.push(waiter);
|
|
9070
|
+
};
|
|
9071
|
+
/** This function starts the next parts in their order, while the limit permits it and no part failed. */
|
|
9072
|
+
const startMore = () => {
|
|
9073
|
+
while (!failed && waiting.length < limit && started < list.length) {
|
|
9074
|
+
const index = started;
|
|
9075
|
+
started += 1;
|
|
9076
|
+
scopes[index] = {
|
|
9077
|
+
prefix: `${address}.${index}/`,
|
|
9078
|
+
counts: /* @__PURE__ */ new Map(),
|
|
9079
|
+
alls: 0
|
|
9080
|
+
};
|
|
9081
|
+
const around = env.scope;
|
|
9082
|
+
env.scope = scopes[index];
|
|
9083
|
+
let steps;
|
|
9084
|
+
try {
|
|
9085
|
+
steps = list[index].evaluate();
|
|
9086
|
+
} catch (error) {
|
|
9087
|
+
end(index, thrown(error, "a part"));
|
|
9088
|
+
continue;
|
|
9089
|
+
} finally {
|
|
9090
|
+
env.scope = around;
|
|
9091
|
+
}
|
|
9092
|
+
goOn(index, steps, { threw: false });
|
|
9093
|
+
}
|
|
9094
|
+
};
|
|
9095
|
+
startMore();
|
|
9096
|
+
while (waiting.length > 0 && !failed) {
|
|
9097
|
+
const chosen = env.journal?.next?.(address, waiting.map((waiter) => waiter.index));
|
|
9098
|
+
const pick = chosen === void 0 ? void 0 : waiting.find((waiter) => waiter.index === chosen);
|
|
9099
|
+
const resumed = yield pick === void 0 ? Promise.race(waiting.map((waiter) => waiter.next)) : pick.next;
|
|
9100
|
+
env.journal?.settled?.(address, resumed.waiter.index);
|
|
9101
|
+
waiting.splice(waiting.indexOf(resumed.waiter), 1);
|
|
9102
|
+
goOn(resumed.waiter.index, resumed.waiter.steps, resumed);
|
|
9103
|
+
startMore();
|
|
8926
9104
|
}
|
|
8927
|
-
if (
|
|
8928
|
-
|
|
9105
|
+
if (!failed) return results;
|
|
9106
|
+
const stopped = fail("#ABORT", "The part stopped: a part before it failed, and the parts run with failFast.");
|
|
9107
|
+
for (const waiter of waiting) try {
|
|
9108
|
+
waiter.steps.return(stopped);
|
|
9109
|
+
} catch {}
|
|
9110
|
+
return results.map((result) => result ?? stopped);
|
|
8929
9111
|
}
|
|
8930
9112
|
/**
|
|
8931
9113
|
* This function evaluates a document by the rules, without a compilation. It is the reference of the plans: a plan,
|
|
8932
|
-
* a traced plan and the second tier of a language must give its results.
|
|
8933
|
-
*
|
|
9114
|
+
* a traced plan and the second tier of a language must give its results. In an evaluation, an effect gives the answer
|
|
9115
|
+
* of the mock or its payload, and a part that waits gives `#CALC!`. `referenceRun` reads the rules of a run.
|
|
8934
9116
|
*
|
|
8935
9117
|
* The verb of the options goes up the ladder at each document, as in `prepare`. Each verb has the same rules: the
|
|
8936
9118
|
* ladder, the contract of the kind and the sealed parts (VERB.AGREE).
|
|
@@ -8942,12 +9124,43 @@ function* together(env, list, options = {}) {
|
|
|
8942
9124
|
* @returns The result.
|
|
8943
9125
|
*/
|
|
8944
9126
|
function reference(machine, doc, context = makeContext(), options = {}) {
|
|
8945
|
-
const env =
|
|
9127
|
+
const env = envOf(machine, options, false);
|
|
9128
|
+
try {
|
|
9129
|
+
return within(env, () => evaluateAt(env, rootAt(machine, doc, context, options)));
|
|
9130
|
+
} catch (error) {
|
|
9131
|
+
return placed(thrown(error, "the reference"), []);
|
|
9132
|
+
}
|
|
9133
|
+
}
|
|
9134
|
+
/**
|
|
9135
|
+
* This function starts a run of a document by the rules, without a compilation (REF.RUN). The host does each effect of a runner,
|
|
9136
|
+
* and a step waits when the host waits (RUN.SEQ). `all` settles the waits in the order of the host, or in the order
|
|
9137
|
+
* of the journal of a replay (RUN.REPLAY). An effect of an evaluator gives `#CALC!` (RUN.NEEDS-RUNNER). A signal that
|
|
9138
|
+
* aborts stops the run with `#ABORT` (RUN.ABORT).
|
|
9139
|
+
*
|
|
9140
|
+
* The part of the run before the first wait happens at once, in the call (RUN.EAGER). `agreeRun` compares the run of
|
|
9141
|
+
* a plan with this run.
|
|
9142
|
+
*
|
|
9143
|
+
* @param machine - The world, the evaluators and the runners.
|
|
9144
|
+
* @param doc - The document.
|
|
9145
|
+
* @param context - The names and the capabilities at the root.
|
|
9146
|
+
* @param options - The options of `reference`, the host and a signal.
|
|
9147
|
+
* @returns The result, when the run ends.
|
|
9148
|
+
*/
|
|
9149
|
+
function referenceRun(machine, doc, context = makeContext(), options = {}) {
|
|
9150
|
+
const env = envOf(machine, options, true);
|
|
9151
|
+
return driveRun(env, stepsAt(env, rootAt(machine, doc, context, options)), options.signal).catch((error) => placed(thrown(error, "the reference"), []));
|
|
9152
|
+
}
|
|
9153
|
+
/** This function makes the state of the reference for one evaluation or one run. */
|
|
9154
|
+
function envOf(machine, options, running) {
|
|
9155
|
+
const perform = options.perform;
|
|
9156
|
+
return {
|
|
8946
9157
|
machine,
|
|
8947
9158
|
definitions: options.definitions ?? {},
|
|
8948
9159
|
effects: options.effects ?? [],
|
|
8949
9160
|
maxDepth: machine.maxDepth ?? 1e3,
|
|
8950
|
-
perform
|
|
9161
|
+
perform,
|
|
9162
|
+
journal: perform === void 0 ? void 0 : perform[JOURNAL],
|
|
9163
|
+
running,
|
|
8951
9164
|
verb: options.verb ?? "eval",
|
|
8952
9165
|
scope: {
|
|
8953
9166
|
prefix: "",
|
|
@@ -8955,26 +9168,66 @@ function reference(machine, doc, context = makeContext(), options = {}) {
|
|
|
8955
9168
|
alls: 0
|
|
8956
9169
|
}
|
|
8957
9170
|
};
|
|
9171
|
+
}
|
|
9172
|
+
/** This function gives the root position of a document. A context without a world gets the world of the machine. */
|
|
9173
|
+
function rootAt(machine, doc, context, options) {
|
|
8958
9174
|
const ctx = context.world === null ? {
|
|
8959
9175
|
...context,
|
|
8960
9176
|
world: worldThing(machine.world)
|
|
8961
9177
|
} : context;
|
|
9178
|
+
return {
|
|
9179
|
+
value: doc,
|
|
9180
|
+
place: [],
|
|
9181
|
+
declared: options.declaredType,
|
|
9182
|
+
depth: 0,
|
|
9183
|
+
address: false,
|
|
9184
|
+
ctx
|
|
9185
|
+
};
|
|
9186
|
+
}
|
|
9187
|
+
/**
|
|
9188
|
+
* This function does one synchronous step of an evaluation or a run. A node of a tree reads the definitions of the
|
|
9189
|
+
* evaluation for its equations (TREE.LADDER). Thus the state of the evaluation is the innermost one in the step.
|
|
9190
|
+
*/
|
|
9191
|
+
function within(env, step) {
|
|
8962
9192
|
activeEnvs.push(env);
|
|
8963
9193
|
try {
|
|
8964
|
-
return
|
|
8965
|
-
value: doc,
|
|
8966
|
-
place: [],
|
|
8967
|
-
declared: options.declaredType,
|
|
8968
|
-
depth: 0,
|
|
8969
|
-
address: false,
|
|
8970
|
-
ctx
|
|
8971
|
-
});
|
|
8972
|
-
} catch (error) {
|
|
8973
|
-
return placed(thrown(error, "the reference"), []);
|
|
9194
|
+
return step();
|
|
8974
9195
|
} finally {
|
|
8975
9196
|
activeEnvs.pop();
|
|
8976
9197
|
}
|
|
8977
9198
|
}
|
|
9199
|
+
/** The mark of a signal that aborted. */
|
|
9200
|
+
const STOPPED$1 = /* @__PURE__ */ Symbol("stopped");
|
|
9201
|
+
/**
|
|
9202
|
+
* This function drives the steps of a run. Each step waits for its promise. A rejection goes into the steps as an
|
|
9203
|
+
* exception. A signal that aborts stops the steps with `#ABORT`, also before the first step (RUN.ABORT).
|
|
9204
|
+
*/
|
|
9205
|
+
async function driveRun(env, steps, signal) {
|
|
9206
|
+
const stop = () => {
|
|
9207
|
+
const aborted = fail("#ABORT", `The host stopped the run: ${describe(signal?.reason ?? "no reason")}`, { cause: signal?.reason });
|
|
9208
|
+
try {
|
|
9209
|
+
within(env, () => steps.return(aborted));
|
|
9210
|
+
} catch {}
|
|
9211
|
+
return aborted;
|
|
9212
|
+
};
|
|
9213
|
+
if (signal?.aborted === true) return stop();
|
|
9214
|
+
const stopping = signal === void 0 ? void 0 : new Promise((resolve) => {
|
|
9215
|
+
signal.addEventListener("abort", () => resolve(STOPPED$1), { once: true });
|
|
9216
|
+
});
|
|
9217
|
+
let step = within(env, () => steps.next());
|
|
9218
|
+
while (step.done !== true) {
|
|
9219
|
+
let input;
|
|
9220
|
+
try {
|
|
9221
|
+
input = await (stopping === void 0 ? step.value : Promise.race([step.value, stopping]));
|
|
9222
|
+
} catch (error) {
|
|
9223
|
+
step = within(env, () => steps.throw(error));
|
|
9224
|
+
continue;
|
|
9225
|
+
}
|
|
9226
|
+
if (input === STOPPED$1) return stop();
|
|
9227
|
+
step = within(env, () => steps.next(input));
|
|
9228
|
+
}
|
|
9229
|
+
return step.value;
|
|
9230
|
+
}
|
|
8978
9231
|
/** The evaluations of the reference that run at this time, the innermost last. */
|
|
8979
9232
|
const activeEnvs = [];
|
|
8980
9233
|
const refNodes = /* @__PURE__ */ new WeakMap();
|
|
@@ -9408,6 +9661,101 @@ function valuesOfStrict(parts) {
|
|
|
9408
9661
|
return { values };
|
|
9409
9662
|
}
|
|
9410
9663
|
//#endregion
|
|
9664
|
+
//#region src/replay.ts
|
|
9665
|
+
/**
|
|
9666
|
+
* This function makes a recorder (RUN.RECORD). The recorder gives each effect to the function `perform` of the host.
|
|
9667
|
+
* Its log keeps each effect with its address, its kind, its payload and the outcome that the kernel gives it. The log
|
|
9668
|
+
* also keeps the order in which the waits of each `all` settle.
|
|
9669
|
+
*
|
|
9670
|
+
* @param perform - The function of the host. Without it, each effect gives its payload, as in a run without a host.
|
|
9671
|
+
* @returns The function `perform` for the run, and the log.
|
|
9672
|
+
*/
|
|
9673
|
+
function recorder(perform) {
|
|
9674
|
+
const seen = [];
|
|
9675
|
+
const settles = [];
|
|
9676
|
+
const journal = { settled: (all, item) => {
|
|
9677
|
+
settles.push({
|
|
9678
|
+
all,
|
|
9679
|
+
item
|
|
9680
|
+
});
|
|
9681
|
+
} };
|
|
9682
|
+
const host = (effect) => {
|
|
9683
|
+
const entry = {
|
|
9684
|
+
effect,
|
|
9685
|
+
waited: false
|
|
9686
|
+
};
|
|
9687
|
+
seen.push(entry);
|
|
9688
|
+
const answer = perform === void 0 ? effect.payload : perform(effect);
|
|
9689
|
+
if (answer instanceof Promise) entry.waited = true;
|
|
9690
|
+
return answer;
|
|
9691
|
+
};
|
|
9692
|
+
return {
|
|
9693
|
+
perform: Object.assign(host, { [JOURNAL]: journal }),
|
|
9694
|
+
log: () => ({
|
|
9695
|
+
effects: seen.map(({ effect, waited }) => ({
|
|
9696
|
+
address: effect.address ?? "",
|
|
9697
|
+
kind: effect.kind,
|
|
9698
|
+
payload: effect.payload,
|
|
9699
|
+
waited,
|
|
9700
|
+
...effect.outcome === void 0 ? {} : { outcome: effect.outcome }
|
|
9701
|
+
})),
|
|
9702
|
+
settles: [...settles]
|
|
9703
|
+
})
|
|
9704
|
+
};
|
|
9705
|
+
}
|
|
9706
|
+
/**
|
|
9707
|
+
* This function makes a replayer: a function `perform` that answers each effect from a log, by its address (RUN.REPLAY). A
|
|
9708
|
+
* recorded error comes again with its code, its reason, its place and its cause. The waits of each `all` settle in the
|
|
9709
|
+
* recorded order. An effect that the log does not have at its address gives `#REPLAY!` (RUN.REPLAY-MISMATCH).
|
|
9710
|
+
*
|
|
9711
|
+
* A replayer keeps its place in the order of the waits, thus each run needs a new replayer.
|
|
9712
|
+
*
|
|
9713
|
+
* @param log - The log of a recorder.
|
|
9714
|
+
* @param options - `wait: false` for an evaluation.
|
|
9715
|
+
* @returns The function `perform` for the replay.
|
|
9716
|
+
*/
|
|
9717
|
+
function replayer(log, options = {}) {
|
|
9718
|
+
const wait = options.wait !== false;
|
|
9719
|
+
const entries = /* @__PURE__ */ new Map();
|
|
9720
|
+
for (const entry of log.effects) entries.set(entry.address, entry);
|
|
9721
|
+
const order = /* @__PURE__ */ new Map();
|
|
9722
|
+
for (const settle of log.settles) {
|
|
9723
|
+
const items = order.get(settle.all);
|
|
9724
|
+
if (items === void 0) order.set(settle.all, [settle.item]);
|
|
9725
|
+
else items.push(settle.item);
|
|
9726
|
+
}
|
|
9727
|
+
const journal = { next: (all, waiting) => {
|
|
9728
|
+
const items = order.get(all);
|
|
9729
|
+
const item = items?.[0];
|
|
9730
|
+
if (items === void 0 || item === void 0 || !waiting.includes(item)) return void 0;
|
|
9731
|
+
items.shift();
|
|
9732
|
+
return item;
|
|
9733
|
+
} };
|
|
9734
|
+
const perform = (effect) => {
|
|
9735
|
+
const entry = effect.address === void 0 ? void 0 : entries.get(effect.address);
|
|
9736
|
+
const wrong = mismatch(effect, entry);
|
|
9737
|
+
if (wrong !== void 0 || entry === void 0) throw wrong;
|
|
9738
|
+
const outcome = entry.outcome;
|
|
9739
|
+
const failure = outcome === void 0 ? replayError(effect, `The log has no outcome for the effect ${effect.kind} at ${effect.address ?? ""}: the recorded run stopped before the answer.`) : outcome.ok ? void 0 : new Failure(outcome.error);
|
|
9740
|
+
const value = outcome?.ok === true ? outcome.value : void 0;
|
|
9741
|
+
if (wait && entry.waited) return failure === void 0 ? Promise.resolve(value) : Promise.reject(failure);
|
|
9742
|
+
if (failure !== void 0) throw failure;
|
|
9743
|
+
return value;
|
|
9744
|
+
};
|
|
9745
|
+
return Object.assign(perform, { [JOURNAL]: journal });
|
|
9746
|
+
}
|
|
9747
|
+
/** This function gives the error of an effect that the log does not have at its address, or `undefined`. */
|
|
9748
|
+
function mismatch(effect, entry) {
|
|
9749
|
+
const at = effect.address ?? "";
|
|
9750
|
+
if (entry === void 0) return replayError(effect, `The log has no effect at the address ${at}, and the run asks for the effect ${effect.kind}.`);
|
|
9751
|
+
if (entry.kind !== effect.kind) return replayError(effect, `The log has the effect ${entry.kind} at the address ${at}, and the run asks for the effect ${effect.kind}.`);
|
|
9752
|
+
if (!docEquals(entry.payload, effect.payload)) return replayError(effect, `The effect ${effect.kind} at the address ${at} has another payload than the log.`);
|
|
9753
|
+
}
|
|
9754
|
+
/** This function gives the exception that carries `#REPLAY!` to the kernel, with the place of the effect. */
|
|
9755
|
+
function replayError(effect, reason) {
|
|
9756
|
+
return new Failure(fail("#REPLAY!", reason, { place: effect.place }).error);
|
|
9757
|
+
}
|
|
9758
|
+
//#endregion
|
|
9411
9759
|
//#region src/agree.ts
|
|
9412
9760
|
/** The tiers that `agree` compares when its options name none. */
|
|
9413
9761
|
const DEFAULT_TIERS = /* @__PURE__ */ Object.freeze([
|
|
@@ -9595,6 +9943,113 @@ function agree(machine, doc, context = makeContext(), options = {}) {
|
|
|
9595
9943
|
effects
|
|
9596
9944
|
};
|
|
9597
9945
|
}
|
|
9946
|
+
/**
|
|
9947
|
+
* This function wraps the function `perform` of a run. The wrapper keeps each effect and each wait of `all` that
|
|
9948
|
+
* settles. It gives each effect to the inner function, and each call of its journal to the inner journal.
|
|
9949
|
+
*/
|
|
9950
|
+
function watched(perform) {
|
|
9951
|
+
const effects = [];
|
|
9952
|
+
const settles = [];
|
|
9953
|
+
const inner = perform[JOURNAL];
|
|
9954
|
+
const settled = (all, item) => {
|
|
9955
|
+
settles.push({
|
|
9956
|
+
all,
|
|
9957
|
+
item
|
|
9958
|
+
});
|
|
9959
|
+
inner?.settled?.(all, item);
|
|
9960
|
+
};
|
|
9961
|
+
const journal = inner?.next === void 0 ? { settled } : {
|
|
9962
|
+
settled,
|
|
9963
|
+
next: (all, waiting) => inner.next?.(all, waiting)
|
|
9964
|
+
};
|
|
9965
|
+
const host = (effect) => {
|
|
9966
|
+
effects.push(effect);
|
|
9967
|
+
return perform(effect);
|
|
9968
|
+
};
|
|
9969
|
+
return {
|
|
9970
|
+
perform: Object.assign(host, { [JOURNAL]: journal }),
|
|
9971
|
+
effects,
|
|
9972
|
+
settles
|
|
9973
|
+
};
|
|
9974
|
+
}
|
|
9975
|
+
function settleText(settle) {
|
|
9976
|
+
return settle === void 0 ? "no item" : `the item ${settle.item} of ${settle.all}`;
|
|
9977
|
+
}
|
|
9978
|
+
/** This function gives the first difference of two orders of the waits of `all`, or `undefined` when they agree. */
|
|
9979
|
+
function settlesDiffer(a, b) {
|
|
9980
|
+
for (let index = 0; index < Math.max(a.length, b.length); index += 1) {
|
|
9981
|
+
const x = a[index];
|
|
9982
|
+
const y = b[index];
|
|
9983
|
+
if (x?.all !== y?.all || x?.item !== y?.item) return `The wait ${index} settles ${settleText(x)} in the plan, and ${settleText(y)} in the tier.`;
|
|
9984
|
+
}
|
|
9985
|
+
}
|
|
9986
|
+
/**
|
|
9987
|
+
* This function compares the run of a document in a plan with its run in other tiers (RUN.AGREES). First the plan
|
|
9988
|
+
* starts the run with a recorder around the host. Then each tier replays the log of that run. These tiers are the
|
|
9989
|
+
* reference run, the traced run, a second run of the plan and the plan with the other choice of folds.
|
|
9990
|
+
*
|
|
9991
|
+
* The rules of `agree` compare the results and the effects, with their addresses and outcomes. A tier must also
|
|
9992
|
+
* settle the waits of each `all` in the order of the plan, thus a replay keeps the order of the waits.
|
|
9993
|
+
*
|
|
9994
|
+
* @param machine - The world, the evaluators and the runners.
|
|
9995
|
+
* @param doc - The document.
|
|
9996
|
+
* @param context - The names and the capabilities at the root.
|
|
9997
|
+
* @param options - The tiers, the definitions for the reference, the rules of the comparison and the host.
|
|
9998
|
+
* @returns The result of the plan, or the first tier that disagrees and the reason.
|
|
9999
|
+
*/
|
|
10000
|
+
async function agreeRun(machine, doc, context = makeContext(), options = {}) {
|
|
10001
|
+
const declared = {
|
|
10002
|
+
...options.declaredType === void 0 ? {} : { declaredType: options.declaredType },
|
|
10003
|
+
...options.verb === void 0 ? {} : { verb: options.verb }
|
|
10004
|
+
};
|
|
10005
|
+
const observe = options.observe;
|
|
10006
|
+
observe?.();
|
|
10007
|
+
const folds = observe === void 0;
|
|
10008
|
+
const plan = prepare(machine, doc, {
|
|
10009
|
+
...declared,
|
|
10010
|
+
fold: folds
|
|
10011
|
+
});
|
|
10012
|
+
const recording = recorder(options.perform);
|
|
10013
|
+
const first = watched(recording.perform);
|
|
10014
|
+
const result = await plan.run(context, { perform: first.perform });
|
|
10015
|
+
const saw = observe?.();
|
|
10016
|
+
const log = recording.log();
|
|
10017
|
+
for (const tier of options.tiers ?? DEFAULT_TIERS) {
|
|
10018
|
+
const replay = watched(replayer(log));
|
|
10019
|
+
const host = { perform: replay.perform };
|
|
10020
|
+
let other;
|
|
10021
|
+
if (tier === "reference") other = await referenceRun(machine, doc, context, {
|
|
10022
|
+
...declared,
|
|
10023
|
+
...host,
|
|
10024
|
+
...options.definitions === void 0 ? {} : { definitions: options.definitions }
|
|
10025
|
+
});
|
|
10026
|
+
else if (tier === "traced") other = (await run(machine, doc, context, {
|
|
10027
|
+
...declared,
|
|
10028
|
+
...host
|
|
10029
|
+
})).result;
|
|
10030
|
+
else if (tier === "folds") other = await prepare(machine, doc, {
|
|
10031
|
+
...declared,
|
|
10032
|
+
fold: !folds
|
|
10033
|
+
}).run(context, host);
|
|
10034
|
+
else other = await plan.run(context, host);
|
|
10035
|
+
const compareCalls = observe !== void 0 && tier !== "folds";
|
|
10036
|
+
const calls = observe?.();
|
|
10037
|
+
if (overflowed(result) || overflowed(other)) continue;
|
|
10038
|
+
const why = resultDiffers(result, other, options) ?? effectsDiffer(first.effects, replay.effects, options) ?? settlesDiffer(first.settles, replay.settles) ?? (compareCalls && !sameValue(saw, calls, options.equal) ? "The host saw other calls in the tier." : void 0);
|
|
10039
|
+
if (why !== void 0) return {
|
|
10040
|
+
ok: false,
|
|
10041
|
+
tier,
|
|
10042
|
+
plan: result,
|
|
10043
|
+
other,
|
|
10044
|
+
reason: why
|
|
10045
|
+
};
|
|
10046
|
+
}
|
|
10047
|
+
return {
|
|
10048
|
+
ok: true,
|
|
10049
|
+
result,
|
|
10050
|
+
effects: first.effects
|
|
10051
|
+
};
|
|
10052
|
+
}
|
|
9598
10053
|
//#endregion
|
|
9599
10054
|
//#region src/generate.ts
|
|
9600
10055
|
/**
|
|
@@ -11810,101 +12265,6 @@ function schedule(machine, options = {}) {
|
|
|
11810
12265
|
};
|
|
11811
12266
|
}
|
|
11812
12267
|
//#endregion
|
|
11813
|
-
//#region src/replay.ts
|
|
11814
|
-
/**
|
|
11815
|
-
* This function makes a recorder (RUN.RECORD). The recorder gives each effect to the function `perform` of the host.
|
|
11816
|
-
* Its log keeps each effect with its address, its kind, its payload and the outcome that the kernel gives it. The log
|
|
11817
|
-
* also keeps the order in which the waits of each `all` settle.
|
|
11818
|
-
*
|
|
11819
|
-
* @param perform - The function of the host. Without it, each effect gives its payload, as in a run without a host.
|
|
11820
|
-
* @returns The function `perform` for the run, and the log.
|
|
11821
|
-
*/
|
|
11822
|
-
function recorder(perform) {
|
|
11823
|
-
const seen = [];
|
|
11824
|
-
const settles = [];
|
|
11825
|
-
const journal = { settled: (all, item) => {
|
|
11826
|
-
settles.push({
|
|
11827
|
-
all,
|
|
11828
|
-
item
|
|
11829
|
-
});
|
|
11830
|
-
} };
|
|
11831
|
-
const host = (effect) => {
|
|
11832
|
-
const entry = {
|
|
11833
|
-
effect,
|
|
11834
|
-
waited: false
|
|
11835
|
-
};
|
|
11836
|
-
seen.push(entry);
|
|
11837
|
-
const answer = perform === void 0 ? effect.payload : perform(effect);
|
|
11838
|
-
if (answer instanceof Promise) entry.waited = true;
|
|
11839
|
-
return answer;
|
|
11840
|
-
};
|
|
11841
|
-
return {
|
|
11842
|
-
perform: Object.assign(host, { [JOURNAL]: journal }),
|
|
11843
|
-
log: () => ({
|
|
11844
|
-
effects: seen.map(({ effect, waited }) => ({
|
|
11845
|
-
address: effect.address ?? "",
|
|
11846
|
-
kind: effect.kind,
|
|
11847
|
-
payload: effect.payload,
|
|
11848
|
-
waited,
|
|
11849
|
-
...effect.outcome === void 0 ? {} : { outcome: effect.outcome }
|
|
11850
|
-
})),
|
|
11851
|
-
settles: [...settles]
|
|
11852
|
-
})
|
|
11853
|
-
};
|
|
11854
|
-
}
|
|
11855
|
-
/**
|
|
11856
|
-
* This function makes a replayer: a function `perform` that answers each effect from a log, by its address (RUN.REPLAY). A
|
|
11857
|
-
* recorded error comes again with its code, its reason, its place and its cause. The waits of each `all` settle in the
|
|
11858
|
-
* recorded order. An effect that the log does not have at its address gives `#REPLAY!` (RUN.REPLAY-MISMATCH).
|
|
11859
|
-
*
|
|
11860
|
-
* A replayer keeps its place in the order of the waits, thus each run needs a new replayer.
|
|
11861
|
-
*
|
|
11862
|
-
* @param log - The log of a recorder.
|
|
11863
|
-
* @param options - `wait: false` for an evaluation.
|
|
11864
|
-
* @returns The function `perform` for the replay.
|
|
11865
|
-
*/
|
|
11866
|
-
function replayer(log, options = {}) {
|
|
11867
|
-
const wait = options.wait !== false;
|
|
11868
|
-
const entries = /* @__PURE__ */ new Map();
|
|
11869
|
-
for (const entry of log.effects) entries.set(entry.address, entry);
|
|
11870
|
-
const order = /* @__PURE__ */ new Map();
|
|
11871
|
-
for (const settle of log.settles) {
|
|
11872
|
-
const items = order.get(settle.all);
|
|
11873
|
-
if (items === void 0) order.set(settle.all, [settle.item]);
|
|
11874
|
-
else items.push(settle.item);
|
|
11875
|
-
}
|
|
11876
|
-
const journal = { next: (all, waiting) => {
|
|
11877
|
-
const items = order.get(all);
|
|
11878
|
-
const item = items?.[0];
|
|
11879
|
-
if (items === void 0 || item === void 0 || !waiting.includes(item)) return void 0;
|
|
11880
|
-
items.shift();
|
|
11881
|
-
return item;
|
|
11882
|
-
} };
|
|
11883
|
-
const perform = (effect) => {
|
|
11884
|
-
const entry = effect.address === void 0 ? void 0 : entries.get(effect.address);
|
|
11885
|
-
const wrong = mismatch(effect, entry);
|
|
11886
|
-
if (wrong !== void 0 || entry === void 0) throw wrong;
|
|
11887
|
-
const outcome = entry.outcome;
|
|
11888
|
-
const failure = outcome === void 0 ? replayError(effect, `The log has no outcome for the effect ${effect.kind} at ${effect.address ?? ""}: the recorded run stopped before the answer.`) : outcome.ok ? void 0 : new Failure(outcome.error);
|
|
11889
|
-
const value = outcome?.ok === true ? outcome.value : void 0;
|
|
11890
|
-
if (wait && entry.waited) return failure === void 0 ? Promise.resolve(value) : Promise.reject(failure);
|
|
11891
|
-
if (failure !== void 0) throw failure;
|
|
11892
|
-
return value;
|
|
11893
|
-
};
|
|
11894
|
-
return Object.assign(perform, { [JOURNAL]: journal });
|
|
11895
|
-
}
|
|
11896
|
-
/** This function gives the error of an effect that the log does not have at its address, or `undefined`. */
|
|
11897
|
-
function mismatch(effect, entry) {
|
|
11898
|
-
const at = effect.address ?? "";
|
|
11899
|
-
if (entry === void 0) return replayError(effect, `The log has no effect at the address ${at}, and the run asks for the effect ${effect.kind}.`);
|
|
11900
|
-
if (entry.kind !== effect.kind) return replayError(effect, `The log has the effect ${entry.kind} at the address ${at}, and the run asks for the effect ${effect.kind}.`);
|
|
11901
|
-
if (!docEquals(entry.payload, effect.payload)) return replayError(effect, `The effect ${effect.kind} at the address ${at} has another payload than the log.`);
|
|
11902
|
-
}
|
|
11903
|
-
/** This function gives the exception that carries `#REPLAY!` to the kernel, with the place of the effect. */
|
|
11904
|
-
function replayError(effect, reason) {
|
|
11905
|
-
return new Failure(fail("#REPLAY!", reason, { place: effect.place }).error);
|
|
11906
|
-
}
|
|
11907
|
-
//#endregion
|
|
11908
12268
|
//#region src/providers.ts
|
|
11909
12269
|
/** The deepest schema that `typeFromSchema` reads. A deeper schema reads as `unknown`. */
|
|
11910
12270
|
const MAX_SCHEMA_DEPTH = 256;
|
|
@@ -12215,6 +12575,6 @@ function typeFromSamples(values, options = {}) {
|
|
|
12215
12575
|
return samplesAt(values, options.closed === true, 0);
|
|
12216
12576
|
}
|
|
12217
12577
|
//#endregion
|
|
12218
|
-
export { ATTRIBUTE_RUNGS, CAPS_TYPE, CONSTRUCTS, ENVELOPE_KEYS, Failure, Held, JOURNAL, MergeError, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_DEFINITIONS, STD_DOMAINS, STD_EVALUATORS, STD_EXT, STD_MACHINE, STD_RUNNERS, STD_SYNTAX, STD_WALKERS, STD_WORLD, THING, agree, ask, attributeTrace, capKeys, check, checkLatent, checkSteps, checkWorld, classify, components, conforms, context, cycles, dataEdges, decodeDomain, decodeValue, derefStep, describe, docEquals, durable, edgesOf, effectEdges, effectsOf, effectsOfVerb, encodeValue, entriesAsJson, equationType, evaluate, extendDomain, factsOf, fail, failures, field, fits, flatten, formulaDefinitions, formulaEvaluators, formulaFacts, formulaKinds, formulaKnown, formulaMachine, fromJson, generate, heights, intersectCaps, isAlias, isAllowedOrder, isClosure, isEnvelope, isHostThing, isKey, isKind, isNamespace, isRecord, isRef, isStackOverflow, isTypeRef, join, jsTruthy, kindDocument, kindNamed, kindOf, layers, looksLikeKind, lookupKind, makeContext, mapRefs, member, mergeMachines, mergeWorlds, narrow, nearestWalker, necessary, nodeAt, nodeLayers, ok, parse, partCaps, partsOf, pathMatches, permits, placed, planner, prepare, print, random, recorder, ref, reference, refsOf, replayer, resolve, restrictMachine, restrictWorld, run, sameResult, sameValue, schedule, splitParts, stored, syntaxFormat, tag, thrown, toJson, typeCheck, typeFromSamples, typeFromSchema, typeIssues, typeName, typePath, typeText, typed, typesOf, valueOfThing, verifyReads, walk, worldThing };
|
|
12578
|
+
export { ATTRIBUTE_RUNGS, CAPS_TYPE, CONSTRUCTS, ENVELOPE_KEYS, Failure, Held, JOURNAL, MergeError, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_DEFINITIONS, STD_DOMAINS, STD_EVALUATORS, STD_EXT, STD_MACHINE, STD_RUNNERS, STD_SYNTAX, STD_WALKERS, STD_WORLD, THING, agree, agreeRun, ask, attributeTrace, capKeys, check, checkLatent, checkSteps, checkWorld, classify, components, conforms, context, cycles, dataEdges, decodeDomain, decodeValue, derefStep, describe, docEquals, durable, edgesOf, effectEdges, effectsOf, effectsOfVerb, encodeValue, entriesAsJson, equationType, evaluate, extendDomain, factsOf, fail, failures, field, fits, flatten, formulaDefinitions, formulaEvaluators, formulaFacts, formulaKinds, formulaKnown, formulaMachine, fromJson, generate, heights, intersectCaps, isAlias, isAllowedOrder, isClosure, isEnvelope, isHostThing, isKey, isKind, isNamespace, isRecord, isRef, isStackOverflow, isTypeRef, join, jsTruthy, kindDocument, kindNamed, kindOf, layers, looksLikeKind, lookupKind, makeContext, mapRefs, member, mergeMachines, mergeWorlds, narrow, nearestWalker, necessary, nodeAt, nodeLayers, ok, parse, partCaps, partsOf, pathMatches, permits, placed, planner, prepare, print, random, recorder, ref, reference, referenceRun, refsOf, replayer, resolve, restrictMachine, restrictWorld, run, sameResult, sameValue, schedule, splitParts, stored, syntaxFormat, tag, thrown, toJson, typeCheck, typeFromSamples, typeFromSchema, typeIssues, typeName, typePath, typeText, typed, typesOf, valueOfThing, verifyReads, walk, worldThing };
|
|
12219
12579
|
|
|
12220
12580
|
//# sourceMappingURL=index.js.map
|