@mark1russell7/coeval 0.6.1 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/build/index.d.ts +189 -22
- package/build/index.d.ts.map +1 -1
- package/build/index.js +390 -105
- package/build/index.js.map +1 -1
- package/package.json +2 -1
- package/src/agree.ts +44 -55
- package/src/deref.ts +17 -6
- package/src/doc.ts +23 -2
- package/src/errors.ts +2 -1
- package/src/evaluate.ts +198 -27
- package/src/formula.ts +2 -2
- package/src/index.ts +6 -1
- package/src/merge.ts +2 -2
- package/src/parts.ts +1 -1
- package/src/reference.ts +86 -15
- package/src/replay.ts +159 -0
- package/src/same.ts +42 -0
- package/src/static.ts +1 -1
- package/src/stdlib.ts +10 -6
package/build/index.js
CHANGED
|
@@ -184,19 +184,36 @@ function fromJson(value) {
|
|
|
184
184
|
* with this function does not keep a stale host value.
|
|
185
185
|
*
|
|
186
186
|
* The function does not use the stack for the depth, thus a deep document cannot overflow it. A cycle of lists or
|
|
187
|
-
* records does not make it loop. `sameValue` of `agree`
|
|
188
|
-
*
|
|
187
|
+
* records does not make it loop. `sameValue` of `agree` and `sameResult` use the same walk, and each adds its rules
|
|
188
|
+
* for other values (D20).
|
|
189
189
|
*
|
|
190
190
|
* @param a - The first value.
|
|
191
191
|
* @param b - The second value.
|
|
192
192
|
* @returns `true` when the values are equal.
|
|
193
193
|
*/
|
|
194
194
|
function docEquals(a, b) {
|
|
195
|
+
return equalWith(a, b, void 0);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* This function is the one walk of `docEquals`, `sameValue` and `sameResult` (D20). It compares two values by the
|
|
199
|
+
* rules of `docEquals`. A rule of the caller answers first for each pair that is not the same by `Object.is`.
|
|
200
|
+
*
|
|
201
|
+
* @param a - The first value.
|
|
202
|
+
* @param b - The second value.
|
|
203
|
+
* @param rule - The rule of the caller. Its `undefined` gives the rules of `docEquals`.
|
|
204
|
+
* @returns `true` when the values are equal.
|
|
205
|
+
*/
|
|
206
|
+
function equalWith(a, b, rule) {
|
|
195
207
|
const stack = [[a, b]];
|
|
196
208
|
let seen;
|
|
197
209
|
while (stack.length > 0) {
|
|
198
210
|
const [x, y] = stack.pop();
|
|
199
211
|
if (Object.is(x, y)) continue;
|
|
212
|
+
const told = rule?.(x, y);
|
|
213
|
+
if (told !== void 0) {
|
|
214
|
+
if (told) continue;
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
200
217
|
const list = Array.isArray(x);
|
|
201
218
|
if (list !== Array.isArray(y)) return false;
|
|
202
219
|
if (!list && (!isRecord(x) || !isRecord(y))) return false;
|
|
@@ -1088,7 +1105,7 @@ function ask(world, doc, verb, options = {}) {
|
|
|
1088
1105
|
* The brand of a host thing: an object of the host that answers the steps of a path itself. The brand is a
|
|
1089
1106
|
* global symbol, thus two copies of the kernel in one program know the things of each other.
|
|
1090
1107
|
*/
|
|
1091
|
-
const THING = Symbol.for("coeval.thing");
|
|
1108
|
+
const THING = /* @__PURE__ */ Symbol.for("coeval.thing");
|
|
1092
1109
|
/**
|
|
1093
1110
|
* This function tells if a thing is a host thing.
|
|
1094
1111
|
*
|
|
@@ -1143,10 +1160,10 @@ function plainStep(thing, key) {
|
|
|
1143
1160
|
function derefStep(nav, thing, key) {
|
|
1144
1161
|
if (isHostThing(thing)) {
|
|
1145
1162
|
const reached = thing.deref(key);
|
|
1146
|
-
return reached
|
|
1163
|
+
return reached !== void 0 || thing.has?.(key) === true ? {
|
|
1147
1164
|
thing: reached,
|
|
1148
1165
|
by: "own"
|
|
1149
|
-
};
|
|
1166
|
+
} : void 0;
|
|
1150
1167
|
}
|
|
1151
1168
|
if (nav.plain !== true && isEnvelope(thing)) {
|
|
1152
1169
|
const envelope = thing;
|
|
@@ -1226,7 +1243,7 @@ function context(parts = {}) {
|
|
|
1226
1243
|
};
|
|
1227
1244
|
}
|
|
1228
1245
|
const MAX_COLLAPSE = 64;
|
|
1229
|
-
const NO_HOPS = Object.freeze([]);
|
|
1246
|
+
const NO_HOPS = /* @__PURE__ */ Object.freeze([]);
|
|
1230
1247
|
/** A value that can be a reference document: a record with the type `ref`. The full check comes after. */
|
|
1231
1248
|
function mayBeRef(thing) {
|
|
1232
1249
|
return typeof thing === "object" && thing !== null && thing.type === "ref";
|
|
@@ -1492,7 +1509,7 @@ function setOwn$1(target, key, value) {
|
|
|
1492
1509
|
});
|
|
1493
1510
|
else target[key] = value;
|
|
1494
1511
|
}
|
|
1495
|
-
const NO_SLOTS = Object.freeze([]);
|
|
1512
|
+
const NO_SLOTS = /* @__PURE__ */ Object.freeze([]);
|
|
1496
1513
|
function hasSlots(template) {
|
|
1497
1514
|
return template.t !== "data";
|
|
1498
1515
|
}
|
|
@@ -1798,9 +1815,9 @@ function narrow(parent, next) {
|
|
|
1798
1815
|
};
|
|
1799
1816
|
}
|
|
1800
1817
|
/** No capabilities. One list serves each masked part. */
|
|
1801
|
-
const NO_CAPS = Object.freeze([]);
|
|
1818
|
+
const NO_CAPS = /* @__PURE__ */ Object.freeze([]);
|
|
1802
1819
|
/** No capability rules. One list serves each part that no rule narrows. */
|
|
1803
|
-
const NO_RULES = Object.freeze([]);
|
|
1820
|
+
const NO_RULES = /* @__PURE__ */ Object.freeze([]);
|
|
1804
1821
|
/**
|
|
1805
1822
|
* This function gives the capabilities that two lists both permit. An effect is permitted by the result when each
|
|
1806
1823
|
* list permits it. When the list `b` permits each capability of `a`, the result is the list `a` itself.
|
|
@@ -1843,6 +1860,43 @@ function capsUnder(rules, caps) {
|
|
|
1843
1860
|
function partCaps(rules, path, caps) {
|
|
1844
1861
|
return capsUnder(rulesFor(rules, path), caps);
|
|
1845
1862
|
}
|
|
1863
|
+
/** The key of the journal of a recorder or of a replayer, on its function. Two copies of the kernel share it. */
|
|
1864
|
+
const JOURNAL = /* @__PURE__ */ Symbol.for("coeval.journal");
|
|
1865
|
+
/** This function gives the journal of a function `perform`, when it has one. */
|
|
1866
|
+
function journalOf(perform) {
|
|
1867
|
+
return perform === void 0 ? void 0 : perform[JOURNAL];
|
|
1868
|
+
}
|
|
1869
|
+
/**
|
|
1870
|
+
* This function gives the outcome of an effect that the host failed. A `Failure` gives its own error value, for
|
|
1871
|
+
* example the recorded error of a replay or `#REPLAY!`. Each other exception gives `#CALC!`, with the exception as the
|
|
1872
|
+
* cause (RUN.CAUSE).
|
|
1873
|
+
*/
|
|
1874
|
+
function hostFailed(kind, place, error) {
|
|
1875
|
+
if (error instanceof Failure) return placed({
|
|
1876
|
+
ok: false,
|
|
1877
|
+
error: error.error
|
|
1878
|
+
}, place);
|
|
1879
|
+
return fail("#CALC!", `The host failed the effect ${kind}: ${describe(error)}`, {
|
|
1880
|
+
place,
|
|
1881
|
+
cause: error
|
|
1882
|
+
});
|
|
1883
|
+
}
|
|
1884
|
+
/**
|
|
1885
|
+
* This function gives the text of a place in an address: each key, with a slash between two keys. A text key with
|
|
1886
|
+
* other characters than letters, digits, `_`, `$` and `-` gets quotes.
|
|
1887
|
+
*
|
|
1888
|
+
* @param place - The keys from the root document to a document.
|
|
1889
|
+
* @returns The text of the place.
|
|
1890
|
+
*/
|
|
1891
|
+
function placeText(place) {
|
|
1892
|
+
let text = "";
|
|
1893
|
+
for (let index = 0; index < place.length; index += 1) {
|
|
1894
|
+
const key = place[index];
|
|
1895
|
+
if (index > 0) text += "/";
|
|
1896
|
+
text += typeof key === "number" || /^[\w$-]+$/.test(key) ? String(key) : JSON.stringify(key);
|
|
1897
|
+
}
|
|
1898
|
+
return text;
|
|
1899
|
+
}
|
|
1846
1900
|
function selfLabel(self) {
|
|
1847
1901
|
try {
|
|
1848
1902
|
if (isHostThing(self)) return self.label;
|
|
@@ -1858,8 +1912,8 @@ const NO_ANSWER = {
|
|
|
1858
1912
|
steps: []
|
|
1859
1913
|
};
|
|
1860
1914
|
/** The options of a resolution without a record of hops, and with one. One object of each serves each call. */
|
|
1861
|
-
const WITHOUT_HOPS = Object.freeze({ hops: false });
|
|
1862
|
-
const WITH_HOPS = Object.freeze({ hops: true });
|
|
1915
|
+
const WITHOUT_HOPS = /* @__PURE__ */ Object.freeze({ hops: false });
|
|
1916
|
+
const WITH_HOPS = /* @__PURE__ */ Object.freeze({ hops: true });
|
|
1863
1917
|
/** This class holds the state of one evaluation or run: the machine, the effects, the clock and the trace. */
|
|
1864
1918
|
var Session = class {
|
|
1865
1919
|
machine;
|
|
@@ -1873,6 +1927,11 @@ var Session = class {
|
|
|
1873
1927
|
nextId = 0;
|
|
1874
1928
|
current;
|
|
1875
1929
|
holder;
|
|
1930
|
+
/**
|
|
1931
|
+
* The scope of the addresses of the code that runs at this time (RUN.ADDRESS). `all` changes it for each item. The
|
|
1932
|
+
* root scope is made at the first effect or `all`, thus an evaluation without effects makes no scope.
|
|
1933
|
+
*/
|
|
1934
|
+
scope;
|
|
1876
1935
|
constructor(machine, running, traced, perform, signal, effects) {
|
|
1877
1936
|
this.machine = machine;
|
|
1878
1937
|
this.running = running;
|
|
@@ -1894,12 +1953,22 @@ var Session = class {
|
|
|
1894
1953
|
permitted(ctx, keys) {
|
|
1895
1954
|
return this.machine.permits === void 0 ? permits(ctx.caps, keys) : this.machine.permits(ctx.caps, keys, ctx);
|
|
1896
1955
|
}
|
|
1956
|
+
/** This function checks the capability of an effect, and records the effect with its address (RUN.ADDRESS). */
|
|
1897
1957
|
record(ctx, kind, payload, place) {
|
|
1898
1958
|
const keys = typeof kind === "string" ? kind.split(".") : kind;
|
|
1899
1959
|
const text = keys.join(".");
|
|
1900
1960
|
if (!this.permitted(ctx, keys)) return fail("#CAP!", `The context has no capability for the effect ${text}.`, { place });
|
|
1901
1961
|
const node = this.current;
|
|
1902
1962
|
const list = this.effects;
|
|
1963
|
+
const scope = this.scope ??= {
|
|
1964
|
+
prefix: "",
|
|
1965
|
+
counts: void 0,
|
|
1966
|
+
alls: 0
|
|
1967
|
+
};
|
|
1968
|
+
const at = placeText(place);
|
|
1969
|
+
const counts = scope.counts ??= /* @__PURE__ */ new Map();
|
|
1970
|
+
const count = counts.get(at) ?? 0;
|
|
1971
|
+
counts.set(at, count + 1);
|
|
1903
1972
|
const effect = {
|
|
1904
1973
|
index: list.length,
|
|
1905
1974
|
kind: text,
|
|
@@ -1908,40 +1977,63 @@ var Session = class {
|
|
|
1908
1977
|
node: node?.id ?? -1,
|
|
1909
1978
|
at: this.tick,
|
|
1910
1979
|
place,
|
|
1911
|
-
context: ctx
|
|
1980
|
+
context: ctx,
|
|
1981
|
+
address: `${scope.prefix}${at}#${count}`
|
|
1912
1982
|
};
|
|
1913
1983
|
list.push(effect);
|
|
1914
1984
|
if (node !== void 0) node.effects.push(effect.index);
|
|
1915
1985
|
return effect;
|
|
1916
1986
|
}
|
|
1987
|
+
/**
|
|
1988
|
+
* This function answers an effect at once, without a wait: the mock gives the result, or the effect gives its
|
|
1989
|
+
* payload without a mock (RUN.MOCK, RUN.EVAL). A promise from the mock gives `#CALC!`.
|
|
1990
|
+
*/
|
|
1991
|
+
answer(effect, place) {
|
|
1992
|
+
const perform = this.perform;
|
|
1993
|
+
if (perform === void 0) return effect.outcome = ok(effect.payload);
|
|
1994
|
+
let done;
|
|
1995
|
+
try {
|
|
1996
|
+
done = perform(effect);
|
|
1997
|
+
} catch (error) {
|
|
1998
|
+
return effect.outcome = hostFailed(effect.kind, place, error);
|
|
1999
|
+
}
|
|
2000
|
+
if (!(done instanceof Promise)) return effect.outcome = ok(done);
|
|
2001
|
+
done.catch(() => void 0);
|
|
2002
|
+
return effect.outcome = fail("#CALC!", `The mock gave a promise for the effect ${effect.kind}, and an evaluation does not wait.`, { place });
|
|
2003
|
+
}
|
|
1917
2004
|
effect(ctx, kind, payload, place) {
|
|
1918
2005
|
const text = typeof kind === "string" ? kind : kind.join(".");
|
|
1919
2006
|
if (this.running) return fail("#CALC!", `The effect ${text} needs a runner: in a run, the host does each effect.`, { place });
|
|
1920
2007
|
const made = this.record(ctx, kind, payload, place);
|
|
1921
|
-
return "ok" in made ? made :
|
|
2008
|
+
return "ok" in made ? made : this.answer(made, place);
|
|
1922
2009
|
}
|
|
1923
2010
|
*runEffect(ctx, kind, payload, place) {
|
|
1924
2011
|
const made = this.record(ctx, kind, payload, place);
|
|
1925
2012
|
if ("ok" in made) return made;
|
|
1926
2013
|
const perform = this.perform;
|
|
1927
|
-
if (!this.running || perform === void 0) return
|
|
1928
|
-
const failed = (error) => fail("#CALC!", `The host failed the effect ${made.kind}: ${describe(error)}`, {
|
|
1929
|
-
place,
|
|
1930
|
-
cause: error
|
|
1931
|
-
});
|
|
2014
|
+
if (!this.running || perform === void 0) return this.answer(made, place);
|
|
1932
2015
|
let done;
|
|
1933
2016
|
try {
|
|
1934
2017
|
done = perform(made);
|
|
1935
2018
|
} catch (error) {
|
|
1936
|
-
return
|
|
2019
|
+
return made.outcome = hostFailed(made.kind, place, error);
|
|
1937
2020
|
}
|
|
1938
|
-
if (!(done instanceof Promise)) return ok(done);
|
|
1939
|
-
return yield done.then((value) => ok(value),
|
|
2021
|
+
if (!(done instanceof Promise)) return made.outcome = ok(done);
|
|
2022
|
+
return made.outcome = yield done.then((value) => ok(value), (error) => hostFailed(made.kind, place, error));
|
|
1940
2023
|
}
|
|
1941
2024
|
*all(list, options = {}) {
|
|
1942
2025
|
const results = new Array(list.length);
|
|
1943
2026
|
const limit = Math.max(1, options.concurrency ?? list.length);
|
|
1944
2027
|
const failFast = options.failFast === true;
|
|
2028
|
+
const outer = this.scope ??= {
|
|
2029
|
+
prefix: "",
|
|
2030
|
+
counts: void 0,
|
|
2031
|
+
alls: 0
|
|
2032
|
+
};
|
|
2033
|
+
const address = `${outer.prefix}all${outer.alls}`;
|
|
2034
|
+
outer.alls += 1;
|
|
2035
|
+
const scopes = new Array(list.length);
|
|
2036
|
+
const journal = journalOf(this.perform);
|
|
1945
2037
|
let failed = false;
|
|
1946
2038
|
const active = [];
|
|
1947
2039
|
let next = 0;
|
|
@@ -1951,11 +2043,15 @@ var Session = class {
|
|
|
1951
2043
|
};
|
|
1952
2044
|
const advance = (index, steps, input) => {
|
|
1953
2045
|
let step;
|
|
2046
|
+
const before = this.scope;
|
|
2047
|
+
this.scope = scopes[index];
|
|
1954
2048
|
try {
|
|
1955
2049
|
step = input.threw ? steps.throw(input.error) : steps.next(input.value);
|
|
1956
2050
|
} catch (error) {
|
|
1957
2051
|
settle(index, thrown(error, "a part"));
|
|
1958
2052
|
return;
|
|
2053
|
+
} finally {
|
|
2054
|
+
this.scope = before;
|
|
1959
2055
|
}
|
|
1960
2056
|
if (step.done === true) {
|
|
1961
2057
|
settle(index, step.value);
|
|
@@ -1981,20 +2077,33 @@ var Session = class {
|
|
|
1981
2077
|
while (!failed && active.length < limit && next < list.length) {
|
|
1982
2078
|
const index = next;
|
|
1983
2079
|
next += 1;
|
|
2080
|
+
const scope = {
|
|
2081
|
+
prefix: `${address}.${index}/`,
|
|
2082
|
+
counts: void 0,
|
|
2083
|
+
alls: 0
|
|
2084
|
+
};
|
|
2085
|
+
scopes[index] = scope;
|
|
1984
2086
|
const begin = list[index];
|
|
1985
2087
|
let steps;
|
|
2088
|
+
const before = this.scope;
|
|
2089
|
+
this.scope = scope;
|
|
1986
2090
|
try {
|
|
1987
2091
|
steps = begin();
|
|
1988
2092
|
} catch (error) {
|
|
1989
2093
|
settle(index, thrown(error, "a part"));
|
|
1990
2094
|
continue;
|
|
2095
|
+
} finally {
|
|
2096
|
+
this.scope = before;
|
|
1991
2097
|
}
|
|
1992
2098
|
advance(index, steps, { threw: false });
|
|
1993
2099
|
}
|
|
1994
2100
|
};
|
|
1995
2101
|
start();
|
|
1996
2102
|
while (active.length > 0 && !failed) {
|
|
1997
|
-
const
|
|
2103
|
+
const chosen = journal?.next?.(address, active.map((item) => item.index));
|
|
2104
|
+
const pick = chosen === void 0 ? void 0 : active.find((item) => item.index === chosen);
|
|
2105
|
+
const settled = yield pick === void 0 ? Promise.race(active.map((item) => item.pending)) : pick.pending;
|
|
2106
|
+
journal?.settled?.(address, settled.item.index);
|
|
1998
2107
|
active.splice(active.indexOf(settled.item), 1);
|
|
1999
2108
|
advance(settled.item.index, settled.item.steps, settled);
|
|
2000
2109
|
start();
|
|
@@ -2066,7 +2175,7 @@ function drive$1(steps) {
|
|
|
2066
2175
|
steps.return(fail("#CALC!", "A part waited, and its parent cannot wait."));
|
|
2067
2176
|
return fail("#CALC!", "A part waited, and its parent cannot wait.");
|
|
2068
2177
|
}
|
|
2069
|
-
const ABORTED = Symbol("aborted");
|
|
2178
|
+
const ABORTED = /* @__PURE__ */ Symbol("aborted");
|
|
2070
2179
|
/** This function drives steps that can wait. The steps before the first wait happen at once, in the call. */
|
|
2071
2180
|
async function driveWaiting(steps, signal) {
|
|
2072
2181
|
const abort = () => {
|
|
@@ -2183,7 +2292,7 @@ function evaluatorOf(world, envelope, kind) {
|
|
|
2183
2292
|
};
|
|
2184
2293
|
}
|
|
2185
2294
|
/** The meta of a node of a compilation without a trace. Only a trace reads the meta of a node. */
|
|
2186
|
-
const NO_META = Object.freeze({
|
|
2295
|
+
const NO_META = /* @__PURE__ */ Object.freeze({
|
|
2187
2296
|
path: [],
|
|
2188
2297
|
doc: {
|
|
2189
2298
|
type: "?",
|
|
@@ -2449,7 +2558,7 @@ function planOf(machine, traced, root) {
|
|
|
2449
2558
|
traced,
|
|
2450
2559
|
root,
|
|
2451
2560
|
evaluate(context = makeContext(), evalOptions) {
|
|
2452
|
-
const s = new Session(machine, false, traced,
|
|
2561
|
+
const s = new Session(machine, false, traced, evalOptions?.perform, void 0, evalOptions?.effects);
|
|
2453
2562
|
return root.code(rootContext(machine, context), s);
|
|
2454
2563
|
},
|
|
2455
2564
|
run(context = makeContext(), runOptions = {}) {
|
|
@@ -2487,7 +2596,7 @@ function planner(machine, options = {}) {
|
|
|
2487
2596
|
return plan;
|
|
2488
2597
|
};
|
|
2489
2598
|
}
|
|
2490
|
-
function record
|
|
2599
|
+
function record(s, result) {
|
|
2491
2600
|
const root = s.holder?.children[0];
|
|
2492
2601
|
if (root === void 0) throw new Error("The evaluation made no trace.");
|
|
2493
2602
|
return {
|
|
@@ -2502,21 +2611,24 @@ function record$1(s, result) {
|
|
|
2502
2611
|
*
|
|
2503
2612
|
* The verb `eval` goes up the ladder for each document. The kind of the document tells where its parts are. The
|
|
2504
2613
|
* evaluator gets each part sealed: it can evaluate the part, and it cannot read its value. A failure gives an error
|
|
2505
|
-
* value, and the function does not throw. An effect is recorded in its order, and
|
|
2614
|
+
* value, and the function does not throw. An effect is recorded in its order, and the host does not do it. A mock
|
|
2615
|
+
* can answer each effect at once (RUN.MOCK).
|
|
2506
2616
|
*
|
|
2507
2617
|
* @param machine - The world and the code of the evaluators.
|
|
2508
2618
|
* @param doc - The document, as JSON.
|
|
2509
2619
|
* @param context - The names and the capabilities at the root.
|
|
2510
|
-
* @param
|
|
2620
|
+
* @param options - The type of a bare root value, or the options: that type and the mock of the host.
|
|
2511
2621
|
* @returns The result, the trace and the effects in their order.
|
|
2512
2622
|
*/
|
|
2513
|
-
function evaluate(machine, doc, context = makeContext(),
|
|
2623
|
+
function evaluate(machine, doc, context = makeContext(), options) {
|
|
2624
|
+
const given = typeof options === "string" ? { declaredType: options } : options ?? {};
|
|
2625
|
+
const declaredType = given.declaredType;
|
|
2514
2626
|
const plan = prepare(machine, doc, {
|
|
2515
2627
|
trace: true,
|
|
2516
2628
|
...declaredType === void 0 ? {} : { declaredType }
|
|
2517
2629
|
});
|
|
2518
|
-
const s = new Session(machine, false, true,
|
|
2519
|
-
return record
|
|
2630
|
+
const s = new Session(machine, false, true, given.perform, void 0, void 0);
|
|
2631
|
+
return record(s, plan.root.code(rootContext(machine, context), s));
|
|
2520
2632
|
}
|
|
2521
2633
|
/**
|
|
2522
2634
|
* This function runs a document: an evaluation in which the host does each effect. A runner waits for its
|
|
@@ -2535,7 +2647,7 @@ async function run(machine, doc, context = makeContext(), options = {}) {
|
|
|
2535
2647
|
...options.declaredType === void 0 ? {} : { declaredType: options.declaredType }
|
|
2536
2648
|
});
|
|
2537
2649
|
const s = new Session(machine, true, true, options.perform, options.signal, void 0);
|
|
2538
|
-
return record
|
|
2650
|
+
return record(s, await driveWaiting(plan.root.run(rootContext(machine, context), s), options.signal));
|
|
2539
2651
|
}
|
|
2540
2652
|
/**
|
|
2541
2653
|
* This function finds a node of a trace by its path.
|
|
@@ -2803,9 +2915,9 @@ function dataOf(type) {
|
|
|
2803
2915
|
}
|
|
2804
2916
|
/** The code of the evaluators of the standard world, by name. */
|
|
2805
2917
|
const STD_EVALUATORS = {
|
|
2806
|
-
"data.number": dataOf("number"),
|
|
2807
|
-
"data.text": dataOf("string"),
|
|
2808
|
-
"data.bool": dataOf("boolean"),
|
|
2918
|
+
"data.number": /* @__PURE__ */ dataOf("number"),
|
|
2919
|
+
"data.text": /* @__PURE__ */ dataOf("string"),
|
|
2920
|
+
"data.bool": /* @__PURE__ */ dataOf("boolean"),
|
|
2809
2921
|
"math.add": ({ parts }) => {
|
|
2810
2922
|
const all = allValues(parts.map((part) => part.evaluate()));
|
|
2811
2923
|
if ("error" in all) return all.error;
|
|
@@ -2835,8 +2947,8 @@ const STD_EVALUATORS = {
|
|
|
2835
2947
|
"tree.node": (input) => treeNode(input)
|
|
2836
2948
|
};
|
|
2837
2949
|
/** The brand of a node of the decorated tree. Its value is the state that the kit reads. */
|
|
2838
|
-
const TREE_NODE = Symbol.for("coeval.tree.node");
|
|
2839
|
-
const BUSY = Object.freeze({ busy: true });
|
|
2950
|
+
const TREE_NODE = /* @__PURE__ */ Symbol.for("coeval.tree.node");
|
|
2951
|
+
const BUSY = /* @__PURE__ */ Object.freeze({ busy: true });
|
|
2840
2952
|
/** The attributes in evaluation, in the order of their demands. An evaluation does not wait, thus one list serves. */
|
|
2841
2953
|
const demands = [];
|
|
2842
2954
|
function attributeText(key, type, place) {
|
|
@@ -2971,6 +3083,9 @@ function treeThing(parts, context, type, place, build) {
|
|
|
2971
3083
|
if (!result.ok) throw new Failure(result.error);
|
|
2972
3084
|
return result.value;
|
|
2973
3085
|
},
|
|
3086
|
+
has(key) {
|
|
3087
|
+
return key === "parent" || key === "type" || parts.some((part) => part.key === String(key));
|
|
3088
|
+
},
|
|
2974
3089
|
toJson() {
|
|
2975
3090
|
const result = state.json(/* @__PURE__ */ new Set());
|
|
2976
3091
|
if (!result.ok) throw new Failure(result.error);
|
|
@@ -3031,7 +3146,7 @@ function nearestWalker(options = {}) {
|
|
|
3031
3146
|
};
|
|
3032
3147
|
}
|
|
3033
3148
|
/** The walkers of the standard world, by name. */
|
|
3034
|
-
const STD_WALKERS = { "walk.nearest": nearestWalker() };
|
|
3149
|
+
const STD_WALKERS = { "walk.nearest": /* @__PURE__ */ nearestWalker() };
|
|
3035
3150
|
/** This function evaluates parts one after the other, and stops at the first error. */
|
|
3036
3151
|
function* inOrder(parts) {
|
|
3037
3152
|
const values = [];
|
|
@@ -3935,7 +4050,7 @@ function check(machine, doc, options = {}) {
|
|
|
3935
4050
|
//#endregion
|
|
3936
4051
|
//#region src/formula.ts
|
|
3937
4052
|
/** The mark of a field that the profile leaves out of a record. */
|
|
3938
|
-
const SKIPPED = Symbol("skipped");
|
|
4053
|
+
const SKIPPED = /* @__PURE__ */ Symbol("skipped");
|
|
3939
4054
|
/** The value of a name whose binding failed. A reference to the name gives the error. */
|
|
3940
4055
|
var Held = class {
|
|
3941
4056
|
error;
|
|
@@ -3943,7 +4058,7 @@ var Held = class {
|
|
|
3943
4058
|
this.error = error;
|
|
3944
4059
|
}
|
|
3945
4060
|
};
|
|
3946
|
-
const CLOSURE$2 = Symbol.for("coeval.closure");
|
|
4061
|
+
const CLOSURE$2 = /* @__PURE__ */ Symbol.for("coeval.closure");
|
|
3947
4062
|
/**
|
|
3948
4063
|
* This function tells if a value is a function that the construct `fn` made.
|
|
3949
4064
|
*
|
|
@@ -4581,13 +4696,20 @@ function resolveFully(machine, ctx, path, options) {
|
|
|
4581
4696
|
hops: []
|
|
4582
4697
|
};
|
|
4583
4698
|
}
|
|
4584
|
-
/**
|
|
4585
|
-
function
|
|
4699
|
+
/**
|
|
4700
|
+
* This function causes an effect when the capabilities permit it (RUN.CAP, CAP.HOOK). It records the effect with its
|
|
4701
|
+
* address (RUN.ADDRESS) and its outcome (RUN.OUTCOME). The mock answers the effect at once (RUN.MOCK). Without a
|
|
4702
|
+
* mock, the effect gives its payload (RUN.EVAL).
|
|
4703
|
+
*/
|
|
4704
|
+
function cause(env, ctx, kind, payload, place) {
|
|
4586
4705
|
const keys = typeof kind === "string" ? kind.split(".") : kind;
|
|
4587
4706
|
const text = keys.join(".");
|
|
4588
4707
|
const machine = env.machine;
|
|
4589
4708
|
if (!(machine.permits === void 0 ? permits(ctx.caps, keys) : machine.permits(ctx.caps, keys, ctx))) return fail("#CAP!", `The context has no capability for the effect ${text}.`, { place });
|
|
4590
|
-
|
|
4709
|
+
const at = placeText(place);
|
|
4710
|
+
const count = env.scope.counts.get(at) ?? 0;
|
|
4711
|
+
env.scope.counts.set(at, count + 1);
|
|
4712
|
+
const effect = {
|
|
4591
4713
|
index: env.effects.length,
|
|
4592
4714
|
kind: text,
|
|
4593
4715
|
path: keys,
|
|
@@ -4595,8 +4717,35 @@ function record(env, ctx, kind, payload, place) {
|
|
|
4595
4717
|
node: -1,
|
|
4596
4718
|
at: 0,
|
|
4597
4719
|
place,
|
|
4598
|
-
context: ctx
|
|
4599
|
-
|
|
4720
|
+
context: ctx,
|
|
4721
|
+
address: `${env.scope.prefix}${at}#${count}`
|
|
4722
|
+
};
|
|
4723
|
+
env.effects.push(effect);
|
|
4724
|
+
effect.outcome = answer(env.perform, effect, place);
|
|
4725
|
+
return effect.outcome;
|
|
4726
|
+
}
|
|
4727
|
+
/**
|
|
4728
|
+
* This function gives the answer of the mock to an effect. A throw gives `#CALC!` with the cause, and a `Failure`
|
|
4729
|
+
* gives its own error value. A promise gives `#CALC!`, because an evaluation does not wait.
|
|
4730
|
+
*/
|
|
4731
|
+
function answer(perform, effect, place) {
|
|
4732
|
+
if (perform === void 0) return ok(effect.payload);
|
|
4733
|
+
let given;
|
|
4734
|
+
try {
|
|
4735
|
+
given = perform(effect);
|
|
4736
|
+
} catch (error) {
|
|
4737
|
+
if (error instanceof Failure) return placed({
|
|
4738
|
+
ok: false,
|
|
4739
|
+
error: error.error
|
|
4740
|
+
}, place);
|
|
4741
|
+
return fail("#CALC!", `The host failed the effect ${effect.kind}: ${describe(error)}`, {
|
|
4742
|
+
place,
|
|
4743
|
+
cause: error
|
|
4744
|
+
});
|
|
4745
|
+
}
|
|
4746
|
+
if (!(given instanceof Promise)) return ok(given);
|
|
4747
|
+
given.catch(() => void 0);
|
|
4748
|
+
return fail("#CALC!", `The mock gave a promise for the effect ${effect.kind}, and an evaluation does not wait.`, { place });
|
|
4600
4749
|
}
|
|
4601
4750
|
/** This function evaluates a document. Each error gets the place of the document where it started (K5). */
|
|
4602
4751
|
function evaluateAt(env, at) {
|
|
@@ -4662,7 +4811,7 @@ function evalInput(env, seen, at) {
|
|
|
4662
4811
|
place: at.place,
|
|
4663
4812
|
...at.address ? { address: true } : {},
|
|
4664
4813
|
resolve: (path, options) => resolveFully(env.machine, at.ctx, path, options),
|
|
4665
|
-
effect: (kind, payload) =>
|
|
4814
|
+
effect: (kind, payload) => cause(env, at.ctx, kind, payload, at.place),
|
|
4666
4815
|
fill
|
|
4667
4816
|
};
|
|
4668
4817
|
}
|
|
@@ -4689,9 +4838,9 @@ function runInput(env, seen, at) {
|
|
|
4689
4838
|
place: at.place,
|
|
4690
4839
|
resolve: (path, options) => resolveFully(env.machine, at.ctx, path, options),
|
|
4691
4840
|
effect: function* (kind, payload) {
|
|
4692
|
-
return
|
|
4841
|
+
return cause(env, at.ctx, kind, payload, at.place);
|
|
4693
4842
|
},
|
|
4694
|
-
all: (list, options) => together(list, options),
|
|
4843
|
+
all: (list, options) => together(env, list, options),
|
|
4695
4844
|
fill
|
|
4696
4845
|
};
|
|
4697
4846
|
}
|
|
@@ -4700,14 +4849,25 @@ function runInput(env, seen, at) {
|
|
|
4700
4849
|
* goes on until it ends or waits. In an evaluation, a part that waits does not end, thus it keeps its place in the
|
|
4701
4850
|
* limit. With `failFast`, the first failure stops the parts that wait, and the later parts do not start
|
|
4702
4851
|
* (RUN.FAIL-FAST). When no part failed and a part waits, the steps wait, and the drive gives `#CALC!`.
|
|
4852
|
+
*
|
|
4853
|
+
* Each part has its own scope of addresses. Its prefix names the use of `all` in the scope around it, and the part
|
|
4854
|
+
* (RUN.ADDRESS).
|
|
4703
4855
|
*/
|
|
4704
|
-
function* together(list, options = {}) {
|
|
4856
|
+
function* together(env, list, options = {}) {
|
|
4705
4857
|
const limit = Math.max(1, options.concurrency ?? list.length);
|
|
4706
4858
|
const results = list.map(() => void 0);
|
|
4707
4859
|
const waiting = [];
|
|
4860
|
+
const outer = env.scope;
|
|
4861
|
+
const address = `${outer.prefix}all${outer.alls}`;
|
|
4862
|
+
outer.alls += 1;
|
|
4708
4863
|
let failed = false;
|
|
4709
4864
|
for (let index = 0; index < list.length && !failed && waiting.length < limit; index += 1) {
|
|
4710
4865
|
let result;
|
|
4866
|
+
env.scope = {
|
|
4867
|
+
prefix: `${address}.${index}/`,
|
|
4868
|
+
counts: /* @__PURE__ */ new Map(),
|
|
4869
|
+
alls: 0
|
|
4870
|
+
};
|
|
4711
4871
|
try {
|
|
4712
4872
|
const steps = list[index].evaluate();
|
|
4713
4873
|
const step = steps.next();
|
|
@@ -4718,6 +4878,8 @@ function* together(list, options = {}) {
|
|
|
4718
4878
|
});
|
|
4719
4879
|
} catch (error) {
|
|
4720
4880
|
result = thrown(error, "a part");
|
|
4881
|
+
} finally {
|
|
4882
|
+
env.scope = outer;
|
|
4721
4883
|
}
|
|
4722
4884
|
results[index] = result;
|
|
4723
4885
|
if (result !== void 0 && options.failFast === true && !result.ok) failed = true;
|
|
@@ -4735,12 +4897,12 @@ function* together(list, options = {}) {
|
|
|
4735
4897
|
/**
|
|
4736
4898
|
* This function evaluates a document by the rules, without a compilation. It is the reference of the plans: a plan,
|
|
4737
4899
|
* a traced plan and the second tier of a language must give its results. It covers evaluations, not runs: an effect
|
|
4738
|
-
* gives its payload, and a part that waits gives `#CALC!`.
|
|
4900
|
+
* gives the answer of the mock or its payload, and a part that waits gives `#CALC!`.
|
|
4739
4901
|
*
|
|
4740
4902
|
* @param machine - The world and the code of the evaluators.
|
|
4741
4903
|
* @param doc - The document.
|
|
4742
4904
|
* @param context - The names and the capabilities at the root.
|
|
4743
|
-
* @param options - The type of a bare root value, the definitions of compiled code,
|
|
4905
|
+
* @param options - The type of a bare root value, the definitions of compiled code, a list for the effects and a mock.
|
|
4744
4906
|
* @returns The result.
|
|
4745
4907
|
*/
|
|
4746
4908
|
function reference(machine, doc, context = makeContext(), options = {}) {
|
|
@@ -4748,7 +4910,13 @@ function reference(machine, doc, context = makeContext(), options = {}) {
|
|
|
4748
4910
|
machine,
|
|
4749
4911
|
definitions: options.definitions ?? {},
|
|
4750
4912
|
effects: options.effects ?? [],
|
|
4751
|
-
maxDepth: machine.maxDepth ?? 1e3
|
|
4913
|
+
maxDepth: machine.maxDepth ?? 1e3,
|
|
4914
|
+
perform: options.perform,
|
|
4915
|
+
scope: {
|
|
4916
|
+
prefix: "",
|
|
4917
|
+
counts: /* @__PURE__ */ new Map(),
|
|
4918
|
+
alls: 0
|
|
4919
|
+
}
|
|
4752
4920
|
};
|
|
4753
4921
|
const ctx = context.world === null ? {
|
|
4754
4922
|
...context,
|
|
@@ -4868,6 +5036,7 @@ const treeDefinition = ({ parts, context, kind, place }) => {
|
|
|
4868
5036
|
if (result === void 0 || result.ok) return result?.ok === true ? result.value : void 0;
|
|
4869
5037
|
throw new Failure(result.error);
|
|
4870
5038
|
},
|
|
5039
|
+
has: (key) => key === "parent" || key === "type" || parts.some((part) => part.key === String(key)),
|
|
4871
5040
|
toJson: () => {
|
|
4872
5041
|
const result = jsonOfNode(node, self, []);
|
|
4873
5042
|
if (result.ok) return result.value;
|
|
@@ -4887,7 +5056,7 @@ const treeDefinition = ({ parts, context, kind, place }) => {
|
|
|
4887
5056
|
* reference uses them for the standard evaluators, also without the option `definitions`. It holds `tree.node`.
|
|
4888
5057
|
*/
|
|
4889
5058
|
const STD_DEFINITIONS = { evaluators: { "tree.node": treeDefinition } };
|
|
4890
|
-
const CLOSURE$1 = Symbol.for("coeval.closure");
|
|
5059
|
+
const CLOSURE$1 = /* @__PURE__ */ Symbol.for("coeval.closure");
|
|
4891
5060
|
/** This function sets an own field, also the field `__proto__` (SHAPE.PROTO). */
|
|
4892
5061
|
function setField(target, key, value) {
|
|
4893
5062
|
Object.defineProperty(target, key, {
|
|
@@ -5092,7 +5261,7 @@ function formulaDefinitions(profile = {}) {
|
|
|
5092
5261
|
} };
|
|
5093
5262
|
}
|
|
5094
5263
|
/** The mark of a field that the profile leaves out of a record (FORMULA.RECORD). */
|
|
5095
|
-
const SKIP = Symbol("skip");
|
|
5264
|
+
const SKIP = /* @__PURE__ */ Symbol("skip");
|
|
5096
5265
|
/** The values of parts in order. The first error stops them, also with `combine` (FORMULA.STEPS, FORMULA.FN). */
|
|
5097
5266
|
function valuesOfStrict(parts) {
|
|
5098
5267
|
const values = [];
|
|
@@ -5114,8 +5283,9 @@ function valuesOfStrict(parts) {
|
|
|
5114
5283
|
* errors have the same code and place. Each other object is the same only as itself, unless the rule of the caller
|
|
5115
5284
|
* tells otherwise.
|
|
5116
5285
|
*
|
|
5117
|
-
* `docEquals`
|
|
5118
|
-
*
|
|
5286
|
+
* `docEquals` and `sameResult` use the same walk (D20). `docEquals` has no rules for closures, for `Held` values and
|
|
5287
|
+
* for a rule of the caller. `sameResult` is for an early cutoff, thus a closure is the same there only as itself, and
|
|
5288
|
+
* two errors also need the same reason.
|
|
5119
5289
|
*
|
|
5120
5290
|
* @param a - The first value.
|
|
5121
5291
|
* @param b - The second value.
|
|
@@ -5123,48 +5293,14 @@ function valuesOfStrict(parts) {
|
|
|
5123
5293
|
* @returns `true` when the values are the same.
|
|
5124
5294
|
*/
|
|
5125
5295
|
function sameValue(a, b, equal) {
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
if (told !== void 0) {
|
|
5133
|
-
if (told) continue;
|
|
5134
|
-
return false;
|
|
5135
|
-
}
|
|
5136
|
-
if (typeof x !== "object" && typeof x !== "function") return false;
|
|
5137
|
-
if (typeof y !== "object" && typeof y !== "function") return false;
|
|
5138
|
-
if (x === null || y === null) return false;
|
|
5139
|
-
const pairs = seen.get(x) ?? /* @__PURE__ */ new Set();
|
|
5140
|
-
if (pairs.has(y)) continue;
|
|
5141
|
-
pairs.add(y);
|
|
5142
|
-
seen.set(x, pairs);
|
|
5143
|
-
if (isClosure(x) || isClosure(y)) {
|
|
5144
|
-
if (!(isClosure(x) && isClosure(y))) return false;
|
|
5145
|
-
stack.push([closureParams(x), closureParams(y)]);
|
|
5146
|
-
continue;
|
|
5147
|
-
}
|
|
5148
|
-
if (x instanceof Held || y instanceof Held) {
|
|
5149
|
-
if (!(x instanceof Held && y instanceof Held) || errorDiffers(x.error, y.error, false) !== void 0) return false;
|
|
5150
|
-
continue;
|
|
5151
|
-
}
|
|
5152
|
-
if (Array.isArray(x) || Array.isArray(y)) {
|
|
5153
|
-
if (!Array.isArray(x) || !Array.isArray(y) || x.length !== y.length) return false;
|
|
5154
|
-
for (let index = 0; index < x.length; index += 1) stack.push([x[index], y[index]]);
|
|
5155
|
-
continue;
|
|
5156
|
-
}
|
|
5157
|
-
if (!isRecord(x) || !isRecord(y)) return false;
|
|
5158
|
-
const keys = Object.keys(x);
|
|
5159
|
-
if (keys.length !== Object.keys(y).length) return false;
|
|
5160
|
-
for (const key of keys) {
|
|
5161
|
-
if (!Object.hasOwn(y, key)) return false;
|
|
5162
|
-
stack.push([x[key], y[key]]);
|
|
5163
|
-
}
|
|
5164
|
-
}
|
|
5165
|
-
return true;
|
|
5296
|
+
return equalWith(a, b, equal === void 0 ? sameInTiers : (x, y) => equal(x, y) ?? sameInTiers(x, y));
|
|
5297
|
+
}
|
|
5298
|
+
/** The rules of `sameValue` for the values that two tiers make again: closures and held errors. */
|
|
5299
|
+
function sameInTiers(x, y) {
|
|
5300
|
+
if (isClosure(x) || isClosure(y)) return isClosure(x) && isClosure(y) && docEquals(closureParams(x), closureParams(y));
|
|
5301
|
+
if (x instanceof Held || y instanceof Held) return x instanceof Held && y instanceof Held && errorDiffers(x.error, y.error, false) === void 0;
|
|
5166
5302
|
}
|
|
5167
|
-
const CLOSURE = Symbol.for("coeval.closure");
|
|
5303
|
+
const CLOSURE = /* @__PURE__ */ Symbol.for("coeval.closure");
|
|
5168
5304
|
function closureParams(fn) {
|
|
5169
5305
|
return fn[CLOSURE]?.params;
|
|
5170
5306
|
}
|
|
@@ -5188,34 +5324,44 @@ function resultDiffers(a, b, options) {
|
|
|
5188
5324
|
if (!a.ok && !b.ok) return errorDiffers(a.error, b.error, options.strict === true);
|
|
5189
5325
|
return a.ok ? `The plan gives a value, and the tier gives ${codeAndPlace(b.error)}.` : `The plan gives ${codeAndPlace(a.error)}, and the tier gives a value.`;
|
|
5190
5326
|
}
|
|
5191
|
-
/**
|
|
5327
|
+
/**
|
|
5328
|
+
* This function gives the difference of two lists of effects, or `undefined` when they agree (EFFECT.ORDER). When both
|
|
5329
|
+
* tiers record the address or the outcome of an effect, these must agree too (AGREE.MOCK).
|
|
5330
|
+
*/
|
|
5192
5331
|
function effectsDiffer(a, b, options) {
|
|
5193
5332
|
if (a.length !== b.length) return `The plan causes ${a.length} effects, and the tier causes ${b.length}.`;
|
|
5194
5333
|
for (let index = 0; index < a.length; index += 1) {
|
|
5195
5334
|
const x = a[index];
|
|
5196
5335
|
const y = b[index];
|
|
5197
5336
|
if (x.kind !== y.kind || !docEquals(x.place, y.place) || !sameValue(x.payload, y.payload, options.equal)) return `The effect ${index} differs: ${x.kind} at [${x.place.join(", ")}] in the plan, and ${y.kind} at [${y.place.join(", ")}] in the tier.`;
|
|
5337
|
+
if (x.address !== void 0 && y.address !== void 0 && x.address !== y.address) return `The effect ${index} has the address ${x.address} in the plan, and ${y.address} in the tier.`;
|
|
5338
|
+
const why = x.outcome !== void 0 && y.outcome !== void 0 ? resultDiffers(x.outcome, y.outcome, options) : void 0;
|
|
5339
|
+
if (why !== void 0) return `The outcome of the effect ${index} differs. ${why}`;
|
|
5198
5340
|
}
|
|
5199
5341
|
}
|
|
5200
5342
|
/**
|
|
5201
5343
|
* This function evaluates a document with a plan and with other tiers, and compares the results (PLAN.AGREES). Two
|
|
5202
5344
|
* results agree when they have the same value by `sameValue`, or errors with the same code, and the same place when
|
|
5203
5345
|
* both have one. The effects must have the same kinds, places and payloads, in the same order. A language gives its
|
|
5204
|
-
* own tier with the option `other`.
|
|
5346
|
+
* own tier with the option `other`. Each tier gets the mock of the option `perform` (AGREE.MOCK).
|
|
5205
5347
|
*
|
|
5206
5348
|
* @param machine - The world and the code of the evaluators.
|
|
5207
5349
|
* @param doc - The document.
|
|
5208
5350
|
* @param context - The names and the capabilities at the root.
|
|
5209
|
-
* @param options - The tiers, the definitions for the reference, the rules of the comparison
|
|
5351
|
+
* @param options - The tiers, the definitions for the reference, the rules of the comparison, the second tier and the mock.
|
|
5210
5352
|
* @returns The result of the plan, or the first tier that disagrees and the reason.
|
|
5211
5353
|
*/
|
|
5212
5354
|
function agree(machine, doc, context = makeContext(), options = {}) {
|
|
5213
5355
|
const declared = options.declaredType === void 0 ? {} : { declaredType: options.declaredType };
|
|
5356
|
+
const mock = options.perform === void 0 ? {} : { perform: options.perform };
|
|
5214
5357
|
const observe = options.observe;
|
|
5215
5358
|
observe?.();
|
|
5216
5359
|
const plan = prepare(machine, doc, declared);
|
|
5217
5360
|
const effects = [];
|
|
5218
|
-
const result = plan.evaluate(context, {
|
|
5361
|
+
const result = plan.evaluate(context, {
|
|
5362
|
+
effects,
|
|
5363
|
+
...mock
|
|
5364
|
+
});
|
|
5219
5365
|
const saw = observe?.();
|
|
5220
5366
|
const differs = (other, otherEffects) => {
|
|
5221
5367
|
if (overflowed(result) || overflowed(other)) return void 0;
|
|
@@ -5232,14 +5378,21 @@ function agree(machine, doc, context = makeContext(), options = {}) {
|
|
|
5232
5378
|
let otherEffects = [];
|
|
5233
5379
|
if (tier === "reference") other = reference(machine, doc, context, {
|
|
5234
5380
|
...declared,
|
|
5381
|
+
...mock,
|
|
5235
5382
|
effects: otherEffects,
|
|
5236
5383
|
...options.definitions === void 0 ? {} : { definitions: options.definitions }
|
|
5237
5384
|
});
|
|
5238
5385
|
else if (tier === "traced") {
|
|
5239
|
-
const traced = evaluate(machine, doc, context,
|
|
5386
|
+
const traced = evaluate(machine, doc, context, {
|
|
5387
|
+
...declared,
|
|
5388
|
+
...mock
|
|
5389
|
+
});
|
|
5240
5390
|
other = traced.result;
|
|
5241
5391
|
otherEffects = [...traced.effects];
|
|
5242
|
-
} else other = plan.evaluate(context, {
|
|
5392
|
+
} else other = plan.evaluate(context, {
|
|
5393
|
+
effects: otherEffects,
|
|
5394
|
+
...mock
|
|
5395
|
+
});
|
|
5243
5396
|
const why = differs(other, otherEffects);
|
|
5244
5397
|
if (why !== void 0) return {
|
|
5245
5398
|
ok: false,
|
|
@@ -5252,7 +5405,7 @@ function agree(machine, doc, context = makeContext(), options = {}) {
|
|
|
5252
5405
|
if (options.other !== void 0) {
|
|
5253
5406
|
let given;
|
|
5254
5407
|
try {
|
|
5255
|
-
given = options.other(doc, context);
|
|
5408
|
+
given = options.other(doc, context, mock);
|
|
5256
5409
|
} catch (error) {
|
|
5257
5410
|
given = thrown(error, "the tier");
|
|
5258
5411
|
}
|
|
@@ -5273,6 +5426,138 @@ function agree(machine, doc, context = makeContext(), options = {}) {
|
|
|
5273
5426
|
};
|
|
5274
5427
|
}
|
|
5275
5428
|
//#endregion
|
|
5429
|
+
//#region src/same.ts
|
|
5430
|
+
/**
|
|
5431
|
+
* This function tells if two results are the same for an early cutoff (RESULT.SAME). A schedule uses it to stop the
|
|
5432
|
+
* work when a part that evaluates again gives the same result. A wrong `true` keeps a stale value, but a wrong `false`
|
|
5433
|
+
* only costs work. Thus each rule gives `false` when it is not sure.
|
|
5434
|
+
*
|
|
5435
|
+
* - Two values compare by the rules of `docEquals`. Lists and plain records compare by their items and fields. Each
|
|
5436
|
+
* other value compares with `Object.is`.
|
|
5437
|
+
* - Thus `-0` is not `0`, and `NaN` is `NaN`. A date, a map and a closure of `fn` are the same only as themselves.
|
|
5438
|
+
* - Two errors are the same when they have the same code, reason and place (RESULT.SAME-ERROR). The cause is not
|
|
5439
|
+
* compared. A `Held` value compares its error by the same rule.
|
|
5440
|
+
* - A value and an error are not the same.
|
|
5441
|
+
*
|
|
5442
|
+
* `sameValue` of `agree` uses the same walk, with rules for two tiers (D20). It compares two closures by their
|
|
5443
|
+
* parameters, and two errors by their code and place.
|
|
5444
|
+
*
|
|
5445
|
+
* @param a - The earlier result.
|
|
5446
|
+
* @param b - The new result.
|
|
5447
|
+
* @param equal - The rule of the host for two values, for example two dates. It answers first, and `undefined` gives the rules above.
|
|
5448
|
+
* @returns `true` when the results are the same.
|
|
5449
|
+
*/
|
|
5450
|
+
function sameResult(a, b, equal) {
|
|
5451
|
+
if (a.ok && b.ok) return equalWith(a.value, b.value, equal === void 0 ? sameHeld : (x, y) => equal(x, y) ?? sameHeld(x, y));
|
|
5452
|
+
if (!a.ok && !b.ok) return sameError(a.error, b.error);
|
|
5453
|
+
return false;
|
|
5454
|
+
}
|
|
5455
|
+
/** The rule of `sameResult` for held errors. Two `Held` values are the same when their errors are the same. */
|
|
5456
|
+
function sameHeld(x, y) {
|
|
5457
|
+
if (x instanceof Held || y instanceof Held) return x instanceof Held && y instanceof Held && sameError(x.error, y.error);
|
|
5458
|
+
}
|
|
5459
|
+
/** Two errors are the same when they have the same code, reason and place. An error without a place has no place in both. */
|
|
5460
|
+
function sameError(a, b) {
|
|
5461
|
+
if (a.code !== b.code || a.reason !== b.reason) return false;
|
|
5462
|
+
if (a.place === void 0 || b.place === void 0) return a.place === b.place;
|
|
5463
|
+
return docEquals(a.place, b.place);
|
|
5464
|
+
}
|
|
5465
|
+
//#endregion
|
|
5466
|
+
//#region src/replay.ts
|
|
5467
|
+
/**
|
|
5468
|
+
* This function makes a recorder (RUN.RECORD). The recorder gives each effect to the function `perform` of the host.
|
|
5469
|
+
* Its log keeps each effect with its address, its kind, its payload and the outcome that the kernel gives it. The log
|
|
5470
|
+
* also keeps the order in which the waits of each `all` settle.
|
|
5471
|
+
*
|
|
5472
|
+
* @param perform - The function of the host. Without it, each effect gives its payload, as in a run without a host.
|
|
5473
|
+
* @returns The function `perform` for the run, and the log.
|
|
5474
|
+
*/
|
|
5475
|
+
function recorder(perform) {
|
|
5476
|
+
const seen = [];
|
|
5477
|
+
const settles = [];
|
|
5478
|
+
const journal = { settled: (all, item) => {
|
|
5479
|
+
settles.push({
|
|
5480
|
+
all,
|
|
5481
|
+
item
|
|
5482
|
+
});
|
|
5483
|
+
} };
|
|
5484
|
+
const host = (effect) => {
|
|
5485
|
+
const entry = {
|
|
5486
|
+
effect,
|
|
5487
|
+
waited: false
|
|
5488
|
+
};
|
|
5489
|
+
seen.push(entry);
|
|
5490
|
+
const answer = perform === void 0 ? effect.payload : perform(effect);
|
|
5491
|
+
if (answer instanceof Promise) entry.waited = true;
|
|
5492
|
+
return answer;
|
|
5493
|
+
};
|
|
5494
|
+
return {
|
|
5495
|
+
perform: Object.assign(host, { [JOURNAL]: journal }),
|
|
5496
|
+
log: () => ({
|
|
5497
|
+
effects: seen.map(({ effect, waited }) => ({
|
|
5498
|
+
address: effect.address ?? "",
|
|
5499
|
+
kind: effect.kind,
|
|
5500
|
+
payload: effect.payload,
|
|
5501
|
+
waited,
|
|
5502
|
+
...effect.outcome === void 0 ? {} : { outcome: effect.outcome }
|
|
5503
|
+
})),
|
|
5504
|
+
settles: [...settles]
|
|
5505
|
+
})
|
|
5506
|
+
};
|
|
5507
|
+
}
|
|
5508
|
+
/**
|
|
5509
|
+
* This function makes a replayer: a function `perform` that answers each effect from a log, by its address (RUN.REPLAY). A
|
|
5510
|
+
* recorded error comes again with its code, its reason, its place and its cause. The waits of each `all` settle in the
|
|
5511
|
+
* recorded order. An effect that the log does not have at its address gives `#REPLAY!` (RUN.REPLAY-MISMATCH).
|
|
5512
|
+
*
|
|
5513
|
+
* A replayer keeps its place in the order of the waits, thus each run needs a new replayer.
|
|
5514
|
+
*
|
|
5515
|
+
* @param log - The log of a recorder.
|
|
5516
|
+
* @param options - `wait: false` for an evaluation.
|
|
5517
|
+
* @returns The function `perform` for the replay.
|
|
5518
|
+
*/
|
|
5519
|
+
function replayer(log, options = {}) {
|
|
5520
|
+
const wait = options.wait !== false;
|
|
5521
|
+
const entries = /* @__PURE__ */ new Map();
|
|
5522
|
+
for (const entry of log.effects) entries.set(entry.address, entry);
|
|
5523
|
+
const order = /* @__PURE__ */ new Map();
|
|
5524
|
+
for (const settle of log.settles) {
|
|
5525
|
+
const items = order.get(settle.all);
|
|
5526
|
+
if (items === void 0) order.set(settle.all, [settle.item]);
|
|
5527
|
+
else items.push(settle.item);
|
|
5528
|
+
}
|
|
5529
|
+
const journal = { next: (all, waiting) => {
|
|
5530
|
+
const items = order.get(all);
|
|
5531
|
+
const item = items?.[0];
|
|
5532
|
+
if (items === void 0 || item === void 0 || !waiting.includes(item)) return void 0;
|
|
5533
|
+
items.shift();
|
|
5534
|
+
return item;
|
|
5535
|
+
} };
|
|
5536
|
+
const perform = (effect) => {
|
|
5537
|
+
const entry = effect.address === void 0 ? void 0 : entries.get(effect.address);
|
|
5538
|
+
const wrong = mismatch(effect, entry);
|
|
5539
|
+
if (wrong !== void 0 || entry === void 0) throw wrong;
|
|
5540
|
+
const outcome = entry.outcome;
|
|
5541
|
+
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);
|
|
5542
|
+
const value = outcome?.ok === true ? outcome.value : void 0;
|
|
5543
|
+
if (wait && entry.waited) return failure === void 0 ? Promise.resolve(value) : Promise.reject(failure);
|
|
5544
|
+
if (failure !== void 0) throw failure;
|
|
5545
|
+
return value;
|
|
5546
|
+
};
|
|
5547
|
+
return Object.assign(perform, { [JOURNAL]: journal });
|
|
5548
|
+
}
|
|
5549
|
+
/** This function gives the error of an effect that the log does not have at its address, or `undefined`. */
|
|
5550
|
+
function mismatch(effect, entry) {
|
|
5551
|
+
const at = effect.address ?? "";
|
|
5552
|
+
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}.`);
|
|
5553
|
+
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}.`);
|
|
5554
|
+
if (!docEquals(entry.payload, effect.payload)) return replayError(effect, `The effect ${effect.kind} at the address ${at} has another payload than the log.`);
|
|
5555
|
+
}
|
|
5556
|
+
/** This function gives the exception that carries `#REPLAY!` to the kernel, with the place of the effect. */
|
|
5557
|
+
function replayError(effect, reason) {
|
|
5558
|
+
return new Failure(fail("#REPLAY!", reason, { place: effect.place }).error);
|
|
5559
|
+
}
|
|
5560
|
+
//#endregion
|
|
5276
5561
|
//#region src/generate.ts
|
|
5277
5562
|
/**
|
|
5278
5563
|
* This function makes a source of random numbers with a seed (the algorithm mulberry32). It needs no package.
|
|
@@ -5672,6 +5957,6 @@ function generate(world, options) {
|
|
|
5672
5957
|
});
|
|
5673
5958
|
}
|
|
5674
5959
|
//#endregion
|
|
5675
|
-
export { CONSTRUCTS, ENVELOPE_KEYS, Failure, Held, MergeError, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_DEFINITIONS, STD_EVALUATORS, STD_EXT, STD_MACHINE, STD_RUNNERS, STD_WALKERS, STD_WORLD, THING, agree, ask, capKeys, check, checkSteps, classify, context, dataEdges, derefStep, describe, docEquals, effectEdges, entriesAsJson, evaluate, fail, failures, field, flatten, formulaDefinitions, formulaEvaluators, formulaKinds, formulaKnown, formulaMachine, fromJson, generate, intersectCaps, isAlias, isAllowedOrder, isClosure, isEnvelope, isHostThing, isKey, isKind, isNamespace, isRecord, isRef, isStackOverflow, isTypeRef, jsTruthy, kindDocument, kindNamed, kindOf, layers, looksLikeKind, lookupKind, makeContext, mapRefs, mergeMachines, mergeWorlds, narrow, nearestWalker, nodeAt, ok, partCaps, partsOf, pathMatches, permits, placed, planner, prepare, random, ref, reference, refsOf, resolve, restrictMachine, restrictWorld, run, sameValue, splitParts, stored, tag, thrown, toJson, typeName, typePath, typed, typesOf, valueOfThing, walk, worldThing };
|
|
5960
|
+
export { CONSTRUCTS, ENVELOPE_KEYS, Failure, Held, JOURNAL, MergeError, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_DEFINITIONS, STD_EVALUATORS, STD_EXT, STD_MACHINE, STD_RUNNERS, STD_WALKERS, STD_WORLD, THING, agree, ask, capKeys, check, checkSteps, classify, context, dataEdges, derefStep, describe, docEquals, effectEdges, entriesAsJson, evaluate, fail, failures, field, flatten, formulaDefinitions, formulaEvaluators, formulaKinds, formulaKnown, formulaMachine, fromJson, generate, intersectCaps, isAlias, isAllowedOrder, isClosure, isEnvelope, isHostThing, isKey, isKind, isNamespace, isRecord, isRef, isStackOverflow, isTypeRef, jsTruthy, kindDocument, kindNamed, kindOf, layers, looksLikeKind, lookupKind, makeContext, mapRefs, mergeMachines, mergeWorlds, narrow, nearestWalker, nodeAt, ok, partCaps, partsOf, pathMatches, permits, placed, planner, prepare, random, recorder, ref, reference, refsOf, replayer, resolve, restrictMachine, restrictWorld, run, sameResult, sameValue, splitParts, stored, tag, thrown, toJson, typeName, typePath, typed, typesOf, valueOfThing, walk, worldThing };
|
|
5676
5961
|
|
|
5677
5962
|
//# sourceMappingURL=index.js.map
|