@mark1russell7/coeval 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/build/index.d.ts +56 -9
- package/build/index.d.ts.map +1 -1
- package/build/index.js +306 -98
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/deref.ts +10 -6
- package/src/evaluate.ts +66 -23
- package/src/formula.ts +91 -19
- package/src/parts.ts +42 -5
- package/src/static.ts +144 -42
- package/src/world.ts +15 -3
package/build/index.js
CHANGED
|
@@ -450,6 +450,9 @@ function entriesAsJson(entries) {
|
|
|
450
450
|
}
|
|
451
451
|
function describeShape(shape) {
|
|
452
452
|
if (shape === void 0 || shape.shape === "none") return "none";
|
|
453
|
+
if (shape.shape === "text") return "a text";
|
|
454
|
+
if (shape.shape === "number") return "a number";
|
|
455
|
+
if (shape.shape === "one") return `one of ${JSON.stringify(shape.values)}`;
|
|
453
456
|
if (shape.shape === "term") return `a part${shape.of === void 0 ? "" : ` of ${shape.of}`}`;
|
|
454
457
|
if (shape.shape === "deep") return "each document in the value";
|
|
455
458
|
if (shape.shape === "list") return `a list${shape.of === void 0 ? "" : ` of ${shape.of}`}`;
|
|
@@ -940,7 +943,7 @@ function resolve(nav, ctx, path, options = {}) {
|
|
|
940
943
|
return reached === void 0 ? void 0 : adopt(reached.thing);
|
|
941
944
|
}
|
|
942
945
|
});
|
|
943
|
-
if (!result.ok) return failed("#N/A", result.reason);
|
|
946
|
+
if (!result.ok) return failed(result.code ?? "#N/A", result.reason);
|
|
944
947
|
here = adopt(result.thing);
|
|
945
948
|
hops?.push({
|
|
946
949
|
step: envelope,
|
|
@@ -1033,6 +1036,22 @@ const NO_SLOTS = Object.freeze([]);
|
|
|
1033
1036
|
function hasSlots(template) {
|
|
1034
1037
|
return template.t !== "data";
|
|
1035
1038
|
}
|
|
1039
|
+
/** This function gives the reason why a value is not data of a shape, or `undefined` when it is. */
|
|
1040
|
+
function dataFault(spec, item) {
|
|
1041
|
+
if (spec.shape === "text") return typeof item === "string" ? void 0 : "The value must be a text.";
|
|
1042
|
+
if (spec.shape === "one") return spec.values.some((value) => docEquals(value, item)) ? void 0 : `The value must be one of ${JSON.stringify(spec.values)}.`;
|
|
1043
|
+
if (typeof item !== "number") return "The value must be a number.";
|
|
1044
|
+
if (spec.whole === true && !Number.isInteger(item)) return "The value must be a whole number.";
|
|
1045
|
+
if (spec.min !== void 0 && !(item >= spec.min)) return `The value must be ${String(spec.min)} or more.`;
|
|
1046
|
+
if (spec.max !== void 0 && !(item <= spec.max)) return `The value must be ${String(spec.max)} or less.`;
|
|
1047
|
+
}
|
|
1048
|
+
/** This function stops a split when a part has a type that its position does not permit. */
|
|
1049
|
+
function kindFault(kinds, item, of, path) {
|
|
1050
|
+
const type = isEnvelope(item) ? typePath(item.type) : of === void 0 ? void 0 : [of];
|
|
1051
|
+
if (type !== void 0 && kinds.some((kind) => kind.length <= type.length && kind.every((key, index) => String(key) === String(type[index])))) return;
|
|
1052
|
+
const names = kinds.map((kind) => kind.join(".")).join(", ");
|
|
1053
|
+
throw new Bad(`${isEnvelope(item) ? `The kind ${typeName(item.type)} is not permitted here.` : "A value without an envelope is not permitted here."} The permitted kinds are ${names}.`, path);
|
|
1054
|
+
}
|
|
1036
1055
|
/**
|
|
1037
1056
|
* This function splits a value into its parts, by the shape that its kind declares. The evaluator, the view and
|
|
1038
1057
|
* the static kit use the same split, thus each one sees the same parts.
|
|
@@ -1049,12 +1068,13 @@ function splitParts(shape, value) {
|
|
|
1049
1068
|
fill: () => value
|
|
1050
1069
|
};
|
|
1051
1070
|
const parts = [];
|
|
1052
|
-
const slot = (item, path, of) => {
|
|
1071
|
+
const slot = (item, path, of, address = false) => {
|
|
1053
1072
|
parts.push({
|
|
1054
1073
|
path,
|
|
1055
1074
|
key: path.join("."),
|
|
1056
1075
|
item,
|
|
1057
|
-
...of === void 0 ? {} : { of }
|
|
1076
|
+
...of === void 0 ? {} : { of },
|
|
1077
|
+
...address ? { address: true } : {}
|
|
1058
1078
|
});
|
|
1059
1079
|
return {
|
|
1060
1080
|
t: "slot",
|
|
@@ -1093,17 +1113,33 @@ function splitParts(shape, value) {
|
|
|
1093
1113
|
t: "data",
|
|
1094
1114
|
value: item
|
|
1095
1115
|
};
|
|
1096
|
-
if (spec.shape === "
|
|
1116
|
+
if (spec.shape === "text" || spec.shape === "number" || spec.shape === "one") {
|
|
1117
|
+
const reason = dataFault(spec, item);
|
|
1118
|
+
if (reason !== void 0) throw new Bad(reason, path);
|
|
1119
|
+
return {
|
|
1120
|
+
t: "data",
|
|
1121
|
+
value: item
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
if (spec.shape === "term") {
|
|
1125
|
+
if (spec.kinds !== void 0) kindFault(spec.kinds, item, spec.of, path);
|
|
1126
|
+
return slot(item, path, spec.of, spec.address === true);
|
|
1127
|
+
}
|
|
1097
1128
|
if (spec.shape === "deep") return deep(item, path, spec.of);
|
|
1098
1129
|
if (spec.shape === "list") {
|
|
1099
1130
|
if (!Array.isArray(item)) throw new Bad("The value must be a list.", path);
|
|
1131
|
+
const list = item;
|
|
1132
|
+
if (spec.distinct === true) {
|
|
1133
|
+
const twice = list.findIndex((child, index) => list.slice(0, index).some((before) => docEquals(before, child)));
|
|
1134
|
+
if (twice !== -1) throw new Bad("The items of the list must be different.", [...path, twice]);
|
|
1135
|
+
}
|
|
1100
1136
|
const each = spec.item ?? {
|
|
1101
1137
|
shape: "term",
|
|
1102
1138
|
...spec.of === void 0 ? {} : { of: spec.of }
|
|
1103
1139
|
};
|
|
1104
1140
|
return {
|
|
1105
1141
|
t: "list",
|
|
1106
|
-
items:
|
|
1142
|
+
items: list.map((child, index) => walk(each, child, [...path, index]))
|
|
1107
1143
|
};
|
|
1108
1144
|
}
|
|
1109
1145
|
if (!isRecord(item)) throw new Bad("The value must be a record.", path);
|
|
@@ -1124,6 +1160,7 @@ function splitParts(shape, value) {
|
|
|
1124
1160
|
}])) : spec.fields;
|
|
1125
1161
|
const fields = [];
|
|
1126
1162
|
for (const key of Object.keys(item)) {
|
|
1163
|
+
if (spec.closed === true && !Object.hasOwn(named, key)) throw new Bad(`The field ${key} is not a field of this kind.`, [...path, key]);
|
|
1127
1164
|
const fieldShape = Object.hasOwn(named, key) ? named[key] : spec.rest;
|
|
1128
1165
|
fields.push([key, walk(fieldShape, item[key], [...path, key])]);
|
|
1129
1166
|
}
|
|
@@ -1591,7 +1628,7 @@ const NO_META = Object.freeze({
|
|
|
1591
1628
|
answer: NO_ANSWER,
|
|
1592
1629
|
side: "derive"
|
|
1593
1630
|
});
|
|
1594
|
-
function compileNode(c, value, place, declared, depth) {
|
|
1631
|
+
function compileNode(c, value, place, declared, depth, address = false) {
|
|
1595
1632
|
const envelope = isEnvelope(value) ? value : void 0;
|
|
1596
1633
|
const doc = envelope ?? {
|
|
1597
1634
|
type: declared ?? "?",
|
|
@@ -1639,7 +1676,7 @@ function compileNode(c, value, place, declared, depth) {
|
|
|
1639
1676
|
}
|
|
1640
1677
|
const parts = split.parts.map((slot) => {
|
|
1641
1678
|
const item = slot.item;
|
|
1642
|
-
const compiled = compileNode(c, item, place.concat(slot.path), slot.of, depth + 1);
|
|
1679
|
+
const compiled = compileNode(c, item, place.concat(slot.path), slot.of, depth + 1, slot.address === true);
|
|
1643
1680
|
const header = typeof item?.name === "string" && isEnvelope(item) ? item.name : void 0;
|
|
1644
1681
|
return header === void 0 ? {
|
|
1645
1682
|
key: slot.key,
|
|
@@ -1663,34 +1700,53 @@ function compileNode(c, value, place, declared, depth) {
|
|
|
1663
1700
|
parts,
|
|
1664
1701
|
place,
|
|
1665
1702
|
machine: c.machine,
|
|
1666
|
-
fill: (
|
|
1703
|
+
fill: fillOf(split, doc),
|
|
1704
|
+
...address ? { address: true } : {}
|
|
1667
1705
|
};
|
|
1668
1706
|
if (runner !== void 0) {
|
|
1669
|
-
const
|
|
1670
|
-
|
|
1671
|
-
try {
|
|
1672
|
-
return placed(yield* inner(ctx, rt), place);
|
|
1673
|
-
} catch (error) {
|
|
1674
|
-
return placed(thrown(error, `the runner ${name}`), place);
|
|
1675
|
-
}
|
|
1676
|
-
};
|
|
1677
|
-
const code = (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));
|
|
1678
|
-
return finish(c, meta, code, run);
|
|
1707
|
+
const run = placedRun(isCompiledRunner(runner) ? runner.compileRun(input) : interpretRun(runner, input, split.fill), place, name);
|
|
1708
|
+
return finish(c, meta, runnerCode(run, place, kindText), run);
|
|
1679
1709
|
}
|
|
1680
|
-
const
|
|
1681
|
-
|
|
1710
|
+
const code = placedCode(isCompiledEvaluator(evaluator) ? evaluator.compile(input) : interpret(evaluator, input, split.fill), place, name);
|
|
1711
|
+
return finish(c, meta, code, runOf(code));
|
|
1712
|
+
}
|
|
1713
|
+
/** This function gives the code of a document: the code of its evaluator, with the place of each error and of each exception. */
|
|
1714
|
+
function placedCode(inner, place, name) {
|
|
1715
|
+
return (ctx, rt) => {
|
|
1682
1716
|
try {
|
|
1683
1717
|
return placed(inner(ctx, rt), place);
|
|
1684
1718
|
} catch (error) {
|
|
1685
1719
|
return placed(thrown(error, `the evaluator ${name}`), place);
|
|
1686
1720
|
}
|
|
1687
1721
|
};
|
|
1688
|
-
|
|
1722
|
+
}
|
|
1723
|
+
/** This function gives the steps of a runner, with the place of each error and of each exception. */
|
|
1724
|
+
function placedRun(inner, place, name) {
|
|
1725
|
+
return function* (ctx, rt) {
|
|
1726
|
+
try {
|
|
1727
|
+
return placed(yield* inner(ctx, rt), place);
|
|
1728
|
+
} catch (error) {
|
|
1729
|
+
return placed(thrown(error, `the runner ${name}`), place);
|
|
1730
|
+
}
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1733
|
+
/** This function gives the code of a runner in an evaluation: its steps, which cannot wait. */
|
|
1734
|
+
function runnerCode(run, place, kindText) {
|
|
1735
|
+
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));
|
|
1736
|
+
}
|
|
1737
|
+
/** This function gives the steps of a run of an evaluator. The code runs at the first step, and it does not wait. */
|
|
1738
|
+
function runOf(code) {
|
|
1739
|
+
return function* (ctx, rt) {
|
|
1689
1740
|
return code(ctx, rt);
|
|
1690
|
-
}
|
|
1741
|
+
};
|
|
1742
|
+
}
|
|
1743
|
+
/** This function gives the fill of a compile input: the value with an item in the place of each part. */
|
|
1744
|
+
function fillOf(split, doc) {
|
|
1745
|
+
return (items) => split.parts.length === 0 ? doc.value : split.fill(items);
|
|
1691
1746
|
}
|
|
1692
1747
|
function interpret(fn, input, fill) {
|
|
1693
1748
|
const { kind, typePath, doc, data, place } = input;
|
|
1749
|
+
const address = input.address === true ? { address: true } : {};
|
|
1694
1750
|
const compiled = input.parts;
|
|
1695
1751
|
const hasParts = compiled.length > 0;
|
|
1696
1752
|
return (ctx, rt) => {
|
|
@@ -1709,6 +1765,7 @@ function interpret(fn, input, fill) {
|
|
|
1709
1765
|
parts,
|
|
1710
1766
|
context: ctx,
|
|
1711
1767
|
place,
|
|
1768
|
+
...address,
|
|
1712
1769
|
resolve: (path, options) => rt.resolve(ctx, path, options),
|
|
1713
1770
|
effect: (effectKind, payload) => rt.effect(ctx, effectKind, payload, place),
|
|
1714
1771
|
fill: input.fill
|
|
@@ -1781,16 +1838,20 @@ function prepare(machine, doc, options = {}) {
|
|
|
1781
1838
|
code: root.code,
|
|
1782
1839
|
run: root.run
|
|
1783
1840
|
};
|
|
1841
|
+
return planOf(machine, c.traced, compiled);
|
|
1842
|
+
}
|
|
1843
|
+
/** This function makes the object of a plan. Its closures keep only the machine and the compiled root. */
|
|
1844
|
+
function planOf(machine, traced, root) {
|
|
1784
1845
|
return {
|
|
1785
1846
|
machine,
|
|
1786
|
-
traced
|
|
1787
|
-
root
|
|
1847
|
+
traced,
|
|
1848
|
+
root,
|
|
1788
1849
|
evaluate(context = makeContext(), evalOptions) {
|
|
1789
|
-
const s = new Session(machine, false,
|
|
1850
|
+
const s = new Session(machine, false, traced, void 0, void 0, evalOptions?.effects);
|
|
1790
1851
|
return root.code(rootContext(machine, context), s);
|
|
1791
1852
|
},
|
|
1792
1853
|
run(context = makeContext(), runOptions = {}) {
|
|
1793
|
-
const s = new Session(machine, true,
|
|
1854
|
+
const s = new Session(machine, true, traced, runOptions.perform, runOptions.signal, void 0);
|
|
1794
1855
|
return driveWaiting(root.run(rootContext(machine, context), s), runOptions.signal);
|
|
1795
1856
|
}
|
|
1796
1857
|
};
|
|
@@ -2790,10 +2851,61 @@ function withoutNames(options) {
|
|
|
2790
2851
|
const { names: _host, ...rest } = options;
|
|
2791
2852
|
return rest;
|
|
2792
2853
|
}
|
|
2854
|
+
/** This function gives the keys of a trail, from its start. It keeps the result in the trail. */
|
|
2855
|
+
function keysOf(trail) {
|
|
2856
|
+
if (trail === void 0) return [];
|
|
2857
|
+
if (trail.full !== void 0) return trail.full;
|
|
2858
|
+
const steps = [];
|
|
2859
|
+
let here = trail;
|
|
2860
|
+
while (here !== void 0 && here.full === void 0) {
|
|
2861
|
+
steps.push(here);
|
|
2862
|
+
here = here.up;
|
|
2863
|
+
}
|
|
2864
|
+
const keys = here?.full === void 0 ? [] : [...here.full];
|
|
2865
|
+
for (let index = steps.length - 1; index >= 0; index -= 1) for (const key of steps[index].keys) keys.push(key);
|
|
2866
|
+
trail.full = keys;
|
|
2867
|
+
return keys;
|
|
2868
|
+
}
|
|
2869
|
+
/** A visit with its paths as trails. It makes the keys of a path only when a reader asks for them. */
|
|
2870
|
+
var Visit = class {
|
|
2871
|
+
doc;
|
|
2872
|
+
declared;
|
|
2873
|
+
depth;
|
|
2874
|
+
names;
|
|
2875
|
+
later;
|
|
2876
|
+
slot;
|
|
2877
|
+
parent;
|
|
2878
|
+
kind;
|
|
2879
|
+
typePath;
|
|
2880
|
+
address;
|
|
2881
|
+
#place;
|
|
2882
|
+
#at;
|
|
2883
|
+
constructor(place, at, doc, declared, depth, names, later, slot, parent, kind, typePath, address) {
|
|
2884
|
+
this.doc = doc;
|
|
2885
|
+
this.declared = declared;
|
|
2886
|
+
this.depth = depth;
|
|
2887
|
+
this.names = names;
|
|
2888
|
+
this.later = later;
|
|
2889
|
+
this.slot = slot;
|
|
2890
|
+
this.parent = parent;
|
|
2891
|
+
this.kind = kind;
|
|
2892
|
+
this.typePath = typePath;
|
|
2893
|
+
this.address = address;
|
|
2894
|
+
this.#place = place;
|
|
2895
|
+
this.#at = at;
|
|
2896
|
+
}
|
|
2897
|
+
get place() {
|
|
2898
|
+
return keysOf(this.#place);
|
|
2899
|
+
}
|
|
2900
|
+
get at() {
|
|
2901
|
+
return keysOf(this.#at);
|
|
2902
|
+
}
|
|
2903
|
+
};
|
|
2793
2904
|
/**
|
|
2794
2905
|
* This function visits each document of a document: the root and each part at each depth, by the contracts of the
|
|
2795
2906
|
* kinds. A part of a kind that the world does not have is not visited. The walk uses no stack of the host for the
|
|
2796
|
-
* depth, thus a deep document cannot overflow it.
|
|
2907
|
+
* depth, thus a deep document cannot overflow it. The time and the memory of a walk are linear in the size of the
|
|
2908
|
+
* document. A visit makes the keys of its place and of its path only when a reader asks for them.
|
|
2797
2909
|
*
|
|
2798
2910
|
* @param world - The world of the kinds.
|
|
2799
2911
|
* @param doc - The document, as JSON.
|
|
@@ -2801,23 +2913,25 @@ function withoutNames(options) {
|
|
|
2801
2913
|
* @param options - The type of a bare root value, the names of the host, and a function for the problems.
|
|
2802
2914
|
*/
|
|
2803
2915
|
function walk(world, doc, visit, options = {}) {
|
|
2804
|
-
const names = options.names === void 0 || options.names.length === 0 ? NO_NAMES : new Set(options.names);
|
|
2805
2916
|
const stack = [{
|
|
2806
2917
|
value: doc,
|
|
2807
|
-
place:
|
|
2808
|
-
at:
|
|
2918
|
+
place: void 0,
|
|
2919
|
+
at: void 0,
|
|
2809
2920
|
declared: options.declaredType,
|
|
2810
2921
|
depth: 0,
|
|
2811
|
-
names,
|
|
2812
|
-
later: NO_NAMES
|
|
2922
|
+
names: options.names === void 0 || options.names.length === 0 ? NO_NAMES : new Set(options.names),
|
|
2923
|
+
later: NO_NAMES,
|
|
2924
|
+
address: false,
|
|
2925
|
+
slot: [],
|
|
2926
|
+
parent: void 0
|
|
2813
2927
|
}];
|
|
2814
2928
|
while (stack.length > 0) {
|
|
2815
2929
|
const item = stack.pop();
|
|
2816
2930
|
const envelope = isEnvelope(item.value) ? item.value : void 0;
|
|
2817
2931
|
if (envelope === void 0 && item.declared === void 0) {
|
|
2818
2932
|
options.problem?.({
|
|
2819
|
-
place: item.place,
|
|
2820
|
-
at: item.at,
|
|
2933
|
+
place: keysOf(item.place),
|
|
2934
|
+
at: keysOf(item.at),
|
|
2821
2935
|
code: "#PART?",
|
|
2822
2936
|
reason: "The value has no envelope, and its position declares no type."
|
|
2823
2937
|
});
|
|
@@ -2829,50 +2943,49 @@ function walk(world, doc, visit, options = {}) {
|
|
|
2829
2943
|
};
|
|
2830
2944
|
const found = lookupKind(world, doc.type);
|
|
2831
2945
|
const kind = found.ok ? found.kind : void 0;
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
at: item.at,
|
|
2835
|
-
doc,
|
|
2836
|
-
declared: envelope === void 0,
|
|
2837
|
-
depth: item.depth,
|
|
2838
|
-
names: item.names,
|
|
2839
|
-
later: item.later
|
|
2840
|
-
} : {
|
|
2841
|
-
place: item.place,
|
|
2842
|
-
at: item.at,
|
|
2843
|
-
doc,
|
|
2844
|
-
declared: envelope === void 0,
|
|
2845
|
-
kind,
|
|
2846
|
-
typePath: found.ok ? found.path : [],
|
|
2847
|
-
depth: item.depth,
|
|
2848
|
-
names: item.names,
|
|
2849
|
-
later: item.later
|
|
2850
|
-
}) === "skip" || kind === void 0) continue;
|
|
2946
|
+
const here = new Visit(item.place, item.at, doc, envelope === void 0, item.depth, item.names, item.later, item.slot, item.parent, kind, found.ok ? found.path : void 0, item.address ? true : void 0);
|
|
2947
|
+
if (visit(here) === "skip" || kind === void 0) continue;
|
|
2851
2948
|
const split = splitParts(kind.parts, doc.value);
|
|
2949
|
+
const base = envelope === void 0 ? item.at : {
|
|
2950
|
+
up: item.at,
|
|
2951
|
+
keys: ["value"]
|
|
2952
|
+
};
|
|
2852
2953
|
if (!split.ok) {
|
|
2853
2954
|
const at = split.at.length === 0 ? "" : ` (at ${split.at.join(".")})`;
|
|
2955
|
+
const where = keysOf({
|
|
2956
|
+
up: base,
|
|
2957
|
+
keys: split.at
|
|
2958
|
+
});
|
|
2854
2959
|
options.problem?.({
|
|
2855
|
-
place: item.place,
|
|
2856
|
-
at:
|
|
2960
|
+
place: keysOf(item.place),
|
|
2961
|
+
at: where,
|
|
2857
2962
|
code: "#VALUE!",
|
|
2858
2963
|
reason: `${split.reason}${at} The kind is ${typeName(doc.type)}.`
|
|
2859
2964
|
});
|
|
2860
2965
|
continue;
|
|
2861
2966
|
}
|
|
2862
2967
|
const { sees, later } = scopesOf(kind.scope, doc.value, split.parts);
|
|
2863
|
-
const base = envelope === void 0 ? item.at : [...item.at, "value"];
|
|
2864
2968
|
for (let index = split.parts.length - 1; index >= 0; index -= 1) {
|
|
2865
2969
|
const slot = split.parts[index];
|
|
2866
2970
|
const extra = sees[index] ?? [];
|
|
2867
2971
|
const hidden = later[index] ?? [];
|
|
2868
2972
|
stack.push({
|
|
2869
2973
|
value: slot.item,
|
|
2870
|
-
place:
|
|
2871
|
-
|
|
2974
|
+
place: slot.path.length === 0 ? item.place : {
|
|
2975
|
+
up: item.place,
|
|
2976
|
+
keys: slot.path
|
|
2977
|
+
},
|
|
2978
|
+
at: slot.path.length === 0 ? base : {
|
|
2979
|
+
up: base,
|
|
2980
|
+
keys: slot.path
|
|
2981
|
+
},
|
|
2872
2982
|
declared: slot.of,
|
|
2873
2983
|
depth: item.depth + 1,
|
|
2874
2984
|
names: extra.length === 0 ? item.names : /* @__PURE__ */ new Set([...item.names, ...extra]),
|
|
2875
|
-
later: hidden.length === 0 ? item.later : /* @__PURE__ */ new Set([...item.later, ...hidden])
|
|
2985
|
+
later: hidden.length === 0 ? item.later : /* @__PURE__ */ new Set([...item.later, ...hidden]),
|
|
2986
|
+
address: slot.address === true,
|
|
2987
|
+
slot: slot.path,
|
|
2988
|
+
parent: here
|
|
2876
2989
|
});
|
|
2877
2990
|
}
|
|
2878
2991
|
}
|
|
@@ -2880,7 +2993,7 @@ function walk(world, doc, visit, options = {}) {
|
|
|
2880
2993
|
/**
|
|
2881
2994
|
* This function finds each reference of a document: each document of the type `ref` that a walk reaches. A
|
|
2882
2995
|
* reference to a name that a kind around it binds is not free. The others are what the document reads from
|
|
2883
|
-
* outside: its dependencies.
|
|
2996
|
+
* outside: its dependencies. When the kind `ref` has parts, the walk also finds the references in its step documents.
|
|
2884
2997
|
*
|
|
2885
2998
|
* @param world - The world of the kinds.
|
|
2886
2999
|
* @param doc - The document, as JSON.
|
|
@@ -2894,15 +3007,25 @@ function refsOf(world, doc, options = {}) {
|
|
|
2894
3007
|
const path = v.doc.value;
|
|
2895
3008
|
const root = typeof path[0] === "string" ? path[0] : "";
|
|
2896
3009
|
const first = path[1];
|
|
2897
|
-
const
|
|
2898
|
-
out.push({
|
|
2899
|
-
place: v.place,
|
|
2900
|
-
at: v.at,
|
|
3010
|
+
const use = {
|
|
2901
3011
|
path,
|
|
2902
3012
|
root,
|
|
2903
|
-
free: !
|
|
3013
|
+
free: !(root === "names" && isKey(first) && v.names.has(String(first))),
|
|
3014
|
+
address: v.address === true
|
|
3015
|
+
};
|
|
3016
|
+
Object.defineProperty(use, "place", {
|
|
3017
|
+
enumerable: true,
|
|
3018
|
+
get: () => v.place
|
|
2904
3019
|
});
|
|
2905
|
-
|
|
3020
|
+
Object.defineProperty(use, "at", {
|
|
3021
|
+
enumerable: true,
|
|
3022
|
+
get: () => v.at
|
|
3023
|
+
});
|
|
3024
|
+
Object.defineProperty(use, "visit", {
|
|
3025
|
+
enumerable: false,
|
|
3026
|
+
value: v
|
|
3027
|
+
});
|
|
3028
|
+
out.push(use);
|
|
2906
3029
|
}, options.names === void 0 ? options : withoutNames(options));
|
|
2907
3030
|
return out;
|
|
2908
3031
|
}
|
|
@@ -2978,7 +3101,8 @@ function check(machine, doc, options = {}) {
|
|
|
2978
3101
|
issues.push({
|
|
2979
3102
|
code: "#DEPTH!",
|
|
2980
3103
|
place: v.place,
|
|
2981
|
-
reason: `The nesting is deeper than ${maxDepth} documents
|
|
3104
|
+
reason: `The nesting is deeper than ${maxDepth} documents.`,
|
|
3105
|
+
at: v.at
|
|
2982
3106
|
});
|
|
2983
3107
|
return "skip";
|
|
2984
3108
|
}
|
|
@@ -2988,7 +3112,8 @@ function check(machine, doc, options = {}) {
|
|
|
2988
3112
|
issues.push({
|
|
2989
3113
|
code: "#KIND?",
|
|
2990
3114
|
place: v.place,
|
|
2991
|
-
reason: `The world has no kind ${typeName(v.doc.type)}
|
|
3115
|
+
reason: `The world has no kind ${typeName(v.doc.type)}.`,
|
|
3116
|
+
at: v.at
|
|
2992
3117
|
});
|
|
2993
3118
|
return "skip";
|
|
2994
3119
|
}
|
|
@@ -2996,31 +3121,47 @@ function check(machine, doc, options = {}) {
|
|
|
2996
3121
|
if (name === void 0 || !has(machine.evaluators, name) && !has(machine.runners, name)) issues.push({
|
|
2997
3122
|
code: "#NOEVAL",
|
|
2998
3123
|
place: v.place,
|
|
2999
|
-
reason: `No rung gives an evaluator for ${typeName(v.doc.type)}
|
|
3124
|
+
reason: `No rung gives an evaluator for ${typeName(v.doc.type)}.`,
|
|
3125
|
+
at: v.at
|
|
3000
3126
|
});
|
|
3001
3127
|
if (options.caps !== void 0 && v.kind?.effects !== void 0) {
|
|
3002
3128
|
const kinds = v.kind.effects === "type" ? [v.typePath ?? []] : v.kind.effects.map((kind) => kind.split("."));
|
|
3003
3129
|
for (const kind of kinds) if (!permits(options.caps, kind)) issues.push({
|
|
3004
3130
|
code: "#CAP!",
|
|
3005
3131
|
place: v.place,
|
|
3006
|
-
reason: `The capabilities do not permit the effect ${kind.join(".")}
|
|
3132
|
+
reason: `The capabilities do not permit the effect ${kind.join(".")}.`,
|
|
3133
|
+
at: v.at
|
|
3007
3134
|
});
|
|
3008
3135
|
}
|
|
3009
|
-
|
|
3136
|
+
const isPath = isRef(v.doc);
|
|
3137
|
+
if (!isPath && v.typePath?.length === 1 && v.typePath[0] === "ref") issues.push({
|
|
3138
|
+
code: "#VALUE!",
|
|
3139
|
+
place: v.place,
|
|
3140
|
+
reason: "The value of ref is a path: a list that starts with a root.",
|
|
3141
|
+
at: [...v.at, ...v.declared ? [] : ["value"]]
|
|
3142
|
+
});
|
|
3143
|
+
if (!isPath) return void 0;
|
|
3010
3144
|
const path = v.doc.value;
|
|
3011
3145
|
const root = path[0];
|
|
3146
|
+
const stepAt = (index) => [
|
|
3147
|
+
...v.at,
|
|
3148
|
+
"value",
|
|
3149
|
+
...index < path.length ? [index] : []
|
|
3150
|
+
];
|
|
3012
3151
|
if (typeof root !== "string" || !ROOTS.includes(root)) {
|
|
3013
3152
|
issues.push({
|
|
3014
3153
|
code: "#REF!",
|
|
3015
3154
|
place: v.place,
|
|
3016
|
-
reason: `A path starts with a root: ${ROOTS.join(", ")}
|
|
3155
|
+
reason: `A path starts with a root: ${ROOTS.join(", ")}.`,
|
|
3156
|
+
at: stepAt(0)
|
|
3017
3157
|
});
|
|
3018
3158
|
return "skip";
|
|
3019
3159
|
}
|
|
3020
3160
|
if (!roots.includes(root)) issues.push({
|
|
3021
3161
|
code: "#REF!",
|
|
3022
3162
|
place: v.place,
|
|
3023
|
-
reason: `The root ${root} is not open here. The open roots are ${roots.join(", ")}
|
|
3163
|
+
reason: `The root ${root} is not open here. The open roots are ${roots.join(", ")}.`,
|
|
3164
|
+
at: stepAt(0)
|
|
3024
3165
|
});
|
|
3025
3166
|
const first = path[1];
|
|
3026
3167
|
if (root === "names" && !anyName && isKey(first) && !v.names.has(String(first))) {
|
|
@@ -3028,33 +3169,37 @@ function check(machine, doc, options = {}) {
|
|
|
3028
3169
|
issues.push({
|
|
3029
3170
|
code: "#NAME?",
|
|
3030
3171
|
place: v.place,
|
|
3031
|
-
reason: later ? `The name ${String(first)} is bound by a later step.` : `Nothing binds the name ${String(first)}
|
|
3172
|
+
reason: later ? `The name ${String(first)} is bound by a later step.` : `Nothing binds the name ${String(first)}.`,
|
|
3173
|
+
at: stepAt(1)
|
|
3032
3174
|
});
|
|
3033
3175
|
}
|
|
3034
|
-
path.
|
|
3035
|
-
if (isKey(step)) return;
|
|
3176
|
+
path.forEach((step, index) => {
|
|
3177
|
+
if (index === 0 || isKey(step)) return;
|
|
3036
3178
|
const found = isEnvelope(step) ? lookupKind(machine.world, step.type) : void 0;
|
|
3037
3179
|
const walker = found?.ok === true && Object.hasOwn(found.kind.verbs, "step") ? found.kind.verbs["step"] : void 0;
|
|
3038
3180
|
if (walker === void 0 || !has(machine.walkers, walker)) issues.push({
|
|
3039
3181
|
code: "#STEP?",
|
|
3040
3182
|
place: v.place,
|
|
3041
|
-
reason: "A step is a key, or a document of a kind with the verb step."
|
|
3183
|
+
reason: "A step is a key, or a document of a kind with the verb step.",
|
|
3184
|
+
at: stepAt(index)
|
|
3042
3185
|
});
|
|
3043
3186
|
});
|
|
3044
|
-
return "skip";
|
|
3045
3187
|
}, {
|
|
3046
3188
|
...options.declaredType === void 0 ? {} : { declaredType: options.declaredType },
|
|
3047
3189
|
...Array.isArray(options.names) ? { names: options.names } : {},
|
|
3048
3190
|
problem: (problem) => issues.push({
|
|
3049
3191
|
code: problem.code,
|
|
3050
3192
|
place: problem.place,
|
|
3051
|
-
reason: problem.reason
|
|
3193
|
+
reason: problem.reason,
|
|
3194
|
+
at: problem.at
|
|
3052
3195
|
})
|
|
3053
3196
|
});
|
|
3054
3197
|
return issues;
|
|
3055
3198
|
}
|
|
3056
3199
|
//#endregion
|
|
3057
3200
|
//#region src/formula.ts
|
|
3201
|
+
/** The mark of a field that the profile leaves out of a record. */
|
|
3202
|
+
const SKIPPED = Symbol("skipped");
|
|
3058
3203
|
/** The value of a name whose binding failed. A reference to the name gives the error. */
|
|
3059
3204
|
var Held = class {
|
|
3060
3205
|
error;
|
|
@@ -3106,6 +3251,16 @@ function setOwn(target, key, value) {
|
|
|
3106
3251
|
function compiled(compile) {
|
|
3107
3252
|
return { compile };
|
|
3108
3253
|
}
|
|
3254
|
+
/** This function evaluates parts in order. It gives the value of each part, or the first error. */
|
|
3255
|
+
function valuesOf(parts, ctx, rt) {
|
|
3256
|
+
const values = new Array(parts.length);
|
|
3257
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
3258
|
+
const result = parts[index].code(ctx, rt);
|
|
3259
|
+
if (!result.ok) return result;
|
|
3260
|
+
values[index] = result.value;
|
|
3261
|
+
}
|
|
3262
|
+
return values;
|
|
3263
|
+
}
|
|
3109
3264
|
function partsOf(value) {
|
|
3110
3265
|
return isRecord(value) ? value : {};
|
|
3111
3266
|
}
|
|
@@ -3252,18 +3407,48 @@ function formulaEvaluators(profile = {}) {
|
|
|
3252
3407
|
return Object.hasOwn(ops, name) ? ops[name] : void 0;
|
|
3253
3408
|
};
|
|
3254
3409
|
const notTruth = (what) => fail("#VALUE!", `The ${what} must give a truth value.`);
|
|
3410
|
+
const combine = profile.combine;
|
|
3411
|
+
/** This function evaluates each part. Without `combine`, the first error stops it. A field that `omit` skips is absent. */
|
|
3412
|
+
const collect = (parts, ctx, rt, skip) => {
|
|
3413
|
+
const values = new Array(parts.length);
|
|
3414
|
+
let errors;
|
|
3415
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
3416
|
+
const result = parts[index].code(ctx, rt);
|
|
3417
|
+
if (result.ok) values[index] = result.value;
|
|
3418
|
+
else if (skip !== void 0 && skip(result.error)) values[index] = SKIPPED;
|
|
3419
|
+
else if (combine === void 0) return result;
|
|
3420
|
+
else (errors ??= []).push(result.error);
|
|
3421
|
+
}
|
|
3422
|
+
if (errors === void 0 || combine === void 0) return values;
|
|
3423
|
+
return {
|
|
3424
|
+
ok: false,
|
|
3425
|
+
error: errors.length === 1 ? errors[0] : combine(errors)
|
|
3426
|
+
};
|
|
3427
|
+
};
|
|
3255
3428
|
return {
|
|
3256
3429
|
"formula.lit": compiled(({ doc }) => {
|
|
3257
3430
|
const result = ok(doc.value);
|
|
3258
3431
|
return () => result;
|
|
3259
3432
|
}),
|
|
3260
|
-
"formula.ref": compiled(({ doc }) => {
|
|
3433
|
+
"formula.ref": compiled(({ doc, parts, fill, address }) => {
|
|
3261
3434
|
const path = doc.value;
|
|
3262
3435
|
if (!Array.isArray(path) || path.length === 0) {
|
|
3263
3436
|
const bad = fail("#VALUE!", "The value of ref is a path: a list that starts with a root, for example [\"names\", \"a\"].");
|
|
3264
3437
|
return () => bad;
|
|
3265
3438
|
}
|
|
3266
3439
|
const head = path[0] === "names" && typeof path[1] === "string" ? path[1] : void 0;
|
|
3440
|
+
const give = (found) => {
|
|
3441
|
+
if (!found.ok) return fail(found.code, found.reason);
|
|
3442
|
+
if (address === true) return ok(found.thing);
|
|
3443
|
+
const reached = valueOfThing(found.thing);
|
|
3444
|
+
if (reached instanceof Held) return {
|
|
3445
|
+
ok: false,
|
|
3446
|
+
error: reached.error
|
|
3447
|
+
};
|
|
3448
|
+
if (missing !== void 0 && missing(reached)) return fail("#N/A", "The path reaches no value.");
|
|
3449
|
+
return ok(reached);
|
|
3450
|
+
};
|
|
3451
|
+
const computed = parts.length > 0;
|
|
3267
3452
|
return (ctx, rt) => {
|
|
3268
3453
|
if (head !== void 0 && Object.hasOwn(ctx.names, head)) {
|
|
3269
3454
|
const bound = ctx.names[head];
|
|
@@ -3272,21 +3457,40 @@ function formulaEvaluators(profile = {}) {
|
|
|
3272
3457
|
error: bound.error
|
|
3273
3458
|
};
|
|
3274
3459
|
}
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
return
|
|
3460
|
+
if (!computed) return give(rt.resolve(ctx, path));
|
|
3461
|
+
const steps = valuesOf(parts, ctx, rt);
|
|
3462
|
+
return Array.isArray(steps) ? give(rt.resolve(ctx, fill(steps))) : steps;
|
|
3463
|
+
};
|
|
3464
|
+
}),
|
|
3465
|
+
"formula.step": compiled(({ doc, parts, fill }) => {
|
|
3466
|
+
if (parts.length === 0) {
|
|
3467
|
+
const same = ok(doc);
|
|
3468
|
+
return () => same;
|
|
3469
|
+
}
|
|
3470
|
+
return (ctx, rt) => {
|
|
3471
|
+
const values = valuesOf(parts, ctx, rt);
|
|
3472
|
+
return Array.isArray(values) ? ok({
|
|
3473
|
+
...doc,
|
|
3474
|
+
value: fill(values)
|
|
3475
|
+
}) : values;
|
|
3284
3476
|
};
|
|
3285
3477
|
}),
|
|
3286
3478
|
"formula.record": compiled(({ value }) => {
|
|
3287
3479
|
const fields = partsOf(value);
|
|
3288
3480
|
const keys = Object.keys(fields);
|
|
3289
3481
|
const codes = keys.map((key) => fields[key].code);
|
|
3482
|
+
if (combine !== void 0) {
|
|
3483
|
+
const parts = keys.map((key) => fields[key]);
|
|
3484
|
+
return (ctx, rt) => {
|
|
3485
|
+
const values = collect(parts, ctx, rt, omit);
|
|
3486
|
+
if (!Array.isArray(values)) return values;
|
|
3487
|
+
const out = {};
|
|
3488
|
+
keys.forEach((key, index) => {
|
|
3489
|
+
if (values[index] !== SKIPPED) setOwn(out, key, values[index]);
|
|
3490
|
+
});
|
|
3491
|
+
return ok(out);
|
|
3492
|
+
};
|
|
3493
|
+
}
|
|
3290
3494
|
return (ctx, rt) => {
|
|
3291
3495
|
const out = {};
|
|
3292
3496
|
for (let index = 0; index < keys.length; index += 1) {
|
|
@@ -3301,13 +3505,8 @@ function formulaEvaluators(profile = {}) {
|
|
|
3301
3505
|
};
|
|
3302
3506
|
}),
|
|
3303
3507
|
"formula.list": compiled(({ parts }) => (ctx, rt) => {
|
|
3304
|
-
const
|
|
3305
|
-
|
|
3306
|
-
const result = parts[index].code(ctx, rt);
|
|
3307
|
-
if (!result.ok) return result;
|
|
3308
|
-
out[index] = result.value;
|
|
3309
|
-
}
|
|
3310
|
-
return ok(out);
|
|
3508
|
+
const values = collect(parts, ctx, rt);
|
|
3509
|
+
return Array.isArray(values) ? ok(values) : values;
|
|
3311
3510
|
}),
|
|
3312
3511
|
"formula.let": compiled(({ value }) => {
|
|
3313
3512
|
const record = partsOf(value);
|
|
@@ -3422,6 +3621,15 @@ function formulaEvaluators(profile = {}) {
|
|
|
3422
3621
|
}
|
|
3423
3622
|
const done = (out) => check === void 0 ? ok(out) : check(out, name) ?? ok(out);
|
|
3424
3623
|
const failed = (error) => thrown(error, `the operation ${name}`);
|
|
3624
|
+
if (combine !== void 0) return (ctx, rt) => {
|
|
3625
|
+
const values = collect(parts, ctx, rt);
|
|
3626
|
+
if (!Array.isArray(values)) return values;
|
|
3627
|
+
try {
|
|
3628
|
+
return done(fn(...values));
|
|
3629
|
+
} catch (error) {
|
|
3630
|
+
return failed(error);
|
|
3631
|
+
}
|
|
3632
|
+
};
|
|
3425
3633
|
if (parts.length === 1) {
|
|
3426
3634
|
const [a] = parts;
|
|
3427
3635
|
return (ctx, rt) => {
|
|
@@ -3469,9 +3677,9 @@ function formulaEvaluators(profile = {}) {
|
|
|
3469
3677
|
const unknown = fail("#NAME?", `The language has no extension ${name}.`);
|
|
3470
3678
|
return () => unknown;
|
|
3471
3679
|
}
|
|
3472
|
-
return () => {
|
|
3680
|
+
return (ctx) => {
|
|
3473
3681
|
try {
|
|
3474
|
-
return handler(doc.value);
|
|
3682
|
+
return handler(doc.value, ctx);
|
|
3475
3683
|
} catch (error) {
|
|
3476
3684
|
return thrown(error, `the extension ${name}`);
|
|
3477
3685
|
}
|