@mark1russell7/coeval 0.24.1 → 0.25.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 +1 -1
- package/build/index.d.ts +85 -6
- package/build/index.d.ts.map +1 -1
- package/build/index.js +365 -37
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/formula.ts +61 -6
- package/src/index.ts +3 -2
- package/src/merge.ts +130 -10
- package/src/reference.ts +13 -2
- package/src/static.ts +2 -1
- package/src/stdlib.ts +15 -7
- package/src/types.ts +65 -0
- package/src/typing.ts +115 -10
package/build/index.js
CHANGED
|
@@ -1353,6 +1353,61 @@ function narrowAt(type, path, at, narrowed, domains) {
|
|
|
1353
1353
|
...form.closed ? { closed: true } : {}
|
|
1354
1354
|
};
|
|
1355
1355
|
}
|
|
1356
|
+
/** This function gives the type of a value that is a member of both types. The result is the narrower known type. */
|
|
1357
|
+
function meetOf(type, proved, domains) {
|
|
1358
|
+
if (isUnknown(proved, domains)) return type;
|
|
1359
|
+
const form = formOf(type, domains);
|
|
1360
|
+
if (form.t === "one") return { one: form.values.filter((value) => member(value, proved, domains)) };
|
|
1361
|
+
return !isUnknown(type, domains) && isPrecise(type, domains) && fits(type, proved, domains) ? type : proved;
|
|
1362
|
+
}
|
|
1363
|
+
/** This function gives the type of a value of the first type that is not a member of the second type. */
|
|
1364
|
+
function withoutOf(type, removed, domains) {
|
|
1365
|
+
if (isUnknown(removed, domains)) return type;
|
|
1366
|
+
const form = formOf(type, domains);
|
|
1367
|
+
if (form.t === "one") return { one: form.values.filter((value) => !member(value, removed, domains)) };
|
|
1368
|
+
if (form.t === "union") {
|
|
1369
|
+
const members = form.members.map((each) => withoutOf(each, removed, domains)).filter((each) => each !== "never");
|
|
1370
|
+
return members.length === 0 ? "never" : members.length === 1 ? members[0] : { union: members };
|
|
1371
|
+
}
|
|
1372
|
+
return isPrecise(type, domains) && fits(type, removed, domains) ? "never" : type;
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1375
|
+
* This function gives a type with one proof at a path (STATIC.OCCURRENCE). A path that leaves the records of the type
|
|
1376
|
+
* gives the type back. A fact about a field also tells if the field exists. A value of a type without `undefined`
|
|
1377
|
+
* exists. An optional field without a remaining type does not.
|
|
1378
|
+
*
|
|
1379
|
+
* @param type - The type of the value at the first `at` keys of the path.
|
|
1380
|
+
* @param proof - The proof.
|
|
1381
|
+
* @param at - The number of keys of the path that the type already holds.
|
|
1382
|
+
* @param domains - The domains of the machine.
|
|
1383
|
+
* @returns The narrowed type.
|
|
1384
|
+
*/
|
|
1385
|
+
function narrowType(type, proof, at, domains) {
|
|
1386
|
+
if (at === proof.path.length) {
|
|
1387
|
+
if (proof.fact === "is") return meetOf(type, proof.type, domains);
|
|
1388
|
+
return proof.fact === "not" ? withoutOf(type, proof.type, domains) : type;
|
|
1389
|
+
}
|
|
1390
|
+
const form = formOf(type, domains);
|
|
1391
|
+
const key = proof.path[at];
|
|
1392
|
+
if (form.t !== "record" || !Object.hasOwn(form.fields, key)) return type;
|
|
1393
|
+
const before = form.fields[key];
|
|
1394
|
+
const optional = new Set(form.optional);
|
|
1395
|
+
let after = narrowType(before, proof, at + 1, domains);
|
|
1396
|
+
if (at + 1 === proof.path.length && optional.has(key)) {
|
|
1397
|
+
if (proof.fact === "present" || proof.fact === "is" && !member(void 0, proof.type, domains)) optional.delete(key);
|
|
1398
|
+
else if (proof.fact === "absent" || after === "never") after = void 0;
|
|
1399
|
+
}
|
|
1400
|
+
const fields = {};
|
|
1401
|
+
for (const name of Object.keys(form.fields)) if (name !== key) setOwn$6(fields, name, form.fields[name]);
|
|
1402
|
+
if (after !== void 0) setOwn$6(fields, key, after);
|
|
1403
|
+
else if (form.closed) optional.delete(key);
|
|
1404
|
+
else setOwn$6(fields, key, "never");
|
|
1405
|
+
return {
|
|
1406
|
+
record: fields,
|
|
1407
|
+
...optional.size === 0 ? {} : { optional: [...optional] },
|
|
1408
|
+
...form.closed ? { closed: true } : {}
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1356
1411
|
/**
|
|
1357
1412
|
* This function is the split rule of `fits` (TYPE.FITS-FINITE, D84, TYPE.FITS-PATH, D89). A record type fits when each
|
|
1358
1413
|
* record of its split fits. A split takes the first field in the order of `splitsOf`. Each member of the field gives a
|
|
@@ -1882,6 +1937,12 @@ var MergeError = class extends Error {
|
|
|
1882
1937
|
}
|
|
1883
1938
|
};
|
|
1884
1939
|
const NONE$2 = /* @__PURE__ */ new Set();
|
|
1940
|
+
/** The verbs that the kernel reads. Thus no language owns them (COMPOSE.PIRACY). */
|
|
1941
|
+
const KERNEL_VERBS = /* @__PURE__ */ new Set([
|
|
1942
|
+
"eval",
|
|
1943
|
+
"deref",
|
|
1944
|
+
"step"
|
|
1945
|
+
]);
|
|
1885
1946
|
function keyOf(path) {
|
|
1886
1947
|
return JSON.stringify(path.map(String));
|
|
1887
1948
|
}
|
|
@@ -2072,16 +2133,123 @@ function mergeEntry(m, earlier, later, path, part, replaces) {
|
|
|
2072
2133
|
m.clash(path, part, a === b ? `Two parts give two different ${a === "an alias" ? "aliases" : "kinds"}.` : `One part gives ${a}, and the other part gives ${b}.`);
|
|
2073
2134
|
return earlier;
|
|
2074
2135
|
}
|
|
2075
|
-
function
|
|
2136
|
+
function worldDraft() {
|
|
2137
|
+
return {
|
|
2138
|
+
kinds: {},
|
|
2139
|
+
defaults: {},
|
|
2140
|
+
owners: /* @__PURE__ */ new Map(),
|
|
2141
|
+
entries: []
|
|
2142
|
+
};
|
|
2143
|
+
}
|
|
2144
|
+
/**
|
|
2145
|
+
* This function gives a copy of a table with a change to the kind at a path, as `replaces` names the path. It gives
|
|
2146
|
+
* `undefined` when no kind is at the path. An alias is not a kind.
|
|
2147
|
+
*/
|
|
2148
|
+
function changeKind(table, path, change) {
|
|
2149
|
+
const [first, key, ...rest] = path;
|
|
2150
|
+
if (String(first) === "fallback" && key === void 0) return table.fallback === void 0 ? void 0 : {
|
|
2151
|
+
...table,
|
|
2152
|
+
fallback: change(table.fallback)
|
|
2153
|
+
};
|
|
2154
|
+
if (String(first) !== "kinds" || key === void 0) return void 0;
|
|
2155
|
+
const entry = own$2(table.kinds, String(key));
|
|
2156
|
+
const next = rest.length === 0 ? isKind(entry) ? change(entry) : void 0 : isNamespace(entry) ? changeKind(entry, rest, change) : void 0;
|
|
2157
|
+
if (next === void 0) return void 0;
|
|
2158
|
+
const kinds = { ...table.kinds };
|
|
2159
|
+
setOwn$5(kinds, String(key), next);
|
|
2160
|
+
return {
|
|
2161
|
+
...table,
|
|
2162
|
+
kinds
|
|
2163
|
+
};
|
|
2164
|
+
}
|
|
2165
|
+
/**
|
|
2166
|
+
* This function adds the entries of `extends` to the kinds of a draft. A later part can give the kind, thus this step
|
|
2167
|
+
* reads the whole draft. Without `m`, it reports no conflict: a function part sees the entries of the parts before it.
|
|
2168
|
+
*/
|
|
2169
|
+
function addVerbs(draft, m) {
|
|
2170
|
+
let table = draft.fallback === void 0 ? { kinds: draft.kinds } : {
|
|
2171
|
+
kinds: draft.kinds,
|
|
2172
|
+
fallback: draft.fallback
|
|
2173
|
+
};
|
|
2174
|
+
for (const entry of draft.entries) {
|
|
2175
|
+
if (!entry.allowed && !entry.replaced) {
|
|
2176
|
+
m?.clash(entry.path, entry.part, "A part adds a verb that it does not own to a kind that it does not give.");
|
|
2177
|
+
continue;
|
|
2178
|
+
}
|
|
2179
|
+
const next = changeKind(table, entry.kind, (kind) => {
|
|
2180
|
+
const verbs = { ...kind.verbs };
|
|
2181
|
+
const earlier = own$2(verbs, entry.verb);
|
|
2182
|
+
if (earlier === void 0) m?.give(entry.path, entry.part);
|
|
2183
|
+
else if (entry.replaced) m?.replace(entry.path, entry.part);
|
|
2184
|
+
else if (earlier !== entry.code) m?.clash(entry.path, entry.part, "Two parts give two different evaluators for this verb of this kind.");
|
|
2185
|
+
if (earlier === void 0 || entry.replaced) setOwn$5(verbs, entry.verb, entry.code);
|
|
2186
|
+
return {
|
|
2187
|
+
...kind,
|
|
2188
|
+
verbs
|
|
2189
|
+
};
|
|
2190
|
+
});
|
|
2191
|
+
if (next !== void 0) table = next;
|
|
2192
|
+
else if (!entry.replaced) m?.clash(entry.path, entry.part, "A part adds a verb to a path without a kind.");
|
|
2193
|
+
}
|
|
2194
|
+
return table;
|
|
2195
|
+
}
|
|
2196
|
+
function worldOf(draft, m) {
|
|
2197
|
+
const table = addVerbs(draft, m);
|
|
2076
2198
|
const world = {
|
|
2077
|
-
kinds: { ...
|
|
2199
|
+
kinds: { ...table.kinds },
|
|
2078
2200
|
defaults: { ...draft.defaults }
|
|
2079
2201
|
};
|
|
2080
|
-
return
|
|
2202
|
+
return table.fallback === void 0 ? world : {
|
|
2081
2203
|
...world,
|
|
2082
|
-
fallback:
|
|
2204
|
+
fallback: table.fallback
|
|
2083
2205
|
};
|
|
2084
2206
|
}
|
|
2207
|
+
/** This function records the verbs that a part owns, and the entries that it adds to kinds. */
|
|
2208
|
+
function mergeVerbs(m, draft, part, index, replaces, base) {
|
|
2209
|
+
const owned = part.verbs ?? [];
|
|
2210
|
+
for (const verb of owned) {
|
|
2211
|
+
const path = [
|
|
2212
|
+
...base,
|
|
2213
|
+
"verbs",
|
|
2214
|
+
verb
|
|
2215
|
+
];
|
|
2216
|
+
const owner = draft.owners.get(verb);
|
|
2217
|
+
if (replaces.has(keyOf(path))) {
|
|
2218
|
+
draft.owners.set(verb, part);
|
|
2219
|
+
m.replace(path, index);
|
|
2220
|
+
} else if (KERNEL_VERBS.has(verb)) m.clash(path, index, "The kernel owns this verb.");
|
|
2221
|
+
else if (owner === void 0) {
|
|
2222
|
+
draft.owners.set(verb, part);
|
|
2223
|
+
m.give(path, index);
|
|
2224
|
+
} else if (!same(owner, part)) m.clash(path, index, "Two parts own this verb.");
|
|
2225
|
+
}
|
|
2226
|
+
const table = part.fallback === void 0 ? { kinds: part.kinds ?? {} } : {
|
|
2227
|
+
kinds: part.kinds ?? {},
|
|
2228
|
+
fallback: part.fallback
|
|
2229
|
+
};
|
|
2230
|
+
for (const { kind, verbs } of part.extends ?? []) {
|
|
2231
|
+
const gives = changeKind(table, kind, (found) => found) !== void 0;
|
|
2232
|
+
for (const verb of Object.keys(verbs)) {
|
|
2233
|
+
const code = verbs[verb];
|
|
2234
|
+
if (code === void 0) continue;
|
|
2235
|
+
const path = [
|
|
2236
|
+
...base,
|
|
2237
|
+
...kind,
|
|
2238
|
+
"verbs",
|
|
2239
|
+
verb
|
|
2240
|
+
];
|
|
2241
|
+
draft.entries.push({
|
|
2242
|
+
kind,
|
|
2243
|
+
verb,
|
|
2244
|
+
code,
|
|
2245
|
+
path,
|
|
2246
|
+
part: index,
|
|
2247
|
+
allowed: gives || owned.includes(verb),
|
|
2248
|
+
replaced: replaces.has(keyOf(path))
|
|
2249
|
+
});
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2085
2253
|
function mergeWorldInto(m, draft, part, index, replaces, base) {
|
|
2086
2254
|
if (part === void 0) return;
|
|
2087
2255
|
if (part.kinds !== void 0) mergeEntries(m, draft.kinds, part.kinds, [...base, "kinds"], index, replaces);
|
|
@@ -2093,6 +2261,7 @@ function mergeWorldInto(m, draft, part, index, replaces, base) {
|
|
|
2093
2261
|
], index, replaces, Object.is, "Two parts give two different defaults for this verb.");
|
|
2094
2262
|
const fallback = mergeFallback(m, draft.fallback, part.fallback, [...base, "fallback"], index, replaces);
|
|
2095
2263
|
if (fallback !== void 0) draft.fallback = fallback;
|
|
2264
|
+
mergeVerbs(m, draft, part, index, replaces, base);
|
|
2096
2265
|
}
|
|
2097
2266
|
/**
|
|
2098
2267
|
* This function merges worlds into one world, in the order of the parts. It does not change a part.
|
|
@@ -2102,6 +2271,8 @@ function mergeWorldInto(m, draft, part, index, replaces, base) {
|
|
|
2102
2271
|
* - The defaults merge verb by verb, and the world keeps one fallback.
|
|
2103
2272
|
* - Each other pair of entries at one path is a conflict, unless the later part names the path in `replaces`.
|
|
2104
2273
|
* Then the later entry wins, with no merge below it.
|
|
2274
|
+
* - The entries of `extends` join their kinds after each part, thus the order of the parts does not change them. A
|
|
2275
|
+
* verb that a part does not own on a kind that it does not give is a conflict (COMPOSE.PIRACY).
|
|
2105
2276
|
*
|
|
2106
2277
|
* A part can be a function of the merge of the parts before it (`prev`, as an overlay of Nix). Thus a part can read
|
|
2107
2278
|
* the entry that it replaces.
|
|
@@ -2112,16 +2283,14 @@ function mergeWorldInto(m, draft, part, index, replaces, base) {
|
|
|
2112
2283
|
*/
|
|
2113
2284
|
function mergeWorlds(...parts) {
|
|
2114
2285
|
const m = new Merger();
|
|
2115
|
-
const draft =
|
|
2116
|
-
kinds: {},
|
|
2117
|
-
defaults: {}
|
|
2118
|
-
};
|
|
2286
|
+
const draft = worldDraft();
|
|
2119
2287
|
parts.forEach((input, index) => {
|
|
2120
2288
|
const part = typeof input === "function" ? input(worldOf(draft)) : input;
|
|
2121
2289
|
mergeWorldInto(m, draft, part, index, new Set((part.replaces ?? []).map(keyOf)), []);
|
|
2122
2290
|
});
|
|
2291
|
+
const world = worldOf(draft, m);
|
|
2123
2292
|
if (m.conflicts.length > 0) throw new MergeError(m.conflicts);
|
|
2124
|
-
return
|
|
2293
|
+
return world;
|
|
2125
2294
|
}
|
|
2126
2295
|
/** The fields of a machine that merge by their own rules. Each other field is one value, or a conflict. */
|
|
2127
2296
|
const TABLES = /* @__PURE__ */ new Set([
|
|
@@ -2174,12 +2343,12 @@ function composeFacts(hooks, replaced) {
|
|
|
2174
2343
|
}
|
|
2175
2344
|
};
|
|
2176
2345
|
}
|
|
2177
|
-
function machineOf(draft) {
|
|
2346
|
+
function machineOf(draft, m) {
|
|
2178
2347
|
const known = composeKnown(draft.hooks, draft.replaced);
|
|
2179
2348
|
const facts = composeFacts(draft.factHooks, draft.replaced);
|
|
2180
2349
|
return {
|
|
2181
2350
|
...draft.rest,
|
|
2182
|
-
world: worldOf(draft.world),
|
|
2351
|
+
world: worldOf(draft.world, m),
|
|
2183
2352
|
evaluators: { ...draft.evaluators },
|
|
2184
2353
|
...draft.runners === void 0 ? {} : { runners: { ...draft.runners } },
|
|
2185
2354
|
...draft.derefs === void 0 ? {} : { derefs: { ...draft.derefs } },
|
|
@@ -2253,10 +2422,7 @@ function checkHidden(m, draft) {
|
|
|
2253
2422
|
function mergeMachines(...parts) {
|
|
2254
2423
|
const m = new Merger();
|
|
2255
2424
|
const draft = {
|
|
2256
|
-
world:
|
|
2257
|
-
kinds: {},
|
|
2258
|
-
defaults: {}
|
|
2259
|
-
},
|
|
2425
|
+
world: worldDraft(),
|
|
2260
2426
|
evaluators: {},
|
|
2261
2427
|
hooks: [],
|
|
2262
2428
|
factHooks: [],
|
|
@@ -2297,8 +2463,9 @@ function mergeMachines(...parts) {
|
|
|
2297
2463
|
}
|
|
2298
2464
|
});
|
|
2299
2465
|
checkHidden(m, draft);
|
|
2466
|
+
const machine = machineOf(draft, m);
|
|
2300
2467
|
if (m.conflicts.length > 0) throw new MergeError(m.conflicts);
|
|
2301
|
-
return
|
|
2468
|
+
return machine;
|
|
2302
2469
|
}
|
|
2303
2470
|
/** This function removes the entry at a path from a table of entries. It gives the same table when no entry is there. */
|
|
2304
2471
|
function removeEntry(entries, path) {
|
|
@@ -6177,11 +6344,17 @@ const STD_EVALUATORS = {
|
|
|
6177
6344
|
const [a, b] = values;
|
|
6178
6345
|
return ok((a ?? 0) > (b ?? 0));
|
|
6179
6346
|
},
|
|
6180
|
-
"ref.resolve": ({ data,
|
|
6181
|
-
if (!Array.isArray(data) || data.length === 0)
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6347
|
+
"ref.resolve": { compile: ({ data, machine }) => {
|
|
6348
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
6349
|
+
const bad = fail("#VALUE!", "The value of ref is a path: a list that starts with a root, for example [\"names\", \"a\"].");
|
|
6350
|
+
return () => bad;
|
|
6351
|
+
}
|
|
6352
|
+
const path = compilePath(machine, data);
|
|
6353
|
+
return (ctx, rt) => {
|
|
6354
|
+
const found = resolveCompiled(rt, ctx, path);
|
|
6355
|
+
return found.ok ? ok(valueOfThing(found.thing)) : fail(found.code, found.reason);
|
|
6356
|
+
};
|
|
6357
|
+
} },
|
|
6185
6358
|
"tree.node": (input) => treeNode(input, void 0)
|
|
6186
6359
|
};
|
|
6187
6360
|
/** The brand of a node of the decorated tree. Its value is the state that the kit reads. */
|
|
@@ -7843,6 +8016,11 @@ function boundariesOf(typing, domains) {
|
|
|
7843
8016
|
});
|
|
7844
8017
|
return found;
|
|
7845
8018
|
}
|
|
8019
|
+
/** The result of a test that proves nothing. */
|
|
8020
|
+
const NO_PROOFS$1 = {
|
|
8021
|
+
yes: [],
|
|
8022
|
+
no: []
|
|
8023
|
+
};
|
|
7846
8024
|
/** The roots that can change, as a rule gives them in `PartType.roots`. */
|
|
7847
8025
|
const CHANGING = [
|
|
7848
8026
|
"self",
|
|
@@ -7865,8 +8043,12 @@ function ruleOf(machine, name) {
|
|
|
7865
8043
|
const rule = typeof evaluator === "object" && evaluator !== null || typeof evaluator === "function" ? evaluator.type : void 0;
|
|
7866
8044
|
return typeof rule === "object" && rule !== null && typeof rule.synth === "function" ? rule : void 0;
|
|
7867
8045
|
}
|
|
7868
|
-
/** This function gives the order of the parts of a document
|
|
7869
|
-
function orderOf(node, nodes) {
|
|
8046
|
+
/** This function gives the order of the parts of a document. The part `first` of the rule comes first, or else the parts that give the names of a rule `keysAt`. */
|
|
8047
|
+
function orderOf(node, nodes, first) {
|
|
8048
|
+
if (first !== void 0) {
|
|
8049
|
+
const isFirst = (index) => String(nodes[index].visit.slot[0]) === first;
|
|
8050
|
+
return [...node.children.filter(isFirst), ...node.children.filter((index) => !isFirst(index))];
|
|
8051
|
+
}
|
|
7870
8052
|
const sources = (node.visit.kind?.scope ?? []).flatMap((rule) => "keysAt" in rule.names ? [rule.names.keysAt] : []);
|
|
7871
8053
|
if (sources.length === 0 || node.children.length < 2) return node.children;
|
|
7872
8054
|
const gives = (index) => {
|
|
@@ -7890,7 +8072,7 @@ function message(error) {
|
|
|
7890
8072
|
* @param rootTypes - The types of the roots and of the names of the host.
|
|
7891
8073
|
* @returns The type of the root, the type issues in the order of the document, and the type of each document.
|
|
7892
8074
|
*/
|
|
7893
|
-
function typePass(machine, nodes, verb, expect, rootTypes = {}) {
|
|
8075
|
+
function typePass(machine, nodes, verb, expect, rootTypes = {}, hasEffect = () => false) {
|
|
7894
8076
|
if (nodes.length === 0) return {
|
|
7895
8077
|
type: "unknown",
|
|
7896
8078
|
issues: [],
|
|
@@ -7902,6 +8084,29 @@ function typePass(machine, nodes, verb, expect, rootTypes = {}) {
|
|
|
7902
8084
|
const types = new Array(nodes.length);
|
|
7903
8085
|
const needed = new Array(nodes.length);
|
|
7904
8086
|
const seen = /* @__PURE__ */ new Set();
|
|
8087
|
+
const inputs = new Array(nodes.length);
|
|
8088
|
+
let changes;
|
|
8089
|
+
const rootsChange = () => changes ??= nodes.some(hasEffect);
|
|
8090
|
+
const narrowed = (names, roots, proofs) => {
|
|
8091
|
+
let nextNames = names;
|
|
8092
|
+
let nextRoots = roots;
|
|
8093
|
+
for (const proof of proofs) {
|
|
8094
|
+
const head = proof.path[0];
|
|
8095
|
+
if (head === "names") {
|
|
8096
|
+
const name = proof.path[1];
|
|
8097
|
+
if (name === void 0) continue;
|
|
8098
|
+
if (nextNames === names) nextNames = new Map(names);
|
|
8099
|
+
nextNames.set(name, narrowType(nextNames.get(name) ?? "unknown", proof, 2, domains));
|
|
8100
|
+
} else if ((head === "self" || head === "world" || head === "event") && !rootsChange()) nextRoots = {
|
|
8101
|
+
...nextRoots,
|
|
8102
|
+
[head]: narrowType(nextRoots[head], proof, 1, domains)
|
|
8103
|
+
};
|
|
8104
|
+
}
|
|
8105
|
+
return {
|
|
8106
|
+
names: nextNames,
|
|
8107
|
+
roots: nextRoots
|
|
8108
|
+
};
|
|
8109
|
+
};
|
|
7905
8110
|
const issueAt = (index, problem) => {
|
|
7906
8111
|
const visit = nodes[index].visit;
|
|
7907
8112
|
const issue = {
|
|
@@ -7939,19 +8144,30 @@ function typePass(machine, nodes, verb, expect, rootTypes = {}) {
|
|
|
7939
8144
|
typeOf: (path) => typed.get(pathKey(path)) ?? "unknown",
|
|
7940
8145
|
nameType: (name) => names.get(name),
|
|
7941
8146
|
rootType: (root) => root === "caps" ? CAPS_TYPE : root === "self" || root === "world" || root === "event" ? roots[root] : "unknown",
|
|
7942
|
-
contract: (type, what) => contract(index, type, what)
|
|
8147
|
+
contract: (type, what) => contract(index, type, what),
|
|
8148
|
+
readOf: (path) => {
|
|
8149
|
+
const key = pathKey(path);
|
|
8150
|
+
const part = node.children.find((child) => pathKey(nodes[child].visit.slot) === key);
|
|
8151
|
+
const read = part === void 0 ? void 0 : ruleOf(machine, nodes[part].evaluator)?.reads;
|
|
8152
|
+
return read === void 0 || nodes[part].marked ? void 0 : read(inputs[part]);
|
|
8153
|
+
},
|
|
8154
|
+
provenBy: (path) => frame.proven?.get(pathKey(path)) ?? NO_PROOFS$1
|
|
7943
8155
|
};
|
|
7944
|
-
|
|
8156
|
+
inputs[index] = input;
|
|
8157
|
+
const rule = node.marked ? void 0 : ruleOf(machine, node.evaluator);
|
|
8158
|
+
const frame = {
|
|
7945
8159
|
index,
|
|
7946
|
-
rule
|
|
8160
|
+
rule,
|
|
7947
8161
|
input,
|
|
7948
|
-
order: orderOf(node, nodes),
|
|
8162
|
+
order: orderOf(node, nodes, rule?.first),
|
|
7949
8163
|
next: 0,
|
|
7950
8164
|
typed,
|
|
7951
8165
|
needs,
|
|
7952
8166
|
names,
|
|
7953
|
-
roots
|
|
8167
|
+
roots,
|
|
8168
|
+
proven: void 0
|
|
7954
8169
|
};
|
|
8170
|
+
return frame;
|
|
7955
8171
|
};
|
|
7956
8172
|
const ruleFailed = (frame, error) => {
|
|
7957
8173
|
issueAt(frame.index, { reason: `The type rule of ${nodes[frame.index].evaluator ?? "the evaluator"} fails: ${message(error)}` });
|
|
@@ -7999,12 +8215,22 @@ function typePass(machine, nodes, verb, expect, rootTypes = {}) {
|
|
|
7999
8215
|
if (contracts && node.expect !== void 0) need(contract(child, node.expect, `expected type of the ${what}`), what);
|
|
8000
8216
|
if (part.expect !== void 0) need(part.expect, part.what ?? what);
|
|
8001
8217
|
let names = frame.names;
|
|
8218
|
+
let kept = frame.roots;
|
|
8219
|
+
if (part.narrow !== void 0 && part.narrow.length > 0) {
|
|
8220
|
+
const after = narrowed(names, kept, part.narrow);
|
|
8221
|
+
names = after.names;
|
|
8222
|
+
kept = after.roots;
|
|
8223
|
+
if (part.keep === true) {
|
|
8224
|
+
frame.names = names;
|
|
8225
|
+
frame.roots = kept;
|
|
8226
|
+
}
|
|
8227
|
+
}
|
|
8002
8228
|
if (node.bound.length > 0) {
|
|
8003
8229
|
const copy = new Map(names);
|
|
8004
8230
|
for (const name of node.bound) copy.set(name, part.names !== void 0 && Object.hasOwn(part.names, name) ? part.names[name] : "unknown");
|
|
8005
8231
|
names = copy;
|
|
8006
8232
|
}
|
|
8007
|
-
let roots = frame.rule === void 0 ? UNKNOWN_ROOTS :
|
|
8233
|
+
let roots = frame.rule === void 0 ? UNKNOWN_ROOTS : kept;
|
|
8008
8234
|
const changes = part.roots;
|
|
8009
8235
|
if (typeof changes === "object" && changes !== null) {
|
|
8010
8236
|
const copy = { ...roots };
|
|
@@ -8039,7 +8265,15 @@ function typePass(machine, nodes, verb, expect, rootTypes = {}) {
|
|
|
8039
8265
|
}
|
|
8040
8266
|
types[frame.index] = type;
|
|
8041
8267
|
needed[frame.index] = frame.needs.length === 0 ? void 0 : frame.needs[frame.needs.length - 1].type;
|
|
8042
|
-
stack[stack.length - 1]
|
|
8268
|
+
const parent = stack[stack.length - 1];
|
|
8269
|
+
if (parent === void 0) continue;
|
|
8270
|
+
const key = pathKey(node.visit.slot);
|
|
8271
|
+
parent.typed.set(key, type);
|
|
8272
|
+
if (parent.rule?.usesProofs === true && frame.rule?.proves !== void 0) try {
|
|
8273
|
+
(parent.proven ??= /* @__PURE__ */ new Map()).set(key, frame.rule.proves(frame.input));
|
|
8274
|
+
} catch (error) {
|
|
8275
|
+
ruleFailed(frame, error);
|
|
8276
|
+
}
|
|
8043
8277
|
}
|
|
8044
8278
|
const places = nodes.map((node, index) => {
|
|
8045
8279
|
const want = needed[index];
|
|
@@ -9838,7 +10072,8 @@ function check(machine, doc, options = {}) {
|
|
|
9838
10072
|
function typeCheck(machine, doc, options = {}) {
|
|
9839
10073
|
const nodes = [];
|
|
9840
10074
|
const issues = checkAll(machine, doc, options, nodes);
|
|
9841
|
-
const
|
|
10075
|
+
const verb = options.verb ?? "eval";
|
|
10076
|
+
const pass = typePass(machine, nodes, verb, options.expect, options.rootTypes, (node) => declaredEffects(node.visit, verb, machine, node.evaluator).length > 0);
|
|
9842
10077
|
return {
|
|
9843
10078
|
type: pass.type,
|
|
9844
10079
|
issues: pass.issues.length === 0 ? issues : [...issues, ...pass.issues],
|
|
@@ -10843,6 +11078,13 @@ function decodeValue(machine, doc) {
|
|
|
10843
11078
|
return prepare(machine, doc, { declaredType: "lit" }).evaluate(makeContext());
|
|
10844
11079
|
}
|
|
10845
11080
|
// src/formula.ts
|
|
11081
|
+
/** The most proofs that one test keeps. A proof only adds knowledge, thus the check can leave one out. */
|
|
11082
|
+
const MAX_PROOFS = 32;
|
|
11083
|
+
/** The result of a test that proves nothing. */
|
|
11084
|
+
const NO_PROOFS = {
|
|
11085
|
+
yes: [],
|
|
11086
|
+
no: []
|
|
11087
|
+
};
|
|
10846
11088
|
/** The mark of a field that the profile leaves out of a record. */
|
|
10847
11089
|
const SKIPPED = /* @__PURE__ */ Symbol("skipped");
|
|
10848
11090
|
/** The value of a name whose binding failed. A reference to the name gives the error. */
|
|
@@ -11243,6 +11485,11 @@ function formulaTypes(profile) {
|
|
|
11243
11485
|
type: sig.to,
|
|
11244
11486
|
what: `result of a signature of ops.${name}`
|
|
11245
11487
|
});
|
|
11488
|
+
if (sig.proves !== void 0) out.push({
|
|
11489
|
+
at: [...at, "proves"],
|
|
11490
|
+
type: sig.proves,
|
|
11491
|
+
what: `proved type of a signature of ops.${name}`
|
|
11492
|
+
});
|
|
11246
11493
|
const earlier = list.slice(0, index).findIndex((other) => isSignature(other) && sameParams(other, sig));
|
|
11247
11494
|
if (earlier >= 0) out.push({
|
|
11248
11495
|
at,
|
|
@@ -11253,7 +11500,30 @@ function formulaTypes(profile) {
|
|
|
11253
11500
|
return out;
|
|
11254
11501
|
};
|
|
11255
11502
|
const logic = (word) => ({
|
|
11256
|
-
|
|
11503
|
+
usesProofs: true,
|
|
11504
|
+
part: (input, path) => {
|
|
11505
|
+
const index = Number(path[0]);
|
|
11506
|
+
const part = truthPart(input, `term ${index + 1} of ${word}`);
|
|
11507
|
+
if (index === 0) return part;
|
|
11508
|
+
const before = input.provenBy([index - 1]);
|
|
11509
|
+
return {
|
|
11510
|
+
...part,
|
|
11511
|
+
narrow: word === "and" ? before.yes : before.no,
|
|
11512
|
+
keep: true
|
|
11513
|
+
};
|
|
11514
|
+
},
|
|
11515
|
+
proves: (input) => {
|
|
11516
|
+
const each = input.paths.map((path) => input.provenBy(path));
|
|
11517
|
+
const all = (side) => each.flatMap((proven) => proven[side]).slice(0, MAX_PROOFS);
|
|
11518
|
+
const one = each.length === 1 ? each[0] : void 0;
|
|
11519
|
+
return word === "and" ? {
|
|
11520
|
+
yes: all("yes"),
|
|
11521
|
+
no: one?.no ?? []
|
|
11522
|
+
} : {
|
|
11523
|
+
yes: one?.yes ?? [],
|
|
11524
|
+
no: all("no")
|
|
11525
|
+
};
|
|
11526
|
+
},
|
|
11257
11527
|
synth: (input) => {
|
|
11258
11528
|
if (!logicValue) return "bool";
|
|
11259
11529
|
if (input.paths.length === 0) return { one: [word === "and"] };
|
|
@@ -11264,6 +11534,11 @@ function formulaTypes(profile) {
|
|
|
11264
11534
|
"formula.lit": { synth: (input) => valueType(input.doc.value) },
|
|
11265
11535
|
"formula.ref": {
|
|
11266
11536
|
read: true,
|
|
11537
|
+
reads: (input) => {
|
|
11538
|
+
const path = input.doc.value;
|
|
11539
|
+
if (input.address || !Array.isArray(path) || path.length === 0) return void 0;
|
|
11540
|
+
return path.every((step) => typeof step === "string") ? path : void 0;
|
|
11541
|
+
},
|
|
11267
11542
|
synth: (input, report) => {
|
|
11268
11543
|
const path = input.doc.value;
|
|
11269
11544
|
if (input.address || !Array.isArray(path) || path.length === 0) return "unknown";
|
|
@@ -11407,11 +11682,16 @@ function formulaTypes(profile) {
|
|
|
11407
11682
|
type: truth,
|
|
11408
11683
|
what: "truth type of the profile"
|
|
11409
11684
|
}] : [],
|
|
11685
|
+
first: "test",
|
|
11686
|
+
usesProofs: true,
|
|
11410
11687
|
part: (input, path) => {
|
|
11411
11688
|
if (path[0] === "test") return truthPart(input, "test of if");
|
|
11412
|
-
|
|
11689
|
+
const proven = input.provenBy(["test"]);
|
|
11690
|
+
const narrow = path[0] === "then" ? proven.yes : proven.no;
|
|
11691
|
+
return isUnknown(input.expected, input.machine.domains) ? { narrow } : {
|
|
11413
11692
|
expect: input.expected,
|
|
11414
|
-
what: `${String(path[0])} branch of if
|
|
11693
|
+
what: `${String(path[0])} branch of if`,
|
|
11694
|
+
narrow
|
|
11415
11695
|
};
|
|
11416
11696
|
},
|
|
11417
11697
|
synth: (input) => {
|
|
@@ -11424,6 +11704,41 @@ function formulaTypes(profile) {
|
|
|
11424
11704
|
"formula.or": logic("or"),
|
|
11425
11705
|
"formula.op": {
|
|
11426
11706
|
declared: sigsDeclared,
|
|
11707
|
+
proves: (input) => {
|
|
11708
|
+
const sigs = signatures(input)?.sigs;
|
|
11709
|
+
const sig = sigs?.length === 1 ? sigs[0] : void 0;
|
|
11710
|
+
if (sig === void 0 || sig.proves === void 0 && sig.present !== true || input.paths.length === 0) return NO_PROOFS;
|
|
11711
|
+
const path = input.readOf(input.paths[0]);
|
|
11712
|
+
if (path === void 0) return NO_PROOFS;
|
|
11713
|
+
const yes = [];
|
|
11714
|
+
const no = [];
|
|
11715
|
+
if (sig.proves !== void 0 && typeIssues(input.contract(sig.proves, `proved type of ops.${nameOf(input)}`), input.machine.domains).length === 0) {
|
|
11716
|
+
yes.push({
|
|
11717
|
+
path,
|
|
11718
|
+
fact: "is",
|
|
11719
|
+
type: sig.proves
|
|
11720
|
+
});
|
|
11721
|
+
no.push({
|
|
11722
|
+
path,
|
|
11723
|
+
fact: "not",
|
|
11724
|
+
type: sig.proves
|
|
11725
|
+
});
|
|
11726
|
+
}
|
|
11727
|
+
if (sig.present === true) {
|
|
11728
|
+
yes.push({
|
|
11729
|
+
path,
|
|
11730
|
+
fact: "present"
|
|
11731
|
+
});
|
|
11732
|
+
no.push({
|
|
11733
|
+
path,
|
|
11734
|
+
fact: "absent"
|
|
11735
|
+
});
|
|
11736
|
+
}
|
|
11737
|
+
return {
|
|
11738
|
+
yes,
|
|
11739
|
+
no
|
|
11740
|
+
};
|
|
11741
|
+
},
|
|
11427
11742
|
part: (input, path) => {
|
|
11428
11743
|
const read = signatures(input);
|
|
11429
11744
|
if (read === void 0 || read.sigs.length !== 1) return {};
|
|
@@ -13211,10 +13526,23 @@ const treeDefinition = ({ parts, context, kind, place, machine, typePath, resolv
|
|
|
13211
13526
|
}
|
|
13212
13527
|
};
|
|
13213
13528
|
/**
|
|
13529
|
+
* The definition of the standard evaluator `ref.resolve` (REF.RESOLVE, D133). It resolves the steps of the path at each
|
|
13530
|
+
* evaluation. The plan compiles the same path one time.
|
|
13531
|
+
*/
|
|
13532
|
+
const refDefinition = ({ data, resolve: walk }) => {
|
|
13533
|
+
if (!Array.isArray(data) || data.length === 0) return fail("#VALUE!", "The value of ref is a path: a list that starts with a root, for example [\"names\", \"a\"].");
|
|
13534
|
+
const found = walk(data);
|
|
13535
|
+
return found.ok ? ok(valueOfThing(found.thing)) : fail(found.code, found.reason);
|
|
13536
|
+
};
|
|
13537
|
+
/**
|
|
13214
13538
|
* The definitions of the reference for the evaluators of the standard machine that need their own reading. The
|
|
13215
|
-
* reference uses them for the standard evaluators, also without the option `definitions`. It holds `tree.node
|
|
13539
|
+
* reference uses them for the standard evaluators, also without the option `definitions`. It holds `tree.node` and
|
|
13540
|
+
* `ref.resolve`. A host that wraps `ref.resolve` wraps this function, because the standard evaluator is compiled.
|
|
13216
13541
|
*/
|
|
13217
|
-
const STD_DEFINITIONS = { evaluators: {
|
|
13542
|
+
const STD_DEFINITIONS = { evaluators: {
|
|
13543
|
+
"tree.node": treeDefinition,
|
|
13544
|
+
"ref.resolve": refDefinition
|
|
13545
|
+
} };
|
|
13218
13546
|
const CLOSURE$1 = /* @__PURE__ */ Symbol.for("coeval.closure");
|
|
13219
13547
|
/** This function sets an own field, also the field `__proto__` (SHAPE.PROTO). */
|
|
13220
13548
|
function setField$1(target, key, value) {
|