@mark1russell7/coeval 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.d.ts +471 -190
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1209 -182
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/agree.ts +175 -7
- package/src/deref.ts +27 -9
- package/src/evaluate.ts +258 -12
- package/src/formula.ts +17 -3
- package/src/index.ts +12 -2
- package/src/meaning.ts +472 -0
- package/src/reads.ts +9 -2
- package/src/reference.ts +247 -42
- package/src/schedule.ts +14 -4
- package/src/stdlib.ts +26 -4
package/build/index.js
CHANGED
|
@@ -2379,14 +2379,25 @@ function resolveFully$1(nav, ctx, path, hops, known) {
|
|
|
2379
2379
|
const walker = given === void 0 ? walkerOf(nav, step) : given ?? void 0;
|
|
2380
2380
|
if (walker === void 0) return failed("#STEP?", "A step is a key, or a document of a kind with the verb step.");
|
|
2381
2381
|
const envelope = step;
|
|
2382
|
-
const
|
|
2382
|
+
const deref = (thing, key) => {
|
|
2383
|
+
const reached = derefStep(nav, thing, key);
|
|
2384
|
+
return reached === void 0 ? void 0 : adopt(reached.thing);
|
|
2385
|
+
};
|
|
2386
|
+
const result = walker(hops === void 0 ? {
|
|
2383
2387
|
data: envelope.value,
|
|
2384
2388
|
here,
|
|
2385
2389
|
context: ctx,
|
|
2386
|
-
deref
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
+
deref
|
|
2391
|
+
} : {
|
|
2392
|
+
data: envelope.value,
|
|
2393
|
+
here,
|
|
2394
|
+
context: ctx,
|
|
2395
|
+
deref,
|
|
2396
|
+
visit: (key, thing) => hops.push({
|
|
2397
|
+
step: key,
|
|
2398
|
+
thing,
|
|
2399
|
+
by: "step"
|
|
2400
|
+
})
|
|
2390
2401
|
});
|
|
2391
2402
|
if (!result.ok) return failed(result.code ?? "#N/A", result.reason);
|
|
2392
2403
|
here = adopt(result.thing);
|
|
@@ -3082,6 +3093,88 @@ const NO_ANSWER = {
|
|
|
3082
3093
|
/** The options of a resolution without a record of hops, and with one. One object of each serves each call. */
|
|
3083
3094
|
const WITHOUT_HOPS = /* @__PURE__ */ Object.freeze({ hops: false });
|
|
3084
3095
|
const WITH_HOPS = /* @__PURE__ */ Object.freeze({ hops: true });
|
|
3096
|
+
/**
|
|
3097
|
+
* This function gives the path of a kept read (READS.CLOSURE). The evaluation did not ask for the read. Thus the read
|
|
3098
|
+
* does not step into a host thing with `deref`. A host thing with `known` answers a step without new work
|
|
3099
|
+
* (SCHED.CLOSURE). Each other host thing ends the path, and a host thing that does not know the step ends it too.
|
|
3100
|
+
* A reference in the data reads the context of its reader, thus the function then gives `undefined`.
|
|
3101
|
+
*
|
|
3102
|
+
* @param nav - The world and the code of the evaluators.
|
|
3103
|
+
* @param root - The context at the root of the evaluation.
|
|
3104
|
+
* @param path - The root and the keys of the static read.
|
|
3105
|
+
* @returns The path of the kept read with what it reached after a step of `known`, or `undefined` for a reference on the way.
|
|
3106
|
+
*/
|
|
3107
|
+
function keptPath(nav, root, path) {
|
|
3108
|
+
const adopt = nav.adopt ?? ((value) => value);
|
|
3109
|
+
let here = adopt(root[path[0]]);
|
|
3110
|
+
let known = false;
|
|
3111
|
+
const end = (index) => {
|
|
3112
|
+
const kept = index >= path.length ? path : path.slice(0, index);
|
|
3113
|
+
return known ? {
|
|
3114
|
+
path: kept,
|
|
3115
|
+
reached: ok(here)
|
|
3116
|
+
} : { path: kept };
|
|
3117
|
+
};
|
|
3118
|
+
for (let index = 1;; index += 1) {
|
|
3119
|
+
if (isHostThing(here)) {
|
|
3120
|
+
if (index >= path.length || here.known === void 0) return end(index);
|
|
3121
|
+
let step;
|
|
3122
|
+
try {
|
|
3123
|
+
step = here.known(path[index]);
|
|
3124
|
+
} catch (error) {
|
|
3125
|
+
return {
|
|
3126
|
+
path: path.slice(0, index + 1),
|
|
3127
|
+
reached: thrown(error, READ_FAILED)
|
|
3128
|
+
};
|
|
3129
|
+
}
|
|
3130
|
+
if (step === void 0) return end(index);
|
|
3131
|
+
known = true;
|
|
3132
|
+
here = adopt(step.thing);
|
|
3133
|
+
continue;
|
|
3134
|
+
}
|
|
3135
|
+
if (nav.plain !== true && isRef(here)) return void 0;
|
|
3136
|
+
if (index >= path.length) return end(index);
|
|
3137
|
+
const reached = derefStep(nav, here, path[index]);
|
|
3138
|
+
if (reached === void 0) return known ? end(index) : { path };
|
|
3139
|
+
here = adopt(reached.thing);
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3142
|
+
/**
|
|
3143
|
+
* This function resolves the path of a kept read again, for `verifyReads` (SCHED.CLOSURE). It walks the path as
|
|
3144
|
+
* `keptPath` did. A host thing with `known` answers its step. A path without such a step resolves with `readAgain`.
|
|
3145
|
+
*
|
|
3146
|
+
* @param nav - The world and the code of the evaluators.
|
|
3147
|
+
* @param ctx - The new context.
|
|
3148
|
+
* @param path - The path of the kept read.
|
|
3149
|
+
* @returns What the path reaches, or `undefined` when a host does not know a step yet. Then the read changed.
|
|
3150
|
+
*/
|
|
3151
|
+
function keptAgain(nav, ctx, path) {
|
|
3152
|
+
const adopt = nav.adopt ?? ((value) => value);
|
|
3153
|
+
let here = adopt(ctx[path[0]]);
|
|
3154
|
+
let known = false;
|
|
3155
|
+
const other = () => known ? void 0 : readAgain(nav, ctx, path, false).reached;
|
|
3156
|
+
for (let index = 1; index < path.length; index += 1) {
|
|
3157
|
+
if (isHostThing(here)) {
|
|
3158
|
+
if (here.known === void 0) return other();
|
|
3159
|
+
let step;
|
|
3160
|
+
try {
|
|
3161
|
+
step = here.known(path[index]);
|
|
3162
|
+
} catch (error) {
|
|
3163
|
+
return thrown(error, READ_FAILED);
|
|
3164
|
+
}
|
|
3165
|
+
if (step === void 0) return void 0;
|
|
3166
|
+
known = true;
|
|
3167
|
+
here = adopt(step.thing);
|
|
3168
|
+
continue;
|
|
3169
|
+
}
|
|
3170
|
+
if (nav.plain !== true && isRef(here)) return other();
|
|
3171
|
+
const reached = derefStep(nav, here, path[index]);
|
|
3172
|
+
if (reached === void 0) return other();
|
|
3173
|
+
here = adopt(reached.thing);
|
|
3174
|
+
}
|
|
3175
|
+
if (!known) return other();
|
|
3176
|
+
return nav.plain !== true && isRef(here) ? void 0 : ok(here);
|
|
3177
|
+
}
|
|
3085
3178
|
/** This function makes the record of the reads of an evaluation, when the host gives a list. */
|
|
3086
3179
|
function readingOf(list, root) {
|
|
3087
3180
|
return list === void 0 ? void 0 : {
|
|
@@ -3188,13 +3281,46 @@ var Session = class {
|
|
|
3188
3281
|
if (compiled !== void 0 && !hops && compiled.nav === this.machine) return compiled.resolve(ctx);
|
|
3189
3282
|
return resolve(this.machine, ctx, path, hops ? WITH_HOPS : WITHOUT_HOPS);
|
|
3190
3283
|
}
|
|
3191
|
-
keepsContext() {
|
|
3284
|
+
keepsContext(ctx, reads) {
|
|
3192
3285
|
const reading = this.reading;
|
|
3193
3286
|
if (reading === void 0 || reading.kept) return;
|
|
3287
|
+
const known = ctx === void 0 ? void 0 : reads?.();
|
|
3288
|
+
if (ctx !== void 0 && known !== void 0 && known.every((read) => this.keepRead(reading, ctx, read))) return;
|
|
3194
3289
|
reading.kept = true;
|
|
3195
3290
|
this.keep(reading, reading.self ? ROOTS.filter((root) => root !== "self") : ROOTS);
|
|
3196
3291
|
}
|
|
3197
3292
|
/**
|
|
3293
|
+
* This function adds the kept read of one static read of a closure (READS.CLOSURE). The read resolves from the
|
|
3294
|
+
* context at the root, one time in an evaluation. A name that a kind binds in the kept context gives no read,
|
|
3295
|
+
* because the reads of its binding are already in the list.
|
|
3296
|
+
*
|
|
3297
|
+
* @returns `false` when the path meets a reference in the data. Then the closure keeps each whole root.
|
|
3298
|
+
*/
|
|
3299
|
+
keepRead(reading, ctx, read) {
|
|
3300
|
+
const root = read.root;
|
|
3301
|
+
const exact = [root, ...read.keys];
|
|
3302
|
+
if (!isFree(reading.root, ctx, exact)) {
|
|
3303
|
+
if (root === "self" && !reading.self) this.keep(reading, ["self"]);
|
|
3304
|
+
return true;
|
|
3305
|
+
}
|
|
3306
|
+
if (root === "self" && reading.self) return true;
|
|
3307
|
+
const walk = keptPath(this.machine, reading.root, read.exact ? exact : [root]);
|
|
3308
|
+
if (walk === void 0) return false;
|
|
3309
|
+
const path = walk.path;
|
|
3310
|
+
const paths = reading.paths ??= /* @__PURE__ */ new Set();
|
|
3311
|
+
const text = JSON.stringify(path);
|
|
3312
|
+
if (paths.has(text) || paths.has(JSON.stringify([root]))) return true;
|
|
3313
|
+
paths.add(text);
|
|
3314
|
+
if (path.length === 1 && root === "self") reading.self = true;
|
|
3315
|
+
reading.list.push({
|
|
3316
|
+
path,
|
|
3317
|
+
reached: walk.reached ?? readAgain(this.machine, reading.root, path, false).reached,
|
|
3318
|
+
free: true,
|
|
3319
|
+
kept: true
|
|
3320
|
+
});
|
|
3321
|
+
return true;
|
|
3322
|
+
}
|
|
3323
|
+
/**
|
|
3198
3324
|
* This function adds a read of each whole root to the list, from the context at the root (READS.KEPT). An exception
|
|
3199
3325
|
* of the host gives the error value of the read, because the evaluation did not ask for the read.
|
|
3200
3326
|
*/
|
|
@@ -3680,7 +3806,10 @@ function compileNode(c, value, place, declared, depth, address = false) {
|
|
|
3680
3806
|
if (frees === void 0 || kind === void 0 || !pureCode(c, envelope, kind, typePath, name, quick)) return done;
|
|
3681
3807
|
const free = freeOf(doc, kind, split.parts, frees);
|
|
3682
3808
|
if (free === void 0) return done;
|
|
3683
|
-
|
|
3809
|
+
if (free.length === 0) return folded(c, done, void 0);
|
|
3810
|
+
const fixed = c.fixed;
|
|
3811
|
+
if (fixed !== void 0 && free.every((each) => Object.hasOwn(fixed, each))) return folded(c, done, free);
|
|
3812
|
+
return {
|
|
3684
3813
|
code: done.code,
|
|
3685
3814
|
run: done.run,
|
|
3686
3815
|
free
|
|
@@ -3760,6 +3889,21 @@ var Probe = class {
|
|
|
3760
3889
|
};
|
|
3761
3890
|
this.root = root;
|
|
3762
3891
|
}
|
|
3892
|
+
/** The context of a part that folds with fixed names: the context of the probe, with the fixed names (PLAN.FIXED). */
|
|
3893
|
+
withFixed;
|
|
3894
|
+
/**
|
|
3895
|
+
* This function gives the context of a part that folds with fixed names. Its root `names` holds only the fixed
|
|
3896
|
+
* names, thus the probe resolves each fixed name and refuses each other name.
|
|
3897
|
+
*
|
|
3898
|
+
* @param fixed - The fixed names and their values.
|
|
3899
|
+
* @returns The context.
|
|
3900
|
+
*/
|
|
3901
|
+
fixedRoot(fixed) {
|
|
3902
|
+
return this.withFixed ??= {
|
|
3903
|
+
...this.root,
|
|
3904
|
+
names: fixed
|
|
3905
|
+
};
|
|
3906
|
+
}
|
|
3763
3907
|
resolve(ctx, path) {
|
|
3764
3908
|
const name = path[1];
|
|
3765
3909
|
if (path[0] === "names" && isKey(name) && Object.hasOwn(ctx.names, String(name))) {
|
|
@@ -3818,13 +3962,14 @@ function trap(refuse, copies) {
|
|
|
3818
3962
|
* one deeply frozen copy, and the code gives that copy at each evaluation (PLAN.SHARED). A fold that the probe cancels
|
|
3819
3963
|
* gives the code without free names, thus no parent tries the fold again.
|
|
3820
3964
|
*/
|
|
3821
|
-
function folded(c, done) {
|
|
3965
|
+
function folded(c, done, fixed) {
|
|
3822
3966
|
const probe = c.probe ??= new Probe(c.machine);
|
|
3823
3967
|
probe.refused = false;
|
|
3824
3968
|
probe.reads = 0;
|
|
3825
3969
|
let constant;
|
|
3826
3970
|
try {
|
|
3827
|
-
const
|
|
3971
|
+
const root = fixed === void 0 ? probe.root : probe.fixedRoot(c.fixed);
|
|
3972
|
+
const result = done.code(root, probe);
|
|
3828
3973
|
if (!probe.refused && (result.ok || result.error.code !== "#DEPTH!")) constant = sharedCopy(result);
|
|
3829
3974
|
} catch {
|
|
3830
3975
|
constant = void 0;
|
|
@@ -3834,18 +3979,100 @@ function folded(c, done) {
|
|
|
3834
3979
|
run: done.run
|
|
3835
3980
|
};
|
|
3836
3981
|
const value = constant;
|
|
3982
|
+
const original = done.code;
|
|
3983
|
+
if (fixed !== void 0) return {
|
|
3984
|
+
code: fixedCode(fixed, c.fixed, value, original),
|
|
3985
|
+
run: void 0,
|
|
3986
|
+
free: fixed
|
|
3987
|
+
};
|
|
3837
3988
|
if (probe.reads === 0) return {
|
|
3838
3989
|
code: () => value,
|
|
3839
3990
|
run: void 0,
|
|
3840
3991
|
free: CLOSED
|
|
3841
3992
|
};
|
|
3842
|
-
const original = done.code;
|
|
3843
3993
|
return {
|
|
3844
3994
|
code: (ctx, rt) => rt instanceof Session && !rt.traced && !rt.recording ? value : original(ctx, rt),
|
|
3845
3995
|
run: void 0,
|
|
3846
3996
|
free: CLOSED
|
|
3847
3997
|
};
|
|
3848
3998
|
}
|
|
3999
|
+
/**
|
|
4000
|
+
* This function gives the code of a part that folded with fixed names (PLAN.FIXED, D60). The code gives the folded
|
|
4001
|
+
* value when the context has the fixed value of each name, by an own-key test and `Object.is`. A fixed value is
|
|
4002
|
+
* deeply frozen, thus the same object gives the same result. Each other context, a list of reads and another runtime
|
|
4003
|
+
* get the code without the fold. The probe of a parent fold gets the folded value, and it counts a read.
|
|
4004
|
+
*/
|
|
4005
|
+
function fixedCode(names, fixed, value, original) {
|
|
4006
|
+
if (names.length === 1) {
|
|
4007
|
+
const name = names[0];
|
|
4008
|
+
const want = fixed[name];
|
|
4009
|
+
return (ctx, rt) => {
|
|
4010
|
+
const given = ctx.names;
|
|
4011
|
+
return Object.hasOwn(given, name) && Object.is(given[name], want) && folds(rt) ? value : original(ctx, rt);
|
|
4012
|
+
};
|
|
4013
|
+
}
|
|
4014
|
+
const wants = names.map((name) => fixed[name]);
|
|
4015
|
+
return (ctx, rt) => {
|
|
4016
|
+
const given = ctx.names;
|
|
4017
|
+
for (let index = 0; index < names.length; index += 1) {
|
|
4018
|
+
const name = names[index];
|
|
4019
|
+
if (!Object.hasOwn(given, name) || !Object.is(given[name], wants[index])) return original(ctx, rt);
|
|
4020
|
+
}
|
|
4021
|
+
return folds(rt) ? value : original(ctx, rt);
|
|
4022
|
+
};
|
|
4023
|
+
}
|
|
4024
|
+
/**
|
|
4025
|
+
* This function tells if the code of a part with fixed names can give its folded value in a runtime. The probe of a
|
|
4026
|
+
* fold counts the read of the part, as the code without the fold reads the names.
|
|
4027
|
+
*/
|
|
4028
|
+
function folds(rt) {
|
|
4029
|
+
if (rt instanceof Session) return !rt.recording;
|
|
4030
|
+
if (!(rt instanceof Probe)) return false;
|
|
4031
|
+
rt.reads += 1;
|
|
4032
|
+
return true;
|
|
4033
|
+
}
|
|
4034
|
+
/**
|
|
4035
|
+
* This function gives the fixed names of the options of `prepare` (PLAN.FIXED). A name fixes only when its value is
|
|
4036
|
+
* deeply frozen plain data. Then no change in place can make the folded value stale.
|
|
4037
|
+
*
|
|
4038
|
+
* @returns A record without a prototype, or `undefined` when no name fixes.
|
|
4039
|
+
*/
|
|
4040
|
+
function fixedNamesOf(given) {
|
|
4041
|
+
if (given === void 0) return void 0;
|
|
4042
|
+
let out;
|
|
4043
|
+
for (const name of Object.keys(given)) {
|
|
4044
|
+
const value = given[name];
|
|
4045
|
+
if (!frozenPlain(value)) continue;
|
|
4046
|
+
out ??= Object.create(null);
|
|
4047
|
+
Object.defineProperty(out, name, {
|
|
4048
|
+
value,
|
|
4049
|
+
enumerable: true
|
|
4050
|
+
});
|
|
4051
|
+
}
|
|
4052
|
+
return out;
|
|
4053
|
+
}
|
|
4054
|
+
/** This function tells if a value is plain data, as `sharedCopy` reads it, and if each of its objects is frozen. */
|
|
4055
|
+
function frozenPlain(value) {
|
|
4056
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4057
|
+
const walk = (here) => {
|
|
4058
|
+
if (typeof here === "function") return false;
|
|
4059
|
+
if (typeof here !== "object" || here === null || SHARED.has(here) || seen.has(here)) return true;
|
|
4060
|
+
if (!Object.isFrozen(here)) return false;
|
|
4061
|
+
seen.add(here);
|
|
4062
|
+
const proto = Object.getPrototypeOf(here);
|
|
4063
|
+
if (Array.isArray(here) ? proto !== Array.prototype : proto !== Object.prototype && proto !== null) return false;
|
|
4064
|
+
for (const key of Reflect.ownKeys(here)) {
|
|
4065
|
+
const field = Object.getOwnPropertyDescriptor(here, key);
|
|
4066
|
+
if (typeof key !== "string" || field === void 0 || !("value" in field) || !walk(field.value)) return false;
|
|
4067
|
+
}
|
|
4068
|
+
return true;
|
|
4069
|
+
};
|
|
4070
|
+
try {
|
|
4071
|
+
return walk(value);
|
|
4072
|
+
} catch {
|
|
4073
|
+
return false;
|
|
4074
|
+
}
|
|
4075
|
+
}
|
|
3849
4076
|
/** The objects that a fold made. Each one is deeply frozen, thus a later fold uses it again and does not copy it. */
|
|
3850
4077
|
const SHARED = /* @__PURE__ */ new WeakSet();
|
|
3851
4078
|
/** The mark of a value that is not plain data. */
|
|
@@ -3853,7 +4080,11 @@ const NOT_PLAIN = /* @__PURE__ */ Symbol("not plain");
|
|
|
3853
4080
|
/**
|
|
3854
4081
|
* This function gives a deeply frozen copy of plain data, or `undefined` for other values. Plain data is a primitive or
|
|
3855
4082
|
* an array without holes and extra keys. It is also a record of enumerable data fields with the prototype of an object
|
|
3856
|
-
* or no prototype. The copy keeps cycles and shared objects. The objects of the host do not change.
|
|
4083
|
+
* or no prototype. The copy keeps cycles and shared objects. The objects of the host do not change. The tier `fixed`
|
|
4084
|
+
* of `agree` uses it. The package does not export it.
|
|
4085
|
+
*
|
|
4086
|
+
* @param value - The value.
|
|
4087
|
+
* @returns The frozen copy, or `undefined`.
|
|
3857
4088
|
*/
|
|
3858
4089
|
function sharedCopy(value) {
|
|
3859
4090
|
try {
|
|
@@ -4091,13 +4322,16 @@ function prepare(machine, doc, options = {}) {
|
|
|
4091
4322
|
steps: []
|
|
4092
4323
|
};
|
|
4093
4324
|
const traced = options.trace === true;
|
|
4325
|
+
const fold = !traced && options.fold !== false;
|
|
4326
|
+
const fixed = fold ? fixedNamesOf(options.fixed?.names) : void 0;
|
|
4094
4327
|
const c = {
|
|
4095
4328
|
machine,
|
|
4096
4329
|
traced,
|
|
4097
4330
|
maxDepth: machine.maxDepth ?? 1e3,
|
|
4098
4331
|
verb,
|
|
4099
4332
|
noAnswer,
|
|
4100
|
-
fold
|
|
4333
|
+
fold,
|
|
4334
|
+
fixed
|
|
4101
4335
|
};
|
|
4102
4336
|
let root;
|
|
4103
4337
|
try {
|
|
@@ -4876,6 +5110,17 @@ function treeThing(input, build, outer) {
|
|
|
4876
5110
|
function attributeTrace(node, attribute) {
|
|
4877
5111
|
return stateOf(node)?.trace(attribute);
|
|
4878
5112
|
}
|
|
5113
|
+
/**
|
|
5114
|
+
* This function gives the place of a node of the decorated tree: the keys from the root document to the document of
|
|
5115
|
+
* the node (HOP.PLACE). A view puts each hop of a resolution on its node with it. The node keeps its place already,
|
|
5116
|
+
* thus the function adds no record.
|
|
5117
|
+
*
|
|
5118
|
+
* @param thing - A thing, for example the thing of a hop.
|
|
5119
|
+
* @returns The keys of the place, or `undefined` for a thing that is not a node of the decorated tree.
|
|
5120
|
+
*/
|
|
5121
|
+
function placeOf(thing) {
|
|
5122
|
+
return stateOf(thing)?.place;
|
|
5123
|
+
}
|
|
4879
5124
|
/** The path of the root `self`. A node of the tree resolves it, thus the list of reads holds the read (C4). */
|
|
4880
5125
|
const SELF_PATH = /* @__PURE__ */ Object.freeze(["self"]);
|
|
4881
5126
|
/** The options of the resolution of `self`: the node needs no hops. */
|
|
@@ -4904,21 +5149,24 @@ function hostType(node, wanted) {
|
|
|
4904
5149
|
/**
|
|
4905
5150
|
* This function makes a walker `nearest`. The walker follows a key (the default is `parent`) until the node that it
|
|
4906
5151
|
* reaches has the wanted type. With `self: true`, the node where the walk starts is the first candidate. A node
|
|
4907
|
-
* without that key ends the walk with an error.
|
|
5152
|
+
* without that key ends the walk with an error. With `eachStep`, a resolution with hops gets a hop for each node on the way.
|
|
4908
5153
|
*
|
|
4909
|
-
* @param options - The test of a type and the
|
|
5154
|
+
* @param options - The test of a type, the most hops and the record of each step.
|
|
4910
5155
|
* @returns The walker.
|
|
4911
5156
|
*/
|
|
4912
5157
|
function nearestWalker(options = {}) {
|
|
4913
5158
|
const matches = options.matches ?? hostType;
|
|
4914
5159
|
const maxHops = options.maxHops ?? 256;
|
|
4915
|
-
|
|
5160
|
+
const eachStep = options.eachStep === true;
|
|
5161
|
+
return ({ data, here, deref, visit }) => {
|
|
4916
5162
|
const follow = isRecord(data) && typeof field(data, "follow") === "string" ? String(field(data, "follow")) : "parent";
|
|
4917
5163
|
const wanted = isRecord(data) ? field(data, "type") : void 0;
|
|
4918
5164
|
const fromSelf = isRecord(data) && field(data, "self") === true;
|
|
5165
|
+
const record = eachStep ? visit : void 0;
|
|
4919
5166
|
let at = here;
|
|
4920
5167
|
for (let hops = 0; hops < maxHops; hops += 1) {
|
|
4921
|
-
|
|
5168
|
+
const stepped = hops > 0 || !fromSelf;
|
|
5169
|
+
if (stepped) at = at === void 0 || at === null ? void 0 : deref(at, follow);
|
|
4922
5170
|
if (at === void 0 || at === null) return {
|
|
4923
5171
|
ok: false,
|
|
4924
5172
|
reason: `No node of the type ${String(wanted)} is on the way.`
|
|
@@ -4927,6 +5175,7 @@ function nearestWalker(options = {}) {
|
|
|
4927
5175
|
ok: true,
|
|
4928
5176
|
thing: at
|
|
4929
5177
|
};
|
|
5178
|
+
if (stepped) record?.(follow, at);
|
|
4930
5179
|
}
|
|
4931
5180
|
return {
|
|
4932
5181
|
ok: false,
|
|
@@ -7539,7 +7788,7 @@ function formulaCode(profile) {
|
|
|
7539
7788
|
}, rt);
|
|
7540
7789
|
};
|
|
7541
7790
|
}),
|
|
7542
|
-
"formula.fn": compiled(({ value, data }) => {
|
|
7791
|
+
"formula.fn": compiled(({ value, data, doc, machine }) => {
|
|
7543
7792
|
const params = isRecord(data) ? data["params"] : void 0;
|
|
7544
7793
|
if (!Array.isArray(params) || !params.every((param) => typeof param === "string") || new Set(params).size !== params.length) {
|
|
7545
7794
|
const bad = fail("#VALUE!", "The params of fn are a list of different texts.");
|
|
@@ -7547,8 +7796,19 @@ function formulaCode(profile) {
|
|
|
7547
7796
|
}
|
|
7548
7797
|
const names = params;
|
|
7549
7798
|
const body = partsOf$1(value)["body"];
|
|
7799
|
+
let known = null;
|
|
7800
|
+
const reads = () => {
|
|
7801
|
+
if (known !== null) return known;
|
|
7802
|
+
try {
|
|
7803
|
+
const facts = factsOf(machine, doc);
|
|
7804
|
+
known = facts.pure ? facts.reads : void 0;
|
|
7805
|
+
} catch {
|
|
7806
|
+
known = void 0;
|
|
7807
|
+
}
|
|
7808
|
+
return known;
|
|
7809
|
+
};
|
|
7550
7810
|
return (ctx, rt) => {
|
|
7551
|
-
rt.keepsContext?.();
|
|
7811
|
+
rt.keepsContext?.(ctx, reads);
|
|
7552
7812
|
const closure = (...args) => {
|
|
7553
7813
|
const scope = { ...ctx.names };
|
|
7554
7814
|
names.forEach((name, index) => setOwn$1(scope, name, args[index]));
|
|
@@ -8762,6 +9022,42 @@ function resolveFully(machine, ctx, path, options) {
|
|
|
8762
9022
|
* mock, the effect gives its payload (RUN.EVAL).
|
|
8763
9023
|
*/
|
|
8764
9024
|
function cause(env, ctx, kind, payload, place) {
|
|
9025
|
+
const effect = recordEffect(env, ctx, kind, payload, place);
|
|
9026
|
+
if ("ok" in effect) return effect;
|
|
9027
|
+
effect.outcome = answer(env.perform, effect, place);
|
|
9028
|
+
return effect.outcome;
|
|
9029
|
+
}
|
|
9030
|
+
/**
|
|
9031
|
+
* This function causes the effect of an evaluator. In a run, the host does each effect, thus only a runner can cause
|
|
9032
|
+
* one. The effect then gives `#CALC!` before the test of its capability (RUN.NEEDS-RUNNER).
|
|
9033
|
+
*/
|
|
9034
|
+
function causeFromEvaluator(env, ctx, kind, payload, place) {
|
|
9035
|
+
const text = typeof kind === "string" ? kind : kind.join(".");
|
|
9036
|
+
if (env.running) return fail("#CALC!", `The effect ${text} needs a runner: in a run, the host does each effect.`, { place });
|
|
9037
|
+
return cause(env, ctx, kind, payload, place);
|
|
9038
|
+
}
|
|
9039
|
+
/**
|
|
9040
|
+
* This function causes the effect of a runner. In an evaluation, the mock answers it at once. In a run, the host does
|
|
9041
|
+
* it, and a promise from the host makes the steps wait (RUN.SEQ). An effect whose wait a stop ends keeps no outcome
|
|
9042
|
+
* (RUN.OUTCOME).
|
|
9043
|
+
*/
|
|
9044
|
+
function* causeAndWait(env, ctx, kind, payload, place) {
|
|
9045
|
+
if (!env.running) return cause(env, ctx, kind, payload, place);
|
|
9046
|
+
const effect = recordEffect(env, ctx, kind, payload, place);
|
|
9047
|
+
if ("ok" in effect) return effect;
|
|
9048
|
+
const perform = env.perform;
|
|
9049
|
+
if (perform === void 0) return effect.outcome = ok(payload);
|
|
9050
|
+
let given;
|
|
9051
|
+
try {
|
|
9052
|
+
given = perform(effect);
|
|
9053
|
+
} catch (error) {
|
|
9054
|
+
return effect.outcome = hostError(effect, place, error);
|
|
9055
|
+
}
|
|
9056
|
+
if (!(given instanceof Promise)) return effect.outcome = ok(given);
|
|
9057
|
+
return effect.outcome = yield given.then((value) => ok(value), (error) => hostError(effect, place, error));
|
|
9058
|
+
}
|
|
9059
|
+
/** This function checks the capability of an effect, and records the effect with its address (RUN.CAP, RUN.ADDRESS). */
|
|
9060
|
+
function recordEffect(env, ctx, kind, payload, place) {
|
|
8765
9061
|
const keys = typeof kind === "string" ? kind.split(".") : kind;
|
|
8766
9062
|
const text = keys.join(".");
|
|
8767
9063
|
const machine = env.machine;
|
|
@@ -8781,8 +9077,18 @@ function cause(env, ctx, kind, payload, place) {
|
|
|
8781
9077
|
address: `${env.scope.prefix}${at}#${count}`
|
|
8782
9078
|
};
|
|
8783
9079
|
env.effects.push(effect);
|
|
8784
|
-
|
|
8785
|
-
|
|
9080
|
+
return effect;
|
|
9081
|
+
}
|
|
9082
|
+
/** This function gives the outcome of an effect that the host failed. A `Failure` gives its own error value (RUN.CAUSE). */
|
|
9083
|
+
function hostError(effect, place, error) {
|
|
9084
|
+
if (error instanceof Failure) return placed({
|
|
9085
|
+
ok: false,
|
|
9086
|
+
error: error.error
|
|
9087
|
+
}, place);
|
|
9088
|
+
return fail("#CALC!", `The host failed the effect ${effect.kind}: ${describe(error)}`, {
|
|
9089
|
+
place,
|
|
9090
|
+
cause: error
|
|
9091
|
+
});
|
|
8786
9092
|
}
|
|
8787
9093
|
/**
|
|
8788
9094
|
* This function gives the answer of the mock to an effect. A throw gives `#CALC!` with the cause, and a `Failure`
|
|
@@ -8794,14 +9100,7 @@ function answer(perform, effect, place) {
|
|
|
8794
9100
|
try {
|
|
8795
9101
|
given = perform(effect);
|
|
8796
9102
|
} catch (error) {
|
|
8797
|
-
|
|
8798
|
-
ok: false,
|
|
8799
|
-
error: error.error
|
|
8800
|
-
}, place);
|
|
8801
|
-
return fail("#CALC!", `The host failed the effect ${effect.kind}: ${describe(error)}`, {
|
|
8802
|
-
place,
|
|
8803
|
-
cause: error
|
|
8804
|
-
});
|
|
9103
|
+
return hostError(effect, place, error);
|
|
8805
9104
|
}
|
|
8806
9105
|
if (!(given instanceof Promise)) return ok(given);
|
|
8807
9106
|
given.catch(() => void 0);
|
|
@@ -8872,7 +9171,7 @@ function evalInput(env, seen, at) {
|
|
|
8872
9171
|
machine: env.machine,
|
|
8873
9172
|
...at.address ? { address: true } : {},
|
|
8874
9173
|
resolve: (path, options) => resolveFully(env.machine, at.ctx, path, options),
|
|
8875
|
-
effect: (kind, payload) =>
|
|
9174
|
+
effect: (kind, payload) => causeFromEvaluator(env, at.ctx, kind, payload, at.place),
|
|
8876
9175
|
fill
|
|
8877
9176
|
};
|
|
8878
9177
|
}
|
|
@@ -8899,67 +9198,116 @@ function runInput(env, seen, at) {
|
|
|
8899
9198
|
place: at.place,
|
|
8900
9199
|
machine: env.machine,
|
|
8901
9200
|
resolve: (path, options) => resolveFully(env.machine, at.ctx, path, options),
|
|
8902
|
-
effect:
|
|
8903
|
-
return cause(env, at.ctx, kind, payload, at.place);
|
|
8904
|
-
},
|
|
9201
|
+
effect: (kind, payload) => causeAndWait(env, at.ctx, kind, payload, at.place),
|
|
8905
9202
|
all: (list, options) => together(env, list, options),
|
|
8906
9203
|
fill
|
|
8907
9204
|
};
|
|
8908
9205
|
}
|
|
8909
9206
|
/**
|
|
8910
9207
|
* This function evaluates parts for `all` (RUN.ALL). The parts start in their order, up to the limit, and each part
|
|
8911
|
-
* goes on until it ends or waits.
|
|
8912
|
-
*
|
|
8913
|
-
*
|
|
9208
|
+
* goes on until it ends or waits. A part that waits keeps its place in the limit. With `failFast`, the first failure
|
|
9209
|
+
* stops the parts that wait, and the later parts do not start (RUN.FAIL-FAST).
|
|
9210
|
+
*
|
|
9211
|
+
* When a part waits, the steps wait for the next part that settles. The journal of the host can choose that part
|
|
9212
|
+
* (RUN.REPLAY), and it gets each part that settled (RUN.RECORD). In an evaluation, the drive stops at the first wait
|
|
9213
|
+
* and gives `#CALC!`. In a run, the host settles the wait, and the part goes on in its own scope.
|
|
8914
9214
|
*
|
|
8915
9215
|
* Each part has its own scope of addresses. Its prefix names the use of `all` in the scope around it, and the part
|
|
8916
9216
|
* (RUN.ADDRESS).
|
|
8917
9217
|
*/
|
|
8918
9218
|
function* together(env, list, options = {}) {
|
|
8919
9219
|
const limit = Math.max(1, options.concurrency ?? list.length);
|
|
9220
|
+
const failFast = options.failFast === true;
|
|
8920
9221
|
const results = list.map(() => void 0);
|
|
9222
|
+
const scopes = [];
|
|
8921
9223
|
const waiting = [];
|
|
8922
9224
|
const outer = env.scope;
|
|
8923
9225
|
const address = `${outer.prefix}all${outer.alls}`;
|
|
8924
9226
|
outer.alls += 1;
|
|
8925
9227
|
let failed = false;
|
|
8926
|
-
|
|
8927
|
-
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
|
|
9228
|
+
let started = 0;
|
|
9229
|
+
const end = (index, result) => {
|
|
9230
|
+
results[index] = result;
|
|
9231
|
+
if (failFast && !result.ok) failed = true;
|
|
9232
|
+
};
|
|
9233
|
+
/** This function gives a part its input in its own scope, and keeps it in the list when it waits again. */
|
|
9234
|
+
const goOn = (index, steps, input) => {
|
|
9235
|
+
const around = env.scope;
|
|
9236
|
+
env.scope = scopes[index];
|
|
9237
|
+
let step;
|
|
8933
9238
|
try {
|
|
8934
|
-
|
|
8935
|
-
const step = steps.next();
|
|
8936
|
-
if (step.done === true) result = step.value;
|
|
8937
|
-
else waiting.push({
|
|
8938
|
-
steps,
|
|
8939
|
-
promise: step.value
|
|
8940
|
-
});
|
|
9239
|
+
step = input.threw ? steps.throw(input.error) : steps.next(input.value);
|
|
8941
9240
|
} catch (error) {
|
|
8942
|
-
|
|
9241
|
+
end(index, thrown(error, "a part"));
|
|
9242
|
+
return;
|
|
8943
9243
|
} finally {
|
|
8944
|
-
env.scope =
|
|
9244
|
+
env.scope = around;
|
|
8945
9245
|
}
|
|
8946
|
-
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
8950
|
-
const
|
|
8951
|
-
|
|
8952
|
-
|
|
8953
|
-
|
|
8954
|
-
|
|
9246
|
+
if (step.done === true) {
|
|
9247
|
+
end(index, step.value);
|
|
9248
|
+
return;
|
|
9249
|
+
}
|
|
9250
|
+
const waiter = {
|
|
9251
|
+
index,
|
|
9252
|
+
steps,
|
|
9253
|
+
next: Promise.resolve()
|
|
9254
|
+
};
|
|
9255
|
+
waiter.next = step.value.then((value) => ({
|
|
9256
|
+
waiter,
|
|
9257
|
+
value,
|
|
9258
|
+
threw: false
|
|
9259
|
+
}), (error) => ({
|
|
9260
|
+
waiter,
|
|
9261
|
+
error,
|
|
9262
|
+
threw: true
|
|
9263
|
+
}));
|
|
9264
|
+
waiting.push(waiter);
|
|
9265
|
+
};
|
|
9266
|
+
/** This function starts the next parts in their order, while the limit permits it and no part failed. */
|
|
9267
|
+
const startMore = () => {
|
|
9268
|
+
while (!failed && waiting.length < limit && started < list.length) {
|
|
9269
|
+
const index = started;
|
|
9270
|
+
started += 1;
|
|
9271
|
+
scopes[index] = {
|
|
9272
|
+
prefix: `${address}.${index}/`,
|
|
9273
|
+
counts: /* @__PURE__ */ new Map(),
|
|
9274
|
+
alls: 0
|
|
9275
|
+
};
|
|
9276
|
+
const around = env.scope;
|
|
9277
|
+
env.scope = scopes[index];
|
|
9278
|
+
let steps;
|
|
9279
|
+
try {
|
|
9280
|
+
steps = list[index].evaluate();
|
|
9281
|
+
} catch (error) {
|
|
9282
|
+
end(index, thrown(error, "a part"));
|
|
9283
|
+
continue;
|
|
9284
|
+
} finally {
|
|
9285
|
+
env.scope = around;
|
|
9286
|
+
}
|
|
9287
|
+
goOn(index, steps, { threw: false });
|
|
9288
|
+
}
|
|
9289
|
+
};
|
|
9290
|
+
startMore();
|
|
9291
|
+
while (waiting.length > 0 && !failed) {
|
|
9292
|
+
const chosen = env.journal?.next?.(address, waiting.map((waiter) => waiter.index));
|
|
9293
|
+
const pick = chosen === void 0 ? void 0 : waiting.find((waiter) => waiter.index === chosen);
|
|
9294
|
+
const resumed = yield pick === void 0 ? Promise.race(waiting.map((waiter) => waiter.next)) : pick.next;
|
|
9295
|
+
env.journal?.settled?.(address, resumed.waiter.index);
|
|
9296
|
+
waiting.splice(waiting.indexOf(resumed.waiter), 1);
|
|
9297
|
+
goOn(resumed.waiter.index, resumed.waiter.steps, resumed);
|
|
9298
|
+
startMore();
|
|
8955
9299
|
}
|
|
8956
|
-
if (
|
|
8957
|
-
|
|
9300
|
+
if (!failed) return results;
|
|
9301
|
+
const stopped = fail("#ABORT", "The part stopped: a part before it failed, and the parts run with failFast.");
|
|
9302
|
+
for (const waiter of waiting) try {
|
|
9303
|
+
waiter.steps.return(stopped);
|
|
9304
|
+
} catch {}
|
|
9305
|
+
return results.map((result) => result ?? stopped);
|
|
8958
9306
|
}
|
|
8959
9307
|
/**
|
|
8960
9308
|
* This function evaluates a document by the rules, without a compilation. It is the reference of the plans: a plan,
|
|
8961
|
-
* a traced plan and the second tier of a language must give its results.
|
|
8962
|
-
*
|
|
9309
|
+
* a traced plan and the second tier of a language must give its results. In an evaluation, an effect gives the answer
|
|
9310
|
+
* of the mock or its payload, and a part that waits gives `#CALC!`. `referenceRun` reads the rules of a run.
|
|
8963
9311
|
*
|
|
8964
9312
|
* The verb of the options goes up the ladder at each document, as in `prepare`. Each verb has the same rules: the
|
|
8965
9313
|
* ladder, the contract of the kind and the sealed parts (VERB.AGREE).
|
|
@@ -8971,12 +9319,43 @@ function* together(env, list, options = {}) {
|
|
|
8971
9319
|
* @returns The result.
|
|
8972
9320
|
*/
|
|
8973
9321
|
function reference(machine, doc, context = makeContext(), options = {}) {
|
|
8974
|
-
const env =
|
|
9322
|
+
const env = envOf(machine, options, false);
|
|
9323
|
+
try {
|
|
9324
|
+
return within(env, () => evaluateAt(env, rootAt(machine, doc, context, options)));
|
|
9325
|
+
} catch (error) {
|
|
9326
|
+
return placed(thrown(error, "the reference"), []);
|
|
9327
|
+
}
|
|
9328
|
+
}
|
|
9329
|
+
/**
|
|
9330
|
+
* This function starts a run of a document by the rules, without a compilation (REF.RUN). The host does each effect of a runner,
|
|
9331
|
+
* and a step waits when the host waits (RUN.SEQ). `all` settles the waits in the order of the host, or in the order
|
|
9332
|
+
* of the journal of a replay (RUN.REPLAY). An effect of an evaluator gives `#CALC!` (RUN.NEEDS-RUNNER). A signal that
|
|
9333
|
+
* aborts stops the run with `#ABORT` (RUN.ABORT).
|
|
9334
|
+
*
|
|
9335
|
+
* The part of the run before the first wait happens at once, in the call (RUN.EAGER). `agreeRun` compares the run of
|
|
9336
|
+
* a plan with this run.
|
|
9337
|
+
*
|
|
9338
|
+
* @param machine - The world, the evaluators and the runners.
|
|
9339
|
+
* @param doc - The document.
|
|
9340
|
+
* @param context - The names and the capabilities at the root.
|
|
9341
|
+
* @param options - The options of `reference`, the host and a signal.
|
|
9342
|
+
* @returns The result, when the run ends.
|
|
9343
|
+
*/
|
|
9344
|
+
function referenceRun(machine, doc, context = makeContext(), options = {}) {
|
|
9345
|
+
const env = envOf(machine, options, true);
|
|
9346
|
+
return driveRun(env, stepsAt(env, rootAt(machine, doc, context, options)), options.signal).catch((error) => placed(thrown(error, "the reference"), []));
|
|
9347
|
+
}
|
|
9348
|
+
/** This function makes the state of the reference for one evaluation or one run. */
|
|
9349
|
+
function envOf(machine, options, running) {
|
|
9350
|
+
const perform = options.perform;
|
|
9351
|
+
return {
|
|
8975
9352
|
machine,
|
|
8976
9353
|
definitions: options.definitions ?? {},
|
|
8977
9354
|
effects: options.effects ?? [],
|
|
8978
9355
|
maxDepth: machine.maxDepth ?? 1e3,
|
|
8979
|
-
perform
|
|
9356
|
+
perform,
|
|
9357
|
+
journal: perform === void 0 ? void 0 : perform[JOURNAL],
|
|
9358
|
+
running,
|
|
8980
9359
|
verb: options.verb ?? "eval",
|
|
8981
9360
|
scope: {
|
|
8982
9361
|
prefix: "",
|
|
@@ -8984,26 +9363,66 @@ function reference(machine, doc, context = makeContext(), options = {}) {
|
|
|
8984
9363
|
alls: 0
|
|
8985
9364
|
}
|
|
8986
9365
|
};
|
|
9366
|
+
}
|
|
9367
|
+
/** This function gives the root position of a document. A context without a world gets the world of the machine. */
|
|
9368
|
+
function rootAt(machine, doc, context, options) {
|
|
8987
9369
|
const ctx = context.world === null ? {
|
|
8988
9370
|
...context,
|
|
8989
9371
|
world: worldThing(machine.world)
|
|
8990
9372
|
} : context;
|
|
9373
|
+
return {
|
|
9374
|
+
value: doc,
|
|
9375
|
+
place: [],
|
|
9376
|
+
declared: options.declaredType,
|
|
9377
|
+
depth: 0,
|
|
9378
|
+
address: false,
|
|
9379
|
+
ctx
|
|
9380
|
+
};
|
|
9381
|
+
}
|
|
9382
|
+
/**
|
|
9383
|
+
* This function does one synchronous step of an evaluation or a run. A node of a tree reads the definitions of the
|
|
9384
|
+
* evaluation for its equations (TREE.LADDER). Thus the state of the evaluation is the innermost one in the step.
|
|
9385
|
+
*/
|
|
9386
|
+
function within(env, step) {
|
|
8991
9387
|
activeEnvs.push(env);
|
|
8992
9388
|
try {
|
|
8993
|
-
return
|
|
8994
|
-
value: doc,
|
|
8995
|
-
place: [],
|
|
8996
|
-
declared: options.declaredType,
|
|
8997
|
-
depth: 0,
|
|
8998
|
-
address: false,
|
|
8999
|
-
ctx
|
|
9000
|
-
});
|
|
9001
|
-
} catch (error) {
|
|
9002
|
-
return placed(thrown(error, "the reference"), []);
|
|
9389
|
+
return step();
|
|
9003
9390
|
} finally {
|
|
9004
9391
|
activeEnvs.pop();
|
|
9005
9392
|
}
|
|
9006
9393
|
}
|
|
9394
|
+
/** The mark of a signal that aborted. */
|
|
9395
|
+
const STOPPED$1 = /* @__PURE__ */ Symbol("stopped");
|
|
9396
|
+
/**
|
|
9397
|
+
* This function drives the steps of a run. Each step waits for its promise. A rejection goes into the steps as an
|
|
9398
|
+
* exception. A signal that aborts stops the steps with `#ABORT`, also before the first step (RUN.ABORT).
|
|
9399
|
+
*/
|
|
9400
|
+
async function driveRun(env, steps, signal) {
|
|
9401
|
+
const stop = () => {
|
|
9402
|
+
const aborted = fail("#ABORT", `The host stopped the run: ${describe(signal?.reason ?? "no reason")}`, { cause: signal?.reason });
|
|
9403
|
+
try {
|
|
9404
|
+
within(env, () => steps.return(aborted));
|
|
9405
|
+
} catch {}
|
|
9406
|
+
return aborted;
|
|
9407
|
+
};
|
|
9408
|
+
if (signal?.aborted === true) return stop();
|
|
9409
|
+
const stopping = signal === void 0 ? void 0 : new Promise((resolve) => {
|
|
9410
|
+
signal.addEventListener("abort", () => resolve(STOPPED$1), { once: true });
|
|
9411
|
+
});
|
|
9412
|
+
let step = within(env, () => steps.next());
|
|
9413
|
+
while (step.done !== true) {
|
|
9414
|
+
let input;
|
|
9415
|
+
try {
|
|
9416
|
+
input = await (stopping === void 0 ? step.value : Promise.race([step.value, stopping]));
|
|
9417
|
+
} catch (error) {
|
|
9418
|
+
step = within(env, () => steps.throw(error));
|
|
9419
|
+
continue;
|
|
9420
|
+
}
|
|
9421
|
+
if (input === STOPPED$1) return stop();
|
|
9422
|
+
step = within(env, () => steps.next(input));
|
|
9423
|
+
}
|
|
9424
|
+
return step.value;
|
|
9425
|
+
}
|
|
9007
9426
|
/** The evaluations of the reference that run at this time, the innermost last. */
|
|
9008
9427
|
const activeEnvs = [];
|
|
9009
9428
|
const refNodes = /* @__PURE__ */ new WeakMap();
|
|
@@ -9437,26 +9856,122 @@ function valuesOfStrict(parts) {
|
|
|
9437
9856
|
return { values };
|
|
9438
9857
|
}
|
|
9439
9858
|
//#endregion
|
|
9440
|
-
//#region src/
|
|
9441
|
-
/** The tiers that `agree` compares when its options name none. */
|
|
9442
|
-
const DEFAULT_TIERS = /* @__PURE__ */ Object.freeze([
|
|
9443
|
-
"reference",
|
|
9444
|
-
"traced",
|
|
9445
|
-
"again",
|
|
9446
|
-
"folds"
|
|
9447
|
-
]);
|
|
9859
|
+
//#region src/replay.ts
|
|
9448
9860
|
/**
|
|
9449
|
-
* This function
|
|
9450
|
-
*
|
|
9451
|
-
*
|
|
9861
|
+
* This function makes a recorder (RUN.RECORD). The recorder gives each effect to the function `perform` of the host.
|
|
9862
|
+
* Its log keeps each effect with its address, its kind, its payload and the outcome that the kernel gives it. The log
|
|
9863
|
+
* also keeps the order in which the waits of each `all` settle.
|
|
9452
9864
|
*
|
|
9453
|
-
*
|
|
9454
|
-
*
|
|
9455
|
-
|
|
9865
|
+
* @param perform - The function of the host. Without it, each effect gives its payload, as in a run without a host.
|
|
9866
|
+
* @returns The function `perform` for the run, and the log.
|
|
9867
|
+
*/
|
|
9868
|
+
function recorder(perform) {
|
|
9869
|
+
const seen = [];
|
|
9870
|
+
const settles = [];
|
|
9871
|
+
const journal = { settled: (all, item) => {
|
|
9872
|
+
settles.push({
|
|
9873
|
+
all,
|
|
9874
|
+
item
|
|
9875
|
+
});
|
|
9876
|
+
} };
|
|
9877
|
+
const host = (effect) => {
|
|
9878
|
+
const entry = {
|
|
9879
|
+
effect,
|
|
9880
|
+
waited: false
|
|
9881
|
+
};
|
|
9882
|
+
seen.push(entry);
|
|
9883
|
+
const answer = perform === void 0 ? effect.payload : perform(effect);
|
|
9884
|
+
if (answer instanceof Promise) entry.waited = true;
|
|
9885
|
+
return answer;
|
|
9886
|
+
};
|
|
9887
|
+
return {
|
|
9888
|
+
perform: Object.assign(host, { [JOURNAL]: journal }),
|
|
9889
|
+
log: () => ({
|
|
9890
|
+
effects: seen.map(({ effect, waited }) => ({
|
|
9891
|
+
address: effect.address ?? "",
|
|
9892
|
+
kind: effect.kind,
|
|
9893
|
+
payload: effect.payload,
|
|
9894
|
+
waited,
|
|
9895
|
+
...effect.outcome === void 0 ? {} : { outcome: effect.outcome }
|
|
9896
|
+
})),
|
|
9897
|
+
settles: [...settles]
|
|
9898
|
+
})
|
|
9899
|
+
};
|
|
9900
|
+
}
|
|
9901
|
+
/**
|
|
9902
|
+
* This function makes a replayer: a function `perform` that answers each effect from a log, by its address (RUN.REPLAY). A
|
|
9903
|
+
* recorded error comes again with its code, its reason, its place and its cause. The waits of each `all` settle in the
|
|
9904
|
+
* recorded order. An effect that the log does not have at its address gives `#REPLAY!` (RUN.REPLAY-MISMATCH).
|
|
9456
9905
|
*
|
|
9457
|
-
*
|
|
9458
|
-
*
|
|
9459
|
-
*
|
|
9906
|
+
* A replayer keeps its place in the order of the waits, thus each run needs a new replayer.
|
|
9907
|
+
*
|
|
9908
|
+
* @param log - The log of a recorder.
|
|
9909
|
+
* @param options - `wait: false` for an evaluation.
|
|
9910
|
+
* @returns The function `perform` for the replay.
|
|
9911
|
+
*/
|
|
9912
|
+
function replayer(log, options = {}) {
|
|
9913
|
+
const wait = options.wait !== false;
|
|
9914
|
+
const entries = /* @__PURE__ */ new Map();
|
|
9915
|
+
for (const entry of log.effects) entries.set(entry.address, entry);
|
|
9916
|
+
const order = /* @__PURE__ */ new Map();
|
|
9917
|
+
for (const settle of log.settles) {
|
|
9918
|
+
const items = order.get(settle.all);
|
|
9919
|
+
if (items === void 0) order.set(settle.all, [settle.item]);
|
|
9920
|
+
else items.push(settle.item);
|
|
9921
|
+
}
|
|
9922
|
+
const journal = { next: (all, waiting) => {
|
|
9923
|
+
const items = order.get(all);
|
|
9924
|
+
const item = items?.[0];
|
|
9925
|
+
if (items === void 0 || item === void 0 || !waiting.includes(item)) return void 0;
|
|
9926
|
+
items.shift();
|
|
9927
|
+
return item;
|
|
9928
|
+
} };
|
|
9929
|
+
const perform = (effect) => {
|
|
9930
|
+
const entry = effect.address === void 0 ? void 0 : entries.get(effect.address);
|
|
9931
|
+
const wrong = mismatch(effect, entry);
|
|
9932
|
+
if (wrong !== void 0 || entry === void 0) throw wrong;
|
|
9933
|
+
const outcome = entry.outcome;
|
|
9934
|
+
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);
|
|
9935
|
+
const value = outcome?.ok === true ? outcome.value : void 0;
|
|
9936
|
+
if (wait && entry.waited) return failure === void 0 ? Promise.resolve(value) : Promise.reject(failure);
|
|
9937
|
+
if (failure !== void 0) throw failure;
|
|
9938
|
+
return value;
|
|
9939
|
+
};
|
|
9940
|
+
return Object.assign(perform, { [JOURNAL]: journal });
|
|
9941
|
+
}
|
|
9942
|
+
/** This function gives the error of an effect that the log does not have at its address, or `undefined`. */
|
|
9943
|
+
function mismatch(effect, entry) {
|
|
9944
|
+
const at = effect.address ?? "";
|
|
9945
|
+
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}.`);
|
|
9946
|
+
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}.`);
|
|
9947
|
+
if (!docEquals(entry.payload, effect.payload)) return replayError(effect, `The effect ${effect.kind} at the address ${at} has another payload than the log.`);
|
|
9948
|
+
}
|
|
9949
|
+
/** This function gives the exception that carries `#REPLAY!` to the kernel, with the place of the effect. */
|
|
9950
|
+
function replayError(effect, reason) {
|
|
9951
|
+
return new Failure(fail("#REPLAY!", reason, { place: effect.place }).error);
|
|
9952
|
+
}
|
|
9953
|
+
//#endregion
|
|
9954
|
+
//#region src/agree.ts
|
|
9955
|
+
/** The tiers that `agree` compares when its options name none. */
|
|
9956
|
+
const DEFAULT_TIERS = /* @__PURE__ */ Object.freeze([
|
|
9957
|
+
"reference",
|
|
9958
|
+
"traced",
|
|
9959
|
+
"again",
|
|
9960
|
+
"folds",
|
|
9961
|
+
"fixed"
|
|
9962
|
+
]);
|
|
9963
|
+
/**
|
|
9964
|
+
* This function tells if two values are the same for `agree`. Lists and plain records compare item by item and field
|
|
9965
|
+
* by field, and the order of the fields does not matter. Each other leaf compares with `Object.is`, thus `-0` is not
|
|
9966
|
+
* `0`, and `NaN` is `NaN`. The identity of a list or of a plain record is not compared.
|
|
9967
|
+
*
|
|
9968
|
+
* Two closures of `fn` are the same when they have the same parameters. Two `Held` values are the same when their
|
|
9969
|
+
* errors have the same code and place. Each other object is the same only as itself, unless the rule of the caller
|
|
9970
|
+
* tells otherwise.
|
|
9971
|
+
*
|
|
9972
|
+
* `docEquals` and `sameResult` use the same walk (D20). `docEquals` has no rules for closures, for `Held` values and
|
|
9973
|
+
* for a rule of the caller. `sameResult` is for an early cutoff, thus a closure is the same there only as itself, and
|
|
9974
|
+
* two errors also need the same reason.
|
|
9460
9975
|
*
|
|
9461
9976
|
* @param a - The first value.
|
|
9462
9977
|
* @param b - The second value.
|
|
@@ -9472,6 +9987,7 @@ function sameInTiers(x, y) {
|
|
|
9472
9987
|
if (x instanceof Held || y instanceof Held) return x instanceof Held && y instanceof Held && errorDiffers(x.error, y.error, false) === void 0;
|
|
9473
9988
|
}
|
|
9474
9989
|
const CLOSURE = /* @__PURE__ */ Symbol.for("coeval.closure");
|
|
9990
|
+
/** This function gives the parameters of a closure of `fn`, or `undefined` for another function. */
|
|
9475
9991
|
function closureParams(fn) {
|
|
9476
9992
|
return fn[CLOSURE]?.params;
|
|
9477
9993
|
}
|
|
@@ -9511,6 +10027,67 @@ function effectsDiffer(a, b, options) {
|
|
|
9511
10027
|
}
|
|
9512
10028
|
}
|
|
9513
10029
|
/**
|
|
10030
|
+
* This function compares the plan with the tier `fixed` (PLAN.FIXED). The tier declares each name of plain data fixed,
|
|
10031
|
+
* with a frozen copy of its value. It evaluates with these copies, thus the folds with fixed names give the result.
|
|
10032
|
+
* Then it evaluates with the context of `agree`, thus the code without the fold gives the result. A fold moves the
|
|
10033
|
+
* calls of the host to the compilation, thus only the result and the effects compare.
|
|
10034
|
+
*/
|
|
10035
|
+
function fixedDiffers(machine, doc, context, result, effects, given, options) {
|
|
10036
|
+
const { perform, ...declared } = given;
|
|
10037
|
+
const held = fixedContext(context);
|
|
10038
|
+
const plan = prepare(machine, doc, {
|
|
10039
|
+
...declared,
|
|
10040
|
+
fixed: { names: held.fixed }
|
|
10041
|
+
});
|
|
10042
|
+
for (const each of [held.context, context]) {
|
|
10043
|
+
const tierEffects = [];
|
|
10044
|
+
const other = plan.evaluate(each, {
|
|
10045
|
+
effects: tierEffects,
|
|
10046
|
+
...perform === void 0 ? {} : { perform }
|
|
10047
|
+
});
|
|
10048
|
+
options.observe?.();
|
|
10049
|
+
if (overflowed(result) || overflowed(other)) continue;
|
|
10050
|
+
const reason = resultDiffers(result, other, options) ?? effectsDiffer(effects, tierEffects, options);
|
|
10051
|
+
if (reason !== void 0) return {
|
|
10052
|
+
other,
|
|
10053
|
+
reason
|
|
10054
|
+
};
|
|
10055
|
+
}
|
|
10056
|
+
}
|
|
10057
|
+
/**
|
|
10058
|
+
* This function gives the context of the tier `fixed`. Each name of plain data gets a deeply frozen copy of its value,
|
|
10059
|
+
* and the record of the fixed names holds the same copy. Each other name keeps its value.
|
|
10060
|
+
*/
|
|
10061
|
+
function fixedContext(context) {
|
|
10062
|
+
const given = context.names;
|
|
10063
|
+
const names = Object.create(Object.getPrototypeOf(given));
|
|
10064
|
+
const fixed = Object.create(null);
|
|
10065
|
+
for (const key of Reflect.ownKeys(given)) {
|
|
10066
|
+
let field = Object.getOwnPropertyDescriptor(given, key);
|
|
10067
|
+
if (typeof key === "string" && "value" in field) {
|
|
10068
|
+
const copy = sharedCopy(field.value);
|
|
10069
|
+
if (copy !== void 0 || field.value === void 0) {
|
|
10070
|
+
Object.defineProperty(fixed, key, {
|
|
10071
|
+
value: copy,
|
|
10072
|
+
enumerable: true
|
|
10073
|
+
});
|
|
10074
|
+
field = {
|
|
10075
|
+
...field,
|
|
10076
|
+
value: copy
|
|
10077
|
+
};
|
|
10078
|
+
}
|
|
10079
|
+
}
|
|
10080
|
+
Object.defineProperty(names, key, field);
|
|
10081
|
+
}
|
|
10082
|
+
return {
|
|
10083
|
+
context: {
|
|
10084
|
+
...context,
|
|
10085
|
+
names
|
|
10086
|
+
},
|
|
10087
|
+
fixed
|
|
10088
|
+
};
|
|
10089
|
+
}
|
|
10090
|
+
/**
|
|
9514
10091
|
* This function evaluates a document with a plan and with other tiers, and compares the results (PLAN.AGREES). Two
|
|
9515
10092
|
* results agree when they have the same value by `sameValue`, or errors with the same code, and the same place when
|
|
9516
10093
|
* both have one. The effects must have the same kinds, places and payloads, in the same order. A language gives its
|
|
@@ -9567,6 +10144,19 @@ function agree(machine, doc, context = makeContext(), options = {}) {
|
|
|
9567
10144
|
});
|
|
9568
10145
|
other = traced.result;
|
|
9569
10146
|
otherEffects = [...traced.effects];
|
|
10147
|
+
} else if (tier === "fixed") {
|
|
10148
|
+
const why = fixedDiffers(machine, doc, context, result, effects, {
|
|
10149
|
+
...declared,
|
|
10150
|
+
...mock
|
|
10151
|
+
}, options);
|
|
10152
|
+
if (why !== void 0) return {
|
|
10153
|
+
ok: false,
|
|
10154
|
+
tier,
|
|
10155
|
+
plan: result,
|
|
10156
|
+
other: why.other,
|
|
10157
|
+
reason: why.reason
|
|
10158
|
+
};
|
|
10159
|
+
continue;
|
|
9570
10160
|
} else if (tier === "folds") {
|
|
9571
10161
|
other = prepare(machine, doc, {
|
|
9572
10162
|
...declared,
|
|
@@ -9624,6 +10214,114 @@ function agree(machine, doc, context = makeContext(), options = {}) {
|
|
|
9624
10214
|
effects
|
|
9625
10215
|
};
|
|
9626
10216
|
}
|
|
10217
|
+
/**
|
|
10218
|
+
* This function wraps the function `perform` of a run. The wrapper keeps each effect and each wait of `all` that
|
|
10219
|
+
* settles. It gives each effect to the inner function, and each call of its journal to the inner journal.
|
|
10220
|
+
*/
|
|
10221
|
+
function watched(perform) {
|
|
10222
|
+
const effects = [];
|
|
10223
|
+
const settles = [];
|
|
10224
|
+
const inner = perform[JOURNAL];
|
|
10225
|
+
const settled = (all, item) => {
|
|
10226
|
+
settles.push({
|
|
10227
|
+
all,
|
|
10228
|
+
item
|
|
10229
|
+
});
|
|
10230
|
+
inner?.settled?.(all, item);
|
|
10231
|
+
};
|
|
10232
|
+
const journal = inner?.next === void 0 ? { settled } : {
|
|
10233
|
+
settled,
|
|
10234
|
+
next: (all, waiting) => inner.next?.(all, waiting)
|
|
10235
|
+
};
|
|
10236
|
+
const host = (effect) => {
|
|
10237
|
+
effects.push(effect);
|
|
10238
|
+
return perform(effect);
|
|
10239
|
+
};
|
|
10240
|
+
return {
|
|
10241
|
+
perform: Object.assign(host, { [JOURNAL]: journal }),
|
|
10242
|
+
effects,
|
|
10243
|
+
settles
|
|
10244
|
+
};
|
|
10245
|
+
}
|
|
10246
|
+
function settleText(settle) {
|
|
10247
|
+
return settle === void 0 ? "no item" : `the item ${settle.item} of ${settle.all}`;
|
|
10248
|
+
}
|
|
10249
|
+
/** This function gives the first difference of two orders of the waits of `all`, or `undefined` when they agree. */
|
|
10250
|
+
function settlesDiffer(a, b) {
|
|
10251
|
+
for (let index = 0; index < Math.max(a.length, b.length); index += 1) {
|
|
10252
|
+
const x = a[index];
|
|
10253
|
+
const y = b[index];
|
|
10254
|
+
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.`;
|
|
10255
|
+
}
|
|
10256
|
+
}
|
|
10257
|
+
/**
|
|
10258
|
+
* This function compares the run of a document in a plan with its run in other tiers (RUN.AGREES). First the plan
|
|
10259
|
+
* starts the run with a recorder around the host. Then each tier replays the log of that run. These tiers are the
|
|
10260
|
+
* reference run, the traced run, a second run of the plan and the plan with the other choice of folds.
|
|
10261
|
+
*
|
|
10262
|
+
* The rules of `agree` compare the results and the effects, with their addresses and outcomes. A tier must also
|
|
10263
|
+
* settle the waits of each `all` in the order of the plan, thus a replay keeps the order of the waits.
|
|
10264
|
+
*
|
|
10265
|
+
* @param machine - The world, the evaluators and the runners.
|
|
10266
|
+
* @param doc - The document.
|
|
10267
|
+
* @param context - The names and the capabilities at the root.
|
|
10268
|
+
* @param options - The tiers, the definitions for the reference, the rules of the comparison and the host.
|
|
10269
|
+
* @returns The result of the plan, or the first tier that disagrees and the reason.
|
|
10270
|
+
*/
|
|
10271
|
+
async function agreeRun(machine, doc, context = makeContext(), options = {}) {
|
|
10272
|
+
const declared = {
|
|
10273
|
+
...options.declaredType === void 0 ? {} : { declaredType: options.declaredType },
|
|
10274
|
+
...options.verb === void 0 ? {} : { verb: options.verb }
|
|
10275
|
+
};
|
|
10276
|
+
const observe = options.observe;
|
|
10277
|
+
observe?.();
|
|
10278
|
+
const folds = observe === void 0;
|
|
10279
|
+
const plan = prepare(machine, doc, {
|
|
10280
|
+
...declared,
|
|
10281
|
+
fold: folds
|
|
10282
|
+
});
|
|
10283
|
+
const recording = recorder(options.perform);
|
|
10284
|
+
const first = watched(recording.perform);
|
|
10285
|
+
const result = await plan.run(context, { perform: first.perform });
|
|
10286
|
+
const saw = observe?.();
|
|
10287
|
+
const log = recording.log();
|
|
10288
|
+
for (const tier of options.tiers ?? DEFAULT_TIERS) {
|
|
10289
|
+
if (tier === "fixed") continue;
|
|
10290
|
+
const replay = watched(replayer(log));
|
|
10291
|
+
const host = { perform: replay.perform };
|
|
10292
|
+
let other;
|
|
10293
|
+
if (tier === "reference") other = await referenceRun(machine, doc, context, {
|
|
10294
|
+
...declared,
|
|
10295
|
+
...host,
|
|
10296
|
+
...options.definitions === void 0 ? {} : { definitions: options.definitions }
|
|
10297
|
+
});
|
|
10298
|
+
else if (tier === "traced") other = (await run(machine, doc, context, {
|
|
10299
|
+
...declared,
|
|
10300
|
+
...host
|
|
10301
|
+
})).result;
|
|
10302
|
+
else if (tier === "folds") other = await prepare(machine, doc, {
|
|
10303
|
+
...declared,
|
|
10304
|
+
fold: !folds
|
|
10305
|
+
}).run(context, host);
|
|
10306
|
+
else other = await plan.run(context, host);
|
|
10307
|
+
const compareCalls = observe !== void 0 && tier !== "folds";
|
|
10308
|
+
const calls = observe?.();
|
|
10309
|
+
if (overflowed(result) || overflowed(other)) continue;
|
|
10310
|
+
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);
|
|
10311
|
+
if (why !== void 0) return {
|
|
10312
|
+
ok: false,
|
|
10313
|
+
tier,
|
|
10314
|
+
plan: result,
|
|
10315
|
+
other,
|
|
10316
|
+
reason: why
|
|
10317
|
+
};
|
|
10318
|
+
}
|
|
10319
|
+
return {
|
|
10320
|
+
ok: true,
|
|
10321
|
+
result,
|
|
10322
|
+
effects: first.effects
|
|
10323
|
+
};
|
|
10324
|
+
}
|
|
9627
10325
|
//#endregion
|
|
9628
10326
|
//#region src/generate.ts
|
|
9629
10327
|
/**
|
|
@@ -11068,6 +11766,22 @@ function verifyReads(target, reads, context, options = {}) {
|
|
|
11068
11766
|
read,
|
|
11069
11767
|
now: stop
|
|
11070
11768
|
};
|
|
11769
|
+
if (read.kept === true) {
|
|
11770
|
+
const now = keptAgain(machine, root, read.path);
|
|
11771
|
+
if (now === void 0) return {
|
|
11772
|
+
same: false,
|
|
11773
|
+
index,
|
|
11774
|
+
read,
|
|
11775
|
+
now: fail("#N/A", "The host does not know the value of the kept read yet.")
|
|
11776
|
+
};
|
|
11777
|
+
if (!sameResult(read.reached, now, equal)) return {
|
|
11778
|
+
same: false,
|
|
11779
|
+
index,
|
|
11780
|
+
read,
|
|
11781
|
+
now
|
|
11782
|
+
};
|
|
11783
|
+
continue;
|
|
11784
|
+
}
|
|
11071
11785
|
const asked = read.hops !== void 0;
|
|
11072
11786
|
const now = readAgain(machine, root, read.path, asked);
|
|
11073
11787
|
if (!sameResult(read.reached, now.reached, equal) || read.hops !== void 0 && !sameHops(read.hops, now.hops, equal)) return {
|
|
@@ -11340,7 +12054,7 @@ function cyclic(graph, members) {
|
|
|
11340
12054
|
return false;
|
|
11341
12055
|
}
|
|
11342
12056
|
/** This function gives the names of the nodes of a component, without the node `all`. */
|
|
11343
|
-
function named(graph, members) {
|
|
12057
|
+
function named$1(graph, members) {
|
|
11344
12058
|
const out = [];
|
|
11345
12059
|
for (const member of members) if (member !== graph.all) out.push(graph.ids[member]);
|
|
11346
12060
|
return out;
|
|
@@ -11379,7 +12093,7 @@ function components(graph, options = {}) {
|
|
|
11379
12093
|
const index = indexed(graph, options);
|
|
11380
12094
|
const out = [];
|
|
11381
12095
|
for (const members of strong(index).list) {
|
|
11382
|
-
const names = named(index, members);
|
|
12096
|
+
const names = named$1(index, members);
|
|
11383
12097
|
if (names.length > 0) out.push(names);
|
|
11384
12098
|
}
|
|
11385
12099
|
return out;
|
|
@@ -11486,7 +12200,7 @@ function cycles(graph) {
|
|
|
11486
12200
|
const out = [];
|
|
11487
12201
|
found.list.forEach((members, component) => {
|
|
11488
12202
|
if (!cyclic(every, members)) return;
|
|
11489
|
-
const nodes = named(every, members);
|
|
12203
|
+
const nodes = named$1(every, members);
|
|
11490
12204
|
const certain = inside.get(component) ?? [];
|
|
11491
12205
|
if (certain.length === 1 && certain[0]?.length === nodes.length) {
|
|
11492
12206
|
out.push({
|
|
@@ -11500,7 +12214,7 @@ function cycles(graph) {
|
|
|
11500
12214
|
certain: false
|
|
11501
12215
|
});
|
|
11502
12216
|
for (const part of certain) out.push({
|
|
11503
|
-
nodes: named(eager, part),
|
|
12217
|
+
nodes: named$1(eager, part),
|
|
11504
12218
|
certain: true
|
|
11505
12219
|
});
|
|
11506
12220
|
});
|
|
@@ -11742,10 +12456,12 @@ function schedule(machine, options = {}) {
|
|
|
11742
12456
|
};
|
|
11743
12457
|
/**
|
|
11744
12458
|
* This function gives the state of the current data epoch. Each data epoch has a new host thing at the root
|
|
11745
|
-
* `world`. Thus a read that reaches the whole root changes at each change of data (READS.KEPT).
|
|
12459
|
+
* `world`. Thus a read that reaches the whole root changes at each change of data (READS.KEPT). A kept read of a
|
|
12460
|
+
* closure reads a node through `known`, without a demand (SCHED.CLOSURE).
|
|
11746
12461
|
*/
|
|
11747
12462
|
const current = () => {
|
|
11748
12463
|
if (now !== void 0) return now;
|
|
12464
|
+
const values = /* @__PURE__ */ new Map();
|
|
11749
12465
|
const nodesThing = {
|
|
11750
12466
|
[THING]: true,
|
|
11751
12467
|
label: "nodes",
|
|
@@ -11757,14 +12473,20 @@ function schedule(machine, options = {}) {
|
|
|
11757
12473
|
return result.value;
|
|
11758
12474
|
},
|
|
11759
12475
|
has: (key) => docs.has(String(key)),
|
|
11760
|
-
toJson: record
|
|
12476
|
+
toJson: record,
|
|
12477
|
+
known(key) {
|
|
12478
|
+
const result = values.get(String(key));
|
|
12479
|
+
if (result === void 0) return void 0;
|
|
12480
|
+
if (!result.ok) throw new Failure(result.error);
|
|
12481
|
+
return { thing: result.value };
|
|
12482
|
+
}
|
|
11761
12483
|
};
|
|
11762
12484
|
now = {
|
|
11763
12485
|
context: rootContext(machine, {
|
|
11764
12486
|
...base,
|
|
11765
12487
|
world: nodesThing
|
|
11766
12488
|
}),
|
|
11767
|
-
values
|
|
12489
|
+
values
|
|
11768
12490
|
};
|
|
11769
12491
|
return now;
|
|
11770
12492
|
};
|
|
@@ -11839,99 +12561,404 @@ function schedule(machine, options = {}) {
|
|
|
11839
12561
|
};
|
|
11840
12562
|
}
|
|
11841
12563
|
//#endregion
|
|
11842
|
-
//#region src/
|
|
12564
|
+
//#region src/meaning.ts
|
|
12565
|
+
/** A list of eight reference types. The generator then gives more references, thus more reads of nodes. */
|
|
12566
|
+
function moreRefs() {
|
|
12567
|
+
return Array.from({ length: 8 }, () => ["ref"]);
|
|
12568
|
+
}
|
|
12569
|
+
/** The default options of the document generator, for `STD_MACHINE`. The kinds `log` and `write` need a capability. */
|
|
12570
|
+
function standardGenerate() {
|
|
12571
|
+
return {
|
|
12572
|
+
maxSize: 16,
|
|
12573
|
+
types: [
|
|
12574
|
+
...typesOf(STD_MACHINE.world).filter((type) => type[0] !== "log" && type[0] !== "write"),
|
|
12575
|
+
...moreRefs(),
|
|
12576
|
+
["if"],
|
|
12577
|
+
["add"]
|
|
12578
|
+
],
|
|
12579
|
+
data: (type, rng) => {
|
|
12580
|
+
const name = type.join(".");
|
|
12581
|
+
if (name === "number") return rng.chance(.9) ? rng.pick([
|
|
12582
|
+
0,
|
|
12583
|
+
1,
|
|
12584
|
+
2,
|
|
12585
|
+
3.5,
|
|
12586
|
+
-1
|
|
12587
|
+
]) : "1";
|
|
12588
|
+
if (name === "text") return rng.chance(.9) ? rng.pick([
|
|
12589
|
+
"",
|
|
12590
|
+
"a",
|
|
12591
|
+
"b"
|
|
12592
|
+
]) : 1;
|
|
12593
|
+
if (name === "bool") return rng.chance(.9) ? rng.chance(.5) : 0;
|
|
12594
|
+
if (name === "nearest") return rng.pick([{ type: "panel" }, "panel"]);
|
|
12595
|
+
}
|
|
12596
|
+
};
|
|
12597
|
+
}
|
|
12598
|
+
/** The default `wire`, for `STD_MACHINE`. A list evaluates each part, also after a failed part. */
|
|
12599
|
+
function standardWire(rng, node) {
|
|
12600
|
+
if (rng.chance(.5)) return {
|
|
12601
|
+
type: "list",
|
|
12602
|
+
value: [node(), node()]
|
|
12603
|
+
};
|
|
12604
|
+
return rng.chance(.5) ? {
|
|
12605
|
+
type: "add",
|
|
12606
|
+
value: [node(), 1]
|
|
12607
|
+
} : node();
|
|
12608
|
+
}
|
|
12609
|
+
/** The default values of the names of the host. Some are equal by structure, and some are host values. */
|
|
12610
|
+
function standardValues() {
|
|
12611
|
+
return [
|
|
12612
|
+
0,
|
|
12613
|
+
1,
|
|
12614
|
+
2,
|
|
12615
|
+
-0,
|
|
12616
|
+
NaN,
|
|
12617
|
+
"two",
|
|
12618
|
+
null,
|
|
12619
|
+
void 0,
|
|
12620
|
+
[1, 2],
|
|
12621
|
+
{ a: 1 },
|
|
12622
|
+
{ a: 2 },
|
|
12623
|
+
{
|
|
12624
|
+
type: "ref",
|
|
12625
|
+
value: ["names", "a"]
|
|
12626
|
+
},
|
|
12627
|
+
new Held({
|
|
12628
|
+
code: "#HELD",
|
|
12629
|
+
reason: "The binding failed."
|
|
12630
|
+
})
|
|
12631
|
+
];
|
|
12632
|
+
}
|
|
12633
|
+
/** The default contexts of the host at the start of a run. */
|
|
12634
|
+
function standardContexts() {
|
|
12635
|
+
return [context({
|
|
12636
|
+
names: {
|
|
12637
|
+
a: 1,
|
|
12638
|
+
b: [1, "two"],
|
|
12639
|
+
x: { a: 1 },
|
|
12640
|
+
n: 2
|
|
12641
|
+
},
|
|
12642
|
+
caps: ["log"]
|
|
12643
|
+
}), context({
|
|
12644
|
+
names: {
|
|
12645
|
+
a: "two",
|
|
12646
|
+
b: 0,
|
|
12647
|
+
x: null
|
|
12648
|
+
},
|
|
12649
|
+
self: { a: 1 },
|
|
12650
|
+
event: { a: 2 }
|
|
12651
|
+
})];
|
|
12652
|
+
}
|
|
12653
|
+
/** This function puts a field on a record, also the field `__proto__`. */
|
|
12654
|
+
function put(target, key, value) {
|
|
12655
|
+
Object.defineProperty(target, key, {
|
|
12656
|
+
value,
|
|
12657
|
+
enumerable: true,
|
|
12658
|
+
writable: true,
|
|
12659
|
+
configurable: true
|
|
12660
|
+
});
|
|
12661
|
+
}
|
|
12662
|
+
/** This function changes each path from `names` to a node into a path from `world`. Some paths read the whole root. */
|
|
12663
|
+
function linked(value, rng, nodes) {
|
|
12664
|
+
if (Array.isArray(value)) {
|
|
12665
|
+
const items = value.map((item) => linked(item, rng, nodes));
|
|
12666
|
+
if (items[0] === "names" && typeof items[1] === "string" && nodes.includes(items[1])) return rng.chance(.03) ? ["world"] : ["world", ...items.slice(1)];
|
|
12667
|
+
return items;
|
|
12668
|
+
}
|
|
12669
|
+
if (typeof value !== "object" || value === null) return value;
|
|
12670
|
+
const out = {};
|
|
12671
|
+
for (const key of Object.keys(value)) put(out, key, linked(value[key], rng, nodes));
|
|
12672
|
+
return out;
|
|
12673
|
+
}
|
|
12674
|
+
/** This function gives new names of the host, with one or two changes. */
|
|
12675
|
+
function change(base, rng, names, values) {
|
|
12676
|
+
const out = { ...base.names };
|
|
12677
|
+
for (let step = 1 + rng.int(2); step > 0; step -= 1) {
|
|
12678
|
+
const name = rng.pick(names);
|
|
12679
|
+
if (rng.chance(.15)) delete out[name];
|
|
12680
|
+
else out[name] = rng.pick(values);
|
|
12681
|
+
}
|
|
12682
|
+
return out;
|
|
12683
|
+
}
|
|
12684
|
+
function shuffle(items, rng) {
|
|
12685
|
+
const out = [...items];
|
|
12686
|
+
for (let index = out.length - 1; index > 0; index -= 1) {
|
|
12687
|
+
const other = rng.int(index + 1);
|
|
12688
|
+
[out[index], out[other]] = [out[other], out[index]];
|
|
12689
|
+
}
|
|
12690
|
+
return out;
|
|
12691
|
+
}
|
|
11843
12692
|
/**
|
|
11844
|
-
* This function
|
|
11845
|
-
*
|
|
11846
|
-
* also keeps the order in which the waits of each `all` settle.
|
|
11847
|
-
*
|
|
11848
|
-
* @param perform - The function of the host. Without it, each effect gives its payload, as in a run without a host.
|
|
11849
|
-
* @returns The function `perform` for the run, and the log.
|
|
12693
|
+
* This function evaluates each node of a graph from scratch, with the meaning of D31. Each node gets a new plan and
|
|
12694
|
+
* one value. The algorithm of Tarjan over the demands finds the cycles. The function keeps nothing between calls.
|
|
11850
12695
|
*/
|
|
11851
|
-
function
|
|
11852
|
-
const
|
|
11853
|
-
const
|
|
11854
|
-
const
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11860
|
-
|
|
11861
|
-
const
|
|
11862
|
-
|
|
11863
|
-
waited: false
|
|
12696
|
+
function fromScratch(machine, docs, base, declaredType) {
|
|
12697
|
+
const declared = declaredType === void 0 ? {} : { declaredType };
|
|
12698
|
+
const values = /* @__PURE__ */ new Map();
|
|
12699
|
+
const index = /* @__PURE__ */ new Map();
|
|
12700
|
+
const lowest = /* @__PURE__ */ new Map();
|
|
12701
|
+
const component = [];
|
|
12702
|
+
const evaluating = [];
|
|
12703
|
+
const selfReads = /* @__PURE__ */ new Set();
|
|
12704
|
+
const visit = (id) => {
|
|
12705
|
+
const reader = evaluating[evaluating.length - 1];
|
|
12706
|
+
const link = (to) => {
|
|
12707
|
+
if (reader !== void 0) lowest.set(reader, Math.min(lowest.get(reader), to));
|
|
11864
12708
|
};
|
|
11865
|
-
|
|
11866
|
-
|
|
11867
|
-
|
|
11868
|
-
|
|
12709
|
+
const done = values.get(id);
|
|
12710
|
+
if (done !== void 0) {
|
|
12711
|
+
if (component.includes(id)) link(index.get(id));
|
|
12712
|
+
return done;
|
|
12713
|
+
}
|
|
12714
|
+
if (evaluating.includes(id)) {
|
|
12715
|
+
link(index.get(id));
|
|
12716
|
+
if (reader === id) selfReads.add(id);
|
|
12717
|
+
return cycleOf(id);
|
|
12718
|
+
}
|
|
12719
|
+
if (factsOf(machine, docs.get(id), declared).side === "run") {
|
|
12720
|
+
const refused = runSide(id);
|
|
12721
|
+
values.set(id, refused);
|
|
12722
|
+
return refused;
|
|
12723
|
+
}
|
|
12724
|
+
index.set(id, index.size);
|
|
12725
|
+
lowest.set(id, index.get(id));
|
|
12726
|
+
component.push(id);
|
|
12727
|
+
evaluating.push(id);
|
|
12728
|
+
let result = prepare(machine, docs.get(id), declared).evaluate(ctx);
|
|
12729
|
+
evaluating.pop();
|
|
12730
|
+
if (lowest.get(id) < index.get(id)) result = cycleOf(id);
|
|
12731
|
+
else if (component.splice(component.indexOf(id)).length > 1 || selfReads.has(id)) result = cycleOf(id);
|
|
12732
|
+
values.set(id, result);
|
|
12733
|
+
link(lowest.get(id));
|
|
12734
|
+
return result;
|
|
11869
12735
|
};
|
|
11870
|
-
|
|
11871
|
-
|
|
11872
|
-
|
|
11873
|
-
|
|
11874
|
-
|
|
11875
|
-
|
|
11876
|
-
|
|
11877
|
-
|
|
11878
|
-
|
|
11879
|
-
|
|
11880
|
-
|
|
11881
|
-
|
|
12736
|
+
const world = {
|
|
12737
|
+
[THING]: true,
|
|
12738
|
+
label: "nodes",
|
|
12739
|
+
deref: (key) => {
|
|
12740
|
+
if (!docs.has(String(key))) return void 0;
|
|
12741
|
+
const result = visit(String(key));
|
|
12742
|
+
if (!result.ok) throw new Failure(result.error);
|
|
12743
|
+
return result.value;
|
|
12744
|
+
},
|
|
12745
|
+
has: (key) => docs.has(String(key)),
|
|
12746
|
+
toJson: () => {
|
|
12747
|
+
const out = {};
|
|
12748
|
+
for (const id of [...docs.keys()].sort()) {
|
|
12749
|
+
if (factsOf(machine, docs.get(id), declared).side === "run") continue;
|
|
12750
|
+
const result = visit(id);
|
|
12751
|
+
if (!result.ok) throw new Failure(result.error);
|
|
12752
|
+
put(out, id, result.value);
|
|
12753
|
+
}
|
|
12754
|
+
return out;
|
|
12755
|
+
}
|
|
11882
12756
|
};
|
|
12757
|
+
const ctx = {
|
|
12758
|
+
...base,
|
|
12759
|
+
world
|
|
12760
|
+
};
|
|
12761
|
+
for (const id of [...docs.keys()].sort()) visit(id);
|
|
12762
|
+
return values;
|
|
12763
|
+
}
|
|
12764
|
+
/** This function names a result for the reason of a disagreement: its error code, or "a value". */
|
|
12765
|
+
function named(result) {
|
|
12766
|
+
return result.ok ? "a value" : result.error.code;
|
|
11883
12767
|
}
|
|
11884
12768
|
/**
|
|
11885
|
-
* This function
|
|
11886
|
-
*
|
|
11887
|
-
*
|
|
12769
|
+
* This function examines an engine of a host against an evaluation from scratch (SCHED.AGREES, R4 P4, D58). Each run
|
|
12770
|
+
* makes a new engine and a random graph of named nodes from the document generator. Then it gives random commands:
|
|
12771
|
+
* edits, new nodes, removed nodes, the same document again, new machines and changes of data.
|
|
11888
12772
|
*
|
|
11889
|
-
*
|
|
12773
|
+
* - Each read command evaluates the whole graph from scratch two times, with the meaning of D31.
|
|
12774
|
+
* - It then demands each node in a random order, or one node, and compares each answer with `sameResult`.
|
|
12775
|
+
* - A read whose answer from scratch differs between the two evaluations is not compared.
|
|
12776
|
+
* - Two errors agree when they have the same code, reason and place. Thus an engine gives the `#CYCLE!` and the
|
|
12777
|
+
* `#REF!` of D31.
|
|
11890
12778
|
*
|
|
11891
|
-
*
|
|
11892
|
-
*
|
|
11893
|
-
* @
|
|
12779
|
+
* The same seed and the same options give the same commands, thus a disagreement occurs again.
|
|
12780
|
+
*
|
|
12781
|
+
* @param engine - This function makes a new engine for each run, for example `schedule`.
|
|
12782
|
+
* @param options - The machines, the generator, the contexts, the number of runs and the rule for host values.
|
|
12783
|
+
* @returns What the harness reached, or the first read whose answer differs from the answer from scratch.
|
|
11894
12784
|
*/
|
|
11895
|
-
function
|
|
11896
|
-
const
|
|
11897
|
-
const
|
|
11898
|
-
|
|
11899
|
-
const
|
|
11900
|
-
|
|
11901
|
-
|
|
11902
|
-
|
|
11903
|
-
|
|
12785
|
+
function agreeSchedule(engine, options = {}) {
|
|
12786
|
+
const machines = options.machines ?? [STD_MACHINE, { ...STD_MACHINE }];
|
|
12787
|
+
const declaredType = options.declaredType;
|
|
12788
|
+
const generateOptions = options.generate ?? standardGenerate();
|
|
12789
|
+
const value = options.value ?? ((rng) => ({
|
|
12790
|
+
type: "number",
|
|
12791
|
+
value: rng.pick([
|
|
12792
|
+
0,
|
|
12793
|
+
1,
|
|
12794
|
+
2
|
|
12795
|
+
])
|
|
12796
|
+
}));
|
|
12797
|
+
const wire = options.wire ?? standardWire;
|
|
12798
|
+
const wired = options.wired ?? .2;
|
|
12799
|
+
const runs = options.runs ?? true;
|
|
12800
|
+
const nodes = options.nodes ?? [
|
|
12801
|
+
"p",
|
|
12802
|
+
"q",
|
|
12803
|
+
"r",
|
|
12804
|
+
"s",
|
|
12805
|
+
"t"
|
|
12806
|
+
];
|
|
12807
|
+
const hostNames = options.names ?? [
|
|
12808
|
+
"a",
|
|
12809
|
+
"b",
|
|
12810
|
+
"x",
|
|
12811
|
+
"n"
|
|
12812
|
+
];
|
|
12813
|
+
const hostValues = options.values ?? standardValues();
|
|
12814
|
+
const contexts = options.contexts ?? standardContexts();
|
|
12815
|
+
const total = options.count ?? 100;
|
|
12816
|
+
const steps = options.steps ?? 30;
|
|
12817
|
+
const first = options.seed ?? 9000011;
|
|
12818
|
+
const given = options.equal;
|
|
12819
|
+
const equal = (a, b) => {
|
|
12820
|
+
const answer = given?.(a, b);
|
|
12821
|
+
if (answer !== void 0) return answer;
|
|
12822
|
+
if (isClosure(a) || isClosure(b)) return isClosure(a) && isClosure(b) && docEquals(closureParams(a), closureParams(b));
|
|
12823
|
+
};
|
|
12824
|
+
const counts = {
|
|
12825
|
+
reads: 0,
|
|
12826
|
+
compared: 0,
|
|
12827
|
+
unrepeatable: 0,
|
|
12828
|
+
values: 0,
|
|
12829
|
+
cycles: 0,
|
|
12830
|
+
runs: 0,
|
|
12831
|
+
data: 0,
|
|
12832
|
+
empty: 0,
|
|
12833
|
+
edits: 0,
|
|
12834
|
+
additions: 0,
|
|
12835
|
+
removes: 0,
|
|
12836
|
+
same: 0,
|
|
12837
|
+
machines: 0
|
|
12838
|
+
};
|
|
12839
|
+
const declared = declaredType === void 0 ? {} : { declaredType };
|
|
12840
|
+
for (let run = 0; run < total; run += 1) {
|
|
12841
|
+
const seed = first + run;
|
|
12842
|
+
const rng = random(seed);
|
|
12843
|
+
const node = () => ({
|
|
12844
|
+
type: "ref",
|
|
12845
|
+
value: ["world", rng.pick(nodes)]
|
|
12846
|
+
});
|
|
12847
|
+
let at = 0;
|
|
12848
|
+
let machine = machines[0];
|
|
12849
|
+
let made = 0;
|
|
12850
|
+
const make = () => {
|
|
12851
|
+
if (rng.chance(wired)) return wire(rng, node);
|
|
12852
|
+
const roll = rng.int(10);
|
|
12853
|
+
if (roll === 0) return value(rng);
|
|
12854
|
+
const names = roll < 5 ? hostNames : [
|
|
12855
|
+
...hostNames,
|
|
12856
|
+
...nodes,
|
|
12857
|
+
...nodes,
|
|
12858
|
+
...nodes
|
|
12859
|
+
];
|
|
12860
|
+
const doc = linked(generate(machine.world, {
|
|
12861
|
+
...generateOptions,
|
|
12862
|
+
names,
|
|
12863
|
+
seed: seed * 31 + made++
|
|
12864
|
+
}), rng, nodes);
|
|
12865
|
+
return runs && roll === 9 ? {
|
|
12866
|
+
type: "log",
|
|
12867
|
+
value: [doc]
|
|
12868
|
+
} : doc;
|
|
12869
|
+
};
|
|
12870
|
+
const docs = new Map(nodes.map((id) => [id, make()]));
|
|
12871
|
+
const start = contexts[run % contexts.length];
|
|
12872
|
+
let base = {
|
|
12873
|
+
...start,
|
|
12874
|
+
names: { ...start.names }
|
|
12875
|
+
};
|
|
12876
|
+
const host = engine(machine, {
|
|
12877
|
+
...declared,
|
|
12878
|
+
context: base
|
|
12879
|
+
});
|
|
12880
|
+
for (const [id, doc] of docs) host.set(id, doc);
|
|
12881
|
+
for (let step = 0; step < steps; step += 1) {
|
|
12882
|
+
const what = rng.int(12);
|
|
12883
|
+
if (what < 4) {
|
|
12884
|
+
const fresh = fromScratch(machine, docs, base, declaredType);
|
|
12885
|
+
const again = fromScratch(machine, docs, base, declaredType);
|
|
12886
|
+
const ids = shuffle([...docs.keys()], rng);
|
|
12887
|
+
for (const id of rng.chance(.5) ? ids : ids.slice(0, 1)) {
|
|
12888
|
+
const got = host.read(id);
|
|
12889
|
+
const want = fresh.get(id);
|
|
12890
|
+
counts.reads += 1;
|
|
12891
|
+
if (!sameResult(want, again.get(id), equal)) {
|
|
12892
|
+
counts.unrepeatable += 1;
|
|
12893
|
+
continue;
|
|
12894
|
+
}
|
|
12895
|
+
counts.compared += 1;
|
|
12896
|
+
if (sameResult(want, cycleOf(id))) counts.cycles += 1;
|
|
12897
|
+
else if (sameResult(want, runSide(id))) counts.runs += 1;
|
|
12898
|
+
else if (want.ok) counts.values += 1;
|
|
12899
|
+
if (!sameResult(got, want, equal)) {
|
|
12900
|
+
const reason = got.ok === want.ok && !got.ok ? `The engine and the evaluation from scratch give two different errors ${named(got)} and ${named(want)} for the node ${id}.` : `The engine gives ${named(got)} for the node ${id}, and the evaluation from scratch gives ${named(want)}.`;
|
|
12901
|
+
return {
|
|
12902
|
+
ok: false,
|
|
12903
|
+
seed,
|
|
12904
|
+
step,
|
|
12905
|
+
node: id,
|
|
12906
|
+
engine: got,
|
|
12907
|
+
scratch: want,
|
|
12908
|
+
docs: new Map(docs),
|
|
12909
|
+
context: base,
|
|
12910
|
+
machine,
|
|
12911
|
+
reason,
|
|
12912
|
+
counts: { ...counts }
|
|
12913
|
+
};
|
|
12914
|
+
}
|
|
12915
|
+
}
|
|
12916
|
+
} else if (what < 6) {
|
|
12917
|
+
base = {
|
|
12918
|
+
...base,
|
|
12919
|
+
names: change(base, rng, hostNames, hostValues)
|
|
12920
|
+
};
|
|
12921
|
+
host.changeData(base);
|
|
12922
|
+
counts.data += 1;
|
|
12923
|
+
} else if (what === 6) {
|
|
12924
|
+
host.changeData(base);
|
|
12925
|
+
counts.empty += 1;
|
|
12926
|
+
} else if (what < 9) {
|
|
12927
|
+
const id = rng.pick(nodes);
|
|
12928
|
+
const doc = make();
|
|
12929
|
+
docs.set(id, doc);
|
|
12930
|
+
host.set(id, doc);
|
|
12931
|
+
counts.edits += 1;
|
|
12932
|
+
} else if (what === 9) {
|
|
12933
|
+
const absent = nodes.filter((id) => !docs.has(id));
|
|
12934
|
+
if (absent.length > 0 && (docs.size <= 2 || rng.chance(.65))) {
|
|
12935
|
+
const id = rng.pick(absent);
|
|
12936
|
+
const doc = make();
|
|
12937
|
+
docs.set(id, doc);
|
|
12938
|
+
host.set(id, doc);
|
|
12939
|
+
counts.additions += 1;
|
|
12940
|
+
} else {
|
|
12941
|
+
const id = rng.pick([...docs.keys()]);
|
|
12942
|
+
docs.delete(id);
|
|
12943
|
+
host.remove(id);
|
|
12944
|
+
counts.removes += 1;
|
|
12945
|
+
}
|
|
12946
|
+
} else if (what === 10) {
|
|
12947
|
+
const id = rng.pick([...docs.keys()]);
|
|
12948
|
+
host.set(id, docs.get(id));
|
|
12949
|
+
counts.same += 1;
|
|
12950
|
+
} else if (host.changeMachine !== void 0) {
|
|
12951
|
+
at = (at + 1) % machines.length;
|
|
12952
|
+
machine = machines[at];
|
|
12953
|
+
host.changeMachine(machine);
|
|
12954
|
+
counts.machines += 1;
|
|
12955
|
+
}
|
|
12956
|
+
}
|
|
11904
12957
|
}
|
|
11905
|
-
|
|
11906
|
-
|
|
11907
|
-
|
|
11908
|
-
if (items === void 0 || item === void 0 || !waiting.includes(item)) return void 0;
|
|
11909
|
-
items.shift();
|
|
11910
|
-
return item;
|
|
11911
|
-
} };
|
|
11912
|
-
const perform = (effect) => {
|
|
11913
|
-
const entry = effect.address === void 0 ? void 0 : entries.get(effect.address);
|
|
11914
|
-
const wrong = mismatch(effect, entry);
|
|
11915
|
-
if (wrong !== void 0 || entry === void 0) throw wrong;
|
|
11916
|
-
const outcome = entry.outcome;
|
|
11917
|
-
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);
|
|
11918
|
-
const value = outcome?.ok === true ? outcome.value : void 0;
|
|
11919
|
-
if (wait && entry.waited) return failure === void 0 ? Promise.resolve(value) : Promise.reject(failure);
|
|
11920
|
-
if (failure !== void 0) throw failure;
|
|
11921
|
-
return value;
|
|
12958
|
+
return {
|
|
12959
|
+
ok: true,
|
|
12960
|
+
counts
|
|
11922
12961
|
};
|
|
11923
|
-
return Object.assign(perform, { [JOURNAL]: journal });
|
|
11924
|
-
}
|
|
11925
|
-
/** This function gives the error of an effect that the log does not have at its address, or `undefined`. */
|
|
11926
|
-
function mismatch(effect, entry) {
|
|
11927
|
-
const at = effect.address ?? "";
|
|
11928
|
-
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}.`);
|
|
11929
|
-
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}.`);
|
|
11930
|
-
if (!docEquals(entry.payload, effect.payload)) return replayError(effect, `The effect ${effect.kind} at the address ${at} has another payload than the log.`);
|
|
11931
|
-
}
|
|
11932
|
-
/** This function gives the exception that carries `#REPLAY!` to the kernel, with the place of the effect. */
|
|
11933
|
-
function replayError(effect, reason) {
|
|
11934
|
-
return new Failure(fail("#REPLAY!", reason, { place: effect.place }).error);
|
|
11935
12962
|
}
|
|
11936
12963
|
//#endregion
|
|
11937
12964
|
//#region src/providers.ts
|
|
@@ -12244,6 +13271,6 @@ function typeFromSamples(values, options = {}) {
|
|
|
12244
13271
|
return samplesAt(values, options.closed === true, 0);
|
|
12245
13272
|
}
|
|
12246
13273
|
//#endregion
|
|
12247
|
-
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 };
|
|
13274
|
+
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, agreeSchedule, 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, placeOf, 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 };
|
|
12248
13275
|
|
|
12249
13276
|
//# sourceMappingURL=index.js.map
|