@mark1russell7/coeval 0.15.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 +315 -87
- package/build/index.d.ts.map +1 -1
- package/build/index.js +728 -32
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/agree.ts +66 -6
- package/src/deref.ts +27 -9
- package/src/evaluate.ts +198 -16
- package/src/index.ts +10 -0
- package/src/meaning.ts +472 -0
- package/src/reads.ts +9 -2
- 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);
|
|
@@ -3084,25 +3095,85 @@ const WITHOUT_HOPS = /* @__PURE__ */ Object.freeze({ hops: false });
|
|
|
3084
3095
|
const WITH_HOPS = /* @__PURE__ */ Object.freeze({ hops: true });
|
|
3085
3096
|
/**
|
|
3086
3097
|
* 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
|
|
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.
|
|
3088
3100
|
* A reference in the data reads the context of its reader, thus the function then gives `undefined`.
|
|
3089
3101
|
*
|
|
3090
3102
|
* @param nav - The world and the code of the evaluators.
|
|
3091
3103
|
* @param root - The context at the root of the evaluation.
|
|
3092
3104
|
* @param path - The root and the keys of the static read.
|
|
3093
|
-
* @returns The path of the kept 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.
|
|
3094
3106
|
*/
|
|
3095
3107
|
function keptPath(nav, root, path) {
|
|
3096
3108
|
const adopt = nav.adopt ?? ((value) => value);
|
|
3097
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
|
+
};
|
|
3098
3118
|
for (let index = 1;; index += 1) {
|
|
3099
|
-
if (isHostThing(here))
|
|
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
|
+
}
|
|
3100
3135
|
if (nav.plain !== true && isRef(here)) return void 0;
|
|
3101
|
-
if (index >= path.length) return
|
|
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();
|
|
3102
3171
|
const reached = derefStep(nav, here, path[index]);
|
|
3103
|
-
if (reached === void 0) return
|
|
3172
|
+
if (reached === void 0) return other();
|
|
3104
3173
|
here = adopt(reached.thing);
|
|
3105
3174
|
}
|
|
3175
|
+
if (!known) return other();
|
|
3176
|
+
return nav.plain !== true && isRef(here) ? void 0 : ok(here);
|
|
3106
3177
|
}
|
|
3107
3178
|
/** This function makes the record of the reads of an evaluation, when the host gives a list. */
|
|
3108
3179
|
function readingOf(list, root) {
|
|
@@ -3233,8 +3304,9 @@ var Session = class {
|
|
|
3233
3304
|
return true;
|
|
3234
3305
|
}
|
|
3235
3306
|
if (root === "self" && reading.self) return true;
|
|
3236
|
-
const
|
|
3237
|
-
if (
|
|
3307
|
+
const walk = keptPath(this.machine, reading.root, read.exact ? exact : [root]);
|
|
3308
|
+
if (walk === void 0) return false;
|
|
3309
|
+
const path = walk.path;
|
|
3238
3310
|
const paths = reading.paths ??= /* @__PURE__ */ new Set();
|
|
3239
3311
|
const text = JSON.stringify(path);
|
|
3240
3312
|
if (paths.has(text) || paths.has(JSON.stringify([root]))) return true;
|
|
@@ -3242,7 +3314,7 @@ var Session = class {
|
|
|
3242
3314
|
if (path.length === 1 && root === "self") reading.self = true;
|
|
3243
3315
|
reading.list.push({
|
|
3244
3316
|
path,
|
|
3245
|
-
reached: readAgain(this.machine, reading.root, path, false).reached,
|
|
3317
|
+
reached: walk.reached ?? readAgain(this.machine, reading.root, path, false).reached,
|
|
3246
3318
|
free: true,
|
|
3247
3319
|
kept: true
|
|
3248
3320
|
});
|
|
@@ -3734,7 +3806,10 @@ function compileNode(c, value, place, declared, depth, address = false) {
|
|
|
3734
3806
|
if (frees === void 0 || kind === void 0 || !pureCode(c, envelope, kind, typePath, name, quick)) return done;
|
|
3735
3807
|
const free = freeOf(doc, kind, split.parts, frees);
|
|
3736
3808
|
if (free === void 0) return done;
|
|
3737
|
-
|
|
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 {
|
|
3738
3813
|
code: done.code,
|
|
3739
3814
|
run: done.run,
|
|
3740
3815
|
free
|
|
@@ -3814,6 +3889,21 @@ var Probe = class {
|
|
|
3814
3889
|
};
|
|
3815
3890
|
this.root = root;
|
|
3816
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
|
+
}
|
|
3817
3907
|
resolve(ctx, path) {
|
|
3818
3908
|
const name = path[1];
|
|
3819
3909
|
if (path[0] === "names" && isKey(name) && Object.hasOwn(ctx.names, String(name))) {
|
|
@@ -3872,13 +3962,14 @@ function trap(refuse, copies) {
|
|
|
3872
3962
|
* one deeply frozen copy, and the code gives that copy at each evaluation (PLAN.SHARED). A fold that the probe cancels
|
|
3873
3963
|
* gives the code without free names, thus no parent tries the fold again.
|
|
3874
3964
|
*/
|
|
3875
|
-
function folded(c, done) {
|
|
3965
|
+
function folded(c, done, fixed) {
|
|
3876
3966
|
const probe = c.probe ??= new Probe(c.machine);
|
|
3877
3967
|
probe.refused = false;
|
|
3878
3968
|
probe.reads = 0;
|
|
3879
3969
|
let constant;
|
|
3880
3970
|
try {
|
|
3881
|
-
const
|
|
3971
|
+
const root = fixed === void 0 ? probe.root : probe.fixedRoot(c.fixed);
|
|
3972
|
+
const result = done.code(root, probe);
|
|
3882
3973
|
if (!probe.refused && (result.ok || result.error.code !== "#DEPTH!")) constant = sharedCopy(result);
|
|
3883
3974
|
} catch {
|
|
3884
3975
|
constant = void 0;
|
|
@@ -3888,18 +3979,100 @@ function folded(c, done) {
|
|
|
3888
3979
|
run: done.run
|
|
3889
3980
|
};
|
|
3890
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
|
+
};
|
|
3891
3988
|
if (probe.reads === 0) return {
|
|
3892
3989
|
code: () => value,
|
|
3893
3990
|
run: void 0,
|
|
3894
3991
|
free: CLOSED
|
|
3895
3992
|
};
|
|
3896
|
-
const original = done.code;
|
|
3897
3993
|
return {
|
|
3898
3994
|
code: (ctx, rt) => rt instanceof Session && !rt.traced && !rt.recording ? value : original(ctx, rt),
|
|
3899
3995
|
run: void 0,
|
|
3900
3996
|
free: CLOSED
|
|
3901
3997
|
};
|
|
3902
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
|
+
}
|
|
3903
4076
|
/** The objects that a fold made. Each one is deeply frozen, thus a later fold uses it again and does not copy it. */
|
|
3904
4077
|
const SHARED = /* @__PURE__ */ new WeakSet();
|
|
3905
4078
|
/** The mark of a value that is not plain data. */
|
|
@@ -3907,7 +4080,11 @@ const NOT_PLAIN = /* @__PURE__ */ Symbol("not plain");
|
|
|
3907
4080
|
/**
|
|
3908
4081
|
* This function gives a deeply frozen copy of plain data, or `undefined` for other values. Plain data is a primitive or
|
|
3909
4082
|
* an array without holes and extra keys. It is also a record of enumerable data fields with the prototype of an object
|
|
3910
|
-
* 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`.
|
|
3911
4088
|
*/
|
|
3912
4089
|
function sharedCopy(value) {
|
|
3913
4090
|
try {
|
|
@@ -4145,13 +4322,16 @@ function prepare(machine, doc, options = {}) {
|
|
|
4145
4322
|
steps: []
|
|
4146
4323
|
};
|
|
4147
4324
|
const traced = options.trace === true;
|
|
4325
|
+
const fold = !traced && options.fold !== false;
|
|
4326
|
+
const fixed = fold ? fixedNamesOf(options.fixed?.names) : void 0;
|
|
4148
4327
|
const c = {
|
|
4149
4328
|
machine,
|
|
4150
4329
|
traced,
|
|
4151
4330
|
maxDepth: machine.maxDepth ?? 1e3,
|
|
4152
4331
|
verb,
|
|
4153
4332
|
noAnswer,
|
|
4154
|
-
fold
|
|
4333
|
+
fold,
|
|
4334
|
+
fixed
|
|
4155
4335
|
};
|
|
4156
4336
|
let root;
|
|
4157
4337
|
try {
|
|
@@ -4930,6 +5110,17 @@ function treeThing(input, build, outer) {
|
|
|
4930
5110
|
function attributeTrace(node, attribute) {
|
|
4931
5111
|
return stateOf(node)?.trace(attribute);
|
|
4932
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
|
+
}
|
|
4933
5124
|
/** The path of the root `self`. A node of the tree resolves it, thus the list of reads holds the read (C4). */
|
|
4934
5125
|
const SELF_PATH = /* @__PURE__ */ Object.freeze(["self"]);
|
|
4935
5126
|
/** The options of the resolution of `self`: the node needs no hops. */
|
|
@@ -4958,21 +5149,24 @@ function hostType(node, wanted) {
|
|
|
4958
5149
|
/**
|
|
4959
5150
|
* This function makes a walker `nearest`. The walker follows a key (the default is `parent`) until the node that it
|
|
4960
5151
|
* reaches has the wanted type. With `self: true`, the node where the walk starts is the first candidate. A node
|
|
4961
|
-
* 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.
|
|
4962
5153
|
*
|
|
4963
|
-
* @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.
|
|
4964
5155
|
* @returns The walker.
|
|
4965
5156
|
*/
|
|
4966
5157
|
function nearestWalker(options = {}) {
|
|
4967
5158
|
const matches = options.matches ?? hostType;
|
|
4968
5159
|
const maxHops = options.maxHops ?? 256;
|
|
4969
|
-
|
|
5160
|
+
const eachStep = options.eachStep === true;
|
|
5161
|
+
return ({ data, here, deref, visit }) => {
|
|
4970
5162
|
const follow = isRecord(data) && typeof field(data, "follow") === "string" ? String(field(data, "follow")) : "parent";
|
|
4971
5163
|
const wanted = isRecord(data) ? field(data, "type") : void 0;
|
|
4972
5164
|
const fromSelf = isRecord(data) && field(data, "self") === true;
|
|
5165
|
+
const record = eachStep ? visit : void 0;
|
|
4973
5166
|
let at = here;
|
|
4974
5167
|
for (let hops = 0; hops < maxHops; hops += 1) {
|
|
4975
|
-
|
|
5168
|
+
const stepped = hops > 0 || !fromSelf;
|
|
5169
|
+
if (stepped) at = at === void 0 || at === null ? void 0 : deref(at, follow);
|
|
4976
5170
|
if (at === void 0 || at === null) return {
|
|
4977
5171
|
ok: false,
|
|
4978
5172
|
reason: `No node of the type ${String(wanted)} is on the way.`
|
|
@@ -4981,6 +5175,7 @@ function nearestWalker(options = {}) {
|
|
|
4981
5175
|
ok: true,
|
|
4982
5176
|
thing: at
|
|
4983
5177
|
};
|
|
5178
|
+
if (stepped) record?.(follow, at);
|
|
4984
5179
|
}
|
|
4985
5180
|
return {
|
|
4986
5181
|
ok: false,
|
|
@@ -9762,7 +9957,8 @@ const DEFAULT_TIERS = /* @__PURE__ */ Object.freeze([
|
|
|
9762
9957
|
"reference",
|
|
9763
9958
|
"traced",
|
|
9764
9959
|
"again",
|
|
9765
|
-
"folds"
|
|
9960
|
+
"folds",
|
|
9961
|
+
"fixed"
|
|
9766
9962
|
]);
|
|
9767
9963
|
/**
|
|
9768
9964
|
* This function tells if two values are the same for `agree`. Lists and plain records compare item by item and field
|
|
@@ -9791,6 +9987,7 @@ function sameInTiers(x, y) {
|
|
|
9791
9987
|
if (x instanceof Held || y instanceof Held) return x instanceof Held && y instanceof Held && errorDiffers(x.error, y.error, false) === void 0;
|
|
9792
9988
|
}
|
|
9793
9989
|
const CLOSURE = /* @__PURE__ */ Symbol.for("coeval.closure");
|
|
9990
|
+
/** This function gives the parameters of a closure of `fn`, or `undefined` for another function. */
|
|
9794
9991
|
function closureParams(fn) {
|
|
9795
9992
|
return fn[CLOSURE]?.params;
|
|
9796
9993
|
}
|
|
@@ -9830,6 +10027,67 @@ function effectsDiffer(a, b, options) {
|
|
|
9830
10027
|
}
|
|
9831
10028
|
}
|
|
9832
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
|
+
/**
|
|
9833
10091
|
* This function evaluates a document with a plan and with other tiers, and compares the results (PLAN.AGREES). Two
|
|
9834
10092
|
* results agree when they have the same value by `sameValue`, or errors with the same code, and the same place when
|
|
9835
10093
|
* both have one. The effects must have the same kinds, places and payloads, in the same order. A language gives its
|
|
@@ -9886,6 +10144,19 @@ function agree(machine, doc, context = makeContext(), options = {}) {
|
|
|
9886
10144
|
});
|
|
9887
10145
|
other = traced.result;
|
|
9888
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;
|
|
9889
10160
|
} else if (tier === "folds") {
|
|
9890
10161
|
other = prepare(machine, doc, {
|
|
9891
10162
|
...declared,
|
|
@@ -10015,6 +10286,7 @@ async function agreeRun(machine, doc, context = makeContext(), options = {}) {
|
|
|
10015
10286
|
const saw = observe?.();
|
|
10016
10287
|
const log = recording.log();
|
|
10017
10288
|
for (const tier of options.tiers ?? DEFAULT_TIERS) {
|
|
10289
|
+
if (tier === "fixed") continue;
|
|
10018
10290
|
const replay = watched(replayer(log));
|
|
10019
10291
|
const host = { perform: replay.perform };
|
|
10020
10292
|
let other;
|
|
@@ -11494,6 +11766,22 @@ function verifyReads(target, reads, context, options = {}) {
|
|
|
11494
11766
|
read,
|
|
11495
11767
|
now: stop
|
|
11496
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
|
+
}
|
|
11497
11785
|
const asked = read.hops !== void 0;
|
|
11498
11786
|
const now = readAgain(machine, root, read.path, asked);
|
|
11499
11787
|
if (!sameResult(read.reached, now.reached, equal) || read.hops !== void 0 && !sameHops(read.hops, now.hops, equal)) return {
|
|
@@ -11766,7 +12054,7 @@ function cyclic(graph, members) {
|
|
|
11766
12054
|
return false;
|
|
11767
12055
|
}
|
|
11768
12056
|
/** This function gives the names of the nodes of a component, without the node `all`. */
|
|
11769
|
-
function named(graph, members) {
|
|
12057
|
+
function named$1(graph, members) {
|
|
11770
12058
|
const out = [];
|
|
11771
12059
|
for (const member of members) if (member !== graph.all) out.push(graph.ids[member]);
|
|
11772
12060
|
return out;
|
|
@@ -11805,7 +12093,7 @@ function components(graph, options = {}) {
|
|
|
11805
12093
|
const index = indexed(graph, options);
|
|
11806
12094
|
const out = [];
|
|
11807
12095
|
for (const members of strong(index).list) {
|
|
11808
|
-
const names = named(index, members);
|
|
12096
|
+
const names = named$1(index, members);
|
|
11809
12097
|
if (names.length > 0) out.push(names);
|
|
11810
12098
|
}
|
|
11811
12099
|
return out;
|
|
@@ -11912,7 +12200,7 @@ function cycles(graph) {
|
|
|
11912
12200
|
const out = [];
|
|
11913
12201
|
found.list.forEach((members, component) => {
|
|
11914
12202
|
if (!cyclic(every, members)) return;
|
|
11915
|
-
const nodes = named(every, members);
|
|
12203
|
+
const nodes = named$1(every, members);
|
|
11916
12204
|
const certain = inside.get(component) ?? [];
|
|
11917
12205
|
if (certain.length === 1 && certain[0]?.length === nodes.length) {
|
|
11918
12206
|
out.push({
|
|
@@ -11926,7 +12214,7 @@ function cycles(graph) {
|
|
|
11926
12214
|
certain: false
|
|
11927
12215
|
});
|
|
11928
12216
|
for (const part of certain) out.push({
|
|
11929
|
-
nodes: named(eager, part),
|
|
12217
|
+
nodes: named$1(eager, part),
|
|
11930
12218
|
certain: true
|
|
11931
12219
|
});
|
|
11932
12220
|
});
|
|
@@ -12168,10 +12456,12 @@ function schedule(machine, options = {}) {
|
|
|
12168
12456
|
};
|
|
12169
12457
|
/**
|
|
12170
12458
|
* This function gives the state of the current data epoch. Each data epoch has a new host thing at the root
|
|
12171
|
-
* `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).
|
|
12172
12461
|
*/
|
|
12173
12462
|
const current = () => {
|
|
12174
12463
|
if (now !== void 0) return now;
|
|
12464
|
+
const values = /* @__PURE__ */ new Map();
|
|
12175
12465
|
const nodesThing = {
|
|
12176
12466
|
[THING]: true,
|
|
12177
12467
|
label: "nodes",
|
|
@@ -12183,14 +12473,20 @@ function schedule(machine, options = {}) {
|
|
|
12183
12473
|
return result.value;
|
|
12184
12474
|
},
|
|
12185
12475
|
has: (key) => docs.has(String(key)),
|
|
12186
|
-
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
|
+
}
|
|
12187
12483
|
};
|
|
12188
12484
|
now = {
|
|
12189
12485
|
context: rootContext(machine, {
|
|
12190
12486
|
...base,
|
|
12191
12487
|
world: nodesThing
|
|
12192
12488
|
}),
|
|
12193
|
-
values
|
|
12489
|
+
values
|
|
12194
12490
|
};
|
|
12195
12491
|
return now;
|
|
12196
12492
|
};
|
|
@@ -12265,6 +12561,406 @@ function schedule(machine, options = {}) {
|
|
|
12265
12561
|
};
|
|
12266
12562
|
}
|
|
12267
12563
|
//#endregion
|
|
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
|
+
}
|
|
12692
|
+
/**
|
|
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.
|
|
12695
|
+
*/
|
|
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));
|
|
12708
|
+
};
|
|
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;
|
|
12735
|
+
};
|
|
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
|
+
}
|
|
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;
|
|
12767
|
+
}
|
|
12768
|
+
/**
|
|
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.
|
|
12772
|
+
*
|
|
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.
|
|
12778
|
+
*
|
|
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.
|
|
12784
|
+
*/
|
|
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
|
+
}
|
|
12957
|
+
}
|
|
12958
|
+
return {
|
|
12959
|
+
ok: true,
|
|
12960
|
+
counts
|
|
12961
|
+
};
|
|
12962
|
+
}
|
|
12963
|
+
//#endregion
|
|
12268
12964
|
//#region src/providers.ts
|
|
12269
12965
|
/** The deepest schema that `typeFromSchema` reads. A deeper schema reads as `unknown`. */
|
|
12270
12966
|
const MAX_SCHEMA_DEPTH = 256;
|
|
@@ -12575,6 +13271,6 @@ function typeFromSamples(values, options = {}) {
|
|
|
12575
13271
|
return samplesAt(values, options.closed === true, 0);
|
|
12576
13272
|
}
|
|
12577
13273
|
//#endregion
|
|
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 };
|
|
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 };
|
|
12579
13275
|
|
|
12580
13276
|
//# sourceMappingURL=index.js.map
|