@mark1russell7/coeval 0.7.0 → 0.8.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 +31 -0
- package/build/index.d.ts +399 -100
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1818 -172
- package/build/index.js.map +1 -1
- package/package.json +2 -2
- package/src/agree.ts +26 -5
- package/src/codec.ts +185 -0
- package/src/conform.ts +2 -3
- package/src/doc.ts +2 -2
- package/src/evaluate.ts +281 -9
- package/src/formula.ts +19 -8
- package/src/index.ts +36 -2
- package/src/parts.ts +70 -1
- package/src/reference.ts +143 -18
- package/src/static.ts +54 -74
- package/src/stdlib.ts +220 -18
- package/src/syntax.ts +846 -0
- package/src/types.ts +28 -1
- package/src/world.ts +50 -0
package/build/index.js
CHANGED
|
@@ -357,6 +357,17 @@ function describe(value) {
|
|
|
357
357
|
//#endregion
|
|
358
358
|
//#region src/world.ts
|
|
359
359
|
/**
|
|
360
|
+
* This function gives the type of a bare equation of a kind (D42). It is the type `of` of the shape of its parts, or
|
|
361
|
+
* else the type `of` of the shape `rest` of a record. The plan, the reference and `check` read it with this one function.
|
|
362
|
+
*
|
|
363
|
+
* @param kind - The kind.
|
|
364
|
+
* @returns The type, or `undefined` when the shape declares none.
|
|
365
|
+
*/
|
|
366
|
+
function equationType(kind) {
|
|
367
|
+
const parts = kind.parts;
|
|
368
|
+
return parts?.of ?? parts?.rest?.of;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
360
371
|
* This function tells if an entry of the world is a kind.
|
|
361
372
|
*
|
|
362
373
|
* @param entry - The entry.
|
|
@@ -759,7 +770,7 @@ function fits(found, expected, domains) {
|
|
|
759
770
|
return fitsAt(found, expected, domains, 0);
|
|
760
771
|
}
|
|
761
772
|
/** This function sets an own field of a new record. Only the key `__proto__` needs `defineProperty`. */
|
|
762
|
-
function setOwn$
|
|
773
|
+
function setOwn$5(target, key, value) {
|
|
763
774
|
if (key === "__proto__") Object.defineProperty(target, key, {
|
|
764
775
|
value,
|
|
765
776
|
enumerable: true,
|
|
@@ -778,10 +789,10 @@ function joinRecords(a, b, domains, depth) {
|
|
|
778
789
|
const inA = Object.hasOwn(a.fields, key);
|
|
779
790
|
const inB = Object.hasOwn(b.fields, key);
|
|
780
791
|
if (inA && inB) {
|
|
781
|
-
setOwn$
|
|
792
|
+
setOwn$5(fields, key, joinAt(a.fields[key], b.fields[key], domains, depth));
|
|
782
793
|
if (a.optional.has(key) || b.optional.has(key)) optional.push(key);
|
|
783
794
|
} else if (closed) {
|
|
784
|
-
setOwn$
|
|
795
|
+
setOwn$5(fields, key, inA ? a.fields[key] : b.fields[key]);
|
|
785
796
|
optional.push(key);
|
|
786
797
|
}
|
|
787
798
|
}
|
|
@@ -1185,7 +1196,7 @@ function keyOf(path) {
|
|
|
1185
1196
|
return JSON.stringify(path.map(String));
|
|
1186
1197
|
}
|
|
1187
1198
|
/** This function sets an own data property. An assignment to the key `__proto__` changes the prototype. */
|
|
1188
|
-
function setOwn$
|
|
1199
|
+
function setOwn$4(target, key, value) {
|
|
1189
1200
|
if (key === "__proto__") Object.defineProperty(target, key, {
|
|
1190
1201
|
value,
|
|
1191
1202
|
enumerable: true,
|
|
@@ -1202,7 +1213,7 @@ function defined(value) {
|
|
|
1202
1213
|
if (Array.isArray(value)) return value.map(defined);
|
|
1203
1214
|
if (!isRecord(value)) return value;
|
|
1204
1215
|
const out = {};
|
|
1205
|
-
for (const key of Object.keys(value)) if (value[key] !== void 0) setOwn$
|
|
1216
|
+
for (const key of Object.keys(value)) if (value[key] !== void 0) setOwn$4(out, key, defined(value[key]));
|
|
1206
1217
|
return out;
|
|
1207
1218
|
}
|
|
1208
1219
|
/** Two values are the same when they are equal by `docEquals`. A function and a host value are equal only to themselves. */
|
|
@@ -1247,7 +1258,7 @@ function extendDomain(name, more) {
|
|
|
1247
1258
|
...Object.keys(steps).length === 0 ? {} : { steps }
|
|
1248
1259
|
};
|
|
1249
1260
|
const domains = {};
|
|
1250
|
-
setOwn$
|
|
1261
|
+
setOwn$4(domains, name, domain);
|
|
1251
1262
|
return {
|
|
1252
1263
|
domains,
|
|
1253
1264
|
replaces: [["domains", name]]
|
|
@@ -1297,10 +1308,10 @@ function mergeLeaf(m, table, key, later, path, part, replaces, equal, reason) {
|
|
|
1297
1308
|
if (later === void 0) return;
|
|
1298
1309
|
const earlier = own$2(table, key);
|
|
1299
1310
|
if (earlier === void 0) {
|
|
1300
|
-
setOwn$
|
|
1311
|
+
setOwn$4(table, key, later);
|
|
1301
1312
|
m.give(path, part);
|
|
1302
1313
|
} else if (replaces.has(keyOf(path))) {
|
|
1303
|
-
setOwn$
|
|
1314
|
+
setOwn$4(table, key, later);
|
|
1304
1315
|
m.replace(path, part);
|
|
1305
1316
|
} else if (!equal(earlier, later)) m.clash(path, part, reason);
|
|
1306
1317
|
}
|
|
@@ -1321,9 +1332,9 @@ function mergeEntries(m, into, from, path, part, replaces) {
|
|
|
1321
1332
|
const at = [...path, key];
|
|
1322
1333
|
const earlier = own$2(into, key);
|
|
1323
1334
|
if (earlier === void 0) {
|
|
1324
|
-
setOwn$
|
|
1335
|
+
setOwn$4(into, key, later);
|
|
1325
1336
|
m.give(at, part);
|
|
1326
|
-
} else setOwn$
|
|
1337
|
+
} else setOwn$4(into, key, mergeEntry(m, earlier, later, at, part, replaces));
|
|
1327
1338
|
}
|
|
1328
1339
|
}
|
|
1329
1340
|
/** This function merges two entries at one path. Two namespaces merge key by key, and their fallbacks merge too. */
|
|
@@ -1476,8 +1487,8 @@ function mergeCode(m, draft, part, index, replaces) {
|
|
|
1476
1487
|
const runnerPath = ["runners", name];
|
|
1477
1488
|
if (replaces.has(keyOf(evaluatorPath)) || replaces.has(keyOf(runnerPath))) {
|
|
1478
1489
|
if (evaluator === void 0) delete draft.evaluators[name];
|
|
1479
|
-
else setOwn$
|
|
1480
|
-
if (runner !== void 0) setOwn$
|
|
1490
|
+
else setOwn$4(draft.evaluators, name, evaluator);
|
|
1491
|
+
if (runner !== void 0) setOwn$4(draft.runners ??= {}, name, runner);
|
|
1481
1492
|
else if (draft.runners !== void 0) delete draft.runners[name];
|
|
1482
1493
|
m.replace(evaluatorPath, index);
|
|
1483
1494
|
m.replace(runnerPath, index);
|
|
@@ -1600,7 +1611,7 @@ function removeEntry(entries, path) {
|
|
|
1600
1611
|
}
|
|
1601
1612
|
const out = { ...entries };
|
|
1602
1613
|
if (next === void 0) delete out[key];
|
|
1603
|
-
else setOwn$
|
|
1614
|
+
else setOwn$4(out, key, next);
|
|
1604
1615
|
return out;
|
|
1605
1616
|
}
|
|
1606
1617
|
/**
|
|
@@ -1694,7 +1705,7 @@ const RUNGS = [
|
|
|
1694
1705
|
"kind",
|
|
1695
1706
|
"default"
|
|
1696
1707
|
];
|
|
1697
|
-
function entryOf(verbs, verb) {
|
|
1708
|
+
function entryOf$1(verbs, verb) {
|
|
1698
1709
|
return verbs !== void 0 && Object.hasOwn(verbs, verb) ? verbs[verb] : void 0;
|
|
1699
1710
|
}
|
|
1700
1711
|
/**
|
|
@@ -1735,18 +1746,18 @@ const EACH_NAME = () => true;
|
|
|
1735
1746
|
* @returns The evaluator name and its rung, or `undefined` when no rung answers.
|
|
1736
1747
|
*/
|
|
1737
1748
|
function answerOf(world, own, kind, verb, has = EACH_NAME) {
|
|
1738
|
-
const ownName = entryOf(own, verb);
|
|
1749
|
+
const ownName = entryOf$1(own, verb);
|
|
1739
1750
|
if (ownName !== void 0 && has(ownName)) return {
|
|
1740
1751
|
name: ownName,
|
|
1741
1752
|
rung: "own"
|
|
1742
1753
|
};
|
|
1743
1754
|
if (kind === null) return void 0;
|
|
1744
|
-
const kindName = kind === void 0 ? void 0 : entryOf(kind.verbs, verb);
|
|
1755
|
+
const kindName = kind === void 0 ? void 0 : entryOf$1(kind.verbs, verb);
|
|
1745
1756
|
if (kindName !== void 0 && has(kindName)) return {
|
|
1746
1757
|
name: kindName,
|
|
1747
1758
|
rung: "kind"
|
|
1748
1759
|
};
|
|
1749
|
-
const fallback = entryOf(world.defaults, verb);
|
|
1760
|
+
const fallback = entryOf$1(world.defaults, verb);
|
|
1750
1761
|
return fallback !== void 0 && has(fallback) ? {
|
|
1751
1762
|
name: fallback,
|
|
1752
1763
|
rung: "default"
|
|
@@ -1768,7 +1779,7 @@ function answerOf(world, own, kind, verb, has = EACH_NAME) {
|
|
|
1768
1779
|
*/
|
|
1769
1780
|
function ask(world, doc, verb, options = {}) {
|
|
1770
1781
|
const steps = [];
|
|
1771
|
-
const own = entryOf(doc.own, verb);
|
|
1782
|
+
const own = entryOf$1(doc.own, verb);
|
|
1772
1783
|
if (own !== void 0) {
|
|
1773
1784
|
steps.push({
|
|
1774
1785
|
rung: "own",
|
|
@@ -1808,7 +1819,7 @@ function ask(world, doc, verb, options = {}) {
|
|
|
1808
1819
|
steps
|
|
1809
1820
|
};
|
|
1810
1821
|
}
|
|
1811
|
-
const entry = entryOf(kind.verbs, verb);
|
|
1822
|
+
const entry = entryOf$1(kind.verbs, verb);
|
|
1812
1823
|
if (entry !== void 0) {
|
|
1813
1824
|
steps.push({
|
|
1814
1825
|
rung: "kind",
|
|
@@ -1831,7 +1842,7 @@ function ask(world, doc, verb, options = {}) {
|
|
|
1831
1842
|
source
|
|
1832
1843
|
});
|
|
1833
1844
|
}
|
|
1834
|
-
const fallback = entryOf(world.defaults, verb);
|
|
1845
|
+
const fallback = entryOf$1(world.defaults, verb);
|
|
1835
1846
|
if (fallback !== void 0) {
|
|
1836
1847
|
steps.push({
|
|
1837
1848
|
rung: "default",
|
|
@@ -2412,7 +2423,7 @@ var Bad = class {
|
|
|
2412
2423
|
}
|
|
2413
2424
|
};
|
|
2414
2425
|
/** This function sets an own data property. Only the key `__proto__` needs `defineProperty`. */
|
|
2415
|
-
function setOwn$
|
|
2426
|
+
function setOwn$3(target, key, value) {
|
|
2416
2427
|
if (key === "__proto__") Object.defineProperty(target, key, {
|
|
2417
2428
|
value,
|
|
2418
2429
|
enumerable: true,
|
|
@@ -2584,7 +2595,7 @@ function splitParts(shape, value) {
|
|
|
2584
2595
|
const out = {};
|
|
2585
2596
|
for (const [key, child] of node.fields) {
|
|
2586
2597
|
const kept = strip(child);
|
|
2587
|
-
if (kept !== void 0) setOwn$
|
|
2598
|
+
if (kept !== void 0) setOwn$3(out, key, kept);
|
|
2588
2599
|
}
|
|
2589
2600
|
return out;
|
|
2590
2601
|
};
|
|
@@ -2593,7 +2604,7 @@ function splitParts(shape, value) {
|
|
|
2593
2604
|
if (node.t === "slot") return handles[node.index];
|
|
2594
2605
|
if (node.t === "list") return node.items.map((child) => fillWith(child, handles));
|
|
2595
2606
|
const out = {};
|
|
2596
|
-
for (const [key, child] of node.fields) setOwn$
|
|
2607
|
+
for (const [key, child] of node.fields) setOwn$3(out, key, fillWith(child, handles));
|
|
2597
2608
|
return out;
|
|
2598
2609
|
};
|
|
2599
2610
|
return {
|
|
@@ -2667,6 +2678,75 @@ function pathMatches(path, pattern) {
|
|
|
2667
2678
|
}
|
|
2668
2679
|
return true;
|
|
2669
2680
|
}
|
|
2681
|
+
function valueAt(value, path) {
|
|
2682
|
+
let here = value;
|
|
2683
|
+
for (const key of path) if (Array.isArray(here)) here = here[Number(key)];
|
|
2684
|
+
else if (isRecord(here) && Object.hasOwn(here, String(key))) here = here[String(key)];
|
|
2685
|
+
else return void 0;
|
|
2686
|
+
return here;
|
|
2687
|
+
}
|
|
2688
|
+
function headerName(item) {
|
|
2689
|
+
return isEnvelope(item) && typeof item.name === "string" ? item.name : void 0;
|
|
2690
|
+
}
|
|
2691
|
+
function namesOf(source, value, covered) {
|
|
2692
|
+
if ("fixed" in source) return source.fixed;
|
|
2693
|
+
if ("keysAt" in source) {
|
|
2694
|
+
const at = valueAt(value, source.keysAt);
|
|
2695
|
+
return isRecord(at) ? Object.keys(at) : [];
|
|
2696
|
+
}
|
|
2697
|
+
if ("listAt" in source) {
|
|
2698
|
+
const at = valueAt(value, source.listAt);
|
|
2699
|
+
return Array.isArray(at) ? at.filter((item) => typeof item === "string") : [];
|
|
2700
|
+
}
|
|
2701
|
+
if ("textAt" in source) {
|
|
2702
|
+
const at = valueAt(value, source.textAt);
|
|
2703
|
+
if (typeof at === "string") return [at];
|
|
2704
|
+
return source.otherwise === void 0 ? [] : [source.otherwise];
|
|
2705
|
+
}
|
|
2706
|
+
return covered.map((slot) => headerName(slot.item)).filter((name) => name !== void 0);
|
|
2707
|
+
}
|
|
2708
|
+
/**
|
|
2709
|
+
* This function gives the names that the scope rules of a kind give each part, and the names that only later parts
|
|
2710
|
+
* see. `check`, `conforms` and the plan read the scope rules with it.
|
|
2711
|
+
*
|
|
2712
|
+
* @param rules - The scope rules of the kind.
|
|
2713
|
+
* @param value - The value of the document.
|
|
2714
|
+
* @param slots - The parts of the value, in order.
|
|
2715
|
+
* @returns The names that each part sees, and the names that only the later parts see, by the index of the part.
|
|
2716
|
+
*/
|
|
2717
|
+
function scopesOf(rules, value, slots) {
|
|
2718
|
+
const sees = slots.map(() => []);
|
|
2719
|
+
const later = slots.map(() => []);
|
|
2720
|
+
for (const rule of rules ?? []) {
|
|
2721
|
+
const covered = slots.map((slot, index) => ({
|
|
2722
|
+
slot,
|
|
2723
|
+
index
|
|
2724
|
+
})).filter(({ slot }) => rule.in.some((pattern) => pathMatches(slot.path, pattern)));
|
|
2725
|
+
if (rule.sequential !== true) {
|
|
2726
|
+
const names = namesOf(rule.names, value, covered.map(({ slot }) => slot));
|
|
2727
|
+
for (const { index } of covered) sees[index]?.push(...names);
|
|
2728
|
+
continue;
|
|
2729
|
+
}
|
|
2730
|
+
if ("headers" in rule.names) {
|
|
2731
|
+
covered.forEach(({ index }, position) => {
|
|
2732
|
+
const before = covered.slice(0, position).map(({ slot }) => headerName(slot.item)).filter((name) => name !== void 0);
|
|
2733
|
+
const after = covered.slice(position).map(({ slot }) => headerName(slot.item)).filter((name) => name !== void 0);
|
|
2734
|
+
sees[index]?.push(...before);
|
|
2735
|
+
later[index]?.push(...after);
|
|
2736
|
+
});
|
|
2737
|
+
continue;
|
|
2738
|
+
}
|
|
2739
|
+
const names = namesOf(rule.names, value, covered.map(({ slot }) => slot));
|
|
2740
|
+
covered.forEach(({ index }, position) => {
|
|
2741
|
+
if (position > 0) sees[index]?.push(...names);
|
|
2742
|
+
else later[index]?.push(...names);
|
|
2743
|
+
});
|
|
2744
|
+
}
|
|
2745
|
+
return {
|
|
2746
|
+
sees,
|
|
2747
|
+
later
|
|
2748
|
+
};
|
|
2749
|
+
}
|
|
2670
2750
|
//#endregion
|
|
2671
2751
|
//#region src/evaluate.ts
|
|
2672
2752
|
/**
|
|
@@ -2727,7 +2807,7 @@ function narrow(parent, next) {
|
|
|
2727
2807
|
};
|
|
2728
2808
|
}
|
|
2729
2809
|
/** No capabilities. One list serves each masked part. */
|
|
2730
|
-
const NO_CAPS$
|
|
2810
|
+
const NO_CAPS$2 = /* @__PURE__ */ Object.freeze([]);
|
|
2731
2811
|
/** No capability rules. One list serves each part that no rule narrows. */
|
|
2732
2812
|
const NO_RULES = /* @__PURE__ */ Object.freeze([]);
|
|
2733
2813
|
/**
|
|
@@ -2755,7 +2835,7 @@ function rulesFor(rules, path) {
|
|
|
2755
2835
|
function capsUnder(rules, caps) {
|
|
2756
2836
|
let out = caps;
|
|
2757
2837
|
for (const rule of rules) {
|
|
2758
|
-
if (rule.rule === "mask") return NO_CAPS$
|
|
2838
|
+
if (rule.rule === "mask") return NO_CAPS$2;
|
|
2759
2839
|
if (rule.rule === "only") out = intersectCaps(out, rule.caps);
|
|
2760
2840
|
}
|
|
2761
2841
|
return out;
|
|
@@ -2786,7 +2866,7 @@ const GRANT = /* @__PURE__ */ Symbol("coeval.grant");
|
|
|
2786
2866
|
function granted(ctx) {
|
|
2787
2867
|
const grant = ctx[GRANT];
|
|
2788
2868
|
if (grant === ctx.caps) return ctx;
|
|
2789
|
-
const caps = grant === void 0 ? NO_CAPS$
|
|
2869
|
+
const caps = grant === void 0 ? NO_CAPS$2 : ctx.caps.every((cap) => permits(grant, cap)) ? ctx.caps : ctx.caps.filter((cap) => permits(grant, cap));
|
|
2790
2870
|
return {
|
|
2791
2871
|
...ctx,
|
|
2792
2872
|
caps,
|
|
@@ -2952,6 +3032,10 @@ var Session = class {
|
|
|
2952
3032
|
get effects() {
|
|
2953
3033
|
return this.list ??= [];
|
|
2954
3034
|
}
|
|
3035
|
+
/** `true` when the evaluation records its reads. Then folded code evaluates again, thus each read goes in the list (PLAN.FOLD). */
|
|
3036
|
+
get recording() {
|
|
3037
|
+
return this.reading !== void 0;
|
|
3038
|
+
}
|
|
2955
3039
|
resolve(ctx, path, options) {
|
|
2956
3040
|
return this.read(ctx, path, void 0, options);
|
|
2957
3041
|
}
|
|
@@ -3416,6 +3500,7 @@ function compileNode(c, value, place, declared, depth, address = false) {
|
|
|
3416
3500
|
let meta = meta0;
|
|
3417
3501
|
let name;
|
|
3418
3502
|
let unknown;
|
|
3503
|
+
let quick;
|
|
3419
3504
|
if (c.traced) {
|
|
3420
3505
|
const answer = ask(world, envelope ?? {}, c.verb, declared === void 0 ? {} : { declaredType: declared });
|
|
3421
3506
|
meta = {
|
|
@@ -3426,7 +3511,7 @@ function compileNode(c, value, place, declared, depth, address = false) {
|
|
|
3426
3511
|
name = answer.evaluator;
|
|
3427
3512
|
unknown = answer.error === "unknown-kind";
|
|
3428
3513
|
} else {
|
|
3429
|
-
|
|
3514
|
+
quick = answerOf(world, envelope?.own, kind ?? null, c.verb);
|
|
3430
3515
|
name = quick?.name;
|
|
3431
3516
|
unknown = quick === void 0 && kind === void 0;
|
|
3432
3517
|
}
|
|
@@ -3437,9 +3522,11 @@ function compileNode(c, value, place, declared, depth, address = false) {
|
|
|
3437
3522
|
const evaluator = name !== void 0 && Object.hasOwn(c.machine.evaluators, name) ? c.machine.evaluators[name] : void 0;
|
|
3438
3523
|
if (name === void 0 || runner === void 0 && evaluator === void 0) return failing(c, meta, place, "#NOEVAL", `No rung gives an evaluator for ${kindText}.`);
|
|
3439
3524
|
if (!split.ok) return failing(c, meta, place, "#VALUE!", split.reason);
|
|
3525
|
+
const frees = c.fold ? [] : void 0;
|
|
3440
3526
|
const parts = split.parts.map((slot) => {
|
|
3441
3527
|
const item = slot.item;
|
|
3442
3528
|
const own = compileNode(c, item, place.concat(slot.path), slot.of, depth + 1, slot.address === true);
|
|
3529
|
+
frees?.push(own.free);
|
|
3443
3530
|
const rules = rulesFor(kind?.caps, slot.path);
|
|
3444
3531
|
const compiled = rules.length === 0 ? own : gated(rules, own);
|
|
3445
3532
|
const header = typeof item?.name === "string" && isEnvelope(item) ? item.name : void 0;
|
|
@@ -3457,9 +3544,10 @@ function compileNode(c, value, place, declared, depth, address = false) {
|
|
|
3457
3544
|
run
|
|
3458
3545
|
};
|
|
3459
3546
|
});
|
|
3547
|
+
const typePath = read.typePath ?? [];
|
|
3460
3548
|
const input = {
|
|
3461
3549
|
kind: kindText,
|
|
3462
|
-
typePath
|
|
3550
|
+
typePath,
|
|
3463
3551
|
doc,
|
|
3464
3552
|
value: split.parts.length === 0 ? doc.value : split.fill(parts),
|
|
3465
3553
|
data: split.data,
|
|
@@ -3485,7 +3573,238 @@ function compileNode(c, value, place, declared, depth, address = false) {
|
|
|
3485
3573
|
} catch (error) {
|
|
3486
3574
|
return failingWith(c, meta, placed(thrown(error, `the evaluator ${name}`), place));
|
|
3487
3575
|
}
|
|
3488
|
-
|
|
3576
|
+
const done = finish$1(c, meta, placedCode(inner, place, name), void 0);
|
|
3577
|
+
if (frees === void 0 || kind === void 0 || !pureCode(c, envelope, kind, typePath, name, quick)) return done;
|
|
3578
|
+
const free = freeOf(doc, kind, split.parts, frees);
|
|
3579
|
+
if (free === void 0) return done;
|
|
3580
|
+
return free.length === 0 ? folded(c, done) : {
|
|
3581
|
+
code: done.code,
|
|
3582
|
+
run: done.run,
|
|
3583
|
+
free
|
|
3584
|
+
};
|
|
3585
|
+
}
|
|
3586
|
+
/** The free names of a closed part. */
|
|
3587
|
+
const CLOSED = /* @__PURE__ */ Object.freeze([]);
|
|
3588
|
+
/**
|
|
3589
|
+
* This function tells if the code of a document is pure, by the facts of D24 (FACTS.PURE). It reads the facts as
|
|
3590
|
+
* `factsOf` reads them. A kind with a run side, with declared effects or with volatile code is not pure here.
|
|
3591
|
+
*/
|
|
3592
|
+
function pureCode(c, envelope, kind, typePath, name, quick) {
|
|
3593
|
+
if (kind.side === "run" || kind.effects !== void 0 && kind.effects.length > 0) return false;
|
|
3594
|
+
let answer;
|
|
3595
|
+
try {
|
|
3596
|
+
answer = c.machine.facts?.(typePath, name);
|
|
3597
|
+
} catch {
|
|
3598
|
+
return false;
|
|
3599
|
+
}
|
|
3600
|
+
const described = c.verb === "eval" && (envelope === void 0 || quick?.rung !== "own" || name === answerOf(c.machine.world, void 0, kind, c.verb)?.name);
|
|
3601
|
+
const pure = described ? answer?.pure ?? kind.pure : answer?.pure;
|
|
3602
|
+
const volatile = described ? answer?.volatile ?? kind.volatile : answer?.volatile;
|
|
3603
|
+
return pure === true && volatile !== true;
|
|
3604
|
+
}
|
|
3605
|
+
/**
|
|
3606
|
+
* This function gives the names that a document reads and does not bind, or `undefined` when the document can read
|
|
3607
|
+
* other roots. A reference reads the name of its path. Each other document reads the free names of its parts, less
|
|
3608
|
+
* the names that its scope rules give each part (STATIC.REFS).
|
|
3609
|
+
*/
|
|
3610
|
+
function freeOf(doc, kind, slots, frees) {
|
|
3611
|
+
if (isRef(doc)) {
|
|
3612
|
+
const path = doc.value;
|
|
3613
|
+
if (path.length < 2 || path[0] !== "names" || !path.every((step, index) => index === 0 || isKey(step))) return void 0;
|
|
3614
|
+
return [String(path[1])];
|
|
3615
|
+
}
|
|
3616
|
+
let sees;
|
|
3617
|
+
let out;
|
|
3618
|
+
for (let index = 0; index < frees.length; index += 1) {
|
|
3619
|
+
const free = frees[index];
|
|
3620
|
+
if (free === void 0) return void 0;
|
|
3621
|
+
if (free.length === 0) continue;
|
|
3622
|
+
if (sees === void 0) sees = kind.scope === void 0 || kind.scope.length === 0 ? [] : scopesOf(kind.scope, doc.value, slots).sees;
|
|
3623
|
+
const bound = sees[index] ?? CLOSED;
|
|
3624
|
+
for (const name of free) {
|
|
3625
|
+
if (bound.includes(name)) continue;
|
|
3626
|
+
out ??= [];
|
|
3627
|
+
if (!out.includes(name)) out.push(name);
|
|
3628
|
+
}
|
|
3629
|
+
}
|
|
3630
|
+
return out ?? CLOSED;
|
|
3631
|
+
}
|
|
3632
|
+
/** This class is the runtime of a fold. It reads only the names that the folded part binds, and it causes no effect. */
|
|
3633
|
+
var Probe = class {
|
|
3634
|
+
machine;
|
|
3635
|
+
running = false;
|
|
3636
|
+
/** `true` after the part asked for something that a fold cannot give. */
|
|
3637
|
+
refused = false;
|
|
3638
|
+
/** The count of the names that the part read. */
|
|
3639
|
+
reads = 0;
|
|
3640
|
+
/**
|
|
3641
|
+
* The context of the folded part. Its roots `self`, `event` and `names` are traps: a direct read of a field
|
|
3642
|
+
* refuses the fold, as a resolution of these roots does. A copy of the record of the names reads no field.
|
|
3643
|
+
*/
|
|
3644
|
+
root;
|
|
3645
|
+
constructor(machine) {
|
|
3646
|
+
this.machine = machine;
|
|
3647
|
+
const refuse = () => {
|
|
3648
|
+
this.refused = true;
|
|
3649
|
+
};
|
|
3650
|
+
const root = {
|
|
3651
|
+
self: trap(refuse, false),
|
|
3652
|
+
event: trap(refuse, false),
|
|
3653
|
+
names: trap(refuse, true),
|
|
3654
|
+
world: worldThing(machine.world),
|
|
3655
|
+
caps: NO_CAPS$2,
|
|
3656
|
+
[GRANT]: NO_CAPS$2
|
|
3657
|
+
};
|
|
3658
|
+
this.root = root;
|
|
3659
|
+
}
|
|
3660
|
+
resolve(ctx, path) {
|
|
3661
|
+
const name = path[1];
|
|
3662
|
+
if (path[0] === "names" && isKey(name) && Object.hasOwn(ctx.names, String(name))) {
|
|
3663
|
+
this.reads += 1;
|
|
3664
|
+
const found = resolve(this.machine, ctx, path, WITH_HOPS);
|
|
3665
|
+
if (found.hops.some((hop) => hop.by === "collapse" || isRef(hop.thing))) this.refused = true;
|
|
3666
|
+
return found;
|
|
3667
|
+
}
|
|
3668
|
+
this.refused = true;
|
|
3669
|
+
return {
|
|
3670
|
+
ok: false,
|
|
3671
|
+
code: "#REF!",
|
|
3672
|
+
reason: "A folded part reads only the names that it binds.",
|
|
3673
|
+
hops: []
|
|
3674
|
+
};
|
|
3675
|
+
}
|
|
3676
|
+
effect() {
|
|
3677
|
+
this.refused = true;
|
|
3678
|
+
return fail("#CAP!", "A folded part causes no effect.");
|
|
3679
|
+
}
|
|
3680
|
+
*runEffect() {
|
|
3681
|
+
return this.effect();
|
|
3682
|
+
}
|
|
3683
|
+
*all() {
|
|
3684
|
+
this.refused = true;
|
|
3685
|
+
return [];
|
|
3686
|
+
}
|
|
3687
|
+
keepsContext() {
|
|
3688
|
+
this.refused = true;
|
|
3689
|
+
}
|
|
3690
|
+
};
|
|
3691
|
+
/**
|
|
3692
|
+
* This function makes a trap for a root of the context of a fold. Each read of a field calls `refuse`. A trap of the
|
|
3693
|
+
* names gives no keys, thus a copy of the names reads no field.
|
|
3694
|
+
*/
|
|
3695
|
+
function trap(refuse, copies) {
|
|
3696
|
+
const no = () => {
|
|
3697
|
+
refuse();
|
|
3698
|
+
};
|
|
3699
|
+
return new Proxy({}, {
|
|
3700
|
+
get: no,
|
|
3701
|
+
getOwnPropertyDescriptor: no,
|
|
3702
|
+
has: () => (refuse(), false),
|
|
3703
|
+
ownKeys: () => {
|
|
3704
|
+
if (!copies) refuse();
|
|
3705
|
+
return [];
|
|
3706
|
+
},
|
|
3707
|
+
getPrototypeOf: () => (refuse(), null),
|
|
3708
|
+
set: () => (refuse(), false),
|
|
3709
|
+
defineProperty: () => (refuse(), false),
|
|
3710
|
+
deleteProperty: () => (refuse(), false)
|
|
3711
|
+
});
|
|
3712
|
+
}
|
|
3713
|
+
/**
|
|
3714
|
+
* This function folds the code of a closed pure part. The part evaluates one time with the probe. Its result becomes
|
|
3715
|
+
* one deeply frozen copy, and the code gives that copy at each evaluation (PLAN.SHARED). A fold that the probe cancels
|
|
3716
|
+
* gives the code without free names, thus no parent tries the fold again.
|
|
3717
|
+
*/
|
|
3718
|
+
function folded(c, done) {
|
|
3719
|
+
const probe = c.probe ??= new Probe(c.machine);
|
|
3720
|
+
probe.refused = false;
|
|
3721
|
+
probe.reads = 0;
|
|
3722
|
+
let constant;
|
|
3723
|
+
try {
|
|
3724
|
+
const result = done.code(probe.root, probe);
|
|
3725
|
+
if (!probe.refused && (result.ok || result.error.code !== "#DEPTH!")) constant = sharedCopy(result);
|
|
3726
|
+
} catch {
|
|
3727
|
+
constant = void 0;
|
|
3728
|
+
}
|
|
3729
|
+
if (constant === void 0) return {
|
|
3730
|
+
code: done.code,
|
|
3731
|
+
run: done.run
|
|
3732
|
+
};
|
|
3733
|
+
const value = constant;
|
|
3734
|
+
if (probe.reads === 0) return {
|
|
3735
|
+
code: () => value,
|
|
3736
|
+
run: void 0,
|
|
3737
|
+
free: CLOSED
|
|
3738
|
+
};
|
|
3739
|
+
const original = done.code;
|
|
3740
|
+
return {
|
|
3741
|
+
code: (ctx, rt) => rt instanceof Session && !rt.traced && !rt.recording ? value : original(ctx, rt),
|
|
3742
|
+
run: void 0,
|
|
3743
|
+
free: CLOSED
|
|
3744
|
+
};
|
|
3745
|
+
}
|
|
3746
|
+
/** The objects that a fold made. Each one is deeply frozen, thus a later fold uses it again and does not copy it. */
|
|
3747
|
+
const SHARED = /* @__PURE__ */ new WeakSet();
|
|
3748
|
+
/** The mark of a value that is not plain data. */
|
|
3749
|
+
const NOT_PLAIN = /* @__PURE__ */ Symbol("not plain");
|
|
3750
|
+
/**
|
|
3751
|
+
* This function gives a deeply frozen copy of plain data, or `undefined` for other values. Plain data is a primitive or
|
|
3752
|
+
* an array without holes and extra keys. It is also a record of enumerable data fields with the prototype of an object
|
|
3753
|
+
* or no prototype. The copy keeps cycles and shared objects. The objects of the host do not change.
|
|
3754
|
+
*/
|
|
3755
|
+
function sharedCopy(value) {
|
|
3756
|
+
try {
|
|
3757
|
+
const out = copyPlain(value, /* @__PURE__ */ new Map());
|
|
3758
|
+
return out === NOT_PLAIN ? void 0 : out;
|
|
3759
|
+
} catch {
|
|
3760
|
+
return;
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3763
|
+
/** This function copies one value for `sharedCopy`. The map holds the copy of each object that the copy saw. */
|
|
3764
|
+
function copyPlain(value, memo) {
|
|
3765
|
+
if (typeof value === "function") return NOT_PLAIN;
|
|
3766
|
+
if (typeof value !== "object" || value === null) return value;
|
|
3767
|
+
if (SHARED.has(value)) return value;
|
|
3768
|
+
const seen = memo.get(value);
|
|
3769
|
+
if (seen !== void 0) return seen;
|
|
3770
|
+
const proto = Object.getPrototypeOf(value);
|
|
3771
|
+
const keys = Reflect.ownKeys(value);
|
|
3772
|
+
let out;
|
|
3773
|
+
if (Array.isArray(value)) {
|
|
3774
|
+
if (proto !== Array.prototype || keys.length !== value.length + 1) return NOT_PLAIN;
|
|
3775
|
+
const list = [];
|
|
3776
|
+
memo.set(value, list);
|
|
3777
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
3778
|
+
const field = Object.getOwnPropertyDescriptor(value, index);
|
|
3779
|
+
if (field === void 0 || !("value" in field) || field.enumerable !== true) return NOT_PLAIN;
|
|
3780
|
+
const item = copyPlain(field.value, memo);
|
|
3781
|
+
if (item === NOT_PLAIN) return NOT_PLAIN;
|
|
3782
|
+
list.push(item);
|
|
3783
|
+
}
|
|
3784
|
+
out = list;
|
|
3785
|
+
} else {
|
|
3786
|
+
if (proto !== Object.prototype && proto !== null) return NOT_PLAIN;
|
|
3787
|
+
const record = proto === null ? Object.create(null) : {};
|
|
3788
|
+
memo.set(value, record);
|
|
3789
|
+
for (const key of keys) {
|
|
3790
|
+
if (typeof key !== "string") return NOT_PLAIN;
|
|
3791
|
+
const field = Object.getOwnPropertyDescriptor(value, key);
|
|
3792
|
+
if (field === void 0 || !("value" in field) || field.enumerable !== true) return NOT_PLAIN;
|
|
3793
|
+
const item = copyPlain(field.value, memo);
|
|
3794
|
+
if (item === NOT_PLAIN) return NOT_PLAIN;
|
|
3795
|
+
if (key === "__proto__") Object.defineProperty(record, key, {
|
|
3796
|
+
value: item,
|
|
3797
|
+
enumerable: true,
|
|
3798
|
+
writable: true,
|
|
3799
|
+
configurable: true
|
|
3800
|
+
});
|
|
3801
|
+
else record[key] = item;
|
|
3802
|
+
}
|
|
3803
|
+
out = record;
|
|
3804
|
+
}
|
|
3805
|
+
Object.freeze(out);
|
|
3806
|
+
SHARED.add(out);
|
|
3807
|
+
return out;
|
|
3489
3808
|
}
|
|
3490
3809
|
/**
|
|
3491
3810
|
* This function gives the code of a document: the code of its evaluator, with the place of each error and of each
|
|
@@ -3555,7 +3874,7 @@ function fillOf(split, doc) {
|
|
|
3555
3874
|
return (items) => split.parts.length === 0 ? doc.value : split.fill(items);
|
|
3556
3875
|
}
|
|
3557
3876
|
function interpret(fn, input, fill) {
|
|
3558
|
-
const { kind, typePath, doc, data, place } = input;
|
|
3877
|
+
const { kind, typePath, doc, data, place, machine } = input;
|
|
3559
3878
|
const address = input.address === true ? { address: true } : {};
|
|
3560
3879
|
const refill = input.fill;
|
|
3561
3880
|
const compiled = input.parts;
|
|
@@ -3576,6 +3895,7 @@ function interpret(fn, input, fill) {
|
|
|
3576
3895
|
parts,
|
|
3577
3896
|
context: ctx,
|
|
3578
3897
|
place,
|
|
3898
|
+
machine,
|
|
3579
3899
|
...address,
|
|
3580
3900
|
resolve: (path, options) => rt.resolve(ctx, path, options),
|
|
3581
3901
|
effect: (effectKind, payload) => rt.effect(ctx, effectKind, payload, place),
|
|
@@ -3584,7 +3904,7 @@ function interpret(fn, input, fill) {
|
|
|
3584
3904
|
};
|
|
3585
3905
|
}
|
|
3586
3906
|
function interpretRun(fn, input, fill) {
|
|
3587
|
-
const { kind, typePath, doc, data, place } = input;
|
|
3907
|
+
const { kind, typePath, doc, data, place, machine } = input;
|
|
3588
3908
|
const refill = input.fill;
|
|
3589
3909
|
const compiled = input.parts;
|
|
3590
3910
|
const hasParts = compiled.length > 0;
|
|
@@ -3604,6 +3924,7 @@ function interpretRun(fn, input, fill) {
|
|
|
3604
3924
|
parts,
|
|
3605
3925
|
context: ctx,
|
|
3606
3926
|
place,
|
|
3927
|
+
machine,
|
|
3607
3928
|
resolve: (path, options) => rt.resolve(ctx, path, options),
|
|
3608
3929
|
effect: (effectKind, payload) => rt.runEffect(ctx, effectKind, payload, place),
|
|
3609
3930
|
all: (list, options) => rt.all(list.map((part) => () => part.evaluate()), options),
|
|
@@ -3648,12 +3969,14 @@ function prepare(machine, doc, options = {}) {
|
|
|
3648
3969
|
error: "no-answer",
|
|
3649
3970
|
steps: []
|
|
3650
3971
|
};
|
|
3972
|
+
const traced = options.trace === true;
|
|
3651
3973
|
const c = {
|
|
3652
3974
|
machine,
|
|
3653
|
-
traced
|
|
3975
|
+
traced,
|
|
3654
3976
|
maxDepth: machine.maxDepth ?? 1e3,
|
|
3655
3977
|
verb,
|
|
3656
|
-
noAnswer
|
|
3978
|
+
noAnswer,
|
|
3979
|
+
fold: !traced && options.fold !== false
|
|
3657
3980
|
};
|
|
3658
3981
|
let root;
|
|
3659
3982
|
try {
|
|
@@ -3751,8 +4074,8 @@ function record(s, result) {
|
|
|
3751
4074
|
* tells where its parts are. The evaluator gets each part sealed: it can evaluate the part, and it cannot read its
|
|
3752
4075
|
* value.
|
|
3753
4076
|
*
|
|
3754
|
-
* A failure gives an error value, and the function does not throw.
|
|
3755
|
-
* does not do it. A mock can answer each effect at once (RUN.MOCK).
|
|
4077
|
+
* A failure gives an error value, and the function does not throw. The function records each effect in its order,
|
|
4078
|
+
* and the host does not do it. A mock can answer each effect at once (RUN.MOCK).
|
|
3756
4079
|
*
|
|
3757
4080
|
* @param machine - The world and the code of the evaluators.
|
|
3758
4081
|
* @param doc - The document, as JSON.
|
|
@@ -4110,6 +4433,56 @@ const STD_EVALUATORS = {
|
|
|
4110
4433
|
};
|
|
4111
4434
|
/** The brand of a node of the decorated tree. Its value is the state that the kit reads. */
|
|
4112
4435
|
const TREE_NODE = /* @__PURE__ */ Symbol.for("coeval.tree.node");
|
|
4436
|
+
/** The rungs of the ladder of an attribute, in their order (TREE.LADDER, D42). */
|
|
4437
|
+
const ATTRIBUTE_RUNGS = /* @__PURE__ */ Object.freeze([
|
|
4438
|
+
"own",
|
|
4439
|
+
"kind",
|
|
4440
|
+
"parent",
|
|
4441
|
+
"copy",
|
|
4442
|
+
"default"
|
|
4443
|
+
]);
|
|
4444
|
+
/** The capabilities of an equation: none (CAP.PURE). */
|
|
4445
|
+
const NO_CAPS$1 = /* @__PURE__ */ Object.freeze([]);
|
|
4446
|
+
/** The names of an equation of `equations`, of a `unit` and of a `default`. */
|
|
4447
|
+
const NO_NAMES$1 = /* @__PURE__ */ Object.freeze({});
|
|
4448
|
+
/** The planners of equations, for each machine and each type of a bare equation (TREE.PLAN). */
|
|
4449
|
+
const equationPlanners = /* @__PURE__ */ new WeakMap();
|
|
4450
|
+
/**
|
|
4451
|
+
* This function gives the plan of an equation. Each equation compiles one time for each machine (PLAN.CACHE). The
|
|
4452
|
+
* plan of the document does not keep it (PLAN.LEAN).
|
|
4453
|
+
*/
|
|
4454
|
+
function equationPlan(machine, of, term) {
|
|
4455
|
+
let byType = equationPlanners.get(machine);
|
|
4456
|
+
if (byType === void 0) {
|
|
4457
|
+
byType = /* @__PURE__ */ new Map();
|
|
4458
|
+
equationPlanners.set(machine, byType);
|
|
4459
|
+
}
|
|
4460
|
+
const key = of ?? "";
|
|
4461
|
+
let plans = byType.get(key);
|
|
4462
|
+
if (plans === void 0) {
|
|
4463
|
+
plans = planner(machine, of === void 0 ? {} : { declaredType: of });
|
|
4464
|
+
byType.set(key, plans);
|
|
4465
|
+
}
|
|
4466
|
+
return plans(term);
|
|
4467
|
+
}
|
|
4468
|
+
/** This function gives the own entry of a table of a tree contract, or `undefined`. */
|
|
4469
|
+
function entryOf(table, key) {
|
|
4470
|
+
return table !== void 0 && Object.hasOwn(table, key) ? table[key] : void 0;
|
|
4471
|
+
}
|
|
4472
|
+
const NO_CONTRACT = /* @__PURE__ */ Object.freeze({
|
|
4473
|
+
tree: void 0,
|
|
4474
|
+
of: void 0
|
|
4475
|
+
});
|
|
4476
|
+
/** This function finds the tree contract of the kind of a node in the world of the machine. */
|
|
4477
|
+
function contractOf$1(machine, typePath) {
|
|
4478
|
+
if (machine === void 0 || typePath.length === 0) return NO_CONTRACT;
|
|
4479
|
+
const found = lookupKind(machine.world, typePath);
|
|
4480
|
+
if (!found.ok || found.kind.tree === void 0) return NO_CONTRACT;
|
|
4481
|
+
return {
|
|
4482
|
+
tree: found.kind.tree,
|
|
4483
|
+
of: equationType(found.kind)
|
|
4484
|
+
};
|
|
4485
|
+
}
|
|
4113
4486
|
const BUSY = /* @__PURE__ */ Object.freeze({ busy: true });
|
|
4114
4487
|
/** The attributes in evaluation, in the order of their demands. An evaluation does not wait, thus one list serves. */
|
|
4115
4488
|
const demands = [];
|
|
@@ -4177,28 +4550,92 @@ function plainOf(value, path) {
|
|
|
4177
4550
|
}
|
|
4178
4551
|
/**
|
|
4179
4552
|
* This function makes a node of the decorated tree. A step `parent` gives the self of the context of the node, and a
|
|
4180
|
-
* step `type` gives its type.
|
|
4181
|
-
*
|
|
4553
|
+
* step `type` gives its type. Another step reads an attribute through its ladder (TREE.LADDER). The rungs are a part of
|
|
4554
|
+
* the node, an equation of its kind and an equation `down` of the kind of its parent. A copy from the parent, then a
|
|
4555
|
+
* fold or a default, come last. A failed attribute throws `Failure` with its own error, thus a reference to it gives that error.
|
|
4182
4556
|
*/
|
|
4183
|
-
function treeThing(
|
|
4557
|
+
function treeThing(input, build) {
|
|
4558
|
+
const { parts, context, kind: type, place, machine } = input;
|
|
4559
|
+
const { tree, of } = contractOf$1(machine, input.typePath);
|
|
4560
|
+
const above = stateOf(context.self);
|
|
4561
|
+
const up = above !== void 0 && above.build === build ? above : void 0;
|
|
4184
4562
|
const cells = /* @__PURE__ */ new Map();
|
|
4563
|
+
/** The positions of the items of the list `children`, for the names of the children. */
|
|
4564
|
+
let positions;
|
|
4565
|
+
const fold = (key, declared) => {
|
|
4566
|
+
let acc = state.equation(declared.unit, NO_NAMES$1);
|
|
4567
|
+
if (!acc.ok) return acc;
|
|
4568
|
+
const children = read("children");
|
|
4569
|
+
if (children === void 0) return acc;
|
|
4570
|
+
if (!children.ok) return children;
|
|
4571
|
+
if (!Array.isArray(children.value)) return fail("#VALUE!", `The fold of ${key} needs a list in children.`);
|
|
4572
|
+
for (const item of children.value) {
|
|
4573
|
+
const child = stateOf(item);
|
|
4574
|
+
if (child === void 0 || child.build !== build) continue;
|
|
4575
|
+
const value = child.read(key);
|
|
4576
|
+
if (value === void 0) return fail("#N/A", `No rung gives ${attributeText(key, child.type, child.place)}.`);
|
|
4577
|
+
if (!value.ok) return value;
|
|
4578
|
+
acc = state.equation(declared.combine, {
|
|
4579
|
+
acc: acc.value,
|
|
4580
|
+
item: value.value
|
|
4581
|
+
});
|
|
4582
|
+
if (!acc.ok) return acc;
|
|
4583
|
+
}
|
|
4584
|
+
return acc;
|
|
4585
|
+
};
|
|
4586
|
+
/** This function visits the rungs of the ladder of an attribute in their order, until one answers. */
|
|
4587
|
+
const climb = (key) => {
|
|
4588
|
+
const part = parts.find((item) => item.key === key);
|
|
4589
|
+
if (part !== void 0) return {
|
|
4590
|
+
result: part.evaluate({
|
|
4591
|
+
...context,
|
|
4592
|
+
self: node
|
|
4593
|
+
}),
|
|
4594
|
+
by: "own"
|
|
4595
|
+
};
|
|
4596
|
+
const equation = entryOf(tree?.equations, key);
|
|
4597
|
+
if (equation !== void 0) return {
|
|
4598
|
+
result: state.equation(equation, NO_NAMES$1),
|
|
4599
|
+
by: "kind"
|
|
4600
|
+
};
|
|
4601
|
+
const down = up === void 0 ? void 0 : entryOf(up.tree?.down, key);
|
|
4602
|
+
if (up !== void 0 && down !== void 0) {
|
|
4603
|
+
const names = up.childNames(node);
|
|
4604
|
+
return {
|
|
4605
|
+
result: names.ok ? up.equation(down, names.value) : names,
|
|
4606
|
+
by: "parent"
|
|
4607
|
+
};
|
|
4608
|
+
}
|
|
4609
|
+
const declared = entryOf(tree?.attributes, key);
|
|
4610
|
+
if (declared?.copy === true && up !== void 0) {
|
|
4611
|
+
const copied = up.read(key);
|
|
4612
|
+
if (copied !== void 0) return {
|
|
4613
|
+
result: copied,
|
|
4614
|
+
by: "copy"
|
|
4615
|
+
};
|
|
4616
|
+
}
|
|
4617
|
+
if (declared?.fold !== void 0) return {
|
|
4618
|
+
result: fold(key, declared.fold),
|
|
4619
|
+
by: "default"
|
|
4620
|
+
};
|
|
4621
|
+
if (declared?.default !== void 0) return {
|
|
4622
|
+
result: state.equation(declared.default, NO_NAMES$1),
|
|
4623
|
+
by: "default"
|
|
4624
|
+
};
|
|
4625
|
+
};
|
|
4185
4626
|
const read = (key) => {
|
|
4186
4627
|
const cell = cells.get(key);
|
|
4187
4628
|
if (cell !== void 0) return cell.busy ? cycleAt(cells, key, attributeText(key, type, place)) : cell.result;
|
|
4188
|
-
|
|
4189
|
-
if (part === void 0) return void 0;
|
|
4629
|
+
if (tree === void 0 && up?.tree === void 0 && !parts.some((item) => item.key === key)) return void 0;
|
|
4190
4630
|
cells.set(key, BUSY);
|
|
4191
4631
|
demands.push({
|
|
4192
4632
|
cells,
|
|
4193
4633
|
key,
|
|
4194
4634
|
text: attributeText(key, type, place)
|
|
4195
4635
|
});
|
|
4196
|
-
let
|
|
4636
|
+
let answer;
|
|
4197
4637
|
try {
|
|
4198
|
-
|
|
4199
|
-
...context,
|
|
4200
|
-
self: node
|
|
4201
|
-
});
|
|
4638
|
+
answer = climb(key);
|
|
4202
4639
|
} catch (error) {
|
|
4203
4640
|
cells.delete(key);
|
|
4204
4641
|
throw error;
|
|
@@ -4207,14 +4644,60 @@ function treeThing(parts, context, type, place, build) {
|
|
|
4207
4644
|
}
|
|
4208
4645
|
cells.set(key, {
|
|
4209
4646
|
busy: false,
|
|
4210
|
-
result
|
|
4647
|
+
result: answer?.result,
|
|
4648
|
+
by: answer?.by
|
|
4211
4649
|
});
|
|
4212
|
-
return result;
|
|
4650
|
+
return answer?.result;
|
|
4213
4651
|
};
|
|
4214
4652
|
const state = {
|
|
4215
4653
|
build,
|
|
4216
4654
|
type,
|
|
4217
4655
|
place,
|
|
4656
|
+
tree,
|
|
4657
|
+
read,
|
|
4658
|
+
equation(term, names) {
|
|
4659
|
+
if (machine === void 0) return fail("#NOEVAL", "The evaluation of an equation needs the machine.");
|
|
4660
|
+
return equationPlan(machine, of, term).evaluate({
|
|
4661
|
+
...context,
|
|
4662
|
+
names,
|
|
4663
|
+
caps: NO_CAPS$1,
|
|
4664
|
+
self: node
|
|
4665
|
+
});
|
|
4666
|
+
},
|
|
4667
|
+
childNames(child) {
|
|
4668
|
+
const children = read("children");
|
|
4669
|
+
if (children !== void 0 && !children.ok) return children;
|
|
4670
|
+
const list = children !== void 0 && Array.isArray(children.value) ? children.value : [];
|
|
4671
|
+
if (positions?.list !== list) {
|
|
4672
|
+
const at = /* @__PURE__ */ new Map();
|
|
4673
|
+
list.forEach((item, index) => {
|
|
4674
|
+
if (!at.has(item)) at.set(item, index);
|
|
4675
|
+
});
|
|
4676
|
+
positions = {
|
|
4677
|
+
list,
|
|
4678
|
+
at
|
|
4679
|
+
};
|
|
4680
|
+
}
|
|
4681
|
+
const index = positions.at.get(child);
|
|
4682
|
+
return ok({
|
|
4683
|
+
child,
|
|
4684
|
+
index: index ?? null,
|
|
4685
|
+
prev: index === void 0 || index === 0 ? null : list[index - 1]
|
|
4686
|
+
});
|
|
4687
|
+
},
|
|
4688
|
+
trace(key) {
|
|
4689
|
+
const cell = cells.get(key);
|
|
4690
|
+
if (cell === void 0 || cell.busy) return void 0;
|
|
4691
|
+
const rungs = cell.by === void 0 ? ATTRIBUTE_RUNGS : ATTRIBUTE_RUNGS.slice(0, ATTRIBUTE_RUNGS.indexOf(cell.by) + 1);
|
|
4692
|
+
return cell.by === void 0 ? {
|
|
4693
|
+
attribute: key,
|
|
4694
|
+
rungs
|
|
4695
|
+
} : {
|
|
4696
|
+
attribute: key,
|
|
4697
|
+
rungs,
|
|
4698
|
+
by: cell.by
|
|
4699
|
+
};
|
|
4700
|
+
},
|
|
4218
4701
|
json(path) {
|
|
4219
4702
|
path.add(node);
|
|
4220
4703
|
try {
|
|
@@ -4246,7 +4729,9 @@ function treeThing(parts, context, type, place, build) {
|
|
|
4246
4729
|
return result.value;
|
|
4247
4730
|
},
|
|
4248
4731
|
has(key) {
|
|
4249
|
-
|
|
4732
|
+
if (key === "parent" || key === "type" || parts.some((part) => part.key === String(key))) return true;
|
|
4733
|
+
const cell = cells.get(String(key));
|
|
4734
|
+
return cell !== void 0 && !cell.busy && cell.result !== void 0;
|
|
4250
4735
|
},
|
|
4251
4736
|
toJson() {
|
|
4252
4737
|
const result = state.json(/* @__PURE__ */ new Set());
|
|
@@ -4257,14 +4742,25 @@ function treeThing(parts, context, type, place, build) {
|
|
|
4257
4742
|
return node;
|
|
4258
4743
|
}
|
|
4259
4744
|
/**
|
|
4745
|
+
* This function gives the record of the demand of an attribute of a node of the decorated tree (TREE.RUNGS). The
|
|
4746
|
+
* record holds the rungs that the demand visited, and the rung that answered. An explorer draws the ladder with it.
|
|
4747
|
+
*
|
|
4748
|
+
* @param node - The thing of a node.
|
|
4749
|
+
* @param attribute - The name of the attribute.
|
|
4750
|
+
* @returns The record, or `undefined` for a thing that is not a node or an attribute that no demand reached.
|
|
4751
|
+
*/
|
|
4752
|
+
function attributeTrace(node, attribute) {
|
|
4753
|
+
return stateOf(node)?.trace(attribute);
|
|
4754
|
+
}
|
|
4755
|
+
/**
|
|
4260
4756
|
* This function evaluates a node of the decorated tree. Inside an attribute of an open tree, the node gives its host
|
|
4261
4757
|
* thing, and no attribute evaluates. Else the node is the root of a new tree, and it gives the JSON of the tree.
|
|
4262
4758
|
*/
|
|
4263
|
-
function treeNode(
|
|
4264
|
-
const above = stateOf(context.self);
|
|
4265
|
-
if (above !== void 0 && above.build.open) return ok(treeThing(
|
|
4759
|
+
function treeNode(input) {
|
|
4760
|
+
const above = stateOf(input.context.self);
|
|
4761
|
+
if (above !== void 0 && above.build.open) return ok(treeThing(input, above.build));
|
|
4266
4762
|
const build = { open: true };
|
|
4267
|
-
const root = treeThing(
|
|
4763
|
+
const root = treeThing(input, build);
|
|
4268
4764
|
try {
|
|
4269
4765
|
return stateOf(root).json(/* @__PURE__ */ new Set());
|
|
4270
4766
|
} finally {
|
|
@@ -5007,96 +5503,27 @@ function typePass(machine, nodes, verb, expect, rootTypes = {}) {
|
|
|
5007
5503
|
}
|
|
5008
5504
|
//#endregion
|
|
5009
5505
|
//#region src/static.ts
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
return
|
|
5016
|
-
}
|
|
5017
|
-
function headerName(item) {
|
|
5018
|
-
return isEnvelope(item) && typeof item.name === "string" ? item.name : void 0;
|
|
5506
|
+
/** The names of a walk without names. A walk does not change a set at any time: it makes a new set to add names. */
|
|
5507
|
+
const NO_NAMES = /* @__PURE__ */ new Set();
|
|
5508
|
+
/** This function gives the options of a walk without the names of the host. */
|
|
5509
|
+
function withoutNames(options) {
|
|
5510
|
+
const { names: _host, ...rest } = options;
|
|
5511
|
+
return rest;
|
|
5019
5512
|
}
|
|
5020
|
-
function
|
|
5021
|
-
|
|
5022
|
-
if (
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
}
|
|
5030
|
-
if ("textAt" in source) {
|
|
5031
|
-
const at = valueAt(value, source.textAt);
|
|
5032
|
-
if (typeof at === "string") return [at];
|
|
5033
|
-
return source.otherwise === void 0 ? [] : [source.otherwise];
|
|
5513
|
+
/** This function gives the keys of a trail, from its start. It keeps the result in the trail. */
|
|
5514
|
+
function keysOf(trail) {
|
|
5515
|
+
if (trail === void 0) return [];
|
|
5516
|
+
if (trail.full !== void 0) return trail.full;
|
|
5517
|
+
const steps = [];
|
|
5518
|
+
let here = trail;
|
|
5519
|
+
while (here !== void 0 && here.full === void 0) {
|
|
5520
|
+
steps.push(here);
|
|
5521
|
+
here = here.up;
|
|
5034
5522
|
}
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
* see. `check` and `conforms` read the scope rules with it.
|
|
5040
|
-
*
|
|
5041
|
-
* @param rules - The scope rules of the kind.
|
|
5042
|
-
* @param value - The value of the document.
|
|
5043
|
-
* @param slots - The parts of the value, in order.
|
|
5044
|
-
* @returns The names that each part sees, and the names that only the later parts see, by the index of the part.
|
|
5045
|
-
*/
|
|
5046
|
-
function scopesOf(rules, value, slots) {
|
|
5047
|
-
const sees = slots.map(() => []);
|
|
5048
|
-
const later = slots.map(() => []);
|
|
5049
|
-
for (const rule of rules ?? []) {
|
|
5050
|
-
const covered = slots.map((slot, index) => ({
|
|
5051
|
-
slot,
|
|
5052
|
-
index
|
|
5053
|
-
})).filter(({ slot }) => rule.in.some((pattern) => pathMatches(slot.path, pattern)));
|
|
5054
|
-
if (rule.sequential !== true) {
|
|
5055
|
-
const names = namesOf(rule.names, value, covered.map(({ slot }) => slot));
|
|
5056
|
-
for (const { index } of covered) sees[index]?.push(...names);
|
|
5057
|
-
continue;
|
|
5058
|
-
}
|
|
5059
|
-
if ("headers" in rule.names) {
|
|
5060
|
-
covered.forEach(({ index }, position) => {
|
|
5061
|
-
const before = covered.slice(0, position).map(({ slot }) => headerName(slot.item)).filter((name) => name !== void 0);
|
|
5062
|
-
const after = covered.slice(position).map(({ slot }) => headerName(slot.item)).filter((name) => name !== void 0);
|
|
5063
|
-
sees[index]?.push(...before);
|
|
5064
|
-
later[index]?.push(...after);
|
|
5065
|
-
});
|
|
5066
|
-
continue;
|
|
5067
|
-
}
|
|
5068
|
-
const names = namesOf(rule.names, value, covered.map(({ slot }) => slot));
|
|
5069
|
-
covered.forEach(({ index }, position) => {
|
|
5070
|
-
if (position > 0) sees[index]?.push(...names);
|
|
5071
|
-
else later[index]?.push(...names);
|
|
5072
|
-
});
|
|
5073
|
-
}
|
|
5074
|
-
return {
|
|
5075
|
-
sees,
|
|
5076
|
-
later
|
|
5077
|
-
};
|
|
5078
|
-
}
|
|
5079
|
-
/** The names of a walk without names. A walk does not change a set at any time: it makes a new set to add names. */
|
|
5080
|
-
const NO_NAMES = /* @__PURE__ */ new Set();
|
|
5081
|
-
/** This function gives the options of a walk without the names of the host. */
|
|
5082
|
-
function withoutNames(options) {
|
|
5083
|
-
const { names: _host, ...rest } = options;
|
|
5084
|
-
return rest;
|
|
5085
|
-
}
|
|
5086
|
-
/** This function gives the keys of a trail, from its start. It keeps the result in the trail. */
|
|
5087
|
-
function keysOf(trail) {
|
|
5088
|
-
if (trail === void 0) return [];
|
|
5089
|
-
if (trail.full !== void 0) return trail.full;
|
|
5090
|
-
const steps = [];
|
|
5091
|
-
let here = trail;
|
|
5092
|
-
while (here !== void 0 && here.full === void 0) {
|
|
5093
|
-
steps.push(here);
|
|
5094
|
-
here = here.up;
|
|
5095
|
-
}
|
|
5096
|
-
const keys = here?.full === void 0 ? [] : [...here.full];
|
|
5097
|
-
for (let index = steps.length - 1; index >= 0; index -= 1) for (const key of steps[index].keys) keys.push(key);
|
|
5098
|
-
trail.full = keys;
|
|
5099
|
-
return keys;
|
|
5523
|
+
const keys = here?.full === void 0 ? [] : [...here.full];
|
|
5524
|
+
for (let index = steps.length - 1; index >= 0; index -= 1) for (const key of steps[index].keys) keys.push(key);
|
|
5525
|
+
trail.full = keys;
|
|
5526
|
+
return keys;
|
|
5100
5527
|
}
|
|
5101
5528
|
/** A visit with its paths as trails. It makes the keys of a path only when a reader asks for them. */
|
|
5102
5529
|
var Visit = class {
|
|
@@ -5420,11 +5847,81 @@ function typeCheck(machine, doc, options = {}) {
|
|
|
5420
5847
|
places: pass.places
|
|
5421
5848
|
};
|
|
5422
5849
|
}
|
|
5850
|
+
/** The names that an equation of `down` sees (TREE.DOWN). */
|
|
5851
|
+
const DOWN_NAMES = /* @__PURE__ */ Object.freeze([
|
|
5852
|
+
"child",
|
|
5853
|
+
"index",
|
|
5854
|
+
"prev"
|
|
5855
|
+
]);
|
|
5856
|
+
/** The names that the term `combine` of a fold sees (TREE.FOLD). */
|
|
5857
|
+
const FOLD_NAMES = /* @__PURE__ */ Object.freeze(["acc", "item"]);
|
|
5858
|
+
/**
|
|
5859
|
+
* This function gives each equation of the tree contract of a kind, with the names that it sees (D42). An equation of
|
|
5860
|
+
* `equations`, a `unit` and a `default` see no names.
|
|
5861
|
+
*/
|
|
5862
|
+
function equationsOf(kind) {
|
|
5863
|
+
const tree = kind.tree;
|
|
5864
|
+
if (tree === void 0) return [];
|
|
5865
|
+
const out = [];
|
|
5866
|
+
for (const [name, term] of Object.entries(tree.equations ?? {})) out.push({
|
|
5867
|
+
label: `tree.equations.${name}`,
|
|
5868
|
+
term,
|
|
5869
|
+
names: []
|
|
5870
|
+
});
|
|
5871
|
+
for (const [name, term] of Object.entries(tree.down ?? {})) out.push({
|
|
5872
|
+
label: `tree.down.${name}`,
|
|
5873
|
+
term,
|
|
5874
|
+
names: DOWN_NAMES
|
|
5875
|
+
});
|
|
5876
|
+
for (const [name, declared] of Object.entries(tree.attributes ?? {})) {
|
|
5877
|
+
if (declared.fold !== void 0) {
|
|
5878
|
+
out.push({
|
|
5879
|
+
label: `tree.attributes.${name}.fold.unit`,
|
|
5880
|
+
term: declared.fold.unit,
|
|
5881
|
+
names: []
|
|
5882
|
+
});
|
|
5883
|
+
out.push({
|
|
5884
|
+
label: `tree.attributes.${name}.fold.combine`,
|
|
5885
|
+
term: declared.fold.combine,
|
|
5886
|
+
names: FOLD_NAMES
|
|
5887
|
+
});
|
|
5888
|
+
}
|
|
5889
|
+
if (declared.default !== void 0) out.push({
|
|
5890
|
+
label: `tree.attributes.${name}.default`,
|
|
5891
|
+
term: declared.default,
|
|
5892
|
+
names: []
|
|
5893
|
+
});
|
|
5894
|
+
}
|
|
5895
|
+
return out;
|
|
5896
|
+
}
|
|
5897
|
+
/**
|
|
5898
|
+
* This function checks the equations of the tree contract of a kind, each with the names of its rung (TREE.DOWN-NAMES).
|
|
5899
|
+
* An equation evaluates without capabilities, thus a check with capabilities gives it none. Each issue gets the place
|
|
5900
|
+
* of the document of the kind, and its reason names the equation.
|
|
5901
|
+
*/
|
|
5902
|
+
function equationIssues(machine, kind, kindText, options, seen) {
|
|
5903
|
+
const of = equationType(kind);
|
|
5904
|
+
const out = [];
|
|
5905
|
+
for (const { label, term, names } of equationsOf(kind)) {
|
|
5906
|
+
const inner = {
|
|
5907
|
+
names,
|
|
5908
|
+
...of === void 0 ? {} : { declaredType: of },
|
|
5909
|
+
...options.caps === void 0 ? {} : { caps: [] }
|
|
5910
|
+
};
|
|
5911
|
+
for (const issue of checkAll(machine, term, inner, void 0, seen)) out.push({
|
|
5912
|
+
code: issue.code,
|
|
5913
|
+
place: [],
|
|
5914
|
+
reason: `In the equation ${label} of the kind ${kindText}: ${issue.reason}`
|
|
5915
|
+
});
|
|
5916
|
+
}
|
|
5917
|
+
return out;
|
|
5918
|
+
}
|
|
5423
5919
|
/**
|
|
5424
5920
|
* This function finds the issues of `check`. With a list of nodes, it also records each document that it visits for
|
|
5425
|
-
* the pass of types. It marks each document with an issue.
|
|
5921
|
+
* the pass of types. It marks each document with an issue. The set `seen` holds the kinds whose equations the check
|
|
5922
|
+
* walked, thus it walks the equations of each kind one time.
|
|
5426
5923
|
*/
|
|
5427
|
-
function checkAll(machine, doc, options, nodes) {
|
|
5924
|
+
function checkAll(machine, doc, options, nodes, seen = /* @__PURE__ */ new Set()) {
|
|
5428
5925
|
const issues = [];
|
|
5429
5926
|
const verb = options.verb ?? "eval";
|
|
5430
5927
|
const indices = nodes === void 0 ? void 0 : /* @__PURE__ */ new Map();
|
|
@@ -5476,6 +5973,14 @@ function checkAll(machine, doc, options, nodes) {
|
|
|
5476
5973
|
});
|
|
5477
5974
|
return "skip";
|
|
5478
5975
|
}
|
|
5976
|
+
if (v.kind?.tree !== void 0 && !seen.has(v.kind)) {
|
|
5977
|
+
seen.add(v.kind);
|
|
5978
|
+
for (const issue of equationIssues(machine, v.kind, typeName(v.doc.type), options, seen)) push({
|
|
5979
|
+
...issue,
|
|
5980
|
+
place: v.place,
|
|
5981
|
+
at: v.at
|
|
5982
|
+
});
|
|
5983
|
+
}
|
|
5479
5984
|
const name = answer.evaluator;
|
|
5480
5985
|
if (current !== void 0) current.evaluator = name;
|
|
5481
5986
|
const runner = name !== void 0 && has(machine.runners, name);
|
|
@@ -5873,6 +6378,178 @@ function checkLatent(machine, declared, body, options = {}) {
|
|
|
5873
6378
|
return issues;
|
|
5874
6379
|
}
|
|
5875
6380
|
//#endregion
|
|
6381
|
+
//#region src/codec.ts
|
|
6382
|
+
/** This function gives the codec of a domain, or `undefined` when the domain has no codec. */
|
|
6383
|
+
function codecOf(domain) {
|
|
6384
|
+
const codec = domain.codec;
|
|
6385
|
+
if (typeof codec !== "object" || codec === null) return void 0;
|
|
6386
|
+
const { encode, decode } = codec;
|
|
6387
|
+
return typeof encode === "function" && typeof decode === "function" ? codec : void 0;
|
|
6388
|
+
}
|
|
6389
|
+
/**
|
|
6390
|
+
* This function reads the value of an extension `ext.domain` (EXT.DOMAIN). The value is a record with only the text
|
|
6391
|
+
* `domain` and the JSON `data`. The codec of the named domain reads the data, and the result must be a member of the
|
|
6392
|
+
* domain.
|
|
6393
|
+
*
|
|
6394
|
+
* @param data - The value of the extension document.
|
|
6395
|
+
* @param domains - The domains of the machine.
|
|
6396
|
+
* @returns The member, `#NAME?` for a domain without a codec, or `#VALUE!` for data that the codec cannot read.
|
|
6397
|
+
*/
|
|
6398
|
+
function decodeDomain(data, domains) {
|
|
6399
|
+
if (!isRecord(data) || typeof data["domain"] !== "string" || !Object.hasOwn(data, "data") || Object.keys(data).length !== 2) return fail("#VALUE!", "The value of ext.domain is a record with only the text domain and the data.");
|
|
6400
|
+
const name = data["domain"];
|
|
6401
|
+
const domain = domainOf(name, domains);
|
|
6402
|
+
if (domain === void 0) return fail("#NAME?", `The machine has no domain ${name}.`);
|
|
6403
|
+
const codec = codecOf(domain);
|
|
6404
|
+
if (codec === void 0) return fail("#NAME?", `The domain ${name} has no codec.`);
|
|
6405
|
+
let value;
|
|
6406
|
+
try {
|
|
6407
|
+
value = codec.decode(data["data"]);
|
|
6408
|
+
} catch (error) {
|
|
6409
|
+
return fail("#VALUE!", `The codec of the domain ${name} cannot read the data: ${describe(error)}`, { cause: error });
|
|
6410
|
+
}
|
|
6411
|
+
if (!member(value, name, domains)) return fail("#VALUE!", `The codec of the domain ${name} gives a value that is not a member of the domain.`);
|
|
6412
|
+
return ok(value);
|
|
6413
|
+
}
|
|
6414
|
+
/**
|
|
6415
|
+
* This function gives the static type of an extension `ext.domain`: the name of its domain when that domain has a
|
|
6416
|
+
* codec. A run then gives a member of the domain or an error, because `decodeDomain` tests the membership.
|
|
6417
|
+
*
|
|
6418
|
+
* @param data - The value of the extension document.
|
|
6419
|
+
* @param domains - The domains of the machine.
|
|
6420
|
+
* @returns The name of the domain, or `unknown`.
|
|
6421
|
+
*/
|
|
6422
|
+
function domainTypeOf(data, domains) {
|
|
6423
|
+
if (!isRecord(data) || typeof data["domain"] !== "string") return "unknown";
|
|
6424
|
+
const domain = domainOf(data["domain"], domains);
|
|
6425
|
+
return domain !== void 0 && codecOf(domain) !== void 0 ? data["domain"] : "unknown";
|
|
6426
|
+
}
|
|
6427
|
+
/** This function sets an own data property. Only the key `__proto__` needs `defineProperty`. */
|
|
6428
|
+
function setOwn$2(target, key, value) {
|
|
6429
|
+
if (key === "__proto__") Object.defineProperty(target, key, {
|
|
6430
|
+
value,
|
|
6431
|
+
enumerable: true,
|
|
6432
|
+
writable: true,
|
|
6433
|
+
configurable: true
|
|
6434
|
+
});
|
|
6435
|
+
else target[key] = value;
|
|
6436
|
+
}
|
|
6437
|
+
/**
|
|
6438
|
+
* This function tells if JSON writes a value exactly. Such a value is a finite number other than `-0`, a text, a truth
|
|
6439
|
+
* value, `null`, or a container of such values.
|
|
6440
|
+
*/
|
|
6441
|
+
function isJson(value, encoder) {
|
|
6442
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
6443
|
+
if (typeof value === "number") return Number.isFinite(value) && !Object.is(value, -0);
|
|
6444
|
+
const list = Array.isArray(value);
|
|
6445
|
+
if (!list && !isRecord(value)) return false;
|
|
6446
|
+
const container = value;
|
|
6447
|
+
const known = encoder.json.get(container);
|
|
6448
|
+
if (known !== void 0) return known;
|
|
6449
|
+
encoder.json.set(container, false);
|
|
6450
|
+
let all = true;
|
|
6451
|
+
if (list) {
|
|
6452
|
+
const items = value;
|
|
6453
|
+
for (let index = 0; index < items.length && all; index += 1) all = isJson(items[index], encoder);
|
|
6454
|
+
} else {
|
|
6455
|
+
const record = value;
|
|
6456
|
+
for (const key of Object.keys(record)) if (!isJson(record[key], encoder)) {
|
|
6457
|
+
all = false;
|
|
6458
|
+
break;
|
|
6459
|
+
}
|
|
6460
|
+
}
|
|
6461
|
+
encoder.json.set(container, all);
|
|
6462
|
+
return all;
|
|
6463
|
+
}
|
|
6464
|
+
/** This function gives the text of a number that JSON cannot write, for `ext.number`. */
|
|
6465
|
+
function numberText(value) {
|
|
6466
|
+
if (Number.isNaN(value)) return "NaN";
|
|
6467
|
+
if (value === Number.POSITIVE_INFINITY) return "Infinity";
|
|
6468
|
+
if (value === Number.NEGATIVE_INFINITY) return "-Infinity";
|
|
6469
|
+
return "-0";
|
|
6470
|
+
}
|
|
6471
|
+
/** This function gives the term document of a value in one call of `encodeValue`. */
|
|
6472
|
+
function encodeTerm(value, encoder) {
|
|
6473
|
+
if (isJson(value, encoder)) return isEnvelope(value) ? {
|
|
6474
|
+
type: "lit",
|
|
6475
|
+
value
|
|
6476
|
+
} : value;
|
|
6477
|
+
if (value === void 0) return {
|
|
6478
|
+
type: ["ext", "undefined"],
|
|
6479
|
+
value: null
|
|
6480
|
+
};
|
|
6481
|
+
if (typeof value === "number") return {
|
|
6482
|
+
type: ["ext", "number"],
|
|
6483
|
+
value: numberText(value)
|
|
6484
|
+
};
|
|
6485
|
+
for (const [name, codec] of encoder.codecs) if (member(value, name, encoder.domains)) return {
|
|
6486
|
+
type: ["ext", "domain"],
|
|
6487
|
+
value: {
|
|
6488
|
+
domain: name,
|
|
6489
|
+
data: codec.encode(value)
|
|
6490
|
+
}
|
|
6491
|
+
};
|
|
6492
|
+
if (!Array.isArray(value) && !isRecord(value)) throw new TypeError(`encodeValue cannot write the value ${describe(value)}: no domain of the machine with a codec holds it.`);
|
|
6493
|
+
if (encoder.open.has(value)) throw new TypeError("encodeValue cannot write a value with a cycle.");
|
|
6494
|
+
encoder.open.add(value);
|
|
6495
|
+
let out;
|
|
6496
|
+
if (Array.isArray(value)) out = {
|
|
6497
|
+
type: "list",
|
|
6498
|
+
value: Array.from(value, (item) => encodeTerm(item, encoder))
|
|
6499
|
+
};
|
|
6500
|
+
else {
|
|
6501
|
+
const fields = {};
|
|
6502
|
+
const record = value;
|
|
6503
|
+
for (const key of Object.keys(record)) setOwn$2(fields, key, encodeTerm(record[key], encoder));
|
|
6504
|
+
out = {
|
|
6505
|
+
type: "record",
|
|
6506
|
+
value: fields
|
|
6507
|
+
};
|
|
6508
|
+
}
|
|
6509
|
+
encoder.open.delete(value);
|
|
6510
|
+
return out;
|
|
6511
|
+
}
|
|
6512
|
+
/**
|
|
6513
|
+
* This function writes a host value as a term document of the formula kit (EXT.ENCODE, D40). Thus the same value has
|
|
6514
|
+
* the same document in each language (D12).
|
|
6515
|
+
*
|
|
6516
|
+
* - A value that JSON writes exactly stays as it is. A literal with the shape of an envelope goes into `lit`.
|
|
6517
|
+
* - `NaN`, an infinity and `-0` become `ext.number`, and `undefined` becomes `ext.undefined`.
|
|
6518
|
+
* - A member of a domain with a codec becomes `ext.domain`. The domains go in the order of their names.
|
|
6519
|
+
* - A list or a plain record with such a value becomes the construct `list` or `record`.
|
|
6520
|
+
*
|
|
6521
|
+
* @param machine - The machine, or only its domains.
|
|
6522
|
+
* @param value - The host value.
|
|
6523
|
+
* @returns The document. `decodeValue` gives the value again (EXT.ROUNDTRIP).
|
|
6524
|
+
* @throws {TypeError} The value has a cycle, or it is not JSON and no domain with a codec holds it.
|
|
6525
|
+
*/
|
|
6526
|
+
function encodeValue(machine, value) {
|
|
6527
|
+
const domains = machine.domains;
|
|
6528
|
+
const codecs = [];
|
|
6529
|
+
if (domains !== void 0) for (const name of Object.keys(domains).sort()) {
|
|
6530
|
+
const domain = domainOf(name, domains);
|
|
6531
|
+
const codec = domain === void 0 ? void 0 : codecOf(domain);
|
|
6532
|
+
if (codec !== void 0) codecs.push([name, codec]);
|
|
6533
|
+
}
|
|
6534
|
+
return encodeTerm(value, {
|
|
6535
|
+
domains,
|
|
6536
|
+
codecs,
|
|
6537
|
+
json: /* @__PURE__ */ new WeakMap(),
|
|
6538
|
+
open: /* @__PURE__ */ new Set()
|
|
6539
|
+
});
|
|
6540
|
+
}
|
|
6541
|
+
/**
|
|
6542
|
+
* This function reads a document of `encodeValue` again: it evaluates the document as a term, in an empty context.
|
|
6543
|
+
* A bare root value is a literal.
|
|
6544
|
+
*
|
|
6545
|
+
* @param machine - A machine of the formula kit, with the domains of the document.
|
|
6546
|
+
* @param doc - The document.
|
|
6547
|
+
* @returns The value, or the error of the evaluation.
|
|
6548
|
+
*/
|
|
6549
|
+
function decodeValue(machine, doc) {
|
|
6550
|
+
return prepare(machine, doc, { declaredType: "lit" }).evaluate(makeContext());
|
|
6551
|
+
}
|
|
6552
|
+
//#endregion
|
|
5876
6553
|
//#region src/formula.ts
|
|
5877
6554
|
/** The mark of a field that the profile leaves out of a record. */
|
|
5878
6555
|
const SKIPPED = /* @__PURE__ */ Symbol("skipped");
|
|
@@ -5893,7 +6570,10 @@ const CLOSURE$2 = /* @__PURE__ */ Symbol.for("coeval.closure");
|
|
|
5893
6570
|
function isClosure(value) {
|
|
5894
6571
|
return typeof value === "function" && value[CLOSURE$2] !== void 0;
|
|
5895
6572
|
}
|
|
5896
|
-
/**
|
|
6573
|
+
/**
|
|
6574
|
+
* The standard extensions: the values that JSON cannot write. `ext.domain` reads a member of a domain through the
|
|
6575
|
+
* codec of the domain (EXT.DOMAIN, D40).
|
|
6576
|
+
*/
|
|
5897
6577
|
const STD_EXT = {
|
|
5898
6578
|
undefined: () => ok(void 0),
|
|
5899
6579
|
number: (data) => {
|
|
@@ -5902,7 +6582,8 @@ const STD_EXT = {
|
|
|
5902
6582
|
if (data === "-Infinity") return ok(Number.NEGATIVE_INFINITY);
|
|
5903
6583
|
if (data === "-0") return ok(-0);
|
|
5904
6584
|
return fail("#VALUE!", "The value of ext.number is NaN, Infinity, -Infinity or -0.");
|
|
5905
|
-
}
|
|
6585
|
+
},
|
|
6586
|
+
domain: (data, _context, domains) => decodeDomain(data, domains)
|
|
5906
6587
|
};
|
|
5907
6588
|
/** The truth of a value in the rules of JavaScript: `0`, `""`, `null`, `undefined` and `NaN` are false. */
|
|
5908
6589
|
function jsTruthy(value) {
|
|
@@ -6369,8 +7050,9 @@ function formulaTypes(profile) {
|
|
|
6369
7050
|
},
|
|
6370
7051
|
"formula.ext": { synth: (input) => {
|
|
6371
7052
|
const name = nameOf(input);
|
|
6372
|
-
|
|
6373
|
-
|
|
7053
|
+
if (profile.ext !== void 0 && Object.hasOwn(profile.ext, name)) return "unknown";
|
|
7054
|
+
if (name === "number") return "number";
|
|
7055
|
+
return name === "domain" ? domainTypeOf(input.doc.value, input.machine.domains) : "unknown";
|
|
6374
7056
|
} }
|
|
6375
7057
|
};
|
|
6376
7058
|
}
|
|
@@ -6688,7 +7370,7 @@ function formulaCode(profile) {
|
|
|
6688
7370
|
}
|
|
6689
7371
|
};
|
|
6690
7372
|
}),
|
|
6691
|
-
"formula.ext": compiled(({ typePath, doc }) => {
|
|
7373
|
+
"formula.ext": compiled(({ typePath, doc, machine }) => {
|
|
6692
7374
|
const name = typePath.slice(1).join(".");
|
|
6693
7375
|
const handler = (profile.ext !== void 0 && Object.hasOwn(profile.ext, name) ? profile.ext[name] : void 0) ?? (Object.hasOwn(STD_EXT, name) ? STD_EXT[name] : void 0);
|
|
6694
7376
|
if (handler === void 0) {
|
|
@@ -6697,7 +7379,7 @@ function formulaCode(profile) {
|
|
|
6697
7379
|
}
|
|
6698
7380
|
return (ctx) => {
|
|
6699
7381
|
try {
|
|
6700
|
-
return handler(doc.value, ctx);
|
|
7382
|
+
return handler(doc.value, ctx, machine.domains);
|
|
6701
7383
|
} catch (error) {
|
|
6702
7384
|
return thrown(error, `the extension ${name}`);
|
|
6703
7385
|
}
|
|
@@ -6798,6 +7480,835 @@ function formulaMachine(profile = {}, more = {}) {
|
|
|
6798
7480
|
}, more);
|
|
6799
7481
|
}
|
|
6800
7482
|
//#endregion
|
|
7483
|
+
//#region src/syntax.ts
|
|
7484
|
+
/**
|
|
7485
|
+
* The standard syntax of the formula kit. It gives the site and the playground a text form. The operators are
|
|
7486
|
+
* `or`, `and`, `not`, the comparisons, `+ - * / %`, a minus sign and `^`, from the loosest to the tightest. A call
|
|
7487
|
+
* `name(args)` applies the operation `ops.name`. `if(test, then, else)` and `list(items)` make the constructs.
|
|
7488
|
+
*/
|
|
7489
|
+
const STD_SYNTAX = /* @__PURE__ */ Object.freeze({
|
|
7490
|
+
operators: [
|
|
7491
|
+
{
|
|
7492
|
+
symbol: "or",
|
|
7493
|
+
fixity: "infix",
|
|
7494
|
+
prec: 1,
|
|
7495
|
+
assoc: "flat",
|
|
7496
|
+
kind: "or"
|
|
7497
|
+
},
|
|
7498
|
+
{
|
|
7499
|
+
symbol: "and",
|
|
7500
|
+
fixity: "infix",
|
|
7501
|
+
prec: 2,
|
|
7502
|
+
assoc: "flat",
|
|
7503
|
+
kind: "and"
|
|
7504
|
+
},
|
|
7505
|
+
{
|
|
7506
|
+
symbol: "not",
|
|
7507
|
+
fixity: "prefix",
|
|
7508
|
+
prec: 3,
|
|
7509
|
+
kind: "ops.not"
|
|
7510
|
+
},
|
|
7511
|
+
{
|
|
7512
|
+
symbol: "==",
|
|
7513
|
+
fixity: "infix",
|
|
7514
|
+
prec: 4,
|
|
7515
|
+
assoc: "none",
|
|
7516
|
+
kind: "ops.eq"
|
|
7517
|
+
},
|
|
7518
|
+
{
|
|
7519
|
+
symbol: "!=",
|
|
7520
|
+
fixity: "infix",
|
|
7521
|
+
prec: 4,
|
|
7522
|
+
assoc: "none",
|
|
7523
|
+
kind: "ops.ne"
|
|
7524
|
+
},
|
|
7525
|
+
{
|
|
7526
|
+
symbol: "<",
|
|
7527
|
+
fixity: "infix",
|
|
7528
|
+
prec: 4,
|
|
7529
|
+
assoc: "none",
|
|
7530
|
+
kind: "ops.lt"
|
|
7531
|
+
},
|
|
7532
|
+
{
|
|
7533
|
+
symbol: "<=",
|
|
7534
|
+
fixity: "infix",
|
|
7535
|
+
prec: 4,
|
|
7536
|
+
assoc: "none",
|
|
7537
|
+
kind: "ops.le"
|
|
7538
|
+
},
|
|
7539
|
+
{
|
|
7540
|
+
symbol: ">",
|
|
7541
|
+
fixity: "infix",
|
|
7542
|
+
prec: 4,
|
|
7543
|
+
assoc: "none",
|
|
7544
|
+
kind: "ops.gt"
|
|
7545
|
+
},
|
|
7546
|
+
{
|
|
7547
|
+
symbol: ">=",
|
|
7548
|
+
fixity: "infix",
|
|
7549
|
+
prec: 4,
|
|
7550
|
+
assoc: "none",
|
|
7551
|
+
kind: "ops.ge"
|
|
7552
|
+
},
|
|
7553
|
+
{
|
|
7554
|
+
symbol: "+",
|
|
7555
|
+
fixity: "infix",
|
|
7556
|
+
prec: 5,
|
|
7557
|
+
kind: "ops.add"
|
|
7558
|
+
},
|
|
7559
|
+
{
|
|
7560
|
+
symbol: "-",
|
|
7561
|
+
fixity: "infix",
|
|
7562
|
+
prec: 5,
|
|
7563
|
+
kind: "ops.sub"
|
|
7564
|
+
},
|
|
7565
|
+
{
|
|
7566
|
+
symbol: "*",
|
|
7567
|
+
fixity: "infix",
|
|
7568
|
+
prec: 6,
|
|
7569
|
+
kind: "ops.mul"
|
|
7570
|
+
},
|
|
7571
|
+
{
|
|
7572
|
+
symbol: "/",
|
|
7573
|
+
fixity: "infix",
|
|
7574
|
+
prec: 6,
|
|
7575
|
+
kind: "ops.div"
|
|
7576
|
+
},
|
|
7577
|
+
{
|
|
7578
|
+
symbol: "%",
|
|
7579
|
+
fixity: "infix",
|
|
7580
|
+
prec: 6,
|
|
7581
|
+
kind: "ops.mod"
|
|
7582
|
+
},
|
|
7583
|
+
{
|
|
7584
|
+
symbol: "-",
|
|
7585
|
+
fixity: "prefix",
|
|
7586
|
+
prec: 7,
|
|
7587
|
+
kind: "ops.neg"
|
|
7588
|
+
},
|
|
7589
|
+
{
|
|
7590
|
+
symbol: "^",
|
|
7591
|
+
fixity: "infix",
|
|
7592
|
+
prec: 8,
|
|
7593
|
+
assoc: "right",
|
|
7594
|
+
kind: "ops.pow"
|
|
7595
|
+
}
|
|
7596
|
+
],
|
|
7597
|
+
literals: { words: {
|
|
7598
|
+
true: true,
|
|
7599
|
+
false: false,
|
|
7600
|
+
null: null,
|
|
7601
|
+
undefined: void 0,
|
|
7602
|
+
NaN: NaN,
|
|
7603
|
+
Infinity: Infinity
|
|
7604
|
+
} },
|
|
7605
|
+
calls: { forms: [{
|
|
7606
|
+
name: "if",
|
|
7607
|
+
kind: "if",
|
|
7608
|
+
fields: [
|
|
7609
|
+
"test",
|
|
7610
|
+
"then",
|
|
7611
|
+
"else"
|
|
7612
|
+
],
|
|
7613
|
+
min: 2
|
|
7614
|
+
}, {
|
|
7615
|
+
name: "list",
|
|
7616
|
+
kind: "list"
|
|
7617
|
+
}] }
|
|
7618
|
+
});
|
|
7619
|
+
const NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
7620
|
+
const NAME_AT = /[A-Za-z_][A-Za-z0-9_]*/y;
|
|
7621
|
+
const DIGITS_AT = /[0-9]+/y;
|
|
7622
|
+
const NUMBER_AT = /(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/y;
|
|
7623
|
+
const SYMBOL = /^[^\sA-Za-z0-9_"'`()$.]+$/;
|
|
7624
|
+
const DEFAULT_WORDS = {
|
|
7625
|
+
true: true,
|
|
7626
|
+
false: false,
|
|
7627
|
+
null: null
|
|
7628
|
+
};
|
|
7629
|
+
const DEFAULT_ROOTS = [
|
|
7630
|
+
"self",
|
|
7631
|
+
"world",
|
|
7632
|
+
"event",
|
|
7633
|
+
"caps"
|
|
7634
|
+
];
|
|
7635
|
+
const compiledProfiles = /* @__PURE__ */ new WeakMap();
|
|
7636
|
+
function typeOfKind(kind) {
|
|
7637
|
+
const keys = kind.split(".");
|
|
7638
|
+
if (kind === "" || keys.some((key) => key === "")) throw new TypeError(`The kind "${kind}" of a syntax profile has an empty key.`);
|
|
7639
|
+
return {
|
|
7640
|
+
type: keys.length === 1 ? keys[0] : keys,
|
|
7641
|
+
key: JSON.stringify(keys)
|
|
7642
|
+
};
|
|
7643
|
+
}
|
|
7644
|
+
function addTo(map, key, value) {
|
|
7645
|
+
const list = map.get(key);
|
|
7646
|
+
if (list === void 0) map.set(key, [value]);
|
|
7647
|
+
else list.push(value);
|
|
7648
|
+
}
|
|
7649
|
+
function compileOps(profile, separator) {
|
|
7650
|
+
const prefix = /* @__PURE__ */ new Map();
|
|
7651
|
+
const binary = /* @__PURE__ */ new Map();
|
|
7652
|
+
const byKind = /* @__PURE__ */ new Map();
|
|
7653
|
+
const assocAt = /* @__PURE__ */ new Map();
|
|
7654
|
+
const opWords = /* @__PURE__ */ new Set();
|
|
7655
|
+
for (const given of profile.operators ?? []) {
|
|
7656
|
+
const word = NAME.test(given.symbol);
|
|
7657
|
+
if (!word && (!SYMBOL.test(given.symbol) || given.symbol.includes(separator))) throw new TypeError(`The operator "${given.symbol}" is not a plain name or a group of symbols.`);
|
|
7658
|
+
if (!Number.isFinite(given.prec)) throw new TypeError(`The operator "${given.symbol}" has no finite precedence.`);
|
|
7659
|
+
const { type, key } = typeOfKind(given.kind);
|
|
7660
|
+
const op = {
|
|
7661
|
+
symbol: given.symbol,
|
|
7662
|
+
fixity: given.fixity,
|
|
7663
|
+
prec: given.prec,
|
|
7664
|
+
assoc: given.assoc ?? "left",
|
|
7665
|
+
type,
|
|
7666
|
+
kindKey: key,
|
|
7667
|
+
word
|
|
7668
|
+
};
|
|
7669
|
+
const table = op.fixity === "prefix" ? prefix : binary;
|
|
7670
|
+
if (table.has(op.symbol)) throw new TypeError(`The operator "${op.symbol}" has two entries of the same place.`);
|
|
7671
|
+
table.set(op.symbol, op);
|
|
7672
|
+
if (op.fixity === "infix") {
|
|
7673
|
+
const assoc = assocAt.get(op.prec);
|
|
7674
|
+
if (assoc !== void 0 && assoc !== op.assoc) throw new TypeError(`The infix operators of the precedence ${String(op.prec)} have different groupings.`);
|
|
7675
|
+
assocAt.set(op.prec, op.assoc);
|
|
7676
|
+
}
|
|
7677
|
+
if (word) opWords.add(op.symbol);
|
|
7678
|
+
addTo(byKind, key, op);
|
|
7679
|
+
}
|
|
7680
|
+
const longestFirst = (table) => [...table.keys()].filter((symbol) => !NAME.test(symbol)).sort((a, b) => b.length - a.length);
|
|
7681
|
+
return {
|
|
7682
|
+
prefix,
|
|
7683
|
+
binary,
|
|
7684
|
+
prefixSymbols: longestFirst(prefix),
|
|
7685
|
+
binarySymbols: longestFirst(binary),
|
|
7686
|
+
byKind,
|
|
7687
|
+
opWords
|
|
7688
|
+
};
|
|
7689
|
+
}
|
|
7690
|
+
function compileProfile(profile) {
|
|
7691
|
+
const cached = compiledProfiles.get(profile);
|
|
7692
|
+
if (cached !== void 0) return cached;
|
|
7693
|
+
const calls = profile.calls ?? {};
|
|
7694
|
+
const separator = calls.separator ?? ",";
|
|
7695
|
+
const ops = compileOps(profile, separator);
|
|
7696
|
+
const literals = profile.literals ?? {};
|
|
7697
|
+
const strings = literals.strings === false ? void 0 : literals.strings ?? {};
|
|
7698
|
+
const words = new Map(Object.entries(literals.words ?? DEFAULT_WORDS));
|
|
7699
|
+
const refs = profile.refs ?? {};
|
|
7700
|
+
const roots = new Set(refs.roots ?? DEFAULT_ROOTS);
|
|
7701
|
+
const forms = /* @__PURE__ */ new Map();
|
|
7702
|
+
const formsByKind = /* @__PURE__ */ new Map();
|
|
7703
|
+
for (const given of calls.forms ?? []) {
|
|
7704
|
+
if (!NAME.test(given.name)) throw new TypeError(`The call form "${given.name}" is not a plain name.`);
|
|
7705
|
+
const { type, key } = typeOfKind(given.kind);
|
|
7706
|
+
const form = {
|
|
7707
|
+
name: given.name,
|
|
7708
|
+
type,
|
|
7709
|
+
fields: given.fields,
|
|
7710
|
+
min: given.min ?? given.fields?.length ?? 0
|
|
7711
|
+
};
|
|
7712
|
+
forms.set(form.name, form);
|
|
7713
|
+
addTo(formsByKind, key, form);
|
|
7714
|
+
}
|
|
7715
|
+
const compiled = {
|
|
7716
|
+
...ops,
|
|
7717
|
+
numbers: literals.numbers ?? true,
|
|
7718
|
+
signed: literals.signed ?? true,
|
|
7719
|
+
quote: strings === void 0 ? void 0 : strings.quote ?? "\"",
|
|
7720
|
+
doubled: strings !== void 0 && strings.escape === "double",
|
|
7721
|
+
words,
|
|
7722
|
+
bare: refs.bare ?? "names",
|
|
7723
|
+
roots,
|
|
7724
|
+
fields: refs.fields ?? true,
|
|
7725
|
+
patterns: (refs.patterns ?? []).map((p) => {
|
|
7726
|
+
const flags = p.pattern.flags.replace(/[gy]/g, "");
|
|
7727
|
+
return {
|
|
7728
|
+
sticky: new RegExp(p.pattern.source, `${flags}y`),
|
|
7729
|
+
whole: new RegExp(`^(?:${p.pattern.source})$`, flags),
|
|
7730
|
+
read: p.read,
|
|
7731
|
+
write: p.write
|
|
7732
|
+
};
|
|
7733
|
+
}),
|
|
7734
|
+
ops: calls.ops === void 0 || calls.ops === true ? true : new Set(calls.ops === false ? [] : calls.ops),
|
|
7735
|
+
forms,
|
|
7736
|
+
formsByKind,
|
|
7737
|
+
separator,
|
|
7738
|
+
reserved: /* @__PURE__ */ new Set([
|
|
7739
|
+
...words.keys(),
|
|
7740
|
+
...ops.opWords,
|
|
7741
|
+
...roots
|
|
7742
|
+
]),
|
|
7743
|
+
maxDepth: profile.maxDepth ?? 256
|
|
7744
|
+
};
|
|
7745
|
+
compiledProfiles.set(profile, compiled);
|
|
7746
|
+
return compiled;
|
|
7747
|
+
}
|
|
7748
|
+
/** The stop of a parse. The parser throws it, and `parse` gives its issue. */
|
|
7749
|
+
var Stop = class {
|
|
7750
|
+
issue;
|
|
7751
|
+
constructor(issue) {
|
|
7752
|
+
this.issue = issue;
|
|
7753
|
+
}
|
|
7754
|
+
};
|
|
7755
|
+
function literalDoc(value) {
|
|
7756
|
+
return encodeValue({}, value);
|
|
7757
|
+
}
|
|
7758
|
+
function opDoc(op, value) {
|
|
7759
|
+
return {
|
|
7760
|
+
type: op.type,
|
|
7761
|
+
value
|
|
7762
|
+
};
|
|
7763
|
+
}
|
|
7764
|
+
const ESCAPES = {
|
|
7765
|
+
"\"": "\"",
|
|
7766
|
+
"'": "'",
|
|
7767
|
+
"\\": "\\",
|
|
7768
|
+
"/": "/",
|
|
7769
|
+
b: "\b",
|
|
7770
|
+
f: "\f",
|
|
7771
|
+
n: "\n",
|
|
7772
|
+
r: "\r",
|
|
7773
|
+
t: " "
|
|
7774
|
+
};
|
|
7775
|
+
var Parser = class {
|
|
7776
|
+
syn;
|
|
7777
|
+
text;
|
|
7778
|
+
pos = 0;
|
|
7779
|
+
depth = 0;
|
|
7780
|
+
constructor(syn, text) {
|
|
7781
|
+
this.syn = syn;
|
|
7782
|
+
this.text = text;
|
|
7783
|
+
}
|
|
7784
|
+
parse() {
|
|
7785
|
+
this.skip();
|
|
7786
|
+
if (this.pos >= this.text.length) this.stop("The text is empty.", 0, 0);
|
|
7787
|
+
const doc = this.expr(0);
|
|
7788
|
+
this.skip();
|
|
7789
|
+
if (this.pos < this.text.length) this.stop("The text continues after the expression.", this.pos, this.tokenLength());
|
|
7790
|
+
return doc;
|
|
7791
|
+
}
|
|
7792
|
+
position() {
|
|
7793
|
+
return this.pos;
|
|
7794
|
+
}
|
|
7795
|
+
stop(message, offset, length) {
|
|
7796
|
+
throw new Stop({
|
|
7797
|
+
offset,
|
|
7798
|
+
length,
|
|
7799
|
+
message
|
|
7800
|
+
});
|
|
7801
|
+
}
|
|
7802
|
+
skip() {
|
|
7803
|
+
while (this.pos < this.text.length && /\s/.test(this.text[this.pos])) this.pos += 1;
|
|
7804
|
+
}
|
|
7805
|
+
at(pattern, pos = this.pos) {
|
|
7806
|
+
pattern.lastIndex = pos;
|
|
7807
|
+
return pattern.exec(this.text)?.[0];
|
|
7808
|
+
}
|
|
7809
|
+
tokenLength() {
|
|
7810
|
+
return (this.at(NAME_AT) ?? this.at(DIGITS_AT) ?? "x").length;
|
|
7811
|
+
}
|
|
7812
|
+
/** This method reads an expression by precedence climbing. It stops at an operator below `min`. */
|
|
7813
|
+
expr(min) {
|
|
7814
|
+
const start = this.pos;
|
|
7815
|
+
this.depth += 1;
|
|
7816
|
+
if (this.depth > this.syn.maxDepth) this.stop(`The text is deeper than ${String(this.syn.maxDepth)} levels.`, start, 0);
|
|
7817
|
+
let left = this.operand();
|
|
7818
|
+
for (;;) {
|
|
7819
|
+
this.skip();
|
|
7820
|
+
const found = this.operatorAt();
|
|
7821
|
+
if (found === void 0 || found.op.prec < min) break;
|
|
7822
|
+
const op = found.op;
|
|
7823
|
+
this.pos += found.length;
|
|
7824
|
+
if (op.fixity === "postfix") {
|
|
7825
|
+
left = opDoc(op, [left]);
|
|
7826
|
+
continue;
|
|
7827
|
+
}
|
|
7828
|
+
if (op.assoc === "flat") {
|
|
7829
|
+
const terms = [left, this.expr(op.prec + 1)];
|
|
7830
|
+
for (;;) {
|
|
7831
|
+
this.skip();
|
|
7832
|
+
const next = this.operatorAt();
|
|
7833
|
+
if (next === void 0 || next.op.fixity !== "infix" || next.op.kindKey !== op.kindKey) break;
|
|
7834
|
+
this.pos += next.length;
|
|
7835
|
+
terms.push(this.expr(op.prec + 1));
|
|
7836
|
+
}
|
|
7837
|
+
left = opDoc(op, terms);
|
|
7838
|
+
continue;
|
|
7839
|
+
}
|
|
7840
|
+
left = opDoc(op, [left, this.expr(op.assoc === "right" ? op.prec : op.prec + 1)]);
|
|
7841
|
+
if (op.assoc === "none") {
|
|
7842
|
+
this.skip();
|
|
7843
|
+
const next = this.operatorAt();
|
|
7844
|
+
if (next !== void 0 && next.op.fixity === "infix" && next.op.prec === op.prec) this.stop(`The operator ${next.op.symbol} does not chain after ${op.symbol}. Add parentheses.`, this.pos, next.length);
|
|
7845
|
+
}
|
|
7846
|
+
}
|
|
7847
|
+
this.depth -= 1;
|
|
7848
|
+
return left;
|
|
7849
|
+
}
|
|
7850
|
+
/** This method finds an infix or a postfix operator at the position. */
|
|
7851
|
+
operatorAt() {
|
|
7852
|
+
const name = this.at(NAME_AT);
|
|
7853
|
+
if (name !== void 0) {
|
|
7854
|
+
const op = this.syn.binary.get(name);
|
|
7855
|
+
return op !== void 0 && op.word ? {
|
|
7856
|
+
op,
|
|
7857
|
+
length: name.length
|
|
7858
|
+
} : void 0;
|
|
7859
|
+
}
|
|
7860
|
+
for (const symbol of this.syn.binarySymbols) if (this.text.startsWith(symbol, this.pos)) return {
|
|
7861
|
+
op: this.syn.binary.get(symbol),
|
|
7862
|
+
length: symbol.length
|
|
7863
|
+
};
|
|
7864
|
+
}
|
|
7865
|
+
operand() {
|
|
7866
|
+
this.skip();
|
|
7867
|
+
const start = this.pos;
|
|
7868
|
+
const c = this.text[start];
|
|
7869
|
+
if (c === void 0) return this.stop("A term is missing at the end of the text.", start, 0);
|
|
7870
|
+
for (const pattern of this.syn.patterns) {
|
|
7871
|
+
const match = this.at(pattern.sticky);
|
|
7872
|
+
if (match !== void 0 && match !== "" && !/[A-Za-z0-9_$(]/.test(this.text[start + match.length] ?? "")) {
|
|
7873
|
+
this.pos += match.length;
|
|
7874
|
+
return {
|
|
7875
|
+
type: "ref",
|
|
7876
|
+
value: pattern.read(match)
|
|
7877
|
+
};
|
|
7878
|
+
}
|
|
7879
|
+
}
|
|
7880
|
+
if (/[0-9]/.test(c)) return this.number(false);
|
|
7881
|
+
if (c === "-" && this.syn.signed && /[0-9]/.test(this.text[start + 1] ?? "")) return this.number(true);
|
|
7882
|
+
if (c === this.syn.quote) return this.string();
|
|
7883
|
+
if (c === "(") return this.group();
|
|
7884
|
+
if (c === "`" || /[A-Za-z_]/.test(c)) return this.named();
|
|
7885
|
+
for (const symbol of this.syn.prefixSymbols) if (this.text.startsWith(symbol, start)) {
|
|
7886
|
+
this.pos += symbol.length;
|
|
7887
|
+
const op = this.syn.prefix.get(symbol);
|
|
7888
|
+
return opDoc(op, [this.expr(op.prec + 1)]);
|
|
7889
|
+
}
|
|
7890
|
+
if (c === ")" || this.text.startsWith(this.syn.separator, start)) return this.stop("A term is missing.", start, 0);
|
|
7891
|
+
return this.stop(`The text "${c}" cannot start a term.`, start, 1);
|
|
7892
|
+
}
|
|
7893
|
+
number(negative) {
|
|
7894
|
+
const start = this.pos;
|
|
7895
|
+
if (!this.syn.numbers) this.stop("This syntax has no numbers.", start, this.tokenLength());
|
|
7896
|
+
if (negative) this.pos += 1;
|
|
7897
|
+
const digits = this.at(NUMBER_AT);
|
|
7898
|
+
this.pos += digits.length;
|
|
7899
|
+
const value = Number(digits);
|
|
7900
|
+
return literalDoc(negative ? -value : value);
|
|
7901
|
+
}
|
|
7902
|
+
string() {
|
|
7903
|
+
const start = this.pos;
|
|
7904
|
+
const quote = this.syn.quote;
|
|
7905
|
+
let out = "";
|
|
7906
|
+
this.pos += 1;
|
|
7907
|
+
for (;;) {
|
|
7908
|
+
const c = this.text[this.pos];
|
|
7909
|
+
if (c === void 0) return this.stop("A closing quote is missing.", start, this.text.length - start);
|
|
7910
|
+
if (c === quote) {
|
|
7911
|
+
if (this.syn.doubled && this.text[this.pos + 1] === quote) {
|
|
7912
|
+
out += quote;
|
|
7913
|
+
this.pos += 2;
|
|
7914
|
+
continue;
|
|
7915
|
+
}
|
|
7916
|
+
this.pos += 1;
|
|
7917
|
+
return literalDoc(out);
|
|
7918
|
+
}
|
|
7919
|
+
if (c === "\\" && !this.syn.doubled) {
|
|
7920
|
+
out += this.escape();
|
|
7921
|
+
continue;
|
|
7922
|
+
}
|
|
7923
|
+
out += c;
|
|
7924
|
+
this.pos += 1;
|
|
7925
|
+
}
|
|
7926
|
+
}
|
|
7927
|
+
escape() {
|
|
7928
|
+
const start = this.pos;
|
|
7929
|
+
const c = this.text[start + 1] ?? "";
|
|
7930
|
+
if (c === "u") {
|
|
7931
|
+
const hex = this.text.slice(start + 2, start + 6);
|
|
7932
|
+
if (!/^[0-9A-Fa-f]{4}$/.test(hex)) return this.stop("The escape \\u needs four hex digits.", start, 2);
|
|
7933
|
+
this.pos += 6;
|
|
7934
|
+
return String.fromCharCode(Number.parseInt(hex, 16));
|
|
7935
|
+
}
|
|
7936
|
+
const out = Object.hasOwn(ESCAPES, c) ? ESCAPES[c] : void 0;
|
|
7937
|
+
if (out === void 0) return this.stop(`The escape \\${c} is not valid.`, start, 2);
|
|
7938
|
+
this.pos += 2;
|
|
7939
|
+
return out;
|
|
7940
|
+
}
|
|
7941
|
+
group() {
|
|
7942
|
+
const open = this.pos;
|
|
7943
|
+
this.pos += 1;
|
|
7944
|
+
const inner = this.expr(0);
|
|
7945
|
+
this.skip();
|
|
7946
|
+
if (this.text[this.pos] !== ")") {
|
|
7947
|
+
if (this.pos >= this.text.length) this.stop("A closing parenthesis is missing.", open, 1);
|
|
7948
|
+
this.stop("A closing parenthesis is missing.", this.pos, this.tokenLength());
|
|
7949
|
+
}
|
|
7950
|
+
this.pos += 1;
|
|
7951
|
+
return inner;
|
|
7952
|
+
}
|
|
7953
|
+
/** This method reads a name in backticks. Two backticks in the name are one backtick. */
|
|
7954
|
+
quoted() {
|
|
7955
|
+
const start = this.pos;
|
|
7956
|
+
let out = "";
|
|
7957
|
+
this.pos += 1;
|
|
7958
|
+
for (;;) {
|
|
7959
|
+
const c = this.text[this.pos];
|
|
7960
|
+
if (c === void 0) return this.stop("A closing backtick is missing.", start, this.text.length - start);
|
|
7961
|
+
if (c === "`") {
|
|
7962
|
+
if (this.text[this.pos + 1] !== "`") {
|
|
7963
|
+
this.pos += 1;
|
|
7964
|
+
return out;
|
|
7965
|
+
}
|
|
7966
|
+
this.pos += 1;
|
|
7967
|
+
}
|
|
7968
|
+
out += c;
|
|
7969
|
+
this.pos += 1;
|
|
7970
|
+
}
|
|
7971
|
+
}
|
|
7972
|
+
/** This method reads a name: a word operator, a call, a literal word or a reference. */
|
|
7973
|
+
named() {
|
|
7974
|
+
const start = this.pos;
|
|
7975
|
+
const plain = this.text[start] !== "`";
|
|
7976
|
+
const first = plain ? this.at(NAME_AT) : this.quoted();
|
|
7977
|
+
if (plain) {
|
|
7978
|
+
this.pos += first.length;
|
|
7979
|
+
const prefix = this.syn.prefix.get(first);
|
|
7980
|
+
if (prefix !== void 0 && prefix.word) return opDoc(prefix, [this.expr(prefix.prec + 1)]);
|
|
7981
|
+
if (this.syn.binary.get(first)?.word === true) this.stop(`A term is missing before ${first}.`, start, first.length);
|
|
7982
|
+
}
|
|
7983
|
+
const path = [first];
|
|
7984
|
+
let allPlain = plain;
|
|
7985
|
+
for (;;) {
|
|
7986
|
+
const back = this.pos;
|
|
7987
|
+
this.skip();
|
|
7988
|
+
if (!this.syn.fields || this.text[this.pos] !== ".") {
|
|
7989
|
+
this.pos = back;
|
|
7990
|
+
break;
|
|
7991
|
+
}
|
|
7992
|
+
this.pos += 1;
|
|
7993
|
+
this.skip();
|
|
7994
|
+
if (this.text[this.pos] === "`") {
|
|
7995
|
+
path.push(this.quoted());
|
|
7996
|
+
allPlain = false;
|
|
7997
|
+
continue;
|
|
7998
|
+
}
|
|
7999
|
+
const name = this.at(NAME_AT);
|
|
8000
|
+
const digits = name === void 0 ? this.at(DIGITS_AT) : void 0;
|
|
8001
|
+
if (name === void 0 && digits === void 0) this.stop("A name is missing after the dot.", this.pos, 0);
|
|
8002
|
+
this.pos += (name ?? digits).length;
|
|
8003
|
+
path.push(name ?? Number(digits));
|
|
8004
|
+
if (name === void 0) allPlain = false;
|
|
8005
|
+
}
|
|
8006
|
+
const back = this.pos;
|
|
8007
|
+
this.skip();
|
|
8008
|
+
if (this.text[this.pos] === "(") {
|
|
8009
|
+
if (!allPlain) this.stop("The name of a call is a plain name.", start, back - start);
|
|
8010
|
+
return this.call(path.join("."), start, back - start);
|
|
8011
|
+
}
|
|
8012
|
+
this.pos = back;
|
|
8013
|
+
if (plain && path.length === 1 && this.syn.words.has(first)) return literalDoc(this.syn.words.get(first));
|
|
8014
|
+
return {
|
|
8015
|
+
type: "ref",
|
|
8016
|
+
value: plain && this.syn.roots.has(first) ? path : [this.syn.bare, ...path]
|
|
8017
|
+
};
|
|
8018
|
+
}
|
|
8019
|
+
call(name, start, nameLength) {
|
|
8020
|
+
const open = this.pos;
|
|
8021
|
+
const args = [];
|
|
8022
|
+
this.pos += 1;
|
|
8023
|
+
this.skip();
|
|
8024
|
+
if (this.text[this.pos] === ")") this.pos += 1;
|
|
8025
|
+
else for (;;) {
|
|
8026
|
+
args.push(this.expr(0));
|
|
8027
|
+
this.skip();
|
|
8028
|
+
if (this.text.startsWith(this.syn.separator, this.pos)) {
|
|
8029
|
+
this.pos += this.syn.separator.length;
|
|
8030
|
+
continue;
|
|
8031
|
+
}
|
|
8032
|
+
if (this.text[this.pos] === ")") {
|
|
8033
|
+
this.pos += 1;
|
|
8034
|
+
break;
|
|
8035
|
+
}
|
|
8036
|
+
if (this.pos >= this.text.length) this.stop("A closing parenthesis is missing.", open, 1);
|
|
8037
|
+
this.stop(`A "${this.syn.separator}" or a closing parenthesis is missing.`, this.pos, this.tokenLength());
|
|
8038
|
+
}
|
|
8039
|
+
const form = this.syn.forms.get(name);
|
|
8040
|
+
if (form !== void 0) {
|
|
8041
|
+
const most = form.fields?.length ?? Number.POSITIVE_INFINITY;
|
|
8042
|
+
if (args.length < form.min || args.length > most) {
|
|
8043
|
+
const range = most === form.min ? String(most) : most === Number.POSITIVE_INFINITY ? `${String(form.min)} or more` : `${String(form.min)} to ${String(most)}`;
|
|
8044
|
+
this.stop(`The form ${name} takes ${range} arguments.`, start, this.pos - start);
|
|
8045
|
+
}
|
|
8046
|
+
if (form.fields === void 0) return opDoc(form, args);
|
|
8047
|
+
const value = {};
|
|
8048
|
+
args.forEach((arg, index) => {
|
|
8049
|
+
value[form.fields?.[index]] = arg;
|
|
8050
|
+
});
|
|
8051
|
+
return opDoc(form, value);
|
|
8052
|
+
}
|
|
8053
|
+
if (this.syn.ops !== true && !this.syn.ops.has(name)) this.stop(`This syntax has no function ${name}.`, start, nameLength);
|
|
8054
|
+
return {
|
|
8055
|
+
type: ["ops", ...name.split(".")],
|
|
8056
|
+
value: args
|
|
8057
|
+
};
|
|
8058
|
+
}
|
|
8059
|
+
};
|
|
8060
|
+
/**
|
|
8061
|
+
* This function reads a text into a document with the rules of a profile (SYNTAX.PARSE). It does not throw for a
|
|
8062
|
+
* bad text. The issue gives the offset, the length and a message (SYNTAX.SPAN).
|
|
8063
|
+
*
|
|
8064
|
+
* @param profile - The syntax profile.
|
|
8065
|
+
* @param text - The text.
|
|
8066
|
+
* @returns The document, or the issues of the text.
|
|
8067
|
+
* @throws {TypeError} The profile is not valid, for example an operator has two entries of the same place.
|
|
8068
|
+
*/
|
|
8069
|
+
function parse(profile, text) {
|
|
8070
|
+
const parser = new Parser(compileProfile(profile), text);
|
|
8071
|
+
try {
|
|
8072
|
+
return {
|
|
8073
|
+
ok: true,
|
|
8074
|
+
doc: parser.parse()
|
|
8075
|
+
};
|
|
8076
|
+
} catch (error) {
|
|
8077
|
+
if (error instanceof Stop) return {
|
|
8078
|
+
ok: false,
|
|
8079
|
+
issues: [error.issue]
|
|
8080
|
+
};
|
|
8081
|
+
if (isStackOverflow(error)) return {
|
|
8082
|
+
ok: false,
|
|
8083
|
+
issues: [{
|
|
8084
|
+
offset: parser.position(),
|
|
8085
|
+
length: 0,
|
|
8086
|
+
message: "The text is too deep for the stack of the host."
|
|
8087
|
+
}]
|
|
8088
|
+
};
|
|
8089
|
+
throw error;
|
|
8090
|
+
}
|
|
8091
|
+
}
|
|
8092
|
+
function none(reason) {
|
|
8093
|
+
return {
|
|
8094
|
+
tag: "none",
|
|
8095
|
+
reason
|
|
8096
|
+
};
|
|
8097
|
+
}
|
|
8098
|
+
function quoteName(name) {
|
|
8099
|
+
return `\`${name.replaceAll("`", "``")}\``;
|
|
8100
|
+
}
|
|
8101
|
+
function wordFor(syn, value) {
|
|
8102
|
+
for (const [word, held] of syn.words) if (Object.is(held, value)) return word;
|
|
8103
|
+
}
|
|
8104
|
+
function textOfString(syn, value) {
|
|
8105
|
+
const quote = syn.quote;
|
|
8106
|
+
if (quote === void 0) return void 0;
|
|
8107
|
+
if (syn.doubled) return `${quote}${value.replaceAll(quote, quote + quote)}${quote}`;
|
|
8108
|
+
const json = JSON.stringify(value);
|
|
8109
|
+
return quote === "\"" ? json : `'${json.slice(1, -1).replaceAll("\\\"", "\"").replaceAll("'", "\\'")}'`;
|
|
8110
|
+
}
|
|
8111
|
+
function textOfNumber(syn, value) {
|
|
8112
|
+
if (!syn.numbers) return wordFor(syn, value);
|
|
8113
|
+
const negative = value < 0 || Object.is(value, -0);
|
|
8114
|
+
if (negative && !syn.signed) return wordFor(syn, value);
|
|
8115
|
+
if (Number.isFinite(value)) return negative ? `-${String(-value)}` : String(value);
|
|
8116
|
+
return wordFor(syn, value) ?? (Number.isNaN(value) ? void 0 : negative ? "-1e999" : "1e999");
|
|
8117
|
+
}
|
|
8118
|
+
function literalShape(syn, value) {
|
|
8119
|
+
const text = typeof value === "string" ? textOfString(syn, value) : typeof value === "number" ? textOfNumber(syn, value) : wordFor(syn, value);
|
|
8120
|
+
return text === void 0 ? none(`This syntax has no text for the literal ${describe(value)}.`) : {
|
|
8121
|
+
tag: "text",
|
|
8122
|
+
text
|
|
8123
|
+
};
|
|
8124
|
+
}
|
|
8125
|
+
function plainSegment(syn, name) {
|
|
8126
|
+
return NAME.test(name) && !syn.reserved.has(name) && !syn.patterns.some((p) => p.whole.test(name));
|
|
8127
|
+
}
|
|
8128
|
+
function laterSegment(step) {
|
|
8129
|
+
if (typeof step === "string") return NAME.test(step) ? step : quoteName(step);
|
|
8130
|
+
return typeof step === "number" && Number.isSafeInteger(step) && step >= 0 ? String(step) : void 0;
|
|
8131
|
+
}
|
|
8132
|
+
function refShape(syn, value) {
|
|
8133
|
+
if (!Array.isArray(value) || value.length === 0) return none("The path of the reference is empty.");
|
|
8134
|
+
for (const pattern of syn.patterns) {
|
|
8135
|
+
const text = pattern.write(value);
|
|
8136
|
+
if (text !== void 0) return {
|
|
8137
|
+
tag: "text",
|
|
8138
|
+
text
|
|
8139
|
+
};
|
|
8140
|
+
}
|
|
8141
|
+
const [root, ...rest] = value;
|
|
8142
|
+
if ((rest.length > 1 || root !== syn.bare && rest.length > 0) && !syn.fields) return none("This syntax has no field steps.");
|
|
8143
|
+
const later = rest.slice(1).map(laterSegment);
|
|
8144
|
+
let first;
|
|
8145
|
+
let tail = later;
|
|
8146
|
+
if (root === syn.bare && typeof rest[0] === "string") first = plainSegment(syn, rest[0]) ? rest[0] : quoteName(rest[0]);
|
|
8147
|
+
else if (typeof root === "string" && syn.roots.has(root) && !syn.patterns.some((p) => p.whole.test(root))) {
|
|
8148
|
+
first = root;
|
|
8149
|
+
tail = rest.map(laterSegment);
|
|
8150
|
+
}
|
|
8151
|
+
if (first === void 0 || tail.includes(void 0)) return none("This syntax has no text for the path of the reference.");
|
|
8152
|
+
return {
|
|
8153
|
+
tag: "text",
|
|
8154
|
+
text: [first, ...tail].join(".")
|
|
8155
|
+
};
|
|
8156
|
+
}
|
|
8157
|
+
function callName(syn, keys) {
|
|
8158
|
+
if (keys.length === 0 || !keys.every((key) => typeof key === "string" && NAME.test(key))) return void 0;
|
|
8159
|
+
const name = keys.join(".");
|
|
8160
|
+
if (syn.forms.has(name) || syn.opWords.has(keys[0])) return void 0;
|
|
8161
|
+
return syn.ops === true || syn.ops.has(name) ? name : void 0;
|
|
8162
|
+
}
|
|
8163
|
+
function formShape(form, value) {
|
|
8164
|
+
if (form.fields === void 0) {
|
|
8165
|
+
if (!Array.isArray(value) || value.length < form.min) return void 0;
|
|
8166
|
+
return {
|
|
8167
|
+
tag: "call",
|
|
8168
|
+
name: form.name,
|
|
8169
|
+
args: value.map((item, index) => [index, item])
|
|
8170
|
+
};
|
|
8171
|
+
}
|
|
8172
|
+
if (!isRecord(value)) return void 0;
|
|
8173
|
+
const count = Object.keys(value).length;
|
|
8174
|
+
const present = form.fields.slice(0, count);
|
|
8175
|
+
if (count < form.min || present.length < count || !present.every((key) => Object.hasOwn(value, key))) return void 0;
|
|
8176
|
+
return {
|
|
8177
|
+
tag: "call",
|
|
8178
|
+
name: form.name,
|
|
8179
|
+
args: present.map((key) => [key, value[key]])
|
|
8180
|
+
};
|
|
8181
|
+
}
|
|
8182
|
+
function opShape(syn, key, terms) {
|
|
8183
|
+
for (const op of syn.byKind.get(key) ?? []) if (op.fixity === "infix" ? terms.length === 2 || op.assoc === "flat" && terms.length > 2 : terms.length === 1) return {
|
|
8184
|
+
tag: "op",
|
|
8185
|
+
op,
|
|
8186
|
+
terms
|
|
8187
|
+
};
|
|
8188
|
+
}
|
|
8189
|
+
/** This function gives the shape of a document: a literal or a reference, an operator, a call, or no text. */
|
|
8190
|
+
function shapeOf(syn, doc) {
|
|
8191
|
+
if (!isEnvelope(doc)) return Array.isArray(doc) || isRecord(doc) ? none("This syntax has no text for a list or a record literal.") : literalShape(syn, doc);
|
|
8192
|
+
if (doc.name !== void 0 || doc.own !== void 0) return none("This syntax has no text for the header of a document.");
|
|
8193
|
+
const path = typePath(doc.type);
|
|
8194
|
+
if (path === void 0) return none("This syntax has no text for a computed type.");
|
|
8195
|
+
const value = doc.value;
|
|
8196
|
+
const name = path.join(".");
|
|
8197
|
+
if (name === "lit") return isEnvelope(value) || Array.isArray(value) || isRecord(value) ? none("This syntax has no text for a list or a record literal.") : literalShape(syn, value);
|
|
8198
|
+
if (name === "ext.undefined") return literalShape(syn, void 0);
|
|
8199
|
+
if (name === "ext.number") {
|
|
8200
|
+
const numbers = {
|
|
8201
|
+
NaN: NaN,
|
|
8202
|
+
Infinity: Number.POSITIVE_INFINITY,
|
|
8203
|
+
"-Infinity": Number.NEGATIVE_INFINITY,
|
|
8204
|
+
"-0": -0
|
|
8205
|
+
};
|
|
8206
|
+
return typeof value === "string" && Object.hasOwn(numbers, value) ? literalShape(syn, numbers[value]) : none("The value of ext.number is not valid.");
|
|
8207
|
+
}
|
|
8208
|
+
if (name === "ref") return refShape(syn, value);
|
|
8209
|
+
const key = JSON.stringify(path);
|
|
8210
|
+
const op = Array.isArray(value) ? opShape(syn, key, value) : void 0;
|
|
8211
|
+
if (op !== void 0) return op;
|
|
8212
|
+
for (const form of syn.formsByKind.get(key) ?? []) {
|
|
8213
|
+
const shape = formShape(form, value);
|
|
8214
|
+
if (shape !== void 0) return shape;
|
|
8215
|
+
}
|
|
8216
|
+
const called = path[0] === "ops" && Array.isArray(value) ? callName(syn, path.slice(1)) : void 0;
|
|
8217
|
+
if (called !== void 0) return {
|
|
8218
|
+
tag: "call",
|
|
8219
|
+
name: called,
|
|
8220
|
+
args: value.map((item, index) => [index, item])
|
|
8221
|
+
};
|
|
8222
|
+
return none(`This syntax has no text for the kind ${name}.`);
|
|
8223
|
+
}
|
|
8224
|
+
function levelOf(syn, doc) {
|
|
8225
|
+
const shape = shapeOf(syn, doc);
|
|
8226
|
+
return shape.tag === "op" ? shape.op.prec : Number.POSITIVE_INFINITY;
|
|
8227
|
+
}
|
|
8228
|
+
/** This function tells if a term needs parentheses in its position, so that `parse` reads the same tree. */
|
|
8229
|
+
function needsGroup(op, index, level) {
|
|
8230
|
+
if (op.fixity === "postfix") return level < op.prec;
|
|
8231
|
+
if (op.fixity === "prefix" || op.assoc === "none" || op.assoc === "flat") return level <= op.prec;
|
|
8232
|
+
return (op.assoc === "left" ? index === 0 : index !== 0) ? level < op.prec : level <= op.prec;
|
|
8233
|
+
}
|
|
8234
|
+
function opText(syn, op, terms, sub) {
|
|
8235
|
+
const texts = [];
|
|
8236
|
+
for (let index = 0; index < terms.length; index += 1) {
|
|
8237
|
+
const term = terms[index];
|
|
8238
|
+
const result = sub(term, [index]);
|
|
8239
|
+
if (!result.ok) return result;
|
|
8240
|
+
let text = result.value;
|
|
8241
|
+
const signedDigit = op.fixity === "prefix" && op.symbol === "-" && syn.signed && /^[0-9]/.test(text);
|
|
8242
|
+
if (needsGroup(op, index, levelOf(syn, term)) || signedDigit) text = `(${text})`;
|
|
8243
|
+
texts.push(text);
|
|
8244
|
+
}
|
|
8245
|
+
const [only = ""] = texts;
|
|
8246
|
+
if (op.fixity === "prefix") return ok(op.word || /^[^A-Za-z0-9_("'`$]/.test(only) ? `${op.symbol} ${only}` : `${op.symbol}${only}`);
|
|
8247
|
+
if (op.fixity === "postfix") return ok(op.word ? `${only} ${op.symbol}` : `${only}${op.symbol}`);
|
|
8248
|
+
return ok(texts.join(` ${op.symbol} `));
|
|
8249
|
+
}
|
|
8250
|
+
/** This function writes one document. `sub` gives the text of each part. */
|
|
8251
|
+
function printNode(syn, doc, sub) {
|
|
8252
|
+
const shape = shapeOf(syn, doc);
|
|
8253
|
+
if (shape.tag === "text") return ok(shape.text);
|
|
8254
|
+
if (shape.tag === "none") return fail("#VALUE!", shape.reason);
|
|
8255
|
+
if (shape.tag === "op") return opText(syn, shape.op, shape.terms, sub);
|
|
8256
|
+
const texts = [];
|
|
8257
|
+
for (const [key, arg] of shape.args) {
|
|
8258
|
+
const result = sub(arg, [key]);
|
|
8259
|
+
if (!result.ok) return result;
|
|
8260
|
+
texts.push(result.value);
|
|
8261
|
+
}
|
|
8262
|
+
return ok(`${shape.name}(${texts.join(`${syn.separator} `)})`);
|
|
8263
|
+
}
|
|
8264
|
+
function printTree(syn, doc, place) {
|
|
8265
|
+
const result = printNode(syn, doc, (child, path) => printTree(syn, child, [...place, ...path]));
|
|
8266
|
+
return result.ok || result.error.place !== void 0 ? result : fail(result.error.code, result.error.reason, { place });
|
|
8267
|
+
}
|
|
8268
|
+
/**
|
|
8269
|
+
* This function writes a document as text with the rules of a profile (SYNTAX.PRINT). It writes the fewest
|
|
8270
|
+
* parentheses that keep the tree. `parse` reads the text back to the same document (SYNTAX.ROUNDTRIP).
|
|
8271
|
+
*
|
|
8272
|
+
* @param profile - The syntax profile.
|
|
8273
|
+
* @param doc - The document.
|
|
8274
|
+
* @returns The text, or `#VALUE!` at the place of a document that the profile cannot write.
|
|
8275
|
+
* @throws {TypeError} The profile is not valid.
|
|
8276
|
+
*/
|
|
8277
|
+
function print(profile, doc) {
|
|
8278
|
+
const syn = compileProfile(profile);
|
|
8279
|
+
try {
|
|
8280
|
+
return printTree(syn, doc, []);
|
|
8281
|
+
} catch (error) {
|
|
8282
|
+
if (isStackOverflow(error)) return fail("#DEPTH!", "The document is too deep for the stack of the host.", { cause: error });
|
|
8283
|
+
throw error;
|
|
8284
|
+
}
|
|
8285
|
+
}
|
|
8286
|
+
/**
|
|
8287
|
+
* This function makes the part of a merge that adds the verb `format` to a machine (D23, D41). Its default writes
|
|
8288
|
+
* each document with the printer of the profile. A part gives its own text through the ladder, thus an own entry or
|
|
8289
|
+
* a kind entry of `format` still wins.
|
|
8290
|
+
*
|
|
8291
|
+
* @param profile - The syntax profile.
|
|
8292
|
+
* @returns The part for `mergeMachines`.
|
|
8293
|
+
*/
|
|
8294
|
+
function syntaxFormat(profile) {
|
|
8295
|
+
const syn = compileProfile(profile);
|
|
8296
|
+
const code = ({ doc, parts, place }) => {
|
|
8297
|
+
const byPath = new Map(parts.map((part) => [JSON.stringify(part.path), part]));
|
|
8298
|
+
return printNode(syn, doc, (child, path) => {
|
|
8299
|
+
const part = byPath.get(JSON.stringify(path));
|
|
8300
|
+
if (part === void 0) return printTree(syn, child, [...place, ...path]);
|
|
8301
|
+
const result = part.evaluate();
|
|
8302
|
+
if (!result.ok) return result;
|
|
8303
|
+
return typeof result.value === "string" ? ok(result.value) : fail("#VALUE!", `The verb format of a part gave ${describe(result.value)}, not a text.`, { place: [...place, ...path] });
|
|
8304
|
+
});
|
|
8305
|
+
};
|
|
8306
|
+
return {
|
|
8307
|
+
world: { defaults: { format: "syntax.format" } },
|
|
8308
|
+
evaluators: { "syntax.format": code }
|
|
8309
|
+
};
|
|
8310
|
+
}
|
|
8311
|
+
//#endregion
|
|
6801
8312
|
//#region src/reference.ts
|
|
6802
8313
|
function own$1(table, name) {
|
|
6803
8314
|
return table !== void 0 && Object.hasOwn(table, name) ? table[name] : void 0;
|
|
@@ -6849,13 +8360,22 @@ function examine$1(env, at) {
|
|
|
6849
8360
|
split
|
|
6850
8361
|
};
|
|
6851
8362
|
}
|
|
8363
|
+
/**
|
|
8364
|
+
* This function gives the effects that two lists of capabilities both permit (CAP.ONLY). The reference reads the rule
|
|
8365
|
+
* from SPEC directly, and not from the intersection of the plan. Thus `agree` finds a bug in either one (D15).
|
|
8366
|
+
*/
|
|
8367
|
+
function bothPermit(a, b) {
|
|
8368
|
+
const out = [];
|
|
8369
|
+
for (const cap of [...a, ...b]) if (permits(a, cap) && permits(b, cap) && !permits(out, cap)) out.push(cap);
|
|
8370
|
+
return out;
|
|
8371
|
+
}
|
|
6852
8372
|
/** This function gives the capabilities that the rules of a kind give one part (D14, CAP.CONTRACT, CAP.ONLY). */
|
|
6853
8373
|
function capsOfPart(rules, path, caps) {
|
|
6854
8374
|
let out = caps;
|
|
6855
8375
|
for (const rule of rules ?? []) {
|
|
6856
8376
|
if (rule.in !== void 0 && !rule.in.some((pattern) => pathMatches(path, pattern))) continue;
|
|
6857
8377
|
if (rule.rule === "mask") return [];
|
|
6858
|
-
if (rule.rule === "only") out =
|
|
8378
|
+
if (rule.rule === "only") out = bothPermit(out, rule.caps);
|
|
6859
8379
|
}
|
|
6860
8380
|
return out;
|
|
6861
8381
|
}
|
|
@@ -7008,6 +8528,7 @@ function evalInput(env, seen, at) {
|
|
|
7008
8528
|
parts,
|
|
7009
8529
|
context: at.ctx,
|
|
7010
8530
|
place: at.place,
|
|
8531
|
+
machine: env.machine,
|
|
7011
8532
|
...at.address ? { address: true } : {},
|
|
7012
8533
|
resolve: (path, options) => resolveFully(env.machine, at.ctx, path, options),
|
|
7013
8534
|
effect: (kind, payload) => cause(env, at.ctx, kind, payload, at.place),
|
|
@@ -7035,6 +8556,7 @@ function runInput(env, seen, at) {
|
|
|
7035
8556
|
parts,
|
|
7036
8557
|
context: at.ctx,
|
|
7037
8558
|
place: at.place,
|
|
8559
|
+
machine: env.machine,
|
|
7038
8560
|
resolve: (path, options) => resolveFully(env.machine, at.ctx, path, options),
|
|
7039
8561
|
effect: function* (kind, payload) {
|
|
7040
8562
|
return cause(env, at.ctx, kind, payload, at.place);
|
|
@@ -7125,6 +8647,7 @@ function reference(machine, doc, context = makeContext(), options = {}) {
|
|
|
7125
8647
|
...context,
|
|
7126
8648
|
world: worldThing(machine.world)
|
|
7127
8649
|
} : context;
|
|
8650
|
+
activeEnvs.push(env);
|
|
7128
8651
|
try {
|
|
7129
8652
|
return evaluateAt(env, {
|
|
7130
8653
|
value: doc,
|
|
@@ -7136,8 +8659,12 @@ function reference(machine, doc, context = makeContext(), options = {}) {
|
|
|
7136
8659
|
});
|
|
7137
8660
|
} catch (error) {
|
|
7138
8661
|
return placed(thrown(error, "the reference"), []);
|
|
8662
|
+
} finally {
|
|
8663
|
+
activeEnvs.pop();
|
|
7139
8664
|
}
|
|
7140
8665
|
}
|
|
8666
|
+
/** The evaluations of the reference that run at this time, the innermost last. */
|
|
8667
|
+
const activeEnvs = [];
|
|
7141
8668
|
const refNodes = /* @__PURE__ */ new WeakMap();
|
|
7142
8669
|
/** The attributes in evaluation in the reference, in the order of their demands (TREE.CYCLE). */
|
|
7143
8670
|
const inEvaluation = [];
|
|
@@ -7147,28 +8674,109 @@ function nameOfAttribute(node, name) {
|
|
|
7147
8674
|
function refNodeOf(thing) {
|
|
7148
8675
|
return typeof thing === "object" && thing !== null ? refNodes.get(thing) : void 0;
|
|
7149
8676
|
}
|
|
7150
|
-
/**
|
|
7151
|
-
|
|
8677
|
+
/**
|
|
8678
|
+
* TREE.LADDER: an equation of the kind of a node evaluates in the reference. It gets the node as `self`, the names of
|
|
8679
|
+
* its rung and no capabilities. A bare equation gets the type `of` of the parts of the kind.
|
|
8680
|
+
*/
|
|
8681
|
+
function equationOf(node, term, names) {
|
|
8682
|
+
const env = node.env;
|
|
8683
|
+
if (env === void 0) return fail("#NOEVAL", "The evaluation of an equation needs the machine.");
|
|
8684
|
+
const ctx = {
|
|
8685
|
+
...node.ctx,
|
|
8686
|
+
names,
|
|
8687
|
+
caps: [],
|
|
8688
|
+
self: node.thing
|
|
8689
|
+
};
|
|
8690
|
+
return reference(env.machine, term, ctx, {
|
|
8691
|
+
definitions: env.definitions,
|
|
8692
|
+
...node.of === void 0 ? {} : { declaredType: node.of }
|
|
8693
|
+
});
|
|
8694
|
+
}
|
|
8695
|
+
function termIn(table, name) {
|
|
8696
|
+
return table === void 0 || !Object.hasOwn(table, name) ? void 0 : table[name];
|
|
8697
|
+
}
|
|
8698
|
+
/** TREE.DOWN: the names of a child for an equation of `down` of its parent: `child`, `index` and `prev`. */
|
|
8699
|
+
function namesOfChild(up, child) {
|
|
8700
|
+
const children = attributeOf(up, "children");
|
|
8701
|
+
if (children !== void 0 && !children.ok) return children;
|
|
8702
|
+
const list = children !== void 0 && Array.isArray(children.value) ? children.value : [];
|
|
8703
|
+
const index = list.indexOf(child.thing);
|
|
8704
|
+
return ok({
|
|
8705
|
+
child: child.thing,
|
|
8706
|
+
index: index < 0 ? null : index,
|
|
8707
|
+
prev: index > 0 ? list[index - 1] : null
|
|
8708
|
+
});
|
|
8709
|
+
}
|
|
8710
|
+
/** TREE.FOLD: the unit, then `combine` with the attribute of each node of this tree in the list `children`. */
|
|
8711
|
+
function foldOf(node, name, fold) {
|
|
8712
|
+
let acc = equationOf(node, fold.unit, {});
|
|
8713
|
+
if (!acc.ok) return acc;
|
|
8714
|
+
const children = attributeOf(node, "children");
|
|
8715
|
+
if (children === void 0) return acc;
|
|
8716
|
+
if (!children.ok) return children;
|
|
8717
|
+
if (!Array.isArray(children.value)) return fail("#VALUE!", `The fold of ${name} needs a list in children.`);
|
|
8718
|
+
for (const item of children.value) {
|
|
8719
|
+
const child = refNodeOf(item);
|
|
8720
|
+
if (child === void 0 || child.tree !== node.tree) continue;
|
|
8721
|
+
const value = attributeOf(child, name);
|
|
8722
|
+
if (value === void 0) return fail("#N/A", `No rung gives ${nameOfAttribute(child, name)}.`);
|
|
8723
|
+
if (!value.ok) return value;
|
|
8724
|
+
acc = equationOf(node, fold.combine, {
|
|
8725
|
+
acc: acc.value,
|
|
8726
|
+
item: value.value
|
|
8727
|
+
});
|
|
8728
|
+
if (!acc.ok) return acc;
|
|
8729
|
+
}
|
|
8730
|
+
return acc;
|
|
8731
|
+
}
|
|
8732
|
+
/**
|
|
8733
|
+
* TREE.LADDER: the rungs of the ladder of an attribute, in their order. A rung gives `undefined` when it does not
|
|
8734
|
+
* answer, and the next rung then tries.
|
|
8735
|
+
*/
|
|
8736
|
+
const REF_RUNGS = [
|
|
8737
|
+
["own", (node, name) => node.parts.find((item) => item.key === name)?.evaluate({
|
|
8738
|
+
...node.ctx,
|
|
8739
|
+
self: node.thing
|
|
8740
|
+
})],
|
|
8741
|
+
["kind", (node, name) => {
|
|
8742
|
+
const term = termIn(node.contract?.equations, name);
|
|
8743
|
+
return term === void 0 ? void 0 : equationOf(node, term, {});
|
|
8744
|
+
}],
|
|
8745
|
+
["parent", (node, name) => {
|
|
8746
|
+
const up = node.up;
|
|
8747
|
+
const term = up === void 0 ? void 0 : termIn(up.contract?.down, name);
|
|
8748
|
+
if (up === void 0 || term === void 0) return void 0;
|
|
8749
|
+
const names = namesOfChild(up, node);
|
|
8750
|
+
return names.ok ? equationOf(up, term, names.value) : names;
|
|
8751
|
+
}],
|
|
8752
|
+
["copy", (node, name) => node.up !== void 0 && termIn(node.contract?.attributes, name)?.copy === true ? attributeOf(node.up, name) : void 0],
|
|
8753
|
+
["default", (node, name) => {
|
|
8754
|
+
const declared = termIn(node.contract?.attributes, name);
|
|
8755
|
+
if (declared?.fold !== void 0) return foldOf(node, name, declared.fold);
|
|
8756
|
+
return declared?.default === void 0 ? void 0 : equationOf(node, declared.default, {});
|
|
8757
|
+
}]
|
|
8758
|
+
];
|
|
8759
|
+
/** TREE.ONCE, TREE.MEMO and TREE.CYCLE: the result of one attribute, or `undefined` when no rung of its ladder answers. */
|
|
8760
|
+
function attributeOf(node, name) {
|
|
7152
8761
|
const done = node.values.get(name);
|
|
7153
8762
|
if (done !== void 0) return done;
|
|
7154
8763
|
if (node.pending.has(name)) {
|
|
7155
8764
|
const from = inEvaluation.findIndex((entry) => entry.node === node && entry.name === name);
|
|
7156
8765
|
return fail("#CYCLE!", `The attributes form a cycle: ${[...from < 0 ? [nameOfAttribute(node, name)] : inEvaluation.slice(from).map((entry) => nameOfAttribute(entry.node, entry.name)), nameOfAttribute(node, name)].join(" -> ")}.`);
|
|
7157
8766
|
}
|
|
7158
|
-
const part = node.parts.find((item) => item.key === name);
|
|
7159
|
-
if (part === void 0) return void 0;
|
|
7160
8767
|
node.pending.add(name);
|
|
7161
8768
|
inEvaluation.push({
|
|
7162
8769
|
node,
|
|
7163
8770
|
name
|
|
7164
8771
|
});
|
|
7165
8772
|
try {
|
|
7166
|
-
const
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
8773
|
+
for (const [, rung] of REF_RUNGS) {
|
|
8774
|
+
const result = rung(node, name);
|
|
8775
|
+
if (result === void 0) continue;
|
|
8776
|
+
node.values.set(name, result);
|
|
8777
|
+
return result;
|
|
8778
|
+
}
|
|
8779
|
+
return;
|
|
7172
8780
|
} finally {
|
|
7173
8781
|
node.pending.delete(name);
|
|
7174
8782
|
inEvaluation.pop();
|
|
@@ -7178,7 +8786,7 @@ function attributeOf(node, self, name) {
|
|
|
7178
8786
|
function jsonOfNode(node, self, open) {
|
|
7179
8787
|
const record = {};
|
|
7180
8788
|
for (const part of node.parts) {
|
|
7181
|
-
const result = attributeOf(node,
|
|
8789
|
+
const result = attributeOf(node, part.key);
|
|
7182
8790
|
if (!result.ok) return result;
|
|
7183
8791
|
const value = plainValue(result.value, [...open, self]);
|
|
7184
8792
|
if (!value.ok) return value;
|
|
@@ -7216,15 +8824,23 @@ function plainValue(value, open) {
|
|
|
7216
8824
|
* TREE.LAZY, TREE.PARENT, TREE.SIBLING and TREE.ERROR: a node of an open tree gives its thing, and the root of a new
|
|
7217
8825
|
* tree gives its JSON. A step on the thing reads an attribute, and a failed attribute throws its own error.
|
|
7218
8826
|
*/
|
|
7219
|
-
const treeDefinition = ({ parts, context, kind, place }) => {
|
|
8827
|
+
const treeDefinition = ({ parts, context, kind, place, machine, typePath }) => {
|
|
7220
8828
|
const outer = refNodeOf(context.self);
|
|
7221
8829
|
const nested = outer !== void 0 && outer.tree.open;
|
|
8830
|
+
const found = machine === void 0 || typePath.length === 0 ? void 0 : lookupKind(machine.world, typePath);
|
|
8831
|
+
const contract = found?.ok === true ? found.kind.tree : void 0;
|
|
8832
|
+
const of = contract === void 0 || found?.ok !== true ? void 0 : equationType(found.kind);
|
|
7222
8833
|
const node = {
|
|
7223
8834
|
tree: nested ? outer.tree : { open: true },
|
|
7224
8835
|
kind,
|
|
7225
8836
|
place,
|
|
7226
8837
|
parts,
|
|
7227
8838
|
ctx: context,
|
|
8839
|
+
contract,
|
|
8840
|
+
of,
|
|
8841
|
+
up: nested ? outer : void 0,
|
|
8842
|
+
env: activeEnvs[activeEnvs.length - 1],
|
|
8843
|
+
thing: null,
|
|
7228
8844
|
values: /* @__PURE__ */ new Map(),
|
|
7229
8845
|
pending: /* @__PURE__ */ new Set()
|
|
7230
8846
|
};
|
|
@@ -7235,17 +8851,18 @@ const treeDefinition = ({ parts, context, kind, place }) => {
|
|
|
7235
8851
|
deref: (key) => {
|
|
7236
8852
|
if (key === "parent") return context.self;
|
|
7237
8853
|
if (key === "type") return kind;
|
|
7238
|
-
const result = attributeOf(node,
|
|
8854
|
+
const result = attributeOf(node, String(key));
|
|
7239
8855
|
if (result === void 0 || result.ok) return result?.ok === true ? result.value : void 0;
|
|
7240
8856
|
throw new Failure(result.error);
|
|
7241
8857
|
},
|
|
7242
|
-
has: (key) => key === "parent" || key === "type" || parts.some((part) => part.key === String(key)),
|
|
8858
|
+
has: (key) => key === "parent" || key === "type" || parts.some((part) => part.key === String(key)) || node.values.has(String(key)),
|
|
7243
8859
|
toJson: () => {
|
|
7244
8860
|
const result = jsonOfNode(node, self, []);
|
|
7245
8861
|
if (result.ok) return result.value;
|
|
7246
8862
|
throw new Failure(result.error);
|
|
7247
8863
|
}
|
|
7248
8864
|
};
|
|
8865
|
+
node.thing = self;
|
|
7249
8866
|
refNodes.set(self, node);
|
|
7250
8867
|
if (nested) return ok(self);
|
|
7251
8868
|
try {
|
|
@@ -7437,12 +9054,12 @@ function formulaDefinitions(profile = {}) {
|
|
|
7437
9054
|
return thrown(error, `the operation ${name}`);
|
|
7438
9055
|
}
|
|
7439
9056
|
};
|
|
7440
|
-
const ext = ({ typePath, doc, context }) => {
|
|
9057
|
+
const ext = ({ typePath, doc, context, machine }) => {
|
|
7441
9058
|
const name = typePath.slice(1).join(".");
|
|
7442
9059
|
const handler = own$1(profile.ext, name) ?? own$1(STD_EXT, name);
|
|
7443
9060
|
if (handler === void 0) return fail("#NAME?", `The language has no extension ${name}.`);
|
|
7444
9061
|
try {
|
|
7445
|
-
return handler(doc.value, context);
|
|
9062
|
+
return handler(doc.value, context, machine?.domains);
|
|
7446
9063
|
} catch (error) {
|
|
7447
9064
|
return thrown(error, `the extension ${name}`);
|
|
7448
9065
|
}
|
|
@@ -7477,6 +9094,13 @@ function valuesOfStrict(parts) {
|
|
|
7477
9094
|
}
|
|
7478
9095
|
//#endregion
|
|
7479
9096
|
//#region src/agree.ts
|
|
9097
|
+
/** The tiers that `agree` compares when its options name none. */
|
|
9098
|
+
const DEFAULT_TIERS = /* @__PURE__ */ Object.freeze([
|
|
9099
|
+
"reference",
|
|
9100
|
+
"traced",
|
|
9101
|
+
"again",
|
|
9102
|
+
"folds"
|
|
9103
|
+
]);
|
|
7480
9104
|
/**
|
|
7481
9105
|
* This function tells if two values are the same for `agree`. Lists and plain records compare item by item and field
|
|
7482
9106
|
* by field, and the order of the fields does not matter. Each other leaf compares with `Object.is`, thus `-0` is not
|
|
@@ -7566,7 +9190,11 @@ function agree(machine, doc, context = makeContext(), options = {}) {
|
|
|
7566
9190
|
};
|
|
7567
9191
|
const observe = options.observe;
|
|
7568
9192
|
observe?.();
|
|
7569
|
-
const
|
|
9193
|
+
const folds = observe === void 0;
|
|
9194
|
+
const plan = prepare(machine, doc, {
|
|
9195
|
+
...declared,
|
|
9196
|
+
fold: folds
|
|
9197
|
+
});
|
|
7570
9198
|
const effects = [];
|
|
7571
9199
|
const result = plan.evaluate(context, {
|
|
7572
9200
|
effects,
|
|
@@ -7579,11 +9207,7 @@ function agree(machine, doc, context = makeContext(), options = {}) {
|
|
|
7579
9207
|
if (why !== void 0 || observe === void 0) return why;
|
|
7580
9208
|
return sameValue(saw, observe(), options.equal) ? void 0 : "The host saw other calls in the tier.";
|
|
7581
9209
|
};
|
|
7582
|
-
for (const tier of options.tiers ??
|
|
7583
|
-
"reference",
|
|
7584
|
-
"traced",
|
|
7585
|
-
"again"
|
|
7586
|
-
]) {
|
|
9210
|
+
for (const tier of options.tiers ?? DEFAULT_TIERS) {
|
|
7587
9211
|
let other;
|
|
7588
9212
|
let otherEffects = [];
|
|
7589
9213
|
if (tier === "reference") other = reference(machine, doc, context, {
|
|
@@ -7599,6 +9223,27 @@ function agree(machine, doc, context = makeContext(), options = {}) {
|
|
|
7599
9223
|
});
|
|
7600
9224
|
other = traced.result;
|
|
7601
9225
|
otherEffects = [...traced.effects];
|
|
9226
|
+
} else if (tier === "folds") {
|
|
9227
|
+
other = prepare(machine, doc, {
|
|
9228
|
+
...declared,
|
|
9229
|
+
fold: !folds
|
|
9230
|
+
}).evaluate(context, {
|
|
9231
|
+
effects: otherEffects,
|
|
9232
|
+
...mock
|
|
9233
|
+
});
|
|
9234
|
+
if (observe !== void 0) {
|
|
9235
|
+
observe();
|
|
9236
|
+
if (overflowed(result) || overflowed(other)) continue;
|
|
9237
|
+
const why = resultDiffers(result, other, options) ?? effectsDiffer(effects, otherEffects, options);
|
|
9238
|
+
if (why !== void 0) return {
|
|
9239
|
+
ok: false,
|
|
9240
|
+
tier,
|
|
9241
|
+
plan: result,
|
|
9242
|
+
other,
|
|
9243
|
+
reason: why
|
|
9244
|
+
};
|
|
9245
|
+
continue;
|
|
9246
|
+
}
|
|
7602
9247
|
} else other = plan.evaluate(context, {
|
|
7603
9248
|
effects: otherEffects,
|
|
7604
9249
|
...mock
|
|
@@ -8633,6 +10278,7 @@ function instanceOf(env, under, doc, split, sample) {
|
|
|
8633
10278
|
doc,
|
|
8634
10279
|
data: split.data,
|
|
8635
10280
|
place: PLACE,
|
|
10281
|
+
machine: env.machine,
|
|
8636
10282
|
fill
|
|
8637
10283
|
};
|
|
8638
10284
|
const who = `the ${env.runner ? "runner" : "evaluator"} ${env.name}`;
|
|
@@ -10244,6 +11890,6 @@ function typeFromSamples(values, options = {}) {
|
|
|
10244
11890
|
return samplesAt(values, options.closed === true, 0);
|
|
10245
11891
|
}
|
|
10246
11892
|
//#endregion
|
|
10247
|
-
export { CAPS_TYPE, CONSTRUCTS, ENVELOPE_KEYS, Failure, Held, JOURNAL, MergeError, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_DEFINITIONS, STD_DOMAINS, STD_EVALUATORS, STD_EXT, STD_MACHINE, STD_RUNNERS, STD_WALKERS, STD_WORLD, THING, agree, ask, capKeys, check, checkLatent, checkSteps, classify, components, conforms, context, cycles, dataEdges, derefStep, describe, docEquals, durable, edgesOf, effectEdges, effectsOf, entriesAsJson, evaluate, extendDomain, factsOf, fail, failures, field, fits, flatten, formulaDefinitions, formulaEvaluators, formulaFacts, formulaKinds, formulaKnown, formulaMachine, fromJson, generate, heights, intersectCaps, isAlias, isAllowedOrder, isClosure, isEnvelope, isHostThing, isKey, isKind, isNamespace, isRecord, isRef, isStackOverflow, isTypeRef, join, jsTruthy, kindDocument, kindNamed, kindOf, layers, looksLikeKind, lookupKind, makeContext, mapRefs, member, mergeMachines, mergeWorlds, narrow, nearestWalker, necessary, nodeAt, nodeLayers, ok, partCaps, partsOf, pathMatches, permits, placed, planner, prepare, random, recorder, ref, reference, refsOf, replayer, resolve, restrictMachine, restrictWorld, run, sameResult, sameValue, schedule, splitParts, stored, tag, thrown, toJson, typeCheck, typeFromSamples, typeFromSchema, typeIssues, typeName, typePath, typeText, typed, typesOf, valueOfThing, verifyReads, walk, worldThing };
|
|
11893
|
+
export { ATTRIBUTE_RUNGS, CAPS_TYPE, CONSTRUCTS, ENVELOPE_KEYS, Failure, Held, JOURNAL, MergeError, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_DEFINITIONS, STD_DOMAINS, STD_EVALUATORS, STD_EXT, STD_MACHINE, STD_RUNNERS, STD_SYNTAX, STD_WALKERS, STD_WORLD, THING, agree, ask, attributeTrace, capKeys, check, checkLatent, checkSteps, classify, components, conforms, context, cycles, dataEdges, decodeDomain, decodeValue, derefStep, describe, docEquals, durable, edgesOf, effectEdges, effectsOf, encodeValue, entriesAsJson, equationType, evaluate, extendDomain, factsOf, fail, failures, field, fits, flatten, formulaDefinitions, formulaEvaluators, formulaFacts, formulaKinds, formulaKnown, formulaMachine, fromJson, generate, heights, intersectCaps, isAlias, isAllowedOrder, isClosure, isEnvelope, isHostThing, isKey, isKind, isNamespace, isRecord, isRef, isStackOverflow, isTypeRef, join, jsTruthy, kindDocument, kindNamed, kindOf, layers, looksLikeKind, lookupKind, makeContext, mapRefs, member, mergeMachines, mergeWorlds, narrow, nearestWalker, necessary, nodeAt, nodeLayers, ok, parse, partCaps, partsOf, pathMatches, permits, placed, planner, prepare, print, random, recorder, ref, reference, refsOf, replayer, resolve, restrictMachine, restrictWorld, run, sameResult, sameValue, schedule, splitParts, stored, syntaxFormat, tag, thrown, toJson, typeCheck, typeFromSamples, typeFromSchema, typeIssues, typeName, typePath, typeText, typed, typesOf, valueOfThing, verifyReads, walk, worldThing };
|
|
10248
11894
|
|
|
10249
11895
|
//# sourceMappingURL=index.js.map
|