@mark1russell7/coeval 0.4.1 → 0.4.2
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 +1 -0
- package/build/index.d.ts +208 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1096 -25
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/agree.ts +231 -0
- package/src/errors.ts +6 -1
- package/src/evaluate.ts +24 -8
- package/src/formula.ts +10 -8
- package/src/generate.ts +367 -0
- package/src/index.ts +3 -0
- package/src/reference.ts +571 -0
package/build/index.js
CHANGED
|
@@ -282,7 +282,11 @@ var Failure = class extends Error {
|
|
|
282
282
|
* @returns `true` for a stack overflow.
|
|
283
283
|
*/
|
|
284
284
|
function isStackOverflow(error) {
|
|
285
|
-
|
|
285
|
+
try {
|
|
286
|
+
return error instanceof RangeError && /call stack/i.test(error.message);
|
|
287
|
+
} catch {
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
286
290
|
}
|
|
287
291
|
/**
|
|
288
292
|
* This function changes an exception into an error value. A `Failure` gives its error value. A stack overflow
|
|
@@ -1543,7 +1547,7 @@ function holderNode() {
|
|
|
1543
1547
|
};
|
|
1544
1548
|
}
|
|
1545
1549
|
/** This function drives steps that cannot wait. */
|
|
1546
|
-
function drive(steps) {
|
|
1550
|
+
function drive$1(steps) {
|
|
1547
1551
|
const step = steps.next();
|
|
1548
1552
|
if (step.done === true) return step.value;
|
|
1549
1553
|
step.value.catch(() => void 0);
|
|
@@ -1586,7 +1590,10 @@ function isCompiledRunner(runner) {
|
|
|
1586
1590
|
return typeof runner === "object" && runner !== null && typeof runner.compileRun === "function";
|
|
1587
1591
|
}
|
|
1588
1592
|
function failing(c, meta, place, code, reason) {
|
|
1589
|
-
|
|
1593
|
+
return failingWith(c, meta, fail(code, reason, { place }));
|
|
1594
|
+
}
|
|
1595
|
+
/** This function gives the code of a document that fails with one result at each evaluation. */
|
|
1596
|
+
function failingWith(c, meta, result) {
|
|
1590
1597
|
const plain = () => result;
|
|
1591
1598
|
return finish(c, meta, plain, function* () {
|
|
1592
1599
|
return result;
|
|
@@ -1752,10 +1759,22 @@ function compileNode(c, value, place, declared, depth, address = false) {
|
|
|
1752
1759
|
...address ? { address: true } : {}
|
|
1753
1760
|
};
|
|
1754
1761
|
if (runner !== void 0) {
|
|
1755
|
-
|
|
1762
|
+
let inner;
|
|
1763
|
+
try {
|
|
1764
|
+
inner = isCompiledRunner(runner) ? runner.compileRun(input) : interpretRun(runner, input, split.fill);
|
|
1765
|
+
} catch (error) {
|
|
1766
|
+
return failingWith(c, meta, placed(thrown(error, `the runner ${name}`), place));
|
|
1767
|
+
}
|
|
1768
|
+
const run = placedRun(inner, place, name);
|
|
1756
1769
|
return finish(c, meta, runnerCode(run, place, kindText), run);
|
|
1757
1770
|
}
|
|
1758
|
-
|
|
1771
|
+
let inner;
|
|
1772
|
+
try {
|
|
1773
|
+
inner = isCompiledEvaluator(evaluator) ? evaluator.compile(input) : interpret(evaluator, input, split.fill);
|
|
1774
|
+
} catch (error) {
|
|
1775
|
+
return failingWith(c, meta, placed(thrown(error, `the evaluator ${name}`), place));
|
|
1776
|
+
}
|
|
1777
|
+
const code = placedCode(inner, place, name);
|
|
1759
1778
|
return finish(c, meta, code, runOf(code));
|
|
1760
1779
|
}
|
|
1761
1780
|
/** This function gives the code of a document: the code of its evaluator, with the place of each error and of each exception. */
|
|
@@ -1797,9 +1816,12 @@ function placedRun(inner, place, name) {
|
|
|
1797
1816
|
}
|
|
1798
1817
|
};
|
|
1799
1818
|
}
|
|
1800
|
-
/**
|
|
1819
|
+
/**
|
|
1820
|
+
* This function gives the code of a runner in an evaluation: its steps, which cannot wait. A step that waits gives
|
|
1821
|
+
* `#CALC!` at the place of the runner, where the error started (RUN.WAIT).
|
|
1822
|
+
*/
|
|
1801
1823
|
function runnerCode(run, place, kindText) {
|
|
1802
|
-
return (ctx, rt) => rt.running ? fail("#CALC!", `${kindText} waits for effects, and its parent cannot wait. Give the parent a runner.`, { place }) : drive(run(ctx, rt));
|
|
1824
|
+
return (ctx, rt) => rt.running ? fail("#CALC!", `${kindText} waits for effects, and its parent cannot wait. Give the parent a runner.`, { place }) : placed(drive$1(run(ctx, rt)), place);
|
|
1803
1825
|
}
|
|
1804
1826
|
/** This function gives the steps of a run of an evaluator. The code runs at the first step, and it does not wait. */
|
|
1805
1827
|
function runOf(code) {
|
|
@@ -1891,7 +1913,7 @@ function prepare(machine, doc, options = {}) {
|
|
|
1891
1913
|
try {
|
|
1892
1914
|
root = compileNode(c, doc, [], options.declaredType, 0);
|
|
1893
1915
|
} catch (error) {
|
|
1894
|
-
const result = thrown(error, "the compilation");
|
|
1916
|
+
const result = placed(thrown(error, "the compilation"), []);
|
|
1895
1917
|
root = {
|
|
1896
1918
|
code: () => result,
|
|
1897
1919
|
run: function* () {
|
|
@@ -1952,7 +1974,7 @@ function planner(machine, options = {}) {
|
|
|
1952
1974
|
return plan;
|
|
1953
1975
|
};
|
|
1954
1976
|
}
|
|
1955
|
-
function record(s, result) {
|
|
1977
|
+
function record$1(s, result) {
|
|
1956
1978
|
const root = s.holder?.children[0];
|
|
1957
1979
|
if (root === void 0) throw new Error("The evaluation made no trace.");
|
|
1958
1980
|
return {
|
|
@@ -1981,7 +2003,7 @@ function evaluate(machine, doc, context = makeContext(), declaredType) {
|
|
|
1981
2003
|
...declaredType === void 0 ? {} : { declaredType }
|
|
1982
2004
|
});
|
|
1983
2005
|
const s = new Session(machine, false, true, void 0, void 0, void 0);
|
|
1984
|
-
return record(s, plan.root.code(rootContext(machine, context), s));
|
|
2006
|
+
return record$1(s, plan.root.code(rootContext(machine, context), s));
|
|
1985
2007
|
}
|
|
1986
2008
|
/**
|
|
1987
2009
|
* This function runs a document: an evaluation in which the host does each effect. A runner waits for its
|
|
@@ -2000,7 +2022,7 @@ async function run(machine, doc, context = makeContext(), options = {}) {
|
|
|
2000
2022
|
...options.declaredType === void 0 ? {} : { declaredType: options.declaredType }
|
|
2001
2023
|
});
|
|
2002
2024
|
const s = new Session(machine, true, true, options.perform, options.signal, void 0);
|
|
2003
|
-
return record(s, await driveWaiting(plan.root.run(rootContext(machine, context), s), options.signal));
|
|
2025
|
+
return record$1(s, await driveWaiting(plan.root.run(rootContext(machine, context), s), options.signal));
|
|
2004
2026
|
}
|
|
2005
2027
|
/**
|
|
2006
2028
|
* This function finds a node of a trace by its path.
|
|
@@ -3290,7 +3312,7 @@ var Held = class {
|
|
|
3290
3312
|
this.error = error;
|
|
3291
3313
|
}
|
|
3292
3314
|
};
|
|
3293
|
-
const CLOSURE = Symbol.for("coeval.closure");
|
|
3315
|
+
const CLOSURE$2 = Symbol.for("coeval.closure");
|
|
3294
3316
|
/**
|
|
3295
3317
|
* This function tells if a value is a function that the construct `fn` made.
|
|
3296
3318
|
*
|
|
@@ -3298,7 +3320,7 @@ const CLOSURE = Symbol.for("coeval.closure");
|
|
|
3298
3320
|
* @returns `true` for a closure.
|
|
3299
3321
|
*/
|
|
3300
3322
|
function isClosure(value) {
|
|
3301
|
-
return typeof value === "function" && value[CLOSURE] !== void 0;
|
|
3323
|
+
return typeof value === "function" && value[CLOSURE$2] !== void 0;
|
|
3302
3324
|
}
|
|
3303
3325
|
/** The standard extensions: the values that JSON cannot write. */
|
|
3304
3326
|
const STD_EXT = {
|
|
@@ -3531,18 +3553,19 @@ function formulaEvaluators(profile = {}) {
|
|
|
3531
3553
|
if (missing !== void 0 && missing(reached)) return fail("#N/A", "The path reaches no value.");
|
|
3532
3554
|
return ok(reached);
|
|
3533
3555
|
};
|
|
3534
|
-
const
|
|
3556
|
+
const held = (ctx) => {
|
|
3557
|
+
if (head === void 0 || !Object.hasOwn(ctx.names, head)) return void 0;
|
|
3558
|
+
const bound = ctx.names[head];
|
|
3559
|
+
return bound instanceof Held ? {
|
|
3560
|
+
ok: false,
|
|
3561
|
+
error: bound.error
|
|
3562
|
+
} : void 0;
|
|
3563
|
+
};
|
|
3564
|
+
if (parts.length === 0) return (ctx, rt) => held(ctx) ?? give(rt.resolve(ctx, path));
|
|
3535
3565
|
return (ctx, rt) => {
|
|
3536
|
-
if (head !== void 0 && Object.hasOwn(ctx.names, head)) {
|
|
3537
|
-
const bound = ctx.names[head];
|
|
3538
|
-
if (bound instanceof Held) return {
|
|
3539
|
-
ok: false,
|
|
3540
|
-
error: bound.error
|
|
3541
|
-
};
|
|
3542
|
-
}
|
|
3543
|
-
if (!computed) return give(rt.resolve(ctx, path));
|
|
3544
3566
|
const steps = valuesOf(parts, ctx, rt);
|
|
3545
|
-
|
|
3567
|
+
if (!Array.isArray(steps)) return steps;
|
|
3568
|
+
return held(ctx) ?? give(rt.resolve(ctx, fill(steps)));
|
|
3546
3569
|
};
|
|
3547
3570
|
}),
|
|
3548
3571
|
"formula.step": compiled(({ doc, parts, fill }) => {
|
|
@@ -3626,7 +3649,7 @@ function formulaEvaluators(profile = {}) {
|
|
|
3626
3649
|
if (!result.ok) throw new Failure(result.error);
|
|
3627
3650
|
return result.value;
|
|
3628
3651
|
};
|
|
3629
|
-
Object.defineProperty(closure, CLOSURE, { value: { params: names } });
|
|
3652
|
+
Object.defineProperty(closure, CLOSURE$2, { value: { params: names } });
|
|
3630
3653
|
return ok(closure);
|
|
3631
3654
|
};
|
|
3632
3655
|
}),
|
|
@@ -3795,6 +3818,1054 @@ function formulaMachine(profile = {}, more = {}) {
|
|
|
3795
3818
|
};
|
|
3796
3819
|
}
|
|
3797
3820
|
//#endregion
|
|
3798
|
-
|
|
3821
|
+
//#region src/reference.ts
|
|
3822
|
+
function own(table, name) {
|
|
3823
|
+
return table !== void 0 && Object.hasOwn(table, name) ? table[name] : void 0;
|
|
3824
|
+
}
|
|
3825
|
+
/** This function finds the code of an evaluator name. A runner wins over an evaluator of the same name. */
|
|
3826
|
+
function choose(env, name) {
|
|
3827
|
+
const runner = own(env.machine.runners, name);
|
|
3828
|
+
if (runner !== void 0) {
|
|
3829
|
+
const defined = own(env.definitions.runners, name);
|
|
3830
|
+
if (defined !== void 0) return { runner: defined };
|
|
3831
|
+
return typeof runner === "function" ? { runner } : { gap: `The reference has no definition of the compiled runner ${name}.` };
|
|
3832
|
+
}
|
|
3833
|
+
const evaluator = own(env.machine.evaluators, name);
|
|
3834
|
+
if (evaluator === void 0) return void 0;
|
|
3835
|
+
const defined = own(env.definitions.evaluators, name);
|
|
3836
|
+
if (defined !== void 0) return { evaluator: defined };
|
|
3837
|
+
return typeof evaluator === "function" ? { evaluator } : { gap: `The reference has no definition of the compiled evaluator ${name}.` };
|
|
3838
|
+
}
|
|
3839
|
+
/**
|
|
3840
|
+
* This function reads one document by the rules, in their order. First come an envelope or a declared type
|
|
3841
|
+
* (EVAL.ERRORS) and the limit of the depth (PLAN.DEPTH). Then come the ladder (LADDER.*), an evaluator and the shape
|
|
3842
|
+
* of the parts (SHAPE.*).
|
|
3843
|
+
*/
|
|
3844
|
+
function examine(env, at) {
|
|
3845
|
+
const { place, declared } = at;
|
|
3846
|
+
const envelope = isEnvelope(at.value) ? at.value : void 0;
|
|
3847
|
+
if (envelope === void 0 && declared === void 0) return fail("#PART?", "The value has no envelope, and its position declares no type.", { place });
|
|
3848
|
+
if (at.depth > env.maxDepth) return fail("#DEPTH!", `The nesting is deeper than ${env.maxDepth} documents.`, { place });
|
|
3849
|
+
const doc = envelope ?? {
|
|
3850
|
+
type: declared,
|
|
3851
|
+
value: at.value
|
|
3852
|
+
};
|
|
3853
|
+
const kindText = typeName(doc.type);
|
|
3854
|
+
const world = env.machine.world;
|
|
3855
|
+
const answer = ask(world, envelope ?? {}, "eval", declared === void 0 ? {} : { declaredType: declared });
|
|
3856
|
+
if (answer.error === "unknown-kind") return fail("#KIND?", `The world has no kind ${kindText}.`, { place });
|
|
3857
|
+
const name = answer.evaluator;
|
|
3858
|
+
const chosen = name === void 0 ? void 0 : choose(env, name);
|
|
3859
|
+
if (name === void 0 || chosen === void 0) return fail("#NOEVAL", `No rung gives an evaluator for ${kindText}.`, { place });
|
|
3860
|
+
const found = lookupKind(world, doc.type);
|
|
3861
|
+
const kind = found.ok ? found.kind : void 0;
|
|
3862
|
+
const split = splitParts(kind?.parts, doc.value);
|
|
3863
|
+
if (!split.ok) {
|
|
3864
|
+
const where = split.at.length === 0 ? "" : ` (at ${split.at.join(".")})`;
|
|
3865
|
+
return fail("#VALUE!", `${split.reason}${where} The kind is ${kindText}.`, { place });
|
|
3866
|
+
}
|
|
3867
|
+
return {
|
|
3868
|
+
doc,
|
|
3869
|
+
kindText,
|
|
3870
|
+
typePath: found.ok ? found.path : [],
|
|
3871
|
+
kind,
|
|
3872
|
+
name,
|
|
3873
|
+
chosen,
|
|
3874
|
+
split
|
|
3875
|
+
};
|
|
3876
|
+
}
|
|
3877
|
+
/** This function gives the capabilities that the rules of a kind give one part (D14, CAP.CONTRACT, CAP.ONLY). */
|
|
3878
|
+
function capsOfPart(rules, path, caps) {
|
|
3879
|
+
let out = caps;
|
|
3880
|
+
for (const rule of rules ?? []) {
|
|
3881
|
+
if (rule.in !== void 0 && !rule.in.some((pattern) => pathMatches(path, pattern))) continue;
|
|
3882
|
+
if (rule.rule === "mask") return [];
|
|
3883
|
+
if (rule.rule === "only") out = intersectCaps(out, rule.caps);
|
|
3884
|
+
}
|
|
3885
|
+
return out;
|
|
3886
|
+
}
|
|
3887
|
+
/**
|
|
3888
|
+
* This function gives the context of a part. A context that the evaluator gives keeps only the capabilities of the
|
|
3889
|
+
* parent (CAP.NARROW). Then the rules of the kind narrow it (CAP.CONTRACT).
|
|
3890
|
+
*/
|
|
3891
|
+
function contextOfPart(seen, slot, parent, given) {
|
|
3892
|
+
const base = given === void 0 || given === parent ? parent : {
|
|
3893
|
+
...given,
|
|
3894
|
+
caps: given.caps.filter((cap) => permits(parent.caps, cap))
|
|
3895
|
+
};
|
|
3896
|
+
const caps = capsOfPart(seen.kind?.caps, slot.path, base.caps);
|
|
3897
|
+
return caps === base.caps ? base : {
|
|
3898
|
+
...base,
|
|
3899
|
+
caps
|
|
3900
|
+
};
|
|
3901
|
+
}
|
|
3902
|
+
function headerOf(item) {
|
|
3903
|
+
return isEnvelope(item) && typeof item.name === "string" ? item.name : void 0;
|
|
3904
|
+
}
|
|
3905
|
+
function partAt(seen, at, slot, given) {
|
|
3906
|
+
return {
|
|
3907
|
+
value: slot.item,
|
|
3908
|
+
place: at.place.concat(slot.path),
|
|
3909
|
+
declared: slot.of,
|
|
3910
|
+
depth: at.depth + 1,
|
|
3911
|
+
address: slot.address === true,
|
|
3912
|
+
ctx: contextOfPart(seen, slot, at.ctx, given)
|
|
3913
|
+
};
|
|
3914
|
+
}
|
|
3915
|
+
/** This function resolves a path with the full algorithm of `resolve`, not with its short path (DEREF.QUICK). */
|
|
3916
|
+
function resolveFully(machine, ctx, path, options) {
|
|
3917
|
+
const found = resolve(machine, ctx, path, { hops: true });
|
|
3918
|
+
return options?.hops === true ? found : {
|
|
3919
|
+
...found,
|
|
3920
|
+
hops: []
|
|
3921
|
+
};
|
|
3922
|
+
}
|
|
3923
|
+
/** This function records an effect when the capabilities permit it (RUN.CAP, CAP.HOOK). */
|
|
3924
|
+
function record(env, ctx, kind, payload, place) {
|
|
3925
|
+
const keys = typeof kind === "string" ? kind.split(".") : kind;
|
|
3926
|
+
const text = keys.join(".");
|
|
3927
|
+
const machine = env.machine;
|
|
3928
|
+
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 });
|
|
3929
|
+
env.effects.push({
|
|
3930
|
+
index: env.effects.length,
|
|
3931
|
+
kind: text,
|
|
3932
|
+
path: keys,
|
|
3933
|
+
payload,
|
|
3934
|
+
node: -1,
|
|
3935
|
+
at: 0,
|
|
3936
|
+
place,
|
|
3937
|
+
context: ctx
|
|
3938
|
+
});
|
|
3939
|
+
}
|
|
3940
|
+
/** This function evaluates a document. Each error gets the place of the document where it started (K5). */
|
|
3941
|
+
function evaluateAt(env, at) {
|
|
3942
|
+
const seen = examine(env, at);
|
|
3943
|
+
if (!("doc" in seen)) return seen;
|
|
3944
|
+
return evaluateSeen(env, seen, at);
|
|
3945
|
+
}
|
|
3946
|
+
function evaluateSeen(env, seen, at) {
|
|
3947
|
+
const { chosen } = seen;
|
|
3948
|
+
const { place } = at;
|
|
3949
|
+
if ("gap" in chosen) return fail("#NOEVAL", chosen.gap, { place });
|
|
3950
|
+
if ("runner" in chosen) return drive(runSeen(env, seen, at, chosen.runner), place);
|
|
3951
|
+
const input = evalInput(env, seen, at);
|
|
3952
|
+
try {
|
|
3953
|
+
return placed(chosen.evaluator(input), place);
|
|
3954
|
+
} catch (error) {
|
|
3955
|
+
return placed(thrown(error, `the evaluator ${seen.name}`), place);
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3958
|
+
/** This function gives the steps of a document for a runner. Nothing happens before the first step. */
|
|
3959
|
+
function* stepsAt(env, at) {
|
|
3960
|
+
const seen = examine(env, at);
|
|
3961
|
+
if (!("doc" in seen)) return seen;
|
|
3962
|
+
if ("runner" in seen.chosen) return yield* runSeen(env, seen, at, seen.chosen.runner);
|
|
3963
|
+
return evaluateSeen(env, seen, at);
|
|
3964
|
+
}
|
|
3965
|
+
function* runSeen(env, seen, at, runner) {
|
|
3966
|
+
try {
|
|
3967
|
+
return placed(yield* runner(runInput(env, seen, at)), at.place);
|
|
3968
|
+
} catch (error) {
|
|
3969
|
+
return placed(thrown(error, `the runner ${seen.name}`), at.place);
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
/** This function drives steps that cannot wait. A step that waits gives `#CALC!` at the document (RUN.WAIT). */
|
|
3973
|
+
function drive(steps, place) {
|
|
3974
|
+
const step = steps.next();
|
|
3975
|
+
if (step.done === true) return step.value;
|
|
3976
|
+
step.value.catch(() => void 0);
|
|
3977
|
+
const waited = fail("#CALC!", "A part waited, and its parent cannot wait.", { place });
|
|
3978
|
+
steps.return(waited);
|
|
3979
|
+
return waited;
|
|
3980
|
+
}
|
|
3981
|
+
function evalInput(env, seen, at) {
|
|
3982
|
+
const { split, doc } = seen;
|
|
3983
|
+
const parts = split.parts.map((slot) => {
|
|
3984
|
+
const name = headerOf(slot.item);
|
|
3985
|
+
return {
|
|
3986
|
+
key: slot.key,
|
|
3987
|
+
path: slot.path,
|
|
3988
|
+
...name === void 0 ? {} : { name },
|
|
3989
|
+
evaluate: (given) => evaluateAt(env, partAt(seen, at, slot, given))
|
|
3990
|
+
};
|
|
3991
|
+
});
|
|
3992
|
+
const fill = (items) => split.parts.length === 0 ? doc.value : split.fill(items);
|
|
3993
|
+
return {
|
|
3994
|
+
kind: seen.kindText,
|
|
3995
|
+
typePath: seen.typePath,
|
|
3996
|
+
doc,
|
|
3997
|
+
value: fill(parts),
|
|
3998
|
+
data: split.data,
|
|
3999
|
+
parts,
|
|
4000
|
+
context: at.ctx,
|
|
4001
|
+
place: at.place,
|
|
4002
|
+
...at.address ? { address: true } : {},
|
|
4003
|
+
resolve: (path, options) => resolveFully(env.machine, at.ctx, path, options),
|
|
4004
|
+
effect: (kind, payload) => record(env, at.ctx, kind, payload, at.place) ?? ok(payload),
|
|
4005
|
+
fill
|
|
4006
|
+
};
|
|
4007
|
+
}
|
|
4008
|
+
function runInput(env, seen, at) {
|
|
4009
|
+
const { split, doc } = seen;
|
|
4010
|
+
const parts = split.parts.map((slot) => {
|
|
4011
|
+
const name = headerOf(slot.item);
|
|
4012
|
+
return {
|
|
4013
|
+
key: slot.key,
|
|
4014
|
+
path: slot.path,
|
|
4015
|
+
...name === void 0 ? {} : { name },
|
|
4016
|
+
evaluate: (given) => stepsAt(env, partAt(seen, at, slot, given))
|
|
4017
|
+
};
|
|
4018
|
+
});
|
|
4019
|
+
const fill = (items) => split.parts.length === 0 ? doc.value : split.fill(items);
|
|
4020
|
+
return {
|
|
4021
|
+
kind: seen.kindText,
|
|
4022
|
+
typePath: seen.typePath,
|
|
4023
|
+
doc,
|
|
4024
|
+
value: fill(parts),
|
|
4025
|
+
data: split.data,
|
|
4026
|
+
parts,
|
|
4027
|
+
context: at.ctx,
|
|
4028
|
+
place: at.place,
|
|
4029
|
+
resolve: (path, options) => resolveFully(env.machine, at.ctx, path, options),
|
|
4030
|
+
effect: function* (kind, payload) {
|
|
4031
|
+
return record(env, at.ctx, kind, payload, at.place) ?? ok(payload);
|
|
4032
|
+
},
|
|
4033
|
+
all: (list, options) => together(list, options),
|
|
4034
|
+
fill
|
|
4035
|
+
};
|
|
4036
|
+
}
|
|
4037
|
+
/**
|
|
4038
|
+
* This function evaluates parts for `all` (RUN.ALL). The parts start in their order, up to the limit, and each part
|
|
4039
|
+
* goes on until it ends or waits. In an evaluation, a part that waits does not end, thus it keeps its place in the
|
|
4040
|
+
* limit. With `failFast`, the first failure stops the parts that wait, and the later parts do not start
|
|
4041
|
+
* (RUN.FAIL-FAST). When no part failed and a part waits, the steps wait, and the drive gives `#CALC!`.
|
|
4042
|
+
*/
|
|
4043
|
+
function* together(list, options = {}) {
|
|
4044
|
+
const limit = Math.max(1, options.concurrency ?? list.length);
|
|
4045
|
+
const results = list.map(() => void 0);
|
|
4046
|
+
const waiting = [];
|
|
4047
|
+
let failed = false;
|
|
4048
|
+
for (let index = 0; index < list.length && !failed && waiting.length < limit; index += 1) {
|
|
4049
|
+
let result;
|
|
4050
|
+
try {
|
|
4051
|
+
const steps = list[index].evaluate();
|
|
4052
|
+
const step = steps.next();
|
|
4053
|
+
if (step.done === true) result = step.value;
|
|
4054
|
+
else waiting.push({
|
|
4055
|
+
steps,
|
|
4056
|
+
promise: step.value
|
|
4057
|
+
});
|
|
4058
|
+
} catch (error) {
|
|
4059
|
+
result = thrown(error, "a part");
|
|
4060
|
+
}
|
|
4061
|
+
results[index] = result;
|
|
4062
|
+
if (result !== void 0 && options.failFast === true && !result.ok) failed = true;
|
|
4063
|
+
}
|
|
4064
|
+
if (failed) {
|
|
4065
|
+
const stopped = fail("#ABORT", "The part stopped: a part before it failed, and the parts run with failFast.");
|
|
4066
|
+
for (const item of waiting) try {
|
|
4067
|
+
item.steps.return(stopped);
|
|
4068
|
+
} catch {}
|
|
4069
|
+
return results.map((result) => result ?? stopped);
|
|
4070
|
+
}
|
|
4071
|
+
if (waiting.length > 0) yield Promise.race(waiting.map((item) => item.promise));
|
|
4072
|
+
return results;
|
|
4073
|
+
}
|
|
4074
|
+
/**
|
|
4075
|
+
* This function evaluates a document by the rules, without a compilation. It is the reference of the plans: a plan,
|
|
4076
|
+
* a traced plan and the second tier of a language must give its results. It covers evaluations, not runs: an effect
|
|
4077
|
+
* gives its payload, and a part that waits gives `#CALC!`.
|
|
4078
|
+
*
|
|
4079
|
+
* @param machine - The world and the code of the evaluators.
|
|
4080
|
+
* @param doc - The document.
|
|
4081
|
+
* @param context - The names and the capabilities at the root.
|
|
4082
|
+
* @param options - The type of a bare root value, the definitions of compiled code, and a list for the effects.
|
|
4083
|
+
* @returns The result.
|
|
4084
|
+
*/
|
|
4085
|
+
function reference(machine, doc, context = makeContext(), options = {}) {
|
|
4086
|
+
const env = {
|
|
4087
|
+
machine,
|
|
4088
|
+
definitions: options.definitions ?? {},
|
|
4089
|
+
effects: options.effects ?? [],
|
|
4090
|
+
maxDepth: machine.maxDepth ?? 1e3
|
|
4091
|
+
};
|
|
4092
|
+
const ctx = context.world === null ? {
|
|
4093
|
+
...context,
|
|
4094
|
+
world: worldThing(machine.world)
|
|
4095
|
+
} : context;
|
|
4096
|
+
try {
|
|
4097
|
+
return evaluateAt(env, {
|
|
4098
|
+
value: doc,
|
|
4099
|
+
place: [],
|
|
4100
|
+
declared: options.declaredType,
|
|
4101
|
+
depth: 0,
|
|
4102
|
+
address: false,
|
|
4103
|
+
ctx
|
|
4104
|
+
});
|
|
4105
|
+
} catch (error) {
|
|
4106
|
+
return placed(thrown(error, "the reference"), []);
|
|
4107
|
+
}
|
|
4108
|
+
}
|
|
4109
|
+
const CLOSURE$1 = Symbol.for("coeval.closure");
|
|
4110
|
+
/** This function sets an own field, also the field `__proto__` (SHAPE.PROTO). */
|
|
4111
|
+
function setField(target, key, value) {
|
|
4112
|
+
Object.defineProperty(target, key, {
|
|
4113
|
+
value,
|
|
4114
|
+
enumerable: true,
|
|
4115
|
+
writable: true,
|
|
4116
|
+
configurable: true
|
|
4117
|
+
});
|
|
4118
|
+
}
|
|
4119
|
+
function partNamed(parts, key) {
|
|
4120
|
+
return parts.find((part) => part.path.length === 1 && part.path[0] === key);
|
|
4121
|
+
}
|
|
4122
|
+
function partsUnder(parts, key) {
|
|
4123
|
+
return parts.filter((part) => part.path.length === 2 && part.path[0] === key);
|
|
4124
|
+
}
|
|
4125
|
+
/**
|
|
4126
|
+
* This function gives the definitions of the formula kit for one profile. Each construct is a function evaluator in
|
|
4127
|
+
* the most direct style. It shares no code with the compilers of `formulaEvaluators`, thus the two readings of the
|
|
4128
|
+
* rules can disagree, and a test finds it.
|
|
4129
|
+
*
|
|
4130
|
+
* @param profile - The operations and the rules of the language.
|
|
4131
|
+
* @returns The definitions, by evaluator name.
|
|
4132
|
+
*/
|
|
4133
|
+
function formulaDefinitions(profile = {}) {
|
|
4134
|
+
const truthy = profile.truthy ?? ((value) => typeof value === "boolean" ? value : void 0);
|
|
4135
|
+
const opOf = (name) => typeof profile.ops === "function" ? profile.ops(name) : own(profile.ops, name);
|
|
4136
|
+
const noTruth = (what) => fail("#VALUE!", `The ${what} must give a truth value.`);
|
|
4137
|
+
/** FORMULA.LIST, FORMULA.OPS and FORMULA.COMBINE: the values of parts in order, or the error. */
|
|
4138
|
+
const valuesOf = (parts, skip) => {
|
|
4139
|
+
const values = [];
|
|
4140
|
+
const errors = [];
|
|
4141
|
+
for (const part of parts) {
|
|
4142
|
+
const result = part.evaluate();
|
|
4143
|
+
if (result.ok) values.push(result.value);
|
|
4144
|
+
else if (skip?.(result.error) === true) values.push(SKIP);
|
|
4145
|
+
else if (profile.combine === void 0) return result;
|
|
4146
|
+
else errors.push(result.error);
|
|
4147
|
+
}
|
|
4148
|
+
if (errors.length === 0 || profile.combine === void 0) return { values };
|
|
4149
|
+
return {
|
|
4150
|
+
ok: false,
|
|
4151
|
+
error: errors.length === 1 ? errors[0] : profile.combine(errors)
|
|
4152
|
+
};
|
|
4153
|
+
};
|
|
4154
|
+
const lit = ({ doc }) => ok(doc.value);
|
|
4155
|
+
const ref = ({ doc, parts, fill, address, context, resolve: walk }) => {
|
|
4156
|
+
const path = doc.value;
|
|
4157
|
+
if (!Array.isArray(path) || path.length === 0) return fail("#VALUE!", "The value of ref is a path: a list that starts with a root, for example [\"names\", \"a\"].");
|
|
4158
|
+
let steps = path;
|
|
4159
|
+
if (parts.length > 0) {
|
|
4160
|
+
const all = valuesOfStrict(parts);
|
|
4161
|
+
if (!("values" in all)) return all;
|
|
4162
|
+
steps = fill(all.values);
|
|
4163
|
+
}
|
|
4164
|
+
const head = path[0] === "names" && typeof path[1] === "string" ? path[1] : void 0;
|
|
4165
|
+
const bound = head === void 0 ? void 0 : own(context.names, head);
|
|
4166
|
+
if (bound instanceof Held) return {
|
|
4167
|
+
ok: false,
|
|
4168
|
+
error: bound.error
|
|
4169
|
+
};
|
|
4170
|
+
const found = walk(steps);
|
|
4171
|
+
if (!found.ok) return fail(found.code, found.reason);
|
|
4172
|
+
if (address === true) return ok(found.thing);
|
|
4173
|
+
const reached = valueOfThing(found.thing);
|
|
4174
|
+
if (reached instanceof Held) return {
|
|
4175
|
+
ok: false,
|
|
4176
|
+
error: reached.error
|
|
4177
|
+
};
|
|
4178
|
+
if (profile.missing?.(reached) === true) return fail("#N/A", "The path reaches no value.");
|
|
4179
|
+
return ok(reached);
|
|
4180
|
+
};
|
|
4181
|
+
const step = ({ doc, parts, fill }) => {
|
|
4182
|
+
if (parts.length === 0) return ok(doc);
|
|
4183
|
+
const all = valuesOfStrict(parts);
|
|
4184
|
+
return "values" in all ? ok({
|
|
4185
|
+
...doc,
|
|
4186
|
+
value: fill(all.values)
|
|
4187
|
+
}) : all;
|
|
4188
|
+
};
|
|
4189
|
+
const record = ({ parts }) => {
|
|
4190
|
+
const all = valuesOf(parts, profile.omit);
|
|
4191
|
+
if (!("values" in all)) return all;
|
|
4192
|
+
const out = {};
|
|
4193
|
+
parts.forEach((part, index) => {
|
|
4194
|
+
const value = all.values[index];
|
|
4195
|
+
if (value !== SKIP) setField(out, String(part.path[0]), value);
|
|
4196
|
+
});
|
|
4197
|
+
return ok(out);
|
|
4198
|
+
};
|
|
4199
|
+
const list = ({ parts }) => {
|
|
4200
|
+
const all = valuesOf(parts);
|
|
4201
|
+
return "values" in all ? ok([...all.values]) : all;
|
|
4202
|
+
};
|
|
4203
|
+
const letIn = ({ parts, context }) => {
|
|
4204
|
+
const names = {};
|
|
4205
|
+
for (const key of Object.keys(context.names)) setField(names, key, context.names[key]);
|
|
4206
|
+
for (const binding of partsUnder(parts, "bind")) {
|
|
4207
|
+
const result = binding.evaluate();
|
|
4208
|
+
setField(names, String(binding.path[1]), result.ok ? result.value : new Held(result.error));
|
|
4209
|
+
}
|
|
4210
|
+
return partNamed(parts, "body").evaluate({
|
|
4211
|
+
...context,
|
|
4212
|
+
names
|
|
4213
|
+
});
|
|
4214
|
+
};
|
|
4215
|
+
const fn = ({ data, parts, context }) => {
|
|
4216
|
+
const params = isRecord(data) ? data["params"] : void 0;
|
|
4217
|
+
if (!Array.isArray(params) || !params.every((param) => typeof param === "string") || new Set(params).size !== params.length) return fail("#VALUE!", "The params of fn are a list of different texts.");
|
|
4218
|
+
const names = params;
|
|
4219
|
+
const body = partNamed(parts, "body");
|
|
4220
|
+
const closure = (...args) => {
|
|
4221
|
+
const scope = {};
|
|
4222
|
+
for (const key of Object.keys(context.names)) setField(scope, key, context.names[key]);
|
|
4223
|
+
names.forEach((name, index) => setField(scope, name, args[index]));
|
|
4224
|
+
const result = body.evaluate({
|
|
4225
|
+
...context,
|
|
4226
|
+
names: scope
|
|
4227
|
+
});
|
|
4228
|
+
if (!result.ok) throw new Failure(result.error);
|
|
4229
|
+
return result.value;
|
|
4230
|
+
};
|
|
4231
|
+
Object.defineProperty(closure, CLOSURE$1, { value: { params: names } });
|
|
4232
|
+
return ok(closure);
|
|
4233
|
+
};
|
|
4234
|
+
const call = ({ parts }) => {
|
|
4235
|
+
const target = partNamed(parts, "fn").evaluate();
|
|
4236
|
+
if (!target.ok) return target;
|
|
4237
|
+
if (typeof target.value !== "function") return fail("#VALUE!", "The fn of call must give a function.");
|
|
4238
|
+
const all = valuesOfStrict(partsUnder(parts, "args"));
|
|
4239
|
+
if (!("values" in all)) return all;
|
|
4240
|
+
try {
|
|
4241
|
+
return ok(target.value(...all.values));
|
|
4242
|
+
} catch (error) {
|
|
4243
|
+
return thrown(error, "the function of call");
|
|
4244
|
+
}
|
|
4245
|
+
};
|
|
4246
|
+
const choice = ({ parts }) => {
|
|
4247
|
+
const test = partNamed(parts, "test").evaluate();
|
|
4248
|
+
if (!test.ok) return test;
|
|
4249
|
+
const truth = truthy(test.value);
|
|
4250
|
+
if (truth === void 0) return noTruth("test of if");
|
|
4251
|
+
if (truth) return partNamed(parts, "then").evaluate();
|
|
4252
|
+
const other = partNamed(parts, "else");
|
|
4253
|
+
if (other !== void 0) return other.evaluate();
|
|
4254
|
+
return ok(Object.hasOwn(profile, "otherwise") ? profile.otherwise : null);
|
|
4255
|
+
};
|
|
4256
|
+
/** FORMULA.LOGIC: `and` stops at the first false term, and `or` at the first true term. */
|
|
4257
|
+
const logic = (stopAt, what) => ({ parts }) => {
|
|
4258
|
+
let last = ok(!stopAt);
|
|
4259
|
+
for (const part of parts) {
|
|
4260
|
+
last = part.evaluate();
|
|
4261
|
+
if (!last.ok) return last;
|
|
4262
|
+
const truth = truthy(last.value);
|
|
4263
|
+
if (truth === void 0) return noTruth(what);
|
|
4264
|
+
if (truth === stopAt) return profile.logic === "value" ? last : ok(stopAt);
|
|
4265
|
+
}
|
|
4266
|
+
return profile.logic === "value" ? last : ok(!stopAt);
|
|
4267
|
+
};
|
|
4268
|
+
const op = ({ typePath, parts }) => {
|
|
4269
|
+
const name = typePath.slice(1).join(".");
|
|
4270
|
+
const lazy = own(profile.lazy, name);
|
|
4271
|
+
if (lazy !== void 0) try {
|
|
4272
|
+
return lazy(parts.map((part) => () => part.evaluate()));
|
|
4273
|
+
} catch (error) {
|
|
4274
|
+
return thrown(error, `the operation ${name}`);
|
|
4275
|
+
}
|
|
4276
|
+
const apply = opOf(name);
|
|
4277
|
+
if (apply === void 0) return fail("#NAME?", `The language has no operation ${name}.`);
|
|
4278
|
+
const all = valuesOf(parts);
|
|
4279
|
+
if (!("values" in all)) return all;
|
|
4280
|
+
try {
|
|
4281
|
+
const out = apply(...all.values);
|
|
4282
|
+
return profile.check?.(out, name) ?? ok(out);
|
|
4283
|
+
} catch (error) {
|
|
4284
|
+
return thrown(error, `the operation ${name}`);
|
|
4285
|
+
}
|
|
4286
|
+
};
|
|
4287
|
+
const ext = ({ typePath, doc, context }) => {
|
|
4288
|
+
const name = typePath.slice(1).join(".");
|
|
4289
|
+
const handler = own(profile.ext, name) ?? own(STD_EXT, name);
|
|
4290
|
+
if (handler === void 0) return fail("#NAME?", `The language has no extension ${name}.`);
|
|
4291
|
+
try {
|
|
4292
|
+
return handler(doc.value, context);
|
|
4293
|
+
} catch (error) {
|
|
4294
|
+
return thrown(error, `the extension ${name}`);
|
|
4295
|
+
}
|
|
4296
|
+
};
|
|
4297
|
+
return { evaluators: {
|
|
4298
|
+
"formula.lit": lit,
|
|
4299
|
+
"formula.ref": ref,
|
|
4300
|
+
"formula.step": step,
|
|
4301
|
+
"formula.record": record,
|
|
4302
|
+
"formula.list": list,
|
|
4303
|
+
"formula.let": letIn,
|
|
4304
|
+
"formula.fn": fn,
|
|
4305
|
+
"formula.call": call,
|
|
4306
|
+
"formula.if": choice,
|
|
4307
|
+
"formula.and": logic(false, "term of and"),
|
|
4308
|
+
"formula.or": logic(true, "term of or"),
|
|
4309
|
+
"formula.op": op,
|
|
4310
|
+
"formula.ext": ext
|
|
4311
|
+
} };
|
|
4312
|
+
}
|
|
4313
|
+
/** The mark of a field that the profile leaves out of a record (FORMULA.RECORD). */
|
|
4314
|
+
const SKIP = Symbol("skip");
|
|
4315
|
+
/** The values of parts in order. The first error stops them, also with `combine` (FORMULA.STEPS, FORMULA.FN). */
|
|
4316
|
+
function valuesOfStrict(parts) {
|
|
4317
|
+
const values = [];
|
|
4318
|
+
for (const part of parts) {
|
|
4319
|
+
const result = part.evaluate();
|
|
4320
|
+
if (!result.ok) return result;
|
|
4321
|
+
values.push(result.value);
|
|
4322
|
+
}
|
|
4323
|
+
return { values };
|
|
4324
|
+
}
|
|
4325
|
+
//#endregion
|
|
4326
|
+
//#region src/agree.ts
|
|
4327
|
+
/**
|
|
4328
|
+
* This function tells if two values are the same for `agree`. Lists and plain records compare item by item and field
|
|
4329
|
+
* by field, and the order of the fields does not matter. Each other leaf compares with `Object.is`, thus `-0` is not
|
|
4330
|
+
* `0`, and `NaN` is `NaN`. Two closures of `fn` are the same when they have the same parameters. Two `Held` values
|
|
4331
|
+
* are the same when their errors have the same code and place. Identity is not compared.
|
|
4332
|
+
*
|
|
4333
|
+
* @param a - The first value.
|
|
4334
|
+
* @param b - The second value.
|
|
4335
|
+
* @param equal - The rule of the caller for other values. It answers first, and `undefined` gives the rules above.
|
|
4336
|
+
* @returns `true` when the values are the same.
|
|
4337
|
+
*/
|
|
4338
|
+
function sameValue(a, b, equal) {
|
|
4339
|
+
const stack = [[a, b]];
|
|
4340
|
+
const seen = /* @__PURE__ */ new Map();
|
|
4341
|
+
while (stack.length > 0) {
|
|
4342
|
+
const [x, y] = stack.pop();
|
|
4343
|
+
if (Object.is(x, y)) continue;
|
|
4344
|
+
const told = equal?.(x, y);
|
|
4345
|
+
if (told !== void 0) {
|
|
4346
|
+
if (told) continue;
|
|
4347
|
+
return false;
|
|
4348
|
+
}
|
|
4349
|
+
if (typeof x !== "object" && typeof x !== "function") return false;
|
|
4350
|
+
if (typeof y !== "object" && typeof y !== "function") return false;
|
|
4351
|
+
if (x === null || y === null) return false;
|
|
4352
|
+
const pairs = seen.get(x) ?? /* @__PURE__ */ new Set();
|
|
4353
|
+
if (pairs.has(y)) continue;
|
|
4354
|
+
pairs.add(y);
|
|
4355
|
+
seen.set(x, pairs);
|
|
4356
|
+
if (isClosure(x) || isClosure(y)) {
|
|
4357
|
+
if (!(isClosure(x) && isClosure(y))) return false;
|
|
4358
|
+
stack.push([closureParams(x), closureParams(y)]);
|
|
4359
|
+
continue;
|
|
4360
|
+
}
|
|
4361
|
+
if (x instanceof Held || y instanceof Held) {
|
|
4362
|
+
if (!(x instanceof Held && y instanceof Held) || errorDiffers(x.error, y.error, false) !== void 0) return false;
|
|
4363
|
+
continue;
|
|
4364
|
+
}
|
|
4365
|
+
if (Array.isArray(x) || Array.isArray(y)) {
|
|
4366
|
+
if (!Array.isArray(x) || !Array.isArray(y) || x.length !== y.length) return false;
|
|
4367
|
+
for (let index = 0; index < x.length; index += 1) stack.push([x[index], y[index]]);
|
|
4368
|
+
continue;
|
|
4369
|
+
}
|
|
4370
|
+
if (!isRecord(x) || !isRecord(y)) return false;
|
|
4371
|
+
const keys = Object.keys(x);
|
|
4372
|
+
if (keys.length !== Object.keys(y).length) return false;
|
|
4373
|
+
for (const key of keys) {
|
|
4374
|
+
if (!Object.hasOwn(y, key)) return false;
|
|
4375
|
+
stack.push([x[key], y[key]]);
|
|
4376
|
+
}
|
|
4377
|
+
}
|
|
4378
|
+
return true;
|
|
4379
|
+
}
|
|
4380
|
+
const CLOSURE = Symbol.for("coeval.closure");
|
|
4381
|
+
function closureParams(fn) {
|
|
4382
|
+
return fn[CLOSURE]?.params;
|
|
4383
|
+
}
|
|
4384
|
+
function codeAndPlace(error) {
|
|
4385
|
+
return error.place === void 0 ? error.code : `${error.code} at [${error.place.join(", ")}]`;
|
|
4386
|
+
}
|
|
4387
|
+
/** This function gives the difference of two errors, or `undefined` when they agree. */
|
|
4388
|
+
function errorDiffers(a, b, strict) {
|
|
4389
|
+
if (a.code !== b.code) return `The plan gives ${codeAndPlace(a)}, and the tier gives ${codeAndPlace(b)}.`;
|
|
4390
|
+
if (strict && (a.place === void 0 || b.place === void 0)) return `An error ${a.code} has no place.`;
|
|
4391
|
+
if (a.place !== void 0 && b.place !== void 0 && !docEquals(a.place, b.place)) return `The error ${a.code} has the place [${a.place.join(", ")}] in the plan, and [${b.place.join(", ")}] in the tier.`;
|
|
4392
|
+
if (strict && a.reason !== b.reason) return `The error ${a.code} has another reason in the tier: "${b.reason}" for "${a.reason}".`;
|
|
4393
|
+
}
|
|
4394
|
+
/** A host stack overflow is the one permitted difference (VALUE.DEPTH): the tiers use different numbers of frames. */
|
|
4395
|
+
function overflowed(result) {
|
|
4396
|
+
return !result.ok && result.error.code === "#DEPTH!" && isStackOverflow(result.error.cause);
|
|
4397
|
+
}
|
|
4398
|
+
/** This function gives the difference of two results, or `undefined` when they agree. */
|
|
4399
|
+
function resultDiffers(a, b, options) {
|
|
4400
|
+
if (a.ok && b.ok) return sameValue(a.value, b.value, options.equal) ? void 0 : "The plan and the tier give different values.";
|
|
4401
|
+
if (!a.ok && !b.ok) return errorDiffers(a.error, b.error, options.strict === true);
|
|
4402
|
+
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.`;
|
|
4403
|
+
}
|
|
4404
|
+
/** This function gives the difference of two lists of effects, or `undefined` when they agree (EFFECT.ORDER). */
|
|
4405
|
+
function effectsDiffer(a, b, options) {
|
|
4406
|
+
if (a.length !== b.length) return `The plan causes ${a.length} effects, and the tier causes ${b.length}.`;
|
|
4407
|
+
for (let index = 0; index < a.length; index += 1) {
|
|
4408
|
+
const x = a[index];
|
|
4409
|
+
const y = b[index];
|
|
4410
|
+
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.`;
|
|
4411
|
+
}
|
|
4412
|
+
}
|
|
4413
|
+
/**
|
|
4414
|
+
* This function evaluates a document with a plan and with other tiers, and compares the results (PLAN.AGREES). Two
|
|
4415
|
+
* results agree when they have the same value by `sameValue`, or errors with the same code, and the same place when
|
|
4416
|
+
* both have one. The effects must have the same kinds, places and payloads, in the same order. A language gives its
|
|
4417
|
+
* own tier with the option `other`.
|
|
4418
|
+
*
|
|
4419
|
+
* @param machine - The world and the code of the evaluators.
|
|
4420
|
+
* @param doc - The document.
|
|
4421
|
+
* @param context - The names and the capabilities at the root.
|
|
4422
|
+
* @param options - The tiers, the definitions for the reference, the rules of the comparison and the second tier.
|
|
4423
|
+
* @returns The result of the plan, or the first tier that disagrees and the reason.
|
|
4424
|
+
*/
|
|
4425
|
+
function agree(machine, doc, context = makeContext(), options = {}) {
|
|
4426
|
+
const declared = options.declaredType === void 0 ? {} : { declaredType: options.declaredType };
|
|
4427
|
+
const observe = options.observe;
|
|
4428
|
+
observe?.();
|
|
4429
|
+
const plan = prepare(machine, doc, declared);
|
|
4430
|
+
const effects = [];
|
|
4431
|
+
const result = plan.evaluate(context, { effects });
|
|
4432
|
+
const saw = observe?.();
|
|
4433
|
+
const differs = (other, otherEffects) => {
|
|
4434
|
+
if (overflowed(result) || overflowed(other)) return void 0;
|
|
4435
|
+
const why = resultDiffers(result, other, options) ?? (otherEffects === void 0 ? void 0 : effectsDiffer(effects, otherEffects, options));
|
|
4436
|
+
if (why !== void 0 || observe === void 0) return why;
|
|
4437
|
+
return sameValue(saw, observe(), options.equal) ? void 0 : "The host saw other calls in the tier.";
|
|
4438
|
+
};
|
|
4439
|
+
for (const tier of options.tiers ?? [
|
|
4440
|
+
"reference",
|
|
4441
|
+
"traced",
|
|
4442
|
+
"again"
|
|
4443
|
+
]) {
|
|
4444
|
+
let other;
|
|
4445
|
+
let otherEffects = [];
|
|
4446
|
+
if (tier === "reference") other = reference(machine, doc, context, {
|
|
4447
|
+
...declared,
|
|
4448
|
+
effects: otherEffects,
|
|
4449
|
+
...options.definitions === void 0 ? {} : { definitions: options.definitions }
|
|
4450
|
+
});
|
|
4451
|
+
else if (tier === "traced") {
|
|
4452
|
+
const traced = evaluate(machine, doc, context, options.declaredType);
|
|
4453
|
+
other = traced.result;
|
|
4454
|
+
otherEffects = [...traced.effects];
|
|
4455
|
+
} else other = plan.evaluate(context, { effects: otherEffects });
|
|
4456
|
+
const why = differs(other, otherEffects);
|
|
4457
|
+
if (why !== void 0) return {
|
|
4458
|
+
ok: false,
|
|
4459
|
+
tier,
|
|
4460
|
+
plan: result,
|
|
4461
|
+
other,
|
|
4462
|
+
reason: why
|
|
4463
|
+
};
|
|
4464
|
+
}
|
|
4465
|
+
if (options.other !== void 0) {
|
|
4466
|
+
let given;
|
|
4467
|
+
try {
|
|
4468
|
+
given = options.other(doc, context);
|
|
4469
|
+
} catch (error) {
|
|
4470
|
+
given = thrown(error, "the tier");
|
|
4471
|
+
}
|
|
4472
|
+
const other = "result" in given ? given.result : given;
|
|
4473
|
+
const why = differs(other, "result" in given ? given.effects : void 0);
|
|
4474
|
+
if (why !== void 0) return {
|
|
4475
|
+
ok: false,
|
|
4476
|
+
tier: "other",
|
|
4477
|
+
plan: result,
|
|
4478
|
+
other,
|
|
4479
|
+
reason: why
|
|
4480
|
+
};
|
|
4481
|
+
}
|
|
4482
|
+
return {
|
|
4483
|
+
ok: true,
|
|
4484
|
+
result,
|
|
4485
|
+
effects
|
|
4486
|
+
};
|
|
4487
|
+
}
|
|
4488
|
+
//#endregion
|
|
4489
|
+
//#region src/generate.ts
|
|
4490
|
+
/**
|
|
4491
|
+
* This function makes a source of random numbers with a seed (the algorithm mulberry32). It needs no package.
|
|
4492
|
+
*
|
|
4493
|
+
* @param seed - The seed: a whole number.
|
|
4494
|
+
* @returns The source.
|
|
4495
|
+
*/
|
|
4496
|
+
function random(seed) {
|
|
4497
|
+
let state = seed >>> 0;
|
|
4498
|
+
const next = () => {
|
|
4499
|
+
state = state + 1831565813 >>> 0;
|
|
4500
|
+
let t = state;
|
|
4501
|
+
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
4502
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
4503
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
4504
|
+
};
|
|
4505
|
+
const int = (below) => Math.floor(next() * below);
|
|
4506
|
+
return {
|
|
4507
|
+
next,
|
|
4508
|
+
int,
|
|
4509
|
+
chance: (p) => next() < p,
|
|
4510
|
+
pick: (items) => items[int(items.length)]
|
|
4511
|
+
};
|
|
4512
|
+
}
|
|
4513
|
+
/**
|
|
4514
|
+
* This function lists the types of a world: each kind and each alias, also in a namespace. A namespace with a
|
|
4515
|
+
* fallback gives no types, because each key names a kind.
|
|
4516
|
+
*
|
|
4517
|
+
* @param world - The world.
|
|
4518
|
+
* @returns The keys of each type.
|
|
4519
|
+
*/
|
|
4520
|
+
function typesOf(world) {
|
|
4521
|
+
const out = [];
|
|
4522
|
+
const visit = (entries, prefix) => {
|
|
4523
|
+
for (const key of Object.keys(entries)) {
|
|
4524
|
+
const entry = entries[key];
|
|
4525
|
+
if (isKind(entry) || isAlias(entry)) out.push([...prefix, key]);
|
|
4526
|
+
else if (isNamespace(entry)) visit(entry.kinds, [...prefix, key]);
|
|
4527
|
+
}
|
|
4528
|
+
};
|
|
4529
|
+
visit(world.kinds, []);
|
|
4530
|
+
return out;
|
|
4531
|
+
}
|
|
4532
|
+
const NAMES = [
|
|
4533
|
+
"a",
|
|
4534
|
+
"b",
|
|
4535
|
+
"x",
|
|
4536
|
+
"n",
|
|
4537
|
+
"last",
|
|
4538
|
+
"__proto__"
|
|
4539
|
+
];
|
|
4540
|
+
const FIELDS = [
|
|
4541
|
+
"a",
|
|
4542
|
+
"b",
|
|
4543
|
+
"c",
|
|
4544
|
+
"1",
|
|
4545
|
+
"__proto__"
|
|
4546
|
+
];
|
|
4547
|
+
const KEYS = [
|
|
4548
|
+
"a",
|
|
4549
|
+
"b",
|
|
4550
|
+
"x",
|
|
4551
|
+
0,
|
|
4552
|
+
1,
|
|
4553
|
+
"0",
|
|
4554
|
+
"length",
|
|
4555
|
+
"type",
|
|
4556
|
+
"value",
|
|
4557
|
+
"parent"
|
|
4558
|
+
];
|
|
4559
|
+
/** This function tells if a kind can be a leaf: it needs no part. */
|
|
4560
|
+
function leafKind(kind) {
|
|
4561
|
+
const shape = kind?.parts;
|
|
4562
|
+
return shape === void 0 || shape.shape === "none" || shape.shape === "list" || shape.shape === "map" || shape.shape === "text" || shape.shape === "number" || shape.shape === "one";
|
|
4563
|
+
}
|
|
4564
|
+
/**
|
|
4565
|
+
* This function builds a random document for a world from the contracts of its kinds. It reads the shapes of the
|
|
4566
|
+
* parts, the declared types, the permitted kinds and the names that the scope rules bind. Some choices are malformed
|
|
4567
|
+
* on purpose. Examples are an unknown kind, a value without its shape, a bare value without a type and a name that
|
|
4568
|
+
* nothing binds. The depth and the size have limits.
|
|
4569
|
+
*
|
|
4570
|
+
* @param world - The world.
|
|
4571
|
+
* @param options - The seed, the types, the limits and the data of the leaves.
|
|
4572
|
+
* @returns The document, as JSON.
|
|
4573
|
+
*/
|
|
4574
|
+
function generate(world, options) {
|
|
4575
|
+
const rng = random(options.seed);
|
|
4576
|
+
const types = options.types ?? typesOf(world);
|
|
4577
|
+
const maxDepth = options.maxDepth ?? 4;
|
|
4578
|
+
const maxSize = options.maxSize ?? 24;
|
|
4579
|
+
const malformed = options.malformed ?? .05;
|
|
4580
|
+
const keys = options.keys ?? KEYS;
|
|
4581
|
+
const stepTypes = types.filter((type) => {
|
|
4582
|
+
const found = lookupKind(world, type);
|
|
4583
|
+
return found.ok && Object.hasOwn(found.kind.verbs, "step");
|
|
4584
|
+
});
|
|
4585
|
+
let size = 0;
|
|
4586
|
+
const kindOf = (type) => {
|
|
4587
|
+
const found = lookupKind(world, type);
|
|
4588
|
+
return found.ok ? found.kind : void 0;
|
|
4589
|
+
};
|
|
4590
|
+
const anyJson = (depth = 0) => {
|
|
4591
|
+
const roll = rng.int(depth > 1 ? 6 : 8);
|
|
4592
|
+
if (roll === 0) return null;
|
|
4593
|
+
if (roll === 1) return rng.chance(.5);
|
|
4594
|
+
if (roll === 2) return rng.pick([
|
|
4595
|
+
0,
|
|
4596
|
+
1,
|
|
4597
|
+
2,
|
|
4598
|
+
-1,
|
|
4599
|
+
.5,
|
|
4600
|
+
1e21
|
|
4601
|
+
]);
|
|
4602
|
+
if (roll === 3) return rng.pick([
|
|
4603
|
+
"",
|
|
4604
|
+
"a",
|
|
4605
|
+
"text",
|
|
4606
|
+
"0"
|
|
4607
|
+
]);
|
|
4608
|
+
if (roll === 4) return rng.int(10);
|
|
4609
|
+
if (roll === 5) return rng.pick(NAMES);
|
|
4610
|
+
if (roll === 6) return Array.from({ length: rng.int(3) }, () => anyJson(depth + 1));
|
|
4611
|
+
const out = {};
|
|
4612
|
+
for (let index = rng.int(3); index > 0; index -= 1) Object.defineProperty(out, rng.pick(FIELDS), {
|
|
4613
|
+
value: anyJson(depth + 1),
|
|
4614
|
+
enumerable: true,
|
|
4615
|
+
writable: true,
|
|
4616
|
+
configurable: true
|
|
4617
|
+
});
|
|
4618
|
+
return out;
|
|
4619
|
+
};
|
|
4620
|
+
const dataOf = (type) => options.data?.(type, rng) ?? anyJson();
|
|
4621
|
+
const renderType = (type) => {
|
|
4622
|
+
const roll = rng.next();
|
|
4623
|
+
if (roll < .06) return {
|
|
4624
|
+
type: "ref",
|
|
4625
|
+
value: ["world", ...type]
|
|
4626
|
+
};
|
|
4627
|
+
if (type.length === 1 && roll < .8) return type[0];
|
|
4628
|
+
return [...type];
|
|
4629
|
+
};
|
|
4630
|
+
const permitted = (type, kinds) => kinds === void 0 || kinds.some((kind) => kind.length <= type.length && kind.every((key, index) => String(key) === String(type[index])));
|
|
4631
|
+
const pickName = (names) => names.length === 0 || rng.chance(malformed) ? rng.pick(["unbound", "zz"]) : rng.pick(names);
|
|
4632
|
+
const someNames = () => {
|
|
4633
|
+
const out = /* @__PURE__ */ new Set();
|
|
4634
|
+
for (let index = 1 + rng.int(2); index > 0; index -= 1) out.add(rng.pick(NAMES));
|
|
4635
|
+
return [...out];
|
|
4636
|
+
};
|
|
4637
|
+
const envelope = (type, value, header) => {
|
|
4638
|
+
const out = {
|
|
4639
|
+
type: renderType(type),
|
|
4640
|
+
value
|
|
4641
|
+
};
|
|
4642
|
+
if (header !== void 0) out["name"] = header;
|
|
4643
|
+
if (options.own !== void 0 && options.own.length > 0 && rng.chance(.03)) out["own"] = { eval: rng.pick(options.own) };
|
|
4644
|
+
return out;
|
|
4645
|
+
};
|
|
4646
|
+
/** A path from a root (D10). It uses the names in scope, and sometimes a step document. */
|
|
4647
|
+
const pathFor = (pos, refKind) => {
|
|
4648
|
+
if (rng.chance(malformed)) return rng.pick([
|
|
4649
|
+
"names",
|
|
4650
|
+
[],
|
|
4651
|
+
{ names: "a" },
|
|
4652
|
+
["nowhere", "a"]
|
|
4653
|
+
]);
|
|
4654
|
+
const root = pos.names.length > 0 && rng.chance(.75) ? "names" : rng.pick(ROOTS.filter((name) => name !== "names"));
|
|
4655
|
+
const path = [root];
|
|
4656
|
+
if (root === "names") path.push(pickName(pos.names));
|
|
4657
|
+
if (root === "world") path.push(String(rng.pick(types)[0]));
|
|
4658
|
+
for (let index = rng.int(3); index > 0; index -= 1) if (stepTypes.length > 0 && rng.chance(.2)) {
|
|
4659
|
+
const type = rng.pick(stepTypes);
|
|
4660
|
+
const kind = kindOf(type);
|
|
4661
|
+
const value = refKind?.parts?.shape === "deep" && kind?.parts !== void 0 ? valueOf(kind.parts, [], {
|
|
4662
|
+
...pos,
|
|
4663
|
+
depth: pos.depth + 1
|
|
4664
|
+
}, emptyScope()) : dataOf(type);
|
|
4665
|
+
path.push({
|
|
4666
|
+
type: renderType(type),
|
|
4667
|
+
value
|
|
4668
|
+
});
|
|
4669
|
+
} else path.push(rng.pick(keys));
|
|
4670
|
+
return path;
|
|
4671
|
+
};
|
|
4672
|
+
const emptyScope = () => ({
|
|
4673
|
+
rules: [],
|
|
4674
|
+
names: [],
|
|
4675
|
+
covered: [],
|
|
4676
|
+
sources: /* @__PURE__ */ new Map()
|
|
4677
|
+
});
|
|
4678
|
+
/** This function decides the names of the scope rules before the parts, so that each part knows what it sees. */
|
|
4679
|
+
const scopeOf = (kind) => {
|
|
4680
|
+
const rules = kind.scope ?? [];
|
|
4681
|
+
const scope = {
|
|
4682
|
+
rules,
|
|
4683
|
+
names: [],
|
|
4684
|
+
covered: rules.map(() => 0),
|
|
4685
|
+
sources: /* @__PURE__ */ new Map()
|
|
4686
|
+
};
|
|
4687
|
+
for (const rule of rules) {
|
|
4688
|
+
const source = rule.names;
|
|
4689
|
+
let names = [];
|
|
4690
|
+
if ("fixed" in source) names = [...source.fixed];
|
|
4691
|
+
else if ("keysAt" in source) {
|
|
4692
|
+
names = someNames();
|
|
4693
|
+
scope.sources.set(source.keysAt.join("."), { keys: names });
|
|
4694
|
+
} else if ("listAt" in source) {
|
|
4695
|
+
names = someNames();
|
|
4696
|
+
scope.sources.set(source.listAt.join("."), { list: rng.chance(malformed) ? [...names, ...names] : names });
|
|
4697
|
+
} else if ("textAt" in source) {
|
|
4698
|
+
names = [rng.pick(NAMES)];
|
|
4699
|
+
scope.sources.set(source.textAt.join("."), { text: names[0] });
|
|
4700
|
+
}
|
|
4701
|
+
scope.names.push(names);
|
|
4702
|
+
}
|
|
4703
|
+
return scope;
|
|
4704
|
+
};
|
|
4705
|
+
/** The names that a part sees from the scope rules of its parent. A rule `sequential` shows only earlier names. */
|
|
4706
|
+
const seenBy = (scope, path) => {
|
|
4707
|
+
const names = [];
|
|
4708
|
+
let headers = false;
|
|
4709
|
+
scope.rules.forEach((rule, index) => {
|
|
4710
|
+
if (!rule.in.some((pattern) => pathMatches(path, pattern))) return;
|
|
4711
|
+
const ruleNames = scope.names[index] ?? [];
|
|
4712
|
+
if ("headers" in rule.names) headers = true;
|
|
4713
|
+
if (rule.sequential !== true || "headers" in rule.names || (scope.covered[index] ?? 0) > 0) names.push(...ruleNames);
|
|
4714
|
+
});
|
|
4715
|
+
return {
|
|
4716
|
+
names,
|
|
4717
|
+
headers
|
|
4718
|
+
};
|
|
4719
|
+
};
|
|
4720
|
+
/** This function records the part at a path, and the header name that it got, for the rules that cover it. */
|
|
4721
|
+
const saw = (scope, path, part) => {
|
|
4722
|
+
const header = isEnvelope(part) && typeof part.name === "string" ? part.name : void 0;
|
|
4723
|
+
scope.rules.forEach((rule, index) => {
|
|
4724
|
+
if (!rule.in.some((pattern) => pathMatches(path, pattern))) return;
|
|
4725
|
+
scope.covered[index] = (scope.covered[index] ?? 0) + 1;
|
|
4726
|
+
if ("headers" in rule.names && header !== void 0) scope.names[index]?.push(header);
|
|
4727
|
+
});
|
|
4728
|
+
};
|
|
4729
|
+
const partAt = (pos, scope, path, spec, parentType) => {
|
|
4730
|
+
const seen = seenBy(scope, path);
|
|
4731
|
+
const header = seen.headers && rng.chance(.5) ? rng.pick(NAMES) : void 0;
|
|
4732
|
+
const part = docAt({
|
|
4733
|
+
declared: spec.of,
|
|
4734
|
+
kinds: "kinds" in spec ? spec.kinds : void 0,
|
|
4735
|
+
names: seen.names.length === 0 ? pos.names : [...pos.names, ...seen.names],
|
|
4736
|
+
depth: pos.depth + 1,
|
|
4737
|
+
hint: parentType === void 0 ? void 0 : options.hints?.(parentType, path)
|
|
4738
|
+
}, header);
|
|
4739
|
+
saw(scope, path, part);
|
|
4740
|
+
return part;
|
|
4741
|
+
};
|
|
4742
|
+
/** This function builds a value of a shape (SHAPE.*). A part is a document at the next depth. */
|
|
4743
|
+
const valueOf = (shape, path, pos, scope, type) => {
|
|
4744
|
+
const source = scope.sources.get(path.join("."));
|
|
4745
|
+
if (source !== void 0 && "list" in source) return [...source.list];
|
|
4746
|
+
if (source !== void 0 && "text" in source) return source.text;
|
|
4747
|
+
const term = (of) => of === void 0 ? { shape: "term" } : {
|
|
4748
|
+
shape: "term",
|
|
4749
|
+
of
|
|
4750
|
+
};
|
|
4751
|
+
const small = () => pos.depth >= maxDepth ? 0 : size >= maxSize ? rng.int(2) : rng.int(4);
|
|
4752
|
+
if (shape === void 0 || shape.shape === "none") return path.length === 0 && type !== void 0 ? dataOf(type) : anyJson();
|
|
4753
|
+
switch (shape.shape) {
|
|
4754
|
+
case "term": return partAt(pos, scope, path, shape, type);
|
|
4755
|
+
case "list": {
|
|
4756
|
+
const items = Array.from({ length: small() }, (_, index) => valueOf(shape.item ?? term(shape.of), [...path, index], pos, scope, type));
|
|
4757
|
+
if (shape.distinct !== true || rng.chance(malformed)) return items;
|
|
4758
|
+
return items.filter((item, index) => !items.slice(0, index).some((before) => docEquals(before, item)));
|
|
4759
|
+
}
|
|
4760
|
+
case "map": {
|
|
4761
|
+
const out = {};
|
|
4762
|
+
const names = source !== void 0 && "keys" in source ? source.keys : Array.from({ length: small() }, () => rng.pick(FIELDS));
|
|
4763
|
+
for (const key of names) Object.defineProperty(out, key, {
|
|
4764
|
+
value: valueOf(shape.item ?? term(shape.of), [...path, key], pos, scope, type),
|
|
4765
|
+
enumerable: true,
|
|
4766
|
+
writable: true,
|
|
4767
|
+
configurable: true
|
|
4768
|
+
});
|
|
4769
|
+
return out;
|
|
4770
|
+
}
|
|
4771
|
+
case "record": {
|
|
4772
|
+
const named = Array.isArray(shape.fields) ? Object.fromEntries(shape.fields.map((name) => [name, term(shape.of)])) : shape.fields;
|
|
4773
|
+
const fieldKeys = /* @__PURE__ */ new Set([...shape.required ?? [], ...Object.keys(named).filter(() => rng.chance(.75))]);
|
|
4774
|
+
if (shape.closed !== true && shape.rest === void 0 && rng.chance(.15)) fieldKeys.add("extra");
|
|
4775
|
+
const ordered = rng.chance(.2) ? [...fieldKeys].reverse() : [...fieldKeys];
|
|
4776
|
+
const out = {};
|
|
4777
|
+
for (const key of ordered) {
|
|
4778
|
+
if (rng.chance(malformed / 2) && (shape.required ?? []).includes(key)) continue;
|
|
4779
|
+
const fieldShape = Object.hasOwn(named, key) ? named[key] : shape.rest;
|
|
4780
|
+
Object.defineProperty(out, key, {
|
|
4781
|
+
value: valueOf(fieldShape, [...path, key], pos, scope, type),
|
|
4782
|
+
enumerable: true,
|
|
4783
|
+
writable: true,
|
|
4784
|
+
configurable: true
|
|
4785
|
+
});
|
|
4786
|
+
}
|
|
4787
|
+
if (shape.closed === true && rng.chance(malformed)) out["stray"] = 1;
|
|
4788
|
+
return out;
|
|
4789
|
+
}
|
|
4790
|
+
case "deep": return rng.chance(.5) ? [partAt(pos, scope, [...path, 0], term(shape.of), type), anyJson()] : {
|
|
4791
|
+
at: partAt(pos, scope, [...path, "at"], term(shape.of), type),
|
|
4792
|
+
data: anyJson()
|
|
4793
|
+
};
|
|
4794
|
+
case "text": return rng.chance(malformed) ? 1 : rng.pick([
|
|
4795
|
+
"",
|
|
4796
|
+
"a",
|
|
4797
|
+
"label"
|
|
4798
|
+
]);
|
|
4799
|
+
case "number": {
|
|
4800
|
+
if (rng.chance(malformed)) return "1";
|
|
4801
|
+
const value = (shape.min ?? -2) + rng.int(5) + (shape.whole === true ? 0 : rng.pick([0, .5]));
|
|
4802
|
+
return shape.max !== void 0 && value > shape.max ? shape.max : value;
|
|
4803
|
+
}
|
|
4804
|
+
case "one": return rng.chance(malformed) ? "other" : rng.pick(shape.values);
|
|
4805
|
+
}
|
|
4806
|
+
};
|
|
4807
|
+
/** A malformed document: an unknown kind, a bad shape, a bare value without a type, or a bad reference. */
|
|
4808
|
+
const brokenAt = (pos) => {
|
|
4809
|
+
const roll = rng.int(5);
|
|
4810
|
+
if (roll === 0) return {
|
|
4811
|
+
type: rng.pick(["nothing", ["no", "such"]]),
|
|
4812
|
+
value: 1
|
|
4813
|
+
};
|
|
4814
|
+
if (roll === 1) return pos.declared === void 0 ? rng.pick([
|
|
4815
|
+
7,
|
|
4816
|
+
"bare",
|
|
4817
|
+
[1]
|
|
4818
|
+
]) : {
|
|
4819
|
+
type: "ref",
|
|
4820
|
+
value: ["names", "unbound"]
|
|
4821
|
+
};
|
|
4822
|
+
if (roll === 2) {
|
|
4823
|
+
const shaped = types.filter((type) => kindOf(type)?.parts !== void 0);
|
|
4824
|
+
if (shaped.length > 0) return envelope(rng.pick(shaped), rng.pick([
|
|
4825
|
+
7,
|
|
4826
|
+
"x",
|
|
4827
|
+
null
|
|
4828
|
+
]));
|
|
4829
|
+
}
|
|
4830
|
+
if (roll === 3) return {
|
|
4831
|
+
type: "ref",
|
|
4832
|
+
value: rng.pick([
|
|
4833
|
+
["names", "unbound"],
|
|
4834
|
+
["nowhere"],
|
|
4835
|
+
"names"
|
|
4836
|
+
])
|
|
4837
|
+
};
|
|
4838
|
+
return {
|
|
4839
|
+
type: rng.pick(types),
|
|
4840
|
+
value: anyJson()
|
|
4841
|
+
};
|
|
4842
|
+
};
|
|
4843
|
+
const docAt = (pos, header) => {
|
|
4844
|
+
size += 1;
|
|
4845
|
+
const leaf = pos.depth >= maxDepth || size >= maxSize;
|
|
4846
|
+
if (rng.chance(malformed)) return brokenAt(pos);
|
|
4847
|
+
if (pos.declared !== void 0 && rng.chance(leaf ? .7 : .3)) return dataOf([pos.declared]);
|
|
4848
|
+
let candidates = types.filter((type) => permitted(type, pos.kinds) && (!leaf || leafKind(kindOf(type))));
|
|
4849
|
+
if (pos.hint !== void 0 && rng.chance(.6)) {
|
|
4850
|
+
const hinted = candidates.filter((type) => pos.hint?.some((hint) => hint.length === type.length && hint.every((key, index) => key === type[index])));
|
|
4851
|
+
if (hinted.length > 0) candidates = hinted;
|
|
4852
|
+
}
|
|
4853
|
+
if (candidates.length === 0) candidates = [...types];
|
|
4854
|
+
const type = rng.pick(candidates);
|
|
4855
|
+
const kind = kindOf(type);
|
|
4856
|
+
if (type.length === 1 && type[0] === "ref") return envelope(type, pathFor(pos, kind), header);
|
|
4857
|
+
const value = kind === void 0 ? dataOf(type) : valueOf(kind.parts, [], pos, scopeOf(kind), type);
|
|
4858
|
+
return envelope(type, value, header);
|
|
4859
|
+
};
|
|
4860
|
+
return docAt({
|
|
4861
|
+
declared: options.declaredType,
|
|
4862
|
+
kinds: void 0,
|
|
4863
|
+
names: options.names ?? [],
|
|
4864
|
+
depth: 0,
|
|
4865
|
+
hint: void 0
|
|
4866
|
+
});
|
|
4867
|
+
}
|
|
4868
|
+
//#endregion
|
|
4869
|
+
export { CONSTRUCTS, ENVELOPE_KEYS, Failure, Held, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, 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, 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, narrow, nearestWalker, nodeAt, ok, partCaps, pathMatches, permits, placed, planner, prepare, random, ref, reference, refsOf, resolve, run, sameValue, splitParts, stored, tag, thrown, toJson, typeName, typePath, typed, typesOf, valueOfThing, walk, worldThing };
|
|
3799
4870
|
|
|
3800
4871
|
//# sourceMappingURL=index.js.map
|