@mark1russell7/coeval 0.15.0 → 0.17.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 +9 -4
- package/build/index.d.ts +828 -249
- package/build/index.d.ts.map +1 -1
- package/build/index.js +3138 -386
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/agree.ts +193 -21
- package/src/conform.ts +49 -5
- package/src/deref.ts +97 -14
- package/src/evaluate.ts +311 -35
- package/src/facts.ts +113 -78
- package/src/formula.ts +55 -10
- package/src/index.ts +33 -1
- package/src/meaning.ts +472 -0
- package/src/merge.ts +46 -14
- package/src/reads.ts +9 -2
- package/src/reference.ts +90 -14
- package/src/replay.ts +964 -18
- package/src/schedule.ts +14 -4
- package/src/static.ts +2 -2
- package/src/stdlib.ts +81 -6
- package/src/syntax.ts +357 -52
- package/src/types.ts +443 -16
package/build/index.js
CHANGED
|
@@ -577,14 +577,17 @@ function looksLikeKind(value) {
|
|
|
577
577
|
const STD_DOMAINS = {
|
|
578
578
|
null: {
|
|
579
579
|
is: (value) => value === null,
|
|
580
|
+
values: [null],
|
|
580
581
|
summary: "The value null."
|
|
581
582
|
},
|
|
582
583
|
undefined: {
|
|
583
584
|
is: (value) => value === void 0,
|
|
585
|
+
values: [void 0],
|
|
584
586
|
summary: "The value undefined. A document holds it as the extension ext.undefined."
|
|
585
587
|
},
|
|
586
588
|
bool: {
|
|
587
589
|
is: (value) => typeof value === "boolean",
|
|
590
|
+
values: [true, false],
|
|
588
591
|
summary: "A truth value: true or false."
|
|
589
592
|
},
|
|
590
593
|
number: {
|
|
@@ -598,6 +601,12 @@ const STD_DOMAINS = {
|
|
|
598
601
|
};
|
|
599
602
|
/** The deepest term that a value type can have. A deeper term reads as `unknown`, thus a check stays total. */
|
|
600
603
|
const MAX_TYPE_DEPTH = 256;
|
|
604
|
+
/** The most values that the finite rule of `fits` reads for one type (TYPE.FITS-FINITE). A type with more values is not finite for the rule. */
|
|
605
|
+
const MAX_FINITE_VALUES = 64;
|
|
606
|
+
/** The most domains of covers that the cover rule of `fits` tests for one type (TYPE.FITS-COVER). A larger test gives `false`. */
|
|
607
|
+
const MAX_COVER_STEPS = 64;
|
|
608
|
+
/** The most records that one split of `fits` builds, with each split inside its work (TYPE.FITS-PATH, D89). A larger split gives `false`. */
|
|
609
|
+
const MAX_SPLIT_RECORDS = 64;
|
|
601
610
|
/** The keys that each term permits beside its own key. */
|
|
602
611
|
const TERM_KEYS = {
|
|
603
612
|
list: ["list"],
|
|
@@ -620,7 +629,7 @@ function domainOf(name, domains) {
|
|
|
620
629
|
return typeof found === "object" && found !== null && typeof found.is === "function" ? found : void 0;
|
|
621
630
|
}
|
|
622
631
|
/** This function gives the term key of a record, when the record has exactly one term key and no other key. */
|
|
623
|
-
function termOf$
|
|
632
|
+
function termOf$2(term) {
|
|
624
633
|
const keys = Object.keys(term);
|
|
625
634
|
const own = keys.filter((key) => TERMS.includes(key));
|
|
626
635
|
if (own.length !== 1) return void 0;
|
|
@@ -638,7 +647,7 @@ function formOf(type, domains) {
|
|
|
638
647
|
};
|
|
639
648
|
}
|
|
640
649
|
if (!isRecord(type)) return TOP;
|
|
641
|
-
const term = termOf$
|
|
650
|
+
const term = termOf$2(type);
|
|
642
651
|
if (term === "list") return {
|
|
643
652
|
t: "list",
|
|
644
653
|
item: type["list"]
|
|
@@ -745,39 +754,358 @@ function member(value, type, domains) {
|
|
|
745
754
|
return memberAt(value, type, domains, 0);
|
|
746
755
|
}
|
|
747
756
|
/** This function tells if the record type `a` fits the record type `b`: width, closed records and optional fields. */
|
|
748
|
-
function recordFits(a, b, domains, depth, gradual) {
|
|
757
|
+
function recordFits(a, b, domains, depth, gradual, budget) {
|
|
749
758
|
if (b.closed && (!a.closed || Object.keys(a.fields).some((key) => !Object.hasOwn(b.fields, key)))) return false;
|
|
750
759
|
for (const key of Object.keys(b.fields)) {
|
|
751
760
|
const want = b.fields[key];
|
|
752
761
|
const needed = !b.optional.has(key);
|
|
753
762
|
if (Object.hasOwn(a.fields, key)) {
|
|
754
763
|
if (needed && a.optional.has(key)) return false;
|
|
755
|
-
if (!fitsAt(a.fields[key], want, domains, depth, gradual)) return false;
|
|
764
|
+
if (!fitsAt(a.fields[key], want, domains, depth, gradual, true, budget)) return false;
|
|
756
765
|
} else if (needed) return false;
|
|
757
766
|
else if (!a.closed && formOf(want, domains).t !== "top") return false;
|
|
758
767
|
}
|
|
759
768
|
return true;
|
|
760
769
|
}
|
|
770
|
+
/** This function adds a value to a list of distinct values, by `docEquals`. */
|
|
771
|
+
function addValue(out, value) {
|
|
772
|
+
if (!out.some((item) => docEquals(item, value))) out.push(value);
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* This function gives the values of a base type with a finite list. It reads the list of the first domain of the line
|
|
776
|
+
* with at most 64 values, and it keeps only the members of the type. Thus the list of a parent serves each domain under it.
|
|
777
|
+
*/
|
|
778
|
+
function baseValues(name, domains) {
|
|
779
|
+
const domain = domainOf(name, domains);
|
|
780
|
+
if (domain === void 0) return void 0;
|
|
781
|
+
const line = Array.isArray(domain.values) || !Array.isArray(domain.under) ? [name] : lineOf(name, domains);
|
|
782
|
+
for (const each of line) {
|
|
783
|
+
const values = domainOf(each, domains)?.values;
|
|
784
|
+
if (!Array.isArray(values) || values.length > MAX_FINITE_VALUES) continue;
|
|
785
|
+
const out = [];
|
|
786
|
+
for (let index = 0; index < values.length; index += 1) if (inDomain(values[index], name, domains)) addValue(out, values[index]);
|
|
787
|
+
return out;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
/** This function gives a copy of a record with one more field. */
|
|
791
|
+
function withField(record, key, value) {
|
|
792
|
+
const out = {};
|
|
793
|
+
for (const name of Object.keys(record)) setOwn$6(out, name, record[name]);
|
|
794
|
+
setOwn$6(out, key, value);
|
|
795
|
+
return out;
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* This function gives the members of a record type, when they are few. A record with a needed field without members
|
|
799
|
+
* has no members. Only a closed record with a finite list for each field has a finite list.
|
|
800
|
+
*/
|
|
801
|
+
function recordValues(form, domains, depth) {
|
|
802
|
+
let out = [{}];
|
|
803
|
+
let finite = form.closed;
|
|
804
|
+
for (const key of Object.keys(form.fields)) {
|
|
805
|
+
const values = valuesOf$1(form.fields[key], domains, depth);
|
|
806
|
+
const optional = form.optional.has(key);
|
|
807
|
+
if (values === void 0) {
|
|
808
|
+
finite = false;
|
|
809
|
+
continue;
|
|
810
|
+
}
|
|
811
|
+
if (values.length === 0 && !optional) return [];
|
|
812
|
+
if (!finite) continue;
|
|
813
|
+
if (out.length * (values.length + (optional ? 1 : 0)) > MAX_FINITE_VALUES) {
|
|
814
|
+
finite = false;
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
out = out.flatMap((record) => [...optional ? [record] : [], ...values.map((value) => withField(record, key, value))]);
|
|
818
|
+
}
|
|
819
|
+
return finite ? out : void 0;
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* This function gives a list of the members of a type, when the type has at most 64 members (TYPE.FITS-FINITE). Each
|
|
823
|
+
* other type gives `undefined`. Such a type is `unknown`, a base type without a finite list, or a function type. A
|
|
824
|
+
* list that can hold an item and an open record with members are also such types.
|
|
825
|
+
*/
|
|
826
|
+
function valuesOf$1(type, domains, depth) {
|
|
827
|
+
if (depth > MAX_TYPE_DEPTH) return void 0;
|
|
828
|
+
const form = formOf(type, domains);
|
|
829
|
+
const next = depth + 1;
|
|
830
|
+
switch (form.t) {
|
|
831
|
+
case "bottom": return [];
|
|
832
|
+
case "base": return baseValues(form.name, domains);
|
|
833
|
+
case "list": return valuesOf$1(form.item, domains, next)?.length === 0 ? [[]] : void 0;
|
|
834
|
+
case "record": return recordValues(form, domains, next);
|
|
835
|
+
case "one":
|
|
836
|
+
case "union": {
|
|
837
|
+
const out = [];
|
|
838
|
+
const items = form.t === "one" ? form.values : form.members;
|
|
839
|
+
for (const item of items) {
|
|
840
|
+
const values = form.t === "one" ? [item] : valuesOf$1(item, domains, next);
|
|
841
|
+
if (values === void 0) return void 0;
|
|
842
|
+
for (const value of values) addValue(out, value);
|
|
843
|
+
if (out.length > MAX_FINITE_VALUES) return void 0;
|
|
844
|
+
}
|
|
845
|
+
return out;
|
|
846
|
+
}
|
|
847
|
+
default: return;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
/** This function gives the members of a type at one depth, for `membersOf`. */
|
|
851
|
+
function membersAt(type, domains, samples, depth) {
|
|
852
|
+
const finite = valuesOf$1(type, domains, depth);
|
|
853
|
+
if (finite !== void 0) return finite;
|
|
854
|
+
const form = formOf(type, domains);
|
|
855
|
+
if (form.t !== "union" || depth > MAX_TYPE_DEPTH) return samples.filter((sample) => member(sample, type, domains));
|
|
856
|
+
const out = [];
|
|
857
|
+
for (const item of form.members) for (const value of membersAt(item, domains, samples, depth + 1)) addValue(out, value);
|
|
858
|
+
return out;
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* This function gives members of a type for a test of a declaration (D86). A type with at most 64 members gives each
|
|
862
|
+
* member, from the lists `values` of its domains (TYPE.FITS-FINITE). A union gives the members of each of its types.
|
|
863
|
+
* Each other type gives the samples that are its members. The kernel does not export the function.
|
|
864
|
+
*
|
|
865
|
+
* @param type - The type.
|
|
866
|
+
* @param domains - The domains of the machine.
|
|
867
|
+
* @param samples - The values to test when the type has no full list of members.
|
|
868
|
+
* @returns Distinct members of the type.
|
|
869
|
+
*/
|
|
870
|
+
function membersOf(type, domains, samples) {
|
|
871
|
+
return membersAt(type, domains, samples, 0);
|
|
872
|
+
}
|
|
873
|
+
/** This function holds the rules of the shapes of `fits`: base types, lists, records and functions. */
|
|
874
|
+
function shapeFits(a, b, domains, next, gradual, budget) {
|
|
875
|
+
if (a.t === "base") return b.t === "base" && lineOf(a.name, domains).has(b.name);
|
|
876
|
+
if (a.t === "list") return b.t === "list" && fitsAt(a.item, b.item, domains, next, gradual, true, budget);
|
|
877
|
+
if (a.t === "record") return b.t === "record" && recordFits(a, b, domains, next, gradual, budget);
|
|
878
|
+
if (a.t !== "fn" || b.t !== "fn" || a.params.length > b.params.length) return false;
|
|
879
|
+
return a.params.every((param, index) => fitsAt(b.params[index], param, domains, next, gradual, true, budget)) && fitsAt(a.to, b.to, domains, next, gradual, true, budget);
|
|
880
|
+
}
|
|
761
881
|
/**
|
|
762
882
|
* This function is the relation `fits` at one depth of two types. The order of its tests is the order of the rules of
|
|
763
883
|
* `fits`. Without `gradual`, `unknown` is only the top: it fits only `unknown`. The join uses this strict form.
|
|
884
|
+
*
|
|
885
|
+
* When the rules of the shapes refuse a type with few members, the finite rule tests each member (TYPE.FITS-FINITE).
|
|
886
|
+
* The cover rule and the split rule come after it. Without `finite`, the call skips these three rules. The members of
|
|
887
|
+
* a union in `expected` get such calls, because the call for the union tests `found` against the full union.
|
|
888
|
+
*
|
|
889
|
+
* The budget is the budget of the outer split, when the call is part of the work of a split (TYPE.FITS-PATH, D89).
|
|
764
890
|
*/
|
|
765
|
-
function fitsAt(found, expected, domains, depth, gradual = true) {
|
|
891
|
+
function fitsAt(found, expected, domains, depth, gradual = true, finite = true, budget) {
|
|
766
892
|
if (depth > MAX_TYPE_DEPTH) return true;
|
|
767
893
|
const a = formOf(found, domains);
|
|
768
894
|
const b = formOf(expected, domains);
|
|
769
895
|
const next = depth + 1;
|
|
770
896
|
if (b.t === "top" || a.t === "bottom") return true;
|
|
771
897
|
if (a.t === "top") return gradual;
|
|
772
|
-
if (a.t === "union") return a.members.every((item) => fitsAt(item, expected, domains, next, gradual));
|
|
898
|
+
if (a.t === "union") return a.members.every((item) => fitsAt(item, expected, domains, next, gradual, true, budget));
|
|
773
899
|
if (a.t === "one") return a.values.every((item) => memberAt(item, expected, domains, next));
|
|
774
|
-
if (b.t === "bottom") return
|
|
775
|
-
if (
|
|
776
|
-
|
|
777
|
-
if (
|
|
778
|
-
if (a.t === "
|
|
779
|
-
|
|
780
|
-
|
|
900
|
+
if (b.t === "union" ? b.members.some((item) => fitsAt(found, item, domains, next, gradual, false, budget)) : b.t !== "bottom" && shapeFits(a, b, domains, next, gradual, budget)) return true;
|
|
901
|
+
if (!finite || a.t === "fn") return false;
|
|
902
|
+
const values = valuesOf$1(found, domains, next);
|
|
903
|
+
if (values !== void 0) return values.every((value) => memberAt(value, expected, domains, next));
|
|
904
|
+
if (a.t === "base") return (b.t === "union" || b.t === "one") && lineCovered(a.name, expected, domains, depth, gradual, void 0);
|
|
905
|
+
return a.t === "record" && (b.t === "union" || b.t === "one" && a.closed) && splitFits(found, a, expected, domains, depth, gradual, budget);
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* This function is the cover rule of `fits` (TYPE.FITS-COVER, D85). A base type fits when a domain of its line has a
|
|
909
|
+
* cover, and each domain of the cover fits `expected`. The function makes the state at the first domain with covers.
|
|
910
|
+
*/
|
|
911
|
+
function lineCovered(name, expected, domains, depth, gradual, state) {
|
|
912
|
+
let now = state;
|
|
913
|
+
for (const each of lineOf(name, domains)) {
|
|
914
|
+
if (!Array.isArray(domainOf(each, domains)?.covers)) continue;
|
|
915
|
+
now ??= {
|
|
916
|
+
held: /* @__PURE__ */ new Map(),
|
|
917
|
+
left: MAX_COVER_STEPS
|
|
918
|
+
};
|
|
919
|
+
if (covered(each, expected, domains, depth, gradual, now)) return true;
|
|
920
|
+
}
|
|
921
|
+
return false;
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* This function tells if a cover of one domain holds the domain in `expected`. A cover that is empty, or that names
|
|
925
|
+
* the domain or a name that is not under it, does not count. Each domain of a cover costs one step, and the rule
|
|
926
|
+
* stops after 64 steps.
|
|
927
|
+
*/
|
|
928
|
+
function covered(name, expected, domains, depth, gradual, state) {
|
|
929
|
+
const known = state.held.get(name);
|
|
930
|
+
if (known !== void 0) return known;
|
|
931
|
+
state.held.set(name, false);
|
|
932
|
+
const covers = domainOf(name, domains)?.covers;
|
|
933
|
+
if (!Array.isArray(covers)) return false;
|
|
934
|
+
for (const cover of covers) {
|
|
935
|
+
if (!Array.isArray(cover) || cover.length === 0) continue;
|
|
936
|
+
let held = true;
|
|
937
|
+
for (const part of cover) {
|
|
938
|
+
state.left -= 1;
|
|
939
|
+
if (state.left < 0) return false;
|
|
940
|
+
if (typeof part !== "string" || part === name || !lineOf(part, domains).has(name) || !partFits(part, expected, domains, depth, gradual, state)) {
|
|
941
|
+
held = false;
|
|
942
|
+
break;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
if (held) {
|
|
946
|
+
state.held.set(name, true);
|
|
947
|
+
return true;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
return false;
|
|
951
|
+
}
|
|
952
|
+
/** This function tells if a domain of a cover fits `expected`. It tries the rules of the shapes, the finite rule and its own covers. */
|
|
953
|
+
function partFits(part, expected, domains, depth, gradual, state) {
|
|
954
|
+
if (fitsAt(part, expected, domains, depth, gradual, false)) return true;
|
|
955
|
+
const values = valuesOf$1(part, domains, depth + 1);
|
|
956
|
+
if (values !== void 0) return values.every((value) => memberAt(value, expected, domains, depth + 1));
|
|
957
|
+
return lineCovered(part, expected, domains, depth, gradual, state);
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* This function gives the fields that the split rule can split, in the order of the split (TYPE.FITS-PATH, D89). The
|
|
961
|
+
* fields of the record come first, in the order of the keys. Then the paths of the next depth follow, and so on. A path
|
|
962
|
+
* goes into a field only when the field is a record without a finite list. A field with a finite list of two or more
|
|
963
|
+
* cases is a split. An optional field also has the absent case.
|
|
964
|
+
*
|
|
965
|
+
* The type splits follow each value split, in the same order (D90). A base type with a valid cover in its line splits
|
|
966
|
+
* into the domains of the cover. A union without a finite list splits into its members. The prefix is the path of `a`.
|
|
967
|
+
*/
|
|
968
|
+
function splitsOf(a, domains, depth, prefix = []) {
|
|
969
|
+
const out = [];
|
|
970
|
+
const later = [];
|
|
971
|
+
let level = [{
|
|
972
|
+
path: prefix,
|
|
973
|
+
form: a
|
|
974
|
+
}];
|
|
975
|
+
for (let at = depth + 1; level.length > 0 && at <= MAX_TYPE_DEPTH; at += 1) {
|
|
976
|
+
const deeper = [];
|
|
977
|
+
for (const { path, form } of level) for (const key of Object.keys(form.fields)) {
|
|
978
|
+
const values = valuesOf$1(form.fields[key], domains, at);
|
|
979
|
+
const optional = form.optional.has(key);
|
|
980
|
+
if (values !== void 0) {
|
|
981
|
+
if (values.length + (optional ? 1 : 0) > 1) out.push({
|
|
982
|
+
path: [...path, key],
|
|
983
|
+
values,
|
|
984
|
+
optional
|
|
985
|
+
});
|
|
986
|
+
continue;
|
|
987
|
+
}
|
|
988
|
+
const inner = formOf(form.fields[key], domains);
|
|
989
|
+
if (inner.t === "record") deeper.push({
|
|
990
|
+
path: [...path, key],
|
|
991
|
+
form: inner
|
|
992
|
+
});
|
|
993
|
+
else if (inner.t === "union") later.push({
|
|
994
|
+
path: [...path, key],
|
|
995
|
+
types: inner.members,
|
|
996
|
+
optional
|
|
997
|
+
});
|
|
998
|
+
else if (inner.t === "base") {
|
|
999
|
+
const cover = coverOf(inner.name, domains, false);
|
|
1000
|
+
if (cover !== void 0) later.push({
|
|
1001
|
+
path: [...path, key],
|
|
1002
|
+
types: cover,
|
|
1003
|
+
optional
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
level = deeper;
|
|
1008
|
+
}
|
|
1009
|
+
return later.length === 0 ? out : [...out, ...later];
|
|
1010
|
+
}
|
|
1011
|
+
/** This function tells if a cover of a domain is valid by the line rule of D85. Such a cover is not empty, and each name in it is a domain under the domain. */
|
|
1012
|
+
function validCover(name, cover, domains) {
|
|
1013
|
+
return Array.isArray(cover) && cover.length > 0 && cover.every((part) => typeof part === "string" && part !== name && lineOf(part, domains).has(name));
|
|
1014
|
+
}
|
|
1015
|
+
/**
|
|
1016
|
+
* This function gives the cover of a base type for a type split (D90). The cover is the first valid cover of the first
|
|
1017
|
+
* domain in the line with a valid cover. With `own`, only the domain of the type itself counts. Each other type gives
|
|
1018
|
+
* `undefined`.
|
|
1019
|
+
*/
|
|
1020
|
+
function coverOf(name, domains, own) {
|
|
1021
|
+
for (const each of own ? [name] : lineOf(name, domains)) {
|
|
1022
|
+
const covers = domainOf(each, domains)?.covers;
|
|
1023
|
+
if (!Array.isArray(covers)) continue;
|
|
1024
|
+
for (const cover of covers) if (validCover(each, cover, domains)) return cover;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* This function gives the splits of a case of a type split, at the path of the case (D90). A case with a finite list
|
|
1029
|
+
* splits by its members. A base type splits by a cover of its own domain, because the covers of its parents hold it
|
|
1030
|
+
* already. A union splits by its members, and a record splits by the paths into its fields.
|
|
1031
|
+
*/
|
|
1032
|
+
function againOf(type, path, domains, depth) {
|
|
1033
|
+
const at = depth + path.length;
|
|
1034
|
+
const values = valuesOf$1(type, domains, at);
|
|
1035
|
+
if (values !== void 0) return values.length > 1 ? [{
|
|
1036
|
+
path,
|
|
1037
|
+
values,
|
|
1038
|
+
optional: false
|
|
1039
|
+
}] : [];
|
|
1040
|
+
const form = formOf(type, domains);
|
|
1041
|
+
if (form.t === "record") return splitsOf(form, domains, at, path);
|
|
1042
|
+
if (form.t === "union") return [{
|
|
1043
|
+
path,
|
|
1044
|
+
types: form.members,
|
|
1045
|
+
optional: false
|
|
1046
|
+
}];
|
|
1047
|
+
const cover = form.t === "base" ? coverOf(form.name, domains, true) : void 0;
|
|
1048
|
+
return cover === void 0 ? [] : [{
|
|
1049
|
+
path,
|
|
1050
|
+
types: cover,
|
|
1051
|
+
optional: false
|
|
1052
|
+
}];
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* This function gives a copy of a record type with one field narrowed at a path. A case type replaces the field. The
|
|
1056
|
+
* absent case removes the field of a closed record, and it gives an optional field of type `never` in an open record.
|
|
1057
|
+
*/
|
|
1058
|
+
function narrowAt(type, path, at, narrowed, domains) {
|
|
1059
|
+
const form = formOf(type, domains);
|
|
1060
|
+
const key = path[at];
|
|
1061
|
+
if (form.t !== "record" || key === void 0) return type;
|
|
1062
|
+
const last = at + 1 === path.length;
|
|
1063
|
+
const fields = {};
|
|
1064
|
+
for (const name of Object.keys(form.fields)) if (name !== key) setOwn$6(fields, name, form.fields[name]);
|
|
1065
|
+
else if (!last) setOwn$6(fields, name, narrowAt(form.fields[name], path, at + 1, narrowed, domains));
|
|
1066
|
+
else if (narrowed !== void 0) setOwn$6(fields, name, narrowed);
|
|
1067
|
+
else if (!form.closed) setOwn$6(fields, name, "never");
|
|
1068
|
+
const optional = [...form.optional].filter((name) => !last || name !== key || narrowed === void 0 && !form.closed);
|
|
1069
|
+
return {
|
|
1070
|
+
record: fields,
|
|
1071
|
+
...optional.length === 0 ? {} : { optional },
|
|
1072
|
+
...form.closed ? { closed: true } : {}
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
* This function is the split rule of `fits` (TYPE.FITS-FINITE, D84, TYPE.FITS-PATH, D89). A record type fits when each
|
|
1077
|
+
* record of its split fits. A split takes the first field in the order of `splitsOf`. Each member of the field gives a
|
|
1078
|
+
* record with the field narrowed to that member. An optional field also gives a record without the field. A type split
|
|
1079
|
+
* narrows the field to each case type instead (TYPE.FITS-SPLIT-TYPE, D90).
|
|
1080
|
+
*
|
|
1081
|
+
* A record of the split that does not fit splits the next such field. A record of a type split first splits its case
|
|
1082
|
+
* again, at the same path. One budget of 64 records serves the outer split and each split inside its work, at each
|
|
1083
|
+
* depth. Thus nested splits do not multiply the work.
|
|
1084
|
+
*/
|
|
1085
|
+
function splitFits(found, a, expected, domains, depth, gradual, budget) {
|
|
1086
|
+
const splits = splitsOf(a, domains, depth);
|
|
1087
|
+
if (splits.length === 0) return false;
|
|
1088
|
+
return casesFit(found, a.closed, splits, 0, expected, domains, depth, gradual, budget ?? { left: MAX_SPLIT_RECORDS });
|
|
1089
|
+
}
|
|
1090
|
+
/** This function tells if each record of the split of one field fits `expected`. It splits the next field for a record that does not fit. */
|
|
1091
|
+
function casesFit(type, closed, splits, index, expected, domains, depth, gradual, budget) {
|
|
1092
|
+
const split = splits[index];
|
|
1093
|
+
if (split === void 0) return false;
|
|
1094
|
+
const cases = (split.values?.map((value) => ({ one: [value] })) ?? split.types ?? []).map((each) => narrowAt(type, split.path, 0, each, domains));
|
|
1095
|
+
if (split.optional) cases.push(narrowAt(type, split.path, 0, void 0, domains));
|
|
1096
|
+
for (const [at, record] of cases.entries()) {
|
|
1097
|
+
budget.left -= 1;
|
|
1098
|
+
if (budget.left < 0) return false;
|
|
1099
|
+
if (fitsAt(record, expected, domains, depth, gradual, false, budget)) continue;
|
|
1100
|
+
const values = closed ? valuesOf$1(record, domains, depth + 1) : void 0;
|
|
1101
|
+
if (values !== void 0) {
|
|
1102
|
+
if (values.every((value) => memberAt(value, expected, domains, depth + 1))) continue;
|
|
1103
|
+
return false;
|
|
1104
|
+
}
|
|
1105
|
+
const again = split.types !== void 0 && at < split.types.length ? againOf(split.types[at], split.path, domains, depth) : [];
|
|
1106
|
+
if (!casesFit(record, closed, again.length === 0 ? splits : [...again, ...splits.slice(index + 1)], again.length === 0 ? index + 1 : 0, expected, domains, depth, gradual, budget)) return false;
|
|
1107
|
+
}
|
|
1108
|
+
return true;
|
|
781
1109
|
}
|
|
782
1110
|
/**
|
|
783
1111
|
* This function tells if each value of the type `found` can be where the type `expected` must be (TYPE.FITS). Each
|
|
@@ -794,9 +1122,33 @@ function fitsAt(found, expected, domains, depth, gradual = true) {
|
|
|
794
1122
|
* field is `unknown`.
|
|
795
1123
|
* - A function fits when it has the same or fewer parameters, each expected parameter fits its parameter, and its
|
|
796
1124
|
* result fits.
|
|
797
|
-
*
|
|
798
|
-
*
|
|
799
|
-
*
|
|
1125
|
+
* - The finite rule comes after these rules (TYPE.FITS-FINITE). A type with at most 64 members fits when each member
|
|
1126
|
+
* is a member of `expected`. Thus `bool` fits `{ "one": [true, false] }`.
|
|
1127
|
+
* - A base type has few members when its domain or a parent declares `values`, as `null`, `undefined` and `bool` do.
|
|
1128
|
+
* A union, a `one`, a closed record of such types and a list of `never` also have few members.
|
|
1129
|
+
* - The cover rule comes next (TYPE.FITS-COVER, D85). A base type fits a union or a `one` when each domain of a cover
|
|
1130
|
+
* fits. The cover is in `covers` of a domain of its line. Each domain of a cover is under the covered
|
|
1131
|
+
* domain. Thus a cover of `number` by `neg` and `nonneg` gives `number` a fit against `neg | nonneg`.
|
|
1132
|
+
* - The split rule comes last (D84). An open record, or a closed record with more than 64 members, splits a field with
|
|
1133
|
+
* few members over a union. A closed record also splits over a `one`. Each record of the split narrows the field to
|
|
1134
|
+
* one member, and each record must fit.
|
|
1135
|
+
* Thus `{ "record": { "a": "bool" } }` fits a union of a record for each value of `a`.
|
|
1136
|
+
* - A split also narrows a field of a record in a field, by its path (TYPE.FITS-PATH, D89). The fields of the record
|
|
1137
|
+
* split first, then the paths of each next depth, in the order of the keys. A path goes into a record without a
|
|
1138
|
+
* finite list. Thus `{a: {x: bool}}` fits the union of `{a: {x: true}}` and `{a: {x: false}}`.
|
|
1139
|
+
* - A type split comes after each value split (TYPE.FITS-SPLIT-TYPE, D90). A field of a base type with a valid cover in
|
|
1140
|
+
* its line splits into the domains of the first such cover. A field of a union without a finite list splits into its
|
|
1141
|
+
* members. A case that does not fit splits again at its path, by the same rules. A base type there uses only a
|
|
1142
|
+
* cover of its own domain. Thus `{a: number}` fits `{a: neg} | {a: nonneg}` when `neg` and `nonneg` cover `number`.
|
|
1143
|
+
*
|
|
1144
|
+
* The relation is sound: a member of `found` is a member of `expected` when no `unknown` is in the types
|
|
1145
|
+
* (TYPE.FITS-SOUND). The finite rule trusts `values`, and the cover rule trusts `covers`. Thus a list without a member
|
|
1146
|
+
* or a false cover makes the relation unsound for that domain.
|
|
1147
|
+
*
|
|
1148
|
+
* The relation is not complete. A split builds at most 64 records, with each split inside its work at each depth. A
|
|
1149
|
+
* type split uses one cover of a field, not each cover of its line. A domain without `values` gets no finite rule, and
|
|
1150
|
+
* neither does a type with more than 64 members that is not a record. The cover rule tests at most 64 domains of
|
|
1151
|
+
* covers. The function does not throw.
|
|
800
1152
|
*
|
|
801
1153
|
* @param found - The type that a part has.
|
|
802
1154
|
* @param expected - The type that the position needs.
|
|
@@ -807,7 +1159,7 @@ function fits(found, expected, domains) {
|
|
|
807
1159
|
return fitsAt(found, expected, domains, 0);
|
|
808
1160
|
}
|
|
809
1161
|
/** This function sets an own field of a new record. Only the key `__proto__` needs `defineProperty`. */
|
|
810
|
-
function setOwn$
|
|
1162
|
+
function setOwn$6(target, key, value) {
|
|
811
1163
|
if (key === "__proto__") Object.defineProperty(target, key, {
|
|
812
1164
|
value,
|
|
813
1165
|
enumerable: true,
|
|
@@ -826,10 +1178,10 @@ function joinRecords(a, b, domains, depth) {
|
|
|
826
1178
|
const inA = Object.hasOwn(a.fields, key);
|
|
827
1179
|
const inB = Object.hasOwn(b.fields, key);
|
|
828
1180
|
if (inA && inB) {
|
|
829
|
-
setOwn$
|
|
1181
|
+
setOwn$6(fields, key, joinAt(a.fields[key], b.fields[key], domains, depth));
|
|
830
1182
|
if (a.optional.has(key) || b.optional.has(key)) optional.push(key);
|
|
831
1183
|
} else if (closed) {
|
|
832
|
-
setOwn$
|
|
1184
|
+
setOwn$6(fields, key, inA ? a.fields[key] : b.fields[key]);
|
|
833
1185
|
optional.push(key);
|
|
834
1186
|
}
|
|
835
1187
|
}
|
|
@@ -932,7 +1284,7 @@ function valueText$1(value) {
|
|
|
932
1284
|
function textAt(type, depth, wrap) {
|
|
933
1285
|
if (depth > MAX_TYPE_DEPTH) return "unknown";
|
|
934
1286
|
if (typeof type === "string") return type;
|
|
935
|
-
const term = isRecord(type) ? termOf$
|
|
1287
|
+
const term = isRecord(type) ? termOf$2(type) : void 0;
|
|
936
1288
|
if (!isRecord(type) || term === void 0) return "unknown";
|
|
937
1289
|
const next = depth + 1;
|
|
938
1290
|
const group = (items) => items.length === 0 ? "never" : items.length > 1 && wrap ? `(${items.join(" | ")})` : items.join(" | ");
|
|
@@ -1155,7 +1507,7 @@ function typeIssues(type, domains) {
|
|
|
1155
1507
|
if (here !== "unknown" && here !== "never" && domainOf(here, domains) === void 0) issue(`The machine has no domain ${here}.`);
|
|
1156
1508
|
continue;
|
|
1157
1509
|
}
|
|
1158
|
-
const term = isRecord(here) ? termOf$
|
|
1510
|
+
const term = isRecord(here) ? termOf$2(here) : void 0;
|
|
1159
1511
|
if (!isRecord(here) || term === void 0) {
|
|
1160
1512
|
issue(`A value type is a text, or a record with one of the keys ${TERMS.join(", ")} and the keys that go with it.`);
|
|
1161
1513
|
continue;
|
|
@@ -1233,7 +1585,7 @@ function keyOf(path) {
|
|
|
1233
1585
|
return JSON.stringify(path.map(String));
|
|
1234
1586
|
}
|
|
1235
1587
|
/** This function sets an own data property. An assignment to the key `__proto__` changes the prototype. */
|
|
1236
|
-
function setOwn$
|
|
1588
|
+
function setOwn$5(target, key, value) {
|
|
1237
1589
|
if (key === "__proto__") Object.defineProperty(target, key, {
|
|
1238
1590
|
value,
|
|
1239
1591
|
enumerable: true,
|
|
@@ -1250,7 +1602,7 @@ function defined(value) {
|
|
|
1250
1602
|
if (Array.isArray(value)) return value.map(defined);
|
|
1251
1603
|
if (!isRecord(value)) return value;
|
|
1252
1604
|
const out = {};
|
|
1253
|
-
for (const key of Object.keys(value)) if (value[key] !== void 0) setOwn$
|
|
1605
|
+
for (const key of Object.keys(value)) if (value[key] !== void 0) setOwn$5(out, key, defined(value[key]));
|
|
1254
1606
|
return out;
|
|
1255
1607
|
}
|
|
1256
1608
|
/** Two values are the same when they are equal by `docEquals`. A function and a host value are equal only to themselves. */
|
|
@@ -1259,23 +1611,38 @@ function same(a, b) {
|
|
|
1259
1611
|
}
|
|
1260
1612
|
/**
|
|
1261
1613
|
* Two domains are the same when they are one object. They are also the same when they have the same test `is`,
|
|
1262
|
-
* parents, summary and
|
|
1614
|
+
* parents, summary, steps, codec, list of values and covers. Thus two languages that give one test under one name give one
|
|
1615
|
+
* domain. Two codecs are the same when they have the same functions `encode` and `decode` (D80).
|
|
1263
1616
|
*/
|
|
1264
1617
|
function sameDomain(a, b) {
|
|
1265
|
-
|
|
1618
|
+
if (a === b) return true;
|
|
1619
|
+
const fields = {
|
|
1620
|
+
is: a.is === b.is,
|
|
1621
|
+
under: same(a.under ?? [], b.under ?? []),
|
|
1622
|
+
summary: a.summary === b.summary,
|
|
1623
|
+
steps: same(a.steps ?? {}, b.steps ?? {}),
|
|
1624
|
+
codec: same(a.codec ?? null, b.codec ?? null),
|
|
1625
|
+
values: same(a.values ?? null, b.values ?? null),
|
|
1626
|
+
covers: same(a.covers ?? [], b.covers ?? [])
|
|
1627
|
+
};
|
|
1628
|
+
return Object.values(fields).every(Boolean);
|
|
1266
1629
|
}
|
|
1267
1630
|
/**
|
|
1268
|
-
* This function makes a part of a merge that adds parents or
|
|
1269
|
-
* example is a domain of another language. The part reads the domain from the merge before it
|
|
1270
|
-
* copy with the same test and
|
|
1271
|
-
*
|
|
1631
|
+
* This function makes a part of a merge that adds parents, steps or covers to a domain of an earlier part
|
|
1632
|
+
* (TYPE.PARENTS). An example is a domain of another language. The part reads the domain from the merge before it
|
|
1633
|
+
* (`prev`). It gives a copy with the same test, summary, codec and list of values, and it names `["domains", name]`
|
|
1634
|
+
* in `replaces` (D17).
|
|
1635
|
+
* A domain of the kernel that the earlier parts do not name comes from `STD_DOMAINS`.
|
|
1636
|
+
*
|
|
1637
|
+
* An extension does not change the codec. Thus each member keeps its document (D80).
|
|
1272
1638
|
*
|
|
1273
1639
|
* A new parent adds its test to the domain (TYPE.MEMBER). Thus `fits` stays sound, but a value that fails the test of
|
|
1274
1640
|
* the parent is no longer a member. The language that adds the parent keeps the signatures of the other language
|
|
1275
|
-
* true. A
|
|
1641
|
+
* true. A new parent of a domain in a cover of another domain can make that cover false (D85). A name without a
|
|
1642
|
+
* domain gives an empty part, and `typeIssues` reports each use of that name.
|
|
1276
1643
|
*
|
|
1277
1644
|
* @param name - The name of the domain.
|
|
1278
|
-
* @param more - The parents and the
|
|
1645
|
+
* @param more - The parents, the steps and the covers to add.
|
|
1279
1646
|
* @returns The part, as a function of the merge before it.
|
|
1280
1647
|
*/
|
|
1281
1648
|
function extendDomain(name, more) {
|
|
@@ -1288,14 +1655,21 @@ function extendDomain(name, more) {
|
|
|
1288
1655
|
...found.steps,
|
|
1289
1656
|
...more.steps
|
|
1290
1657
|
};
|
|
1291
|
-
const
|
|
1658
|
+
const covers = [...found.covers ?? []];
|
|
1659
|
+
for (const cover of more.covers ?? []) if (!covers.some((each) => same(each, cover))) covers.push([...cover]);
|
|
1660
|
+
const fields = {
|
|
1292
1661
|
is: found.is,
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1662
|
+
under: under.length === 0 ? void 0 : under,
|
|
1663
|
+
summary: found.summary,
|
|
1664
|
+
steps: Object.keys(steps).length === 0 ? void 0 : steps,
|
|
1665
|
+
codec: found.codec,
|
|
1666
|
+
values: found.values,
|
|
1667
|
+
covers: covers.length === 0 ? void 0 : covers
|
|
1296
1668
|
};
|
|
1669
|
+
const domain = {};
|
|
1670
|
+
for (const [key, value] of Object.entries(fields)) if (value !== void 0) domain[key] = value;
|
|
1297
1671
|
const domains = {};
|
|
1298
|
-
setOwn$
|
|
1672
|
+
setOwn$5(domains, name, domain);
|
|
1299
1673
|
return {
|
|
1300
1674
|
domains,
|
|
1301
1675
|
replaces: [["domains", name]]
|
|
@@ -1345,10 +1719,10 @@ function mergeLeaf(m, table, key, later, path, part, replaces, equal, reason) {
|
|
|
1345
1719
|
if (later === void 0) return;
|
|
1346
1720
|
const earlier = own$2(table, key);
|
|
1347
1721
|
if (earlier === void 0) {
|
|
1348
|
-
setOwn$
|
|
1722
|
+
setOwn$5(table, key, later);
|
|
1349
1723
|
m.give(path, part);
|
|
1350
1724
|
} else if (replaces.has(keyOf(path))) {
|
|
1351
|
-
setOwn$
|
|
1725
|
+
setOwn$5(table, key, later);
|
|
1352
1726
|
m.replace(path, part);
|
|
1353
1727
|
} else if (!equal(earlier, later)) m.clash(path, part, reason);
|
|
1354
1728
|
}
|
|
@@ -1369,9 +1743,9 @@ function mergeEntries(m, into, from, path, part, replaces) {
|
|
|
1369
1743
|
const at = [...path, key];
|
|
1370
1744
|
const earlier = own$2(into, key);
|
|
1371
1745
|
if (earlier === void 0) {
|
|
1372
|
-
setOwn$
|
|
1746
|
+
setOwn$5(into, key, later);
|
|
1373
1747
|
m.give(at, part);
|
|
1374
|
-
} else setOwn$
|
|
1748
|
+
} else setOwn$5(into, key, mergeEntry(m, earlier, later, at, part, replaces));
|
|
1375
1749
|
}
|
|
1376
1750
|
}
|
|
1377
1751
|
/** This function merges two entries at one path. Two namespaces merge key by key, and their fallbacks merge too. */
|
|
@@ -1524,8 +1898,8 @@ function mergeCode(m, draft, part, index, replaces) {
|
|
|
1524
1898
|
const runnerPath = ["runners", name];
|
|
1525
1899
|
if (replaces.has(keyOf(evaluatorPath)) || replaces.has(keyOf(runnerPath))) {
|
|
1526
1900
|
if (evaluator === void 0) delete draft.evaluators[name];
|
|
1527
|
-
else setOwn$
|
|
1528
|
-
if (runner !== void 0) setOwn$
|
|
1901
|
+
else setOwn$5(draft.evaluators, name, evaluator);
|
|
1902
|
+
if (runner !== void 0) setOwn$5(draft.runners ??= {}, name, runner);
|
|
1529
1903
|
else if (draft.runners !== void 0) delete draft.runners[name];
|
|
1530
1904
|
m.replace(evaluatorPath, index);
|
|
1531
1905
|
m.replace(runnerPath, index);
|
|
@@ -1648,7 +2022,7 @@ function removeEntry(entries, path) {
|
|
|
1648
2022
|
}
|
|
1649
2023
|
const out = { ...entries };
|
|
1650
2024
|
if (next === void 0) delete out[key];
|
|
1651
|
-
else setOwn$
|
|
2025
|
+
else setOwn$5(out, key, next);
|
|
1652
2026
|
return out;
|
|
1653
2027
|
}
|
|
1654
2028
|
/**
|
|
@@ -2043,11 +2417,12 @@ function derefStep(nav, thing, key) {
|
|
|
2043
2417
|
const derefs = nav.derefs;
|
|
2044
2418
|
const answer = derefs === void 0 ? void 0 : answerOf(nav.world, ownEntries(envelope), kindFor(nav.world, envelope.type), "deref", (name) => codeIn(derefs, name));
|
|
2045
2419
|
if (answer !== void 0) {
|
|
2046
|
-
const
|
|
2047
|
-
|
|
2420
|
+
const code = derefs[answer.name];
|
|
2421
|
+
const reached = code?.(envelope, key);
|
|
2422
|
+
return reached !== void 0 || code?.has?.(envelope, key) === true ? {
|
|
2048
2423
|
thing: reached,
|
|
2049
2424
|
by: answer.rung
|
|
2050
|
-
};
|
|
2425
|
+
} : void 0;
|
|
2051
2426
|
}
|
|
2052
2427
|
const reached = plainStep(envelope.value, key);
|
|
2053
2428
|
return reached === NOTHING ? void 0 : {
|
|
@@ -2285,6 +2660,25 @@ function resolve(nav, ctx, path, options = {}) {
|
|
|
2285
2660
|
}
|
|
2286
2661
|
return resolveFully$1(nav, ctx, path, options.hops === false ? void 0 : [], void 0);
|
|
2287
2662
|
}
|
|
2663
|
+
/** The functions of the last navigator that gave a walker its input. A walk with the same navigator makes no new functions. */
|
|
2664
|
+
let lastSteps;
|
|
2665
|
+
/**
|
|
2666
|
+
* This function gives the functions `deref` and `has` of the input of a walker. They depend only on the navigator,
|
|
2667
|
+
* thus the function keeps the functions of the last navigator (K170).
|
|
2668
|
+
*/
|
|
2669
|
+
function walkSteps(nav) {
|
|
2670
|
+
if (lastSteps !== void 0 && lastSteps.nav === nav) return lastSteps;
|
|
2671
|
+
const adopt = nav.adopt;
|
|
2672
|
+
lastSteps = {
|
|
2673
|
+
nav,
|
|
2674
|
+
deref: adopt === void 0 ? (thing, key) => derefStep(nav, thing, key)?.thing : (thing, key) => {
|
|
2675
|
+
const reached = derefStep(nav, thing, key);
|
|
2676
|
+
return reached === void 0 ? void 0 : adopt(reached.thing);
|
|
2677
|
+
},
|
|
2678
|
+
has: (thing, key) => derefStep(nav, thing, key) !== void 0
|
|
2679
|
+
};
|
|
2680
|
+
return lastSteps;
|
|
2681
|
+
}
|
|
2288
2682
|
/**
|
|
2289
2683
|
* This function resolves a path with the full algorithm of `resolve`.
|
|
2290
2684
|
*
|
|
@@ -2379,14 +2773,24 @@ function resolveFully$1(nav, ctx, path, hops, known) {
|
|
|
2379
2773
|
const walker = given === void 0 ? walkerOf(nav, step) : given ?? void 0;
|
|
2380
2774
|
if (walker === void 0) return failed("#STEP?", "A step is a key, or a document of a kind with the verb step.");
|
|
2381
2775
|
const envelope = step;
|
|
2382
|
-
const
|
|
2776
|
+
const { deref, has } = walkSteps(nav);
|
|
2777
|
+
const result = walker(hops === void 0 ? {
|
|
2383
2778
|
data: envelope.value,
|
|
2384
2779
|
here,
|
|
2385
2780
|
context: ctx,
|
|
2386
|
-
deref
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2781
|
+
deref,
|
|
2782
|
+
has
|
|
2783
|
+
} : {
|
|
2784
|
+
data: envelope.value,
|
|
2785
|
+
here,
|
|
2786
|
+
context: ctx,
|
|
2787
|
+
deref,
|
|
2788
|
+
has,
|
|
2789
|
+
visit: (key, thing) => hops.push({
|
|
2790
|
+
step: key,
|
|
2791
|
+
thing,
|
|
2792
|
+
by: "step"
|
|
2793
|
+
})
|
|
2390
2794
|
});
|
|
2391
2795
|
if (!result.ok) return failed(result.code ?? "#N/A", result.reason);
|
|
2392
2796
|
here = adopt(result.thing);
|
|
@@ -2468,7 +2872,7 @@ var Bad = class {
|
|
|
2468
2872
|
}
|
|
2469
2873
|
};
|
|
2470
2874
|
/** This function sets an own data property. Only the key `__proto__` needs `defineProperty`. */
|
|
2471
|
-
function setOwn$
|
|
2875
|
+
function setOwn$4(target, key, value) {
|
|
2472
2876
|
if (key === "__proto__") Object.defineProperty(target, key, {
|
|
2473
2877
|
value,
|
|
2474
2878
|
enumerable: true,
|
|
@@ -2482,7 +2886,7 @@ function hasSlots(template) {
|
|
|
2482
2886
|
return template.t !== "data";
|
|
2483
2887
|
}
|
|
2484
2888
|
/** This function gives the shape of a part with the default type `of` and the value type `expect` of its container. */
|
|
2485
|
-
function termOf(of, expect) {
|
|
2889
|
+
function termOf$1(of, expect) {
|
|
2486
2890
|
return {
|
|
2487
2891
|
shape: "term",
|
|
2488
2892
|
...of === void 0 ? {} : { of },
|
|
@@ -2591,7 +2995,7 @@ function splitParts(shape, value) {
|
|
|
2591
2995
|
const twice = list.findIndex((child, index) => list.slice(0, index).some((before) => docEquals(before, child)));
|
|
2592
2996
|
if (twice !== -1) throw new Bad("The items of the list must be different.", [...path, twice]);
|
|
2593
2997
|
}
|
|
2594
|
-
const each = spec.item ?? termOf(spec.of, spec.expect);
|
|
2998
|
+
const each = spec.item ?? termOf$1(spec.of, spec.expect);
|
|
2595
2999
|
const items = list.map((child, index) => walk(each, child, [...path, index]));
|
|
2596
3000
|
return isData(each) ? {
|
|
2597
3001
|
t: "data",
|
|
@@ -2603,14 +3007,14 @@ function splitParts(shape, value) {
|
|
|
2603
3007
|
}
|
|
2604
3008
|
if (!isRecord(item)) throw new Bad("The value must be a record.", path);
|
|
2605
3009
|
if (spec.shape === "map") {
|
|
2606
|
-
const each = spec.item ?? termOf(spec.of, spec.expect);
|
|
3010
|
+
const each = spec.item ?? termOf$1(spec.of, spec.expect);
|
|
2607
3011
|
return {
|
|
2608
3012
|
t: "record",
|
|
2609
3013
|
fields: Object.keys(item).map((key) => [key, walk(each, item[key], [...path, key])])
|
|
2610
3014
|
};
|
|
2611
3015
|
}
|
|
2612
3016
|
for (const name of spec.required ?? []) if (!Object.hasOwn(item, name)) throw new Bad(`The field ${name} is missing.`, path);
|
|
2613
|
-
const named = Array.isArray(spec.fields) ? Object.fromEntries(spec.fields.map((name) => [name, termOf(spec.of, spec.expect)])) : spec.fields;
|
|
3017
|
+
const named = Array.isArray(spec.fields) ? Object.fromEntries(spec.fields.map((name) => [name, termOf$1(spec.of, spec.expect)])) : spec.fields;
|
|
2614
3018
|
const fields = [];
|
|
2615
3019
|
for (const key of Object.keys(item)) {
|
|
2616
3020
|
if (spec.closed === true && !Object.hasOwn(named, key)) throw new Bad(`The field ${key} is not a field of this kind.`, [...path, key]);
|
|
@@ -2640,7 +3044,7 @@ function splitParts(shape, value) {
|
|
|
2640
3044
|
const out = {};
|
|
2641
3045
|
for (const [key, child] of node.fields) {
|
|
2642
3046
|
const kept = strip(child);
|
|
2643
|
-
if (kept !== void 0) setOwn$
|
|
3047
|
+
if (kept !== void 0) setOwn$4(out, key, kept);
|
|
2644
3048
|
}
|
|
2645
3049
|
return out;
|
|
2646
3050
|
};
|
|
@@ -2649,7 +3053,7 @@ function splitParts(shape, value) {
|
|
|
2649
3053
|
if (node.t === "slot") return handles[node.index];
|
|
2650
3054
|
if (node.t === "list") return node.items.map((child) => fillWith(child, handles));
|
|
2651
3055
|
const out = {};
|
|
2652
|
-
for (const [key, child] of node.fields) setOwn$
|
|
3056
|
+
for (const [key, child] of node.fields) setOwn$4(out, key, fillWith(child, handles));
|
|
2653
3057
|
return out;
|
|
2654
3058
|
};
|
|
2655
3059
|
return {
|
|
@@ -2907,10 +3311,19 @@ const GRANT = /* @__PURE__ */ Symbol("coeval.grant");
|
|
|
2907
3311
|
/**
|
|
2908
3312
|
* The key of the epoch of a grant (CAP.NARROW-KEPT). The epoch is the list of capabilities that the host gave the root
|
|
2909
3313
|
* of the evaluation of the grant. A spread of a context keeps its epoch, as it keeps its grant. An
|
|
2910
|
-
* effect and a resolution read a context of another epoch as a context without a grant.
|
|
3314
|
+
* effect and a resolution read a context of another epoch as a context without a grant. The root has no field of
|
|
3315
|
+
* the epoch, because its grant is the list of the host (`epochOf`).
|
|
2911
3316
|
*/
|
|
2912
3317
|
const EPOCH = /* @__PURE__ */ Symbol("coeval.epoch");
|
|
2913
3318
|
/**
|
|
3319
|
+
* This function gives the epoch of the grant of a context (CAP.NARROW-KEPT). A context without the field of the epoch
|
|
3320
|
+
* has the epoch of its grant. Only a root has no field, thus its grant is the list of the host. The root then has
|
|
3321
|
+
* one field fewer, and each evaluation makes it faster (K154).
|
|
3322
|
+
*/
|
|
3323
|
+
function epochOf(ctx) {
|
|
3324
|
+
return ctx[EPOCH] ?? ctx[GRANT];
|
|
3325
|
+
}
|
|
3326
|
+
/**
|
|
2914
3327
|
* This function gives the capabilities that a context and its grant both permit, the meet `intersectCaps` (CAP.MEET).
|
|
2915
3328
|
* A context whose grant is its own list gives that list after one comparison. A context without a grant gives none.
|
|
2916
3329
|
*/
|
|
@@ -2930,10 +3343,36 @@ function granted(ctx) {
|
|
|
2930
3343
|
return {
|
|
2931
3344
|
...ctx,
|
|
2932
3345
|
caps,
|
|
2933
|
-
[GRANT]: caps
|
|
3346
|
+
[GRANT]: caps,
|
|
3347
|
+
[EPOCH]: epochOf(ctx)
|
|
2934
3348
|
};
|
|
2935
3349
|
}
|
|
2936
3350
|
/**
|
|
3351
|
+
* This function tells if a list of capabilities is equal to the epoch of the evaluation (CAP.EPOCH-EQUAL). An equal list
|
|
3352
|
+
* has the same length, and at each index the same text or the same keys in the same order. The runtime uses it only
|
|
3353
|
+
* when the two lists are not the same object. Thus a host that gives a new copy of its list for each evaluation keeps
|
|
3354
|
+
* the grant of a kept context.
|
|
3355
|
+
*/
|
|
3356
|
+
function sameCaps(caps, epoch) {
|
|
3357
|
+
if (caps === void 0 || caps.length !== epoch.length) return false;
|
|
3358
|
+
for (let index = 0; index < caps.length; index += 1) {
|
|
3359
|
+
const a = caps[index];
|
|
3360
|
+
const b = epoch[index];
|
|
3361
|
+
if (a === b) continue;
|
|
3362
|
+
if (typeof a === "string" || typeof b === "string" || a.length !== b.length) return false;
|
|
3363
|
+
for (let key = 0; key < a.length; key += 1) if (!Object.is(a[key], b[key])) return false;
|
|
3364
|
+
}
|
|
3365
|
+
return true;
|
|
3366
|
+
}
|
|
3367
|
+
/**
|
|
3368
|
+
* This function tells if a context has the epoch of the evaluation: the same list, or an equal list (CAP.NARROW-KEPT,
|
|
3369
|
+
* CAP.EPOCH-EQUAL). The comparison of the entries happens only when the two lists are not the same object.
|
|
3370
|
+
*/
|
|
3371
|
+
function inEpoch(ctx, epoch) {
|
|
3372
|
+
const own = epochOf(ctx);
|
|
3373
|
+
return own === epoch || sameCaps(own, epoch);
|
|
3374
|
+
}
|
|
3375
|
+
/**
|
|
2937
3376
|
* This function gives a context of another epoch the epoch of the evaluation, without capabilities (CAP.NARROW-KEPT).
|
|
2938
3377
|
* Thus the context reads as a context without a grant. The kernel cannot know the document of the context in this
|
|
2939
3378
|
* evaluation.
|
|
@@ -2951,16 +3390,26 @@ function ungranted(ctx, epoch) {
|
|
|
2951
3390
|
* The context has the meet of its list and its grant, and a context without a grant has none. Thus a read of the root
|
|
2952
3391
|
* `caps` and a walker of a step document get the capabilities of the document from a widened context.
|
|
2953
3392
|
* A context that the code narrowed keeps its own list. A context whose list does not change stays the same object.
|
|
2954
|
-
*
|
|
3393
|
+
*
|
|
3394
|
+
* A context of another epoch has no capabilities (CAP.NARROW-KEPT). An equal list of the host is the same epoch
|
|
3395
|
+
* (CAP.EPOCH-EQUAL). The first test answers for a context of the epoch whose grant is its list, for example the root.
|
|
3396
|
+
* The other contexts go to `readableOther`, thus the hot part of the function stays small (K154).
|
|
2955
3397
|
*/
|
|
2956
3398
|
function readable(ctx, epoch) {
|
|
2957
|
-
|
|
3399
|
+
const grant = ctx[GRANT];
|
|
3400
|
+
if (grant === ctx.caps && (ctx[EPOCH] ?? grant) === epoch) return ctx;
|
|
3401
|
+
return readableOther(ctx, epoch);
|
|
3402
|
+
}
|
|
3403
|
+
/** This function gives the result of `readable` for each other context: a context of another epoch or a narrowed context. */
|
|
3404
|
+
function readableOther(ctx, epoch) {
|
|
3405
|
+
if (!inEpoch(ctx, epoch)) return ctx.caps.length === 0 ? ctx : ungranted(ctx, epoch);
|
|
2958
3406
|
const caps = grantedCaps(ctx);
|
|
2959
3407
|
if (caps === ctx.caps || caps.length === 0 && ctx.caps.length === 0) return ctx;
|
|
2960
3408
|
return {
|
|
2961
3409
|
...ctx,
|
|
2962
3410
|
caps,
|
|
2963
|
-
[GRANT]: caps
|
|
3411
|
+
[GRANT]: caps,
|
|
3412
|
+
[EPOCH]: epochOf(ctx)
|
|
2964
3413
|
};
|
|
2965
3414
|
}
|
|
2966
3415
|
/**
|
|
@@ -2970,8 +3419,8 @@ function readable(ctx, epoch) {
|
|
|
2970
3419
|
*/
|
|
2971
3420
|
function handed(parent, next) {
|
|
2972
3421
|
const ctx = narrow(parent, next);
|
|
2973
|
-
const epoch = parent
|
|
2974
|
-
if (ctx[GRANT] === ctx.caps && ctx
|
|
3422
|
+
const epoch = epochOf(parent);
|
|
3423
|
+
if (ctx[GRANT] === ctx.caps && epochOf(ctx) === epoch) return ctx;
|
|
2975
3424
|
return {
|
|
2976
3425
|
...ctx,
|
|
2977
3426
|
[GRANT]: ctx.caps,
|
|
@@ -2979,7 +3428,7 @@ function handed(parent, next) {
|
|
|
2979
3428
|
};
|
|
2980
3429
|
}
|
|
2981
3430
|
/** The text that names a resolution in the error of an exception of the host. */
|
|
2982
|
-
const READ_FAILED = "the resolution of a path";
|
|
3431
|
+
const READ_FAILED$1 = "the resolution of a path";
|
|
2983
3432
|
/**
|
|
2984
3433
|
* This function gives what a resolution reached as a result: the thing, or the error with its code and its reason.
|
|
2985
3434
|
* The record of a read and `verifyReads` share it.
|
|
@@ -3008,7 +3457,7 @@ function readAgain(nav, ctx, path, hops) {
|
|
|
3008
3457
|
hops: found.hops
|
|
3009
3458
|
} : { reached: reachedOf(found) };
|
|
3010
3459
|
} catch (error) {
|
|
3011
|
-
return { reached: thrown(error, READ_FAILED) };
|
|
3460
|
+
return { reached: thrown(error, READ_FAILED$1) };
|
|
3012
3461
|
}
|
|
3013
3462
|
}
|
|
3014
3463
|
/**
|
|
@@ -3084,25 +3533,85 @@ const WITHOUT_HOPS = /* @__PURE__ */ Object.freeze({ hops: false });
|
|
|
3084
3533
|
const WITH_HOPS = /* @__PURE__ */ Object.freeze({ hops: true });
|
|
3085
3534
|
/**
|
|
3086
3535
|
* This function gives the path of a kept read (READS.CLOSURE). The evaluation did not ask for the read. Thus the read
|
|
3087
|
-
* does not step into a host thing
|
|
3536
|
+
* does not step into a host thing with `deref`. A host thing with `known` answers a step without new work
|
|
3537
|
+
* (SCHED.CLOSURE). Each other host thing ends the path, and a host thing that does not know the step ends it too.
|
|
3088
3538
|
* A reference in the data reads the context of its reader, thus the function then gives `undefined`.
|
|
3089
3539
|
*
|
|
3090
3540
|
* @param nav - The world and the code of the evaluators.
|
|
3091
3541
|
* @param root - The context at the root of the evaluation.
|
|
3092
3542
|
* @param path - The root and the keys of the static read.
|
|
3093
|
-
* @returns The path of the kept read
|
|
3543
|
+
* @returns The path of the kept read with what it reached after a step of `known`, or `undefined` for a reference on the way.
|
|
3094
3544
|
*/
|
|
3095
3545
|
function keptPath(nav, root, path) {
|
|
3096
3546
|
const adopt = nav.adopt ?? ((value) => value);
|
|
3097
3547
|
let here = adopt(root[path[0]]);
|
|
3548
|
+
let known = false;
|
|
3549
|
+
const end = (index) => {
|
|
3550
|
+
const kept = index >= path.length ? path : path.slice(0, index);
|
|
3551
|
+
return known ? {
|
|
3552
|
+
path: kept,
|
|
3553
|
+
reached: ok(here)
|
|
3554
|
+
} : { path: kept };
|
|
3555
|
+
};
|
|
3098
3556
|
for (let index = 1;; index += 1) {
|
|
3099
|
-
if (isHostThing(here))
|
|
3557
|
+
if (isHostThing(here)) {
|
|
3558
|
+
if (index >= path.length || here.known === void 0) return end(index);
|
|
3559
|
+
let step;
|
|
3560
|
+
try {
|
|
3561
|
+
step = here.known(path[index]);
|
|
3562
|
+
} catch (error) {
|
|
3563
|
+
return {
|
|
3564
|
+
path: path.slice(0, index + 1),
|
|
3565
|
+
reached: thrown(error, READ_FAILED$1)
|
|
3566
|
+
};
|
|
3567
|
+
}
|
|
3568
|
+
if (step === void 0) return end(index);
|
|
3569
|
+
known = true;
|
|
3570
|
+
here = adopt(step.thing);
|
|
3571
|
+
continue;
|
|
3572
|
+
}
|
|
3100
3573
|
if (nav.plain !== true && isRef(here)) return void 0;
|
|
3101
|
-
if (index >= path.length) return
|
|
3574
|
+
if (index >= path.length) return end(index);
|
|
3575
|
+
const reached = derefStep(nav, here, path[index]);
|
|
3576
|
+
if (reached === void 0) return known ? end(index) : { path };
|
|
3577
|
+
here = adopt(reached.thing);
|
|
3578
|
+
}
|
|
3579
|
+
}
|
|
3580
|
+
/**
|
|
3581
|
+
* This function resolves the path of a kept read again, for `verifyReads` (SCHED.CLOSURE). It walks the path as
|
|
3582
|
+
* `keptPath` did. A host thing with `known` answers its step. A path without such a step resolves with `readAgain`.
|
|
3583
|
+
*
|
|
3584
|
+
* @param nav - The world and the code of the evaluators.
|
|
3585
|
+
* @param ctx - The new context.
|
|
3586
|
+
* @param path - The path of the kept read.
|
|
3587
|
+
* @returns What the path reaches, or `undefined` when a host does not know a step yet. Then the read changed.
|
|
3588
|
+
*/
|
|
3589
|
+
function keptAgain(nav, ctx, path) {
|
|
3590
|
+
const adopt = nav.adopt ?? ((value) => value);
|
|
3591
|
+
let here = adopt(ctx[path[0]]);
|
|
3592
|
+
let known = false;
|
|
3593
|
+
const other = () => known ? void 0 : readAgain(nav, ctx, path, false).reached;
|
|
3594
|
+
for (let index = 1; index < path.length; index += 1) {
|
|
3595
|
+
if (isHostThing(here)) {
|
|
3596
|
+
if (here.known === void 0) return other();
|
|
3597
|
+
let step;
|
|
3598
|
+
try {
|
|
3599
|
+
step = here.known(path[index]);
|
|
3600
|
+
} catch (error) {
|
|
3601
|
+
return thrown(error, READ_FAILED$1);
|
|
3602
|
+
}
|
|
3603
|
+
if (step === void 0) return void 0;
|
|
3604
|
+
known = true;
|
|
3605
|
+
here = adopt(step.thing);
|
|
3606
|
+
continue;
|
|
3607
|
+
}
|
|
3608
|
+
if (nav.plain !== true && isRef(here)) return other();
|
|
3102
3609
|
const reached = derefStep(nav, here, path[index]);
|
|
3103
|
-
if (reached === void 0) return
|
|
3610
|
+
if (reached === void 0) return other();
|
|
3104
3611
|
here = adopt(reached.thing);
|
|
3105
3612
|
}
|
|
3613
|
+
if (!known) return other();
|
|
3614
|
+
return nav.plain !== true && isRef(here) ? void 0 : ok(here);
|
|
3106
3615
|
}
|
|
3107
3616
|
/** This function makes the record of the reads of an evaluation, when the host gives a list. */
|
|
3108
3617
|
function readingOf(list, root) {
|
|
@@ -3184,7 +3693,7 @@ var Session = class {
|
|
|
3184
3693
|
} catch (error) {
|
|
3185
3694
|
reading.list.push({
|
|
3186
3695
|
path,
|
|
3187
|
-
reached: thrown(error, READ_FAILED),
|
|
3696
|
+
reached: thrown(error, READ_FAILED$1),
|
|
3188
3697
|
free
|
|
3189
3698
|
});
|
|
3190
3699
|
throw error;
|
|
@@ -3233,8 +3742,9 @@ var Session = class {
|
|
|
3233
3742
|
return true;
|
|
3234
3743
|
}
|
|
3235
3744
|
if (root === "self" && reading.self) return true;
|
|
3236
|
-
const
|
|
3237
|
-
if (
|
|
3745
|
+
const walk = keptPath(this.machine, reading.root, read.exact ? exact : [root]);
|
|
3746
|
+
if (walk === void 0) return false;
|
|
3747
|
+
const path = walk.path;
|
|
3238
3748
|
const paths = reading.paths ??= /* @__PURE__ */ new Set();
|
|
3239
3749
|
const text = JSON.stringify(path);
|
|
3240
3750
|
if (paths.has(text) || paths.has(JSON.stringify([root]))) return true;
|
|
@@ -3242,7 +3752,7 @@ var Session = class {
|
|
|
3242
3752
|
if (path.length === 1 && root === "self") reading.self = true;
|
|
3243
3753
|
reading.list.push({
|
|
3244
3754
|
path,
|
|
3245
|
-
reached: readAgain(this.machine, reading.root, path, false).reached,
|
|
3755
|
+
reached: walk.reached ?? readAgain(this.machine, reading.root, path, false).reached,
|
|
3246
3756
|
free: true,
|
|
3247
3757
|
kept: true
|
|
3248
3758
|
});
|
|
@@ -3271,10 +3781,10 @@ var Session = class {
|
|
|
3271
3781
|
* This function checks the capability of an effect, and records the effect with its address (RUN.ADDRESS). The
|
|
3272
3782
|
* context keeps only the capabilities of its grant, thus compiled code cannot widen the context of its own effect
|
|
3273
3783
|
* (CAP.NARROW-CODE). A context of another epoch has no capabilities, thus code cannot keep a context for a later
|
|
3274
|
-
* evaluation with fewer capabilities (CAP.NARROW-KEPT).
|
|
3784
|
+
* evaluation with fewer capabilities (CAP.NARROW-KEPT). An equal list of the host is the same epoch (CAP.EPOCH-EQUAL).
|
|
3275
3785
|
*/
|
|
3276
3786
|
record(given, kind, payload, place) {
|
|
3277
|
-
const ctx = given
|
|
3787
|
+
const ctx = inEpoch(given, this.epoch) ? granted(given) : ungranted(given, this.epoch);
|
|
3278
3788
|
const keys = typeof kind === "string" ? kind.split(".") : kind;
|
|
3279
3789
|
const text = keys.join(".");
|
|
3280
3790
|
if (!this.permitted(ctx, keys)) return fail("#CAP!", `The context has no capability for the effect ${text}.`, { place });
|
|
@@ -3357,6 +3867,7 @@ var Session = class {
|
|
|
3357
3867
|
let failed = false;
|
|
3358
3868
|
const active = [];
|
|
3359
3869
|
let next = 0;
|
|
3870
|
+
let before = 0;
|
|
3360
3871
|
const settle = (index, result) => {
|
|
3361
3872
|
results[index] = result;
|
|
3362
3873
|
if (failFast && !result.ok) failed = true;
|
|
@@ -3420,10 +3931,11 @@ var Session = class {
|
|
|
3420
3931
|
};
|
|
3421
3932
|
start();
|
|
3422
3933
|
while (active.length > 0 && !failed) {
|
|
3423
|
-
const chosen = journal?.next?.(address, active.map((item) => item.index));
|
|
3934
|
+
const chosen = journal?.next?.(address, active.map((item) => item.index), before);
|
|
3424
3935
|
const pick = chosen === void 0 ? void 0 : active.find((item) => item.index === chosen);
|
|
3425
3936
|
const settled = yield pick === void 0 ? Promise.race(active.map((item) => item.pending)) : pick.pending;
|
|
3426
3937
|
journal?.settled?.(address, settled.item.index);
|
|
3938
|
+
before += 1;
|
|
3427
3939
|
active.splice(active.indexOf(settled.item), 1);
|
|
3428
3940
|
advance(settled.item.index, settled.item.steps, settled);
|
|
3429
3941
|
start();
|
|
@@ -3734,7 +4246,10 @@ function compileNode(c, value, place, declared, depth, address = false) {
|
|
|
3734
4246
|
if (frees === void 0 || kind === void 0 || !pureCode(c, envelope, kind, typePath, name, quick)) return done;
|
|
3735
4247
|
const free = freeOf(doc, kind, split.parts, frees);
|
|
3736
4248
|
if (free === void 0) return done;
|
|
3737
|
-
|
|
4249
|
+
if (free.length === 0) return folded(c, done, void 0);
|
|
4250
|
+
const fixed = c.fixed;
|
|
4251
|
+
if (fixed !== void 0 && free.every((each) => Object.hasOwn(fixed, each))) return folded(c, done, free);
|
|
4252
|
+
return {
|
|
3738
4253
|
code: done.code,
|
|
3739
4254
|
run: done.run,
|
|
3740
4255
|
free
|
|
@@ -3814,6 +4329,21 @@ var Probe = class {
|
|
|
3814
4329
|
};
|
|
3815
4330
|
this.root = root;
|
|
3816
4331
|
}
|
|
4332
|
+
/** The context of a part that folds with fixed names: the context of the probe, with the fixed names (PLAN.FIXED). */
|
|
4333
|
+
withFixed;
|
|
4334
|
+
/**
|
|
4335
|
+
* This function gives the context of a part that folds with fixed names. Its root `names` holds only the fixed
|
|
4336
|
+
* names, thus the probe resolves each fixed name and refuses each other name.
|
|
4337
|
+
*
|
|
4338
|
+
* @param fixed - The fixed names and their values.
|
|
4339
|
+
* @returns The context.
|
|
4340
|
+
*/
|
|
4341
|
+
fixedRoot(fixed) {
|
|
4342
|
+
return this.withFixed ??= {
|
|
4343
|
+
...this.root,
|
|
4344
|
+
names: fixed
|
|
4345
|
+
};
|
|
4346
|
+
}
|
|
3817
4347
|
resolve(ctx, path) {
|
|
3818
4348
|
const name = path[1];
|
|
3819
4349
|
if (path[0] === "names" && isKey(name) && Object.hasOwn(ctx.names, String(name))) {
|
|
@@ -3872,13 +4402,14 @@ function trap(refuse, copies) {
|
|
|
3872
4402
|
* one deeply frozen copy, and the code gives that copy at each evaluation (PLAN.SHARED). A fold that the probe cancels
|
|
3873
4403
|
* gives the code without free names, thus no parent tries the fold again.
|
|
3874
4404
|
*/
|
|
3875
|
-
function folded(c, done) {
|
|
4405
|
+
function folded(c, done, fixed) {
|
|
3876
4406
|
const probe = c.probe ??= new Probe(c.machine);
|
|
3877
4407
|
probe.refused = false;
|
|
3878
4408
|
probe.reads = 0;
|
|
3879
4409
|
let constant;
|
|
3880
4410
|
try {
|
|
3881
|
-
const
|
|
4411
|
+
const root = fixed === void 0 ? probe.root : probe.fixedRoot(c.fixed);
|
|
4412
|
+
const result = done.code(root, probe);
|
|
3882
4413
|
if (!probe.refused && (result.ok || result.error.code !== "#DEPTH!")) constant = sharedCopy(result);
|
|
3883
4414
|
} catch {
|
|
3884
4415
|
constant = void 0;
|
|
@@ -3888,18 +4419,100 @@ function folded(c, done) {
|
|
|
3888
4419
|
run: done.run
|
|
3889
4420
|
};
|
|
3890
4421
|
const value = constant;
|
|
4422
|
+
const original = done.code;
|
|
4423
|
+
if (fixed !== void 0) return {
|
|
4424
|
+
code: fixedCode(fixed, c.fixed, value, original),
|
|
4425
|
+
run: void 0,
|
|
4426
|
+
free: fixed
|
|
4427
|
+
};
|
|
3891
4428
|
if (probe.reads === 0) return {
|
|
3892
4429
|
code: () => value,
|
|
3893
4430
|
run: void 0,
|
|
3894
4431
|
free: CLOSED
|
|
3895
4432
|
};
|
|
3896
|
-
const original = done.code;
|
|
3897
4433
|
return {
|
|
3898
4434
|
code: (ctx, rt) => rt instanceof Session && !rt.traced && !rt.recording ? value : original(ctx, rt),
|
|
3899
4435
|
run: void 0,
|
|
3900
4436
|
free: CLOSED
|
|
3901
4437
|
};
|
|
3902
4438
|
}
|
|
4439
|
+
/**
|
|
4440
|
+
* This function gives the code of a part that folded with fixed names (PLAN.FIXED, D60). The code gives the folded
|
|
4441
|
+
* value when the context has the fixed value of each name, by an own-key test and `Object.is`. A fixed value is
|
|
4442
|
+
* deeply frozen, thus the same object gives the same result. Each other context, a list of reads and another runtime
|
|
4443
|
+
* get the code without the fold. The probe of a parent fold gets the folded value, and it counts a read.
|
|
4444
|
+
*/
|
|
4445
|
+
function fixedCode(names, fixed, value, original) {
|
|
4446
|
+
if (names.length === 1) {
|
|
4447
|
+
const name = names[0];
|
|
4448
|
+
const want = fixed[name];
|
|
4449
|
+
return (ctx, rt) => {
|
|
4450
|
+
const given = ctx.names;
|
|
4451
|
+
return Object.hasOwn(given, name) && Object.is(given[name], want) && folds(rt) ? value : original(ctx, rt);
|
|
4452
|
+
};
|
|
4453
|
+
}
|
|
4454
|
+
const wants = names.map((name) => fixed[name]);
|
|
4455
|
+
return (ctx, rt) => {
|
|
4456
|
+
const given = ctx.names;
|
|
4457
|
+
for (let index = 0; index < names.length; index += 1) {
|
|
4458
|
+
const name = names[index];
|
|
4459
|
+
if (!Object.hasOwn(given, name) || !Object.is(given[name], wants[index])) return original(ctx, rt);
|
|
4460
|
+
}
|
|
4461
|
+
return folds(rt) ? value : original(ctx, rt);
|
|
4462
|
+
};
|
|
4463
|
+
}
|
|
4464
|
+
/**
|
|
4465
|
+
* This function tells if the code of a part with fixed names can give its folded value in a runtime. The probe of a
|
|
4466
|
+
* fold counts the read of the part, as the code without the fold reads the names.
|
|
4467
|
+
*/
|
|
4468
|
+
function folds(rt) {
|
|
4469
|
+
if (rt instanceof Session) return !rt.recording;
|
|
4470
|
+
if (!(rt instanceof Probe)) return false;
|
|
4471
|
+
rt.reads += 1;
|
|
4472
|
+
return true;
|
|
4473
|
+
}
|
|
4474
|
+
/**
|
|
4475
|
+
* This function gives the fixed names of the options of `prepare` (PLAN.FIXED). A name fixes only when its value is
|
|
4476
|
+
* deeply frozen plain data. Then no change in place can make the folded value stale.
|
|
4477
|
+
*
|
|
4478
|
+
* @returns A record without a prototype, or `undefined` when no name fixes.
|
|
4479
|
+
*/
|
|
4480
|
+
function fixedNamesOf(given) {
|
|
4481
|
+
if (given === void 0) return void 0;
|
|
4482
|
+
let out;
|
|
4483
|
+
for (const name of Object.keys(given)) {
|
|
4484
|
+
const value = given[name];
|
|
4485
|
+
if (!frozenPlain(value)) continue;
|
|
4486
|
+
out ??= Object.create(null);
|
|
4487
|
+
Object.defineProperty(out, name, {
|
|
4488
|
+
value,
|
|
4489
|
+
enumerable: true
|
|
4490
|
+
});
|
|
4491
|
+
}
|
|
4492
|
+
return out;
|
|
4493
|
+
}
|
|
4494
|
+
/** This function tells if a value is plain data, as `sharedCopy` reads it, and if each of its objects is frozen. */
|
|
4495
|
+
function frozenPlain(value) {
|
|
4496
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4497
|
+
const walk = (here) => {
|
|
4498
|
+
if (typeof here === "function") return false;
|
|
4499
|
+
if (typeof here !== "object" || here === null || SHARED.has(here) || seen.has(here)) return true;
|
|
4500
|
+
if (!Object.isFrozen(here)) return false;
|
|
4501
|
+
seen.add(here);
|
|
4502
|
+
const proto = Object.getPrototypeOf(here);
|
|
4503
|
+
if (Array.isArray(here) ? proto !== Array.prototype : proto !== Object.prototype && proto !== null) return false;
|
|
4504
|
+
for (const key of Reflect.ownKeys(here)) {
|
|
4505
|
+
const field = Object.getOwnPropertyDescriptor(here, key);
|
|
4506
|
+
if (typeof key !== "string" || field === void 0 || !("value" in field) || !walk(field.value)) return false;
|
|
4507
|
+
}
|
|
4508
|
+
return true;
|
|
4509
|
+
};
|
|
4510
|
+
try {
|
|
4511
|
+
return walk(value);
|
|
4512
|
+
} catch {
|
|
4513
|
+
return false;
|
|
4514
|
+
}
|
|
4515
|
+
}
|
|
3903
4516
|
/** The objects that a fold made. Each one is deeply frozen, thus a later fold uses it again and does not copy it. */
|
|
3904
4517
|
const SHARED = /* @__PURE__ */ new WeakSet();
|
|
3905
4518
|
/** The mark of a value that is not plain data. */
|
|
@@ -3907,7 +4520,11 @@ const NOT_PLAIN = /* @__PURE__ */ Symbol("not plain");
|
|
|
3907
4520
|
/**
|
|
3908
4521
|
* This function gives a deeply frozen copy of plain data, or `undefined` for other values. Plain data is a primitive or
|
|
3909
4522
|
* an array without holes and extra keys. It is also a record of enumerable data fields with the prototype of an object
|
|
3910
|
-
* or no prototype. The copy keeps cycles and shared objects. The objects of the host do not change.
|
|
4523
|
+
* or no prototype. The copy keeps cycles and shared objects. The objects of the host do not change. The tier `fixed`
|
|
4524
|
+
* of `agree` uses it. The package does not export it.
|
|
4525
|
+
*
|
|
4526
|
+
* @param value - The value.
|
|
4527
|
+
* @returns The frozen copy, or `undefined`.
|
|
3911
4528
|
*/
|
|
3912
4529
|
function sharedCopy(value) {
|
|
3913
4530
|
try {
|
|
@@ -4007,7 +4624,8 @@ function gated(rules, part) {
|
|
|
4007
4624
|
return {
|
|
4008
4625
|
...ctx,
|
|
4009
4626
|
caps,
|
|
4010
|
-
[GRANT]: caps
|
|
4627
|
+
[GRANT]: caps,
|
|
4628
|
+
[EPOCH]: epochOf(ctx)
|
|
4011
4629
|
};
|
|
4012
4630
|
};
|
|
4013
4631
|
return {
|
|
@@ -4046,6 +4664,30 @@ function runOf(code) {
|
|
|
4046
4664
|
function fillOf(split, doc) {
|
|
4047
4665
|
return (items) => split.parts.length === 0 ? doc.value : split.fill(items);
|
|
4048
4666
|
}
|
|
4667
|
+
/**
|
|
4668
|
+
* The key of the runtime on the input of an evaluator. Only the kernel reads it. The node of a tree evaluates its
|
|
4669
|
+
* equations in the evaluation of this runtime, thus their reads go in its list (READS.EQUATION). A copy of the input
|
|
4670
|
+
* with a spread keeps the key.
|
|
4671
|
+
*/
|
|
4672
|
+
const RUNTIME = /* @__PURE__ */ Symbol("coeval.runtime");
|
|
4673
|
+
/**
|
|
4674
|
+
* This function evaluates a plan inside the evaluation of another runtime, for example an equation of a tree inside
|
|
4675
|
+
* the evaluation of its tree (READS.EQUATION). The plan gets its own runtime, as in `plan.evaluate`. The reads of the
|
|
4676
|
+
* plan go in the record of the reads of `outer`. The context at the root of `outer` tells which reads are free
|
|
4677
|
+
* (READS.FREE). Without such a record, the function gives `plan.evaluate(context)`.
|
|
4678
|
+
*
|
|
4679
|
+
* @param plan - The plan.
|
|
4680
|
+
* @param context - The context of the plan.
|
|
4681
|
+
* @param outer - The runtime of the evaluation around the plan, or `undefined`.
|
|
4682
|
+
* @returns The result.
|
|
4683
|
+
*/
|
|
4684
|
+
function evaluateWithin(plan, context, outer) {
|
|
4685
|
+
const reading = outer instanceof Session ? outer.reading : void 0;
|
|
4686
|
+
if (reading === void 0 || !(plan instanceof CompiledPlan)) return plan.evaluate(context);
|
|
4687
|
+
const machine = plan.machine;
|
|
4688
|
+
const ctx = rootContext(machine, context);
|
|
4689
|
+
return plan.code(ctx, new Session(machine, ctx.caps, false, plan.traced, void 0, void 0, void 0, reading));
|
|
4690
|
+
}
|
|
4049
4691
|
function interpret(fn, input, fill) {
|
|
4050
4692
|
const { kind, typePath, doc, data, place, machine } = input;
|
|
4051
4693
|
const address = input.address === true ? { address: true } : {};
|
|
@@ -4072,7 +4714,8 @@ function interpret(fn, input, fill) {
|
|
|
4072
4714
|
...address,
|
|
4073
4715
|
resolve: (path, options) => rt.resolve(ctx, path, options),
|
|
4074
4716
|
effect: (effectKind, payload) => rt.effect(ctx, effectKind, payload, place),
|
|
4075
|
-
fill: refill
|
|
4717
|
+
fill: refill,
|
|
4718
|
+
[RUNTIME]: rt
|
|
4076
4719
|
});
|
|
4077
4720
|
};
|
|
4078
4721
|
}
|
|
@@ -4108,18 +4751,27 @@ function interpretRun(fn, input, fill) {
|
|
|
4108
4751
|
/**
|
|
4109
4752
|
* This function gives the context at the root of an evaluation. It is the context of the host, with the world of
|
|
4110
4753
|
* the machine when the context has none. The host gives the capabilities, thus the root has the grant of each one
|
|
4111
|
-
* (CAP.NARROW-CODE). The list of the host is also the epoch of the grant (CAP.NARROW-KEPT).
|
|
4112
|
-
* `verifyReads` share it.
|
|
4754
|
+
* (CAP.NARROW-CODE). The list of the host is also the epoch of the grant (CAP.NARROW-KEPT). The root has no field of
|
|
4755
|
+
* the epoch, thus it has the shape of a context before K120 (K154). An evaluation and `verifyReads` share it.
|
|
4756
|
+
*
|
|
4757
|
+
* The function reads the epoch of the context one time, and one test keeps a context that is already a root (K169).
|
|
4113
4758
|
*
|
|
4114
4759
|
* @param machine - The machine.
|
|
4115
4760
|
* @param ctx - The context of the host.
|
|
4116
4761
|
* @returns The context at the root.
|
|
4117
4762
|
*/
|
|
4118
4763
|
function rootContext(machine, ctx) {
|
|
4119
|
-
|
|
4764
|
+
const epoch = ctx[EPOCH];
|
|
4765
|
+
if (ctx.world !== null && ctx[GRANT] === ctx.caps && (epoch === void 0 || epoch === ctx.caps)) return ctx;
|
|
4766
|
+
const world = ctx.world === null ? worldThing(machine.world) : ctx.world;
|
|
4767
|
+
if (epoch === void 0) return {
|
|
4768
|
+
...ctx,
|
|
4769
|
+
world,
|
|
4770
|
+
[GRANT]: ctx.caps
|
|
4771
|
+
};
|
|
4120
4772
|
return {
|
|
4121
4773
|
...ctx,
|
|
4122
|
-
world
|
|
4774
|
+
world,
|
|
4123
4775
|
[GRANT]: ctx.caps,
|
|
4124
4776
|
[EPOCH]: ctx.caps
|
|
4125
4777
|
};
|
|
@@ -4145,13 +4797,16 @@ function prepare(machine, doc, options = {}) {
|
|
|
4145
4797
|
steps: []
|
|
4146
4798
|
};
|
|
4147
4799
|
const traced = options.trace === true;
|
|
4800
|
+
const fold = !traced && options.fold !== false;
|
|
4801
|
+
const fixed = fold ? fixedNamesOf(options.fixed?.names) : void 0;
|
|
4148
4802
|
const c = {
|
|
4149
4803
|
machine,
|
|
4150
4804
|
traced,
|
|
4151
4805
|
maxDepth: machine.maxDepth ?? 1e3,
|
|
4152
4806
|
verb,
|
|
4153
4807
|
noAnswer,
|
|
4154
|
-
fold
|
|
4808
|
+
fold,
|
|
4809
|
+
fixed
|
|
4155
4810
|
};
|
|
4156
4811
|
let root;
|
|
4157
4812
|
try {
|
|
@@ -4556,48 +5211,200 @@ const STD_WORLD = {
|
|
|
4556
5211
|
},
|
|
4557
5212
|
defaults: { view: "view.card" }
|
|
4558
5213
|
};
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
5214
|
+
/**
|
|
5215
|
+
* The text syntax of the standard world (K162, D64). The syntax kit (D41) reads it, thus a host of `STD_WORLD` does
|
|
5216
|
+
* not write its own profile. The operators `>`, `+` and `*` make `greater`, `add` and `multiply`. A call form makes
|
|
5217
|
+
* each other kind, and `ref` uses the reference syntax of the kit.
|
|
5218
|
+
*
|
|
5219
|
+
* A literal in a place that takes a document gets its data envelope (K159, D65). Such a place is a part of `if`,
|
|
5220
|
+
* `list`, `seq` or `pure`, the value of `write`, or the whole text. The document after a header name is such a place
|
|
5221
|
+
* too. Thus `list(1, "a")` evaluates to `[1, "a"]`. The forms `add`, `multiply` and `greater` write an operator document that its operator
|
|
5222
|
+
* does not write, for example `add(5)` (K163).
|
|
5223
|
+
*
|
|
5224
|
+
* A host with more kinds spreads this profile and adds its forms to `calls.forms`. `STD_SYNTAX` stays the profile of
|
|
5225
|
+
* the formula kit.
|
|
5226
|
+
*/
|
|
5227
|
+
const WORLD_SYNTAX = /* @__PURE__ */ Object.freeze({
|
|
5228
|
+
operators: [
|
|
5229
|
+
{
|
|
5230
|
+
symbol: ">",
|
|
5231
|
+
fixity: "infix",
|
|
5232
|
+
prec: 4,
|
|
5233
|
+
assoc: "none",
|
|
5234
|
+
kind: "greater"
|
|
5235
|
+
},
|
|
5236
|
+
{
|
|
5237
|
+
symbol: "+",
|
|
5238
|
+
fixity: "infix",
|
|
5239
|
+
prec: 5,
|
|
5240
|
+
assoc: "flat",
|
|
5241
|
+
kind: "add"
|
|
5242
|
+
},
|
|
5243
|
+
{
|
|
5244
|
+
symbol: "*",
|
|
5245
|
+
fixity: "infix",
|
|
5246
|
+
prec: 6,
|
|
5247
|
+
assoc: "flat",
|
|
5248
|
+
kind: "multiply"
|
|
5249
|
+
}
|
|
5250
|
+
],
|
|
5251
|
+
calls: {
|
|
5252
|
+
ops: [],
|
|
5253
|
+
forms: [
|
|
5254
|
+
{
|
|
5255
|
+
name: "if",
|
|
5256
|
+
kind: "if",
|
|
5257
|
+
fields: [
|
|
5258
|
+
"test",
|
|
5259
|
+
"then",
|
|
5260
|
+
"else"
|
|
5261
|
+
],
|
|
5262
|
+
min: 2,
|
|
5263
|
+
wrap: true
|
|
5264
|
+
},
|
|
5265
|
+
{
|
|
5266
|
+
name: "list",
|
|
5267
|
+
kind: "list",
|
|
5268
|
+
wrap: true
|
|
5269
|
+
},
|
|
5270
|
+
{
|
|
5271
|
+
name: "seq",
|
|
5272
|
+
kind: "seq",
|
|
5273
|
+
wrap: true
|
|
5274
|
+
},
|
|
5275
|
+
{
|
|
5276
|
+
name: "pure",
|
|
5277
|
+
kind: "pure",
|
|
5278
|
+
min: 1,
|
|
5279
|
+
wrap: true
|
|
5280
|
+
},
|
|
5281
|
+
{
|
|
5282
|
+
name: "log",
|
|
5283
|
+
kind: "log",
|
|
5284
|
+
min: 1
|
|
5285
|
+
},
|
|
5286
|
+
{
|
|
5287
|
+
name: "write",
|
|
5288
|
+
kind: "write",
|
|
5289
|
+
fields: ["key", "value"],
|
|
5290
|
+
wrap: ["value"]
|
|
5291
|
+
},
|
|
5292
|
+
{
|
|
5293
|
+
name: "math.sum",
|
|
5294
|
+
kind: "math.sum"
|
|
5295
|
+
},
|
|
5296
|
+
{
|
|
5297
|
+
name: "math.product",
|
|
5298
|
+
kind: "math.product"
|
|
5299
|
+
},
|
|
5300
|
+
{
|
|
5301
|
+
name: "number",
|
|
5302
|
+
kind: "number",
|
|
5303
|
+
args: "literal"
|
|
5304
|
+
},
|
|
5305
|
+
{
|
|
5306
|
+
name: "text",
|
|
5307
|
+
kind: "text",
|
|
5308
|
+
args: "literal"
|
|
5309
|
+
},
|
|
5310
|
+
{
|
|
5311
|
+
name: "bool",
|
|
5312
|
+
kind: "bool",
|
|
5313
|
+
args: "literal"
|
|
5314
|
+
},
|
|
5315
|
+
{
|
|
5316
|
+
name: "panel",
|
|
5317
|
+
kind: "panel",
|
|
5318
|
+
args: "named",
|
|
5319
|
+
fields: [
|
|
5320
|
+
"width",
|
|
5321
|
+
"height",
|
|
5322
|
+
"children"
|
|
5323
|
+
]
|
|
5324
|
+
},
|
|
5325
|
+
{
|
|
5326
|
+
name: "box",
|
|
5327
|
+
kind: "box",
|
|
5328
|
+
args: "named",
|
|
5329
|
+
fields: [
|
|
5330
|
+
"width",
|
|
5331
|
+
"height",
|
|
5332
|
+
"children"
|
|
5333
|
+
]
|
|
5334
|
+
},
|
|
5335
|
+
{
|
|
5336
|
+
name: "nearest",
|
|
5337
|
+
kind: "nearest",
|
|
5338
|
+
args: "named",
|
|
5339
|
+
fields: [
|
|
5340
|
+
"type",
|
|
5341
|
+
"follow",
|
|
5342
|
+
"self"
|
|
5343
|
+
]
|
|
5344
|
+
},
|
|
5345
|
+
{
|
|
5346
|
+
name: "add",
|
|
5347
|
+
kind: "add"
|
|
5348
|
+
},
|
|
5349
|
+
{
|
|
5350
|
+
name: "multiply",
|
|
5351
|
+
kind: "multiply"
|
|
5352
|
+
},
|
|
5353
|
+
{
|
|
5354
|
+
name: "greater",
|
|
5355
|
+
kind: "greater"
|
|
5356
|
+
}
|
|
5357
|
+
]
|
|
5358
|
+
},
|
|
5359
|
+
literals: { envelopes: {
|
|
5360
|
+
number: "number",
|
|
5361
|
+
text: "text",
|
|
5362
|
+
bool: "bool"
|
|
5363
|
+
} },
|
|
5364
|
+
header: { bind: "=" }
|
|
5365
|
+
});
|
|
5366
|
+
function isNumber(value) {
|
|
5367
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
5368
|
+
}
|
|
5369
|
+
/** This function evaluates each part, collects the errors, and gives the values when no part failed. */
|
|
5370
|
+
function allValues(results) {
|
|
5371
|
+
const errors = results.filter((result) => !result.ok);
|
|
5372
|
+
const first = errors[0];
|
|
5373
|
+
if (first !== void 0 && errors.length === 1) return { error: first };
|
|
5374
|
+
if (errors.length > 1) return { error: fail("#ARGS", `${errors.length} parts failed.`) };
|
|
5375
|
+
return { values: results.map((result) => result.ok ? result.value : null) };
|
|
5376
|
+
}
|
|
5377
|
+
function numbers(values, kind) {
|
|
5378
|
+
const bad = values.findIndex((value) => !isNumber(value));
|
|
5379
|
+
if (bad !== -1) return fail("#VALUE!", `Part ${bad} of ${kind} is not a number.`);
|
|
5380
|
+
return values.filter(isNumber);
|
|
5381
|
+
}
|
|
5382
|
+
function dataOf(type) {
|
|
5383
|
+
return ({ data }) => typeof data === type && (type !== "number" || isNumber(data)) ? ok(data) : fail("#VALUE!", `The value must be a ${type === "string" ? "text" : type}.`);
|
|
5384
|
+
}
|
|
5385
|
+
/** The code of the evaluators of the standard world, by name. */
|
|
5386
|
+
const STD_EVALUATORS = {
|
|
5387
|
+
"data.number": /* @__PURE__ */ dataOf("number"),
|
|
5388
|
+
"data.text": /* @__PURE__ */ dataOf("string"),
|
|
5389
|
+
"data.bool": /* @__PURE__ */ dataOf("boolean"),
|
|
5390
|
+
"math.add": ({ parts }) => {
|
|
5391
|
+
const all = allValues(parts.map((part) => part.evaluate()));
|
|
5392
|
+
if ("error" in all) return all.error;
|
|
5393
|
+
const values = numbers(all.values, "add");
|
|
5394
|
+
return Array.isArray(values) ? ok(values.reduce((sum, value) => sum + value, 0)) : values;
|
|
5395
|
+
},
|
|
5396
|
+
"math.multiply": ({ parts }) => {
|
|
5397
|
+
const all = allValues(parts.map((part) => part.evaluate()));
|
|
5398
|
+
if ("error" in all) return all.error;
|
|
5399
|
+
const values = numbers(all.values, "multiply");
|
|
5400
|
+
return Array.isArray(values) ? ok(values.reduce((product, value) => product * value, 1)) : values;
|
|
5401
|
+
},
|
|
5402
|
+
"math.greater": ({ parts }) => {
|
|
5403
|
+
if (parts.length !== 2) return fail("#VALUE!", "greater needs exactly two parts.");
|
|
5404
|
+
const all = allValues(parts.map((part) => part.evaluate()));
|
|
5405
|
+
if ("error" in all) return all.error;
|
|
5406
|
+
const values = numbers(all.values, "greater");
|
|
5407
|
+
if (!Array.isArray(values)) return values;
|
|
4601
5408
|
const [a, b] = values;
|
|
4602
5409
|
return ok((a ?? 0) > (b ?? 0));
|
|
4603
5410
|
},
|
|
@@ -4734,6 +5541,7 @@ function plainOf(value, path) {
|
|
|
4734
5541
|
*/
|
|
4735
5542
|
function treeThing(input, build, outer) {
|
|
4736
5543
|
const { parts, context, kind: type, place, machine } = input;
|
|
5544
|
+
const runtime = input[RUNTIME];
|
|
4737
5545
|
const { tree, of } = contractOf$1(machine, input.typePath);
|
|
4738
5546
|
const above = stateOf(outer);
|
|
4739
5547
|
const up = above !== void 0 && above.build === build ? above : void 0;
|
|
@@ -4835,12 +5643,12 @@ function treeThing(input, build, outer) {
|
|
|
4835
5643
|
read,
|
|
4836
5644
|
equation(term, names) {
|
|
4837
5645
|
if (machine === void 0) return fail("#NOEVAL", "The evaluation of an equation needs the machine.");
|
|
4838
|
-
return equationPlan(machine, of, term)
|
|
5646
|
+
return evaluateWithin(equationPlan(machine, of, term), {
|
|
4839
5647
|
...context,
|
|
4840
5648
|
names,
|
|
4841
5649
|
caps: NO_CAPS$1,
|
|
4842
5650
|
self: node
|
|
4843
|
-
});
|
|
5651
|
+
}, runtime);
|
|
4844
5652
|
},
|
|
4845
5653
|
childNames(child) {
|
|
4846
5654
|
const children = read("children");
|
|
@@ -4930,6 +5738,17 @@ function treeThing(input, build, outer) {
|
|
|
4930
5738
|
function attributeTrace(node, attribute) {
|
|
4931
5739
|
return stateOf(node)?.trace(attribute);
|
|
4932
5740
|
}
|
|
5741
|
+
/**
|
|
5742
|
+
* This function gives the place of a node of the decorated tree: the keys from the root document to the document of
|
|
5743
|
+
* the node (HOP.PLACE). A view puts each hop of a resolution on its node with it. The node keeps its place already,
|
|
5744
|
+
* thus the function adds no record.
|
|
5745
|
+
*
|
|
5746
|
+
* @param thing - A thing, for example the thing of a hop.
|
|
5747
|
+
* @returns The keys of the place, or `undefined` for a thing that is not a node of the decorated tree.
|
|
5748
|
+
*/
|
|
5749
|
+
function placeOf(thing) {
|
|
5750
|
+
return stateOf(thing)?.place;
|
|
5751
|
+
}
|
|
4933
5752
|
/** The path of the root `self`. A node of the tree resolves it, thus the list of reads holds the read (C4). */
|
|
4934
5753
|
const SELF_PATH = /* @__PURE__ */ Object.freeze(["self"]);
|
|
4935
5754
|
/** The options of the resolution of `self`: the node needs no hops. */
|
|
@@ -4958,21 +5777,24 @@ function hostType(node, wanted) {
|
|
|
4958
5777
|
/**
|
|
4959
5778
|
* This function makes a walker `nearest`. The walker follows a key (the default is `parent`) until the node that it
|
|
4960
5779
|
* reaches has the wanted type. With `self: true`, the node where the walk starts is the first candidate. A node
|
|
4961
|
-
* without that key ends the walk with an error.
|
|
5780
|
+
* without that key ends the walk with an error. With `eachStep`, a resolution with hops gets a hop for each node on the way.
|
|
4962
5781
|
*
|
|
4963
|
-
* @param options - The test of a type and the
|
|
5782
|
+
* @param options - The test of a type, the most hops and the record of each step.
|
|
4964
5783
|
* @returns The walker.
|
|
4965
5784
|
*/
|
|
4966
5785
|
function nearestWalker(options = {}) {
|
|
4967
5786
|
const matches = options.matches ?? hostType;
|
|
4968
5787
|
const maxHops = options.maxHops ?? 256;
|
|
4969
|
-
|
|
5788
|
+
const eachStep = options.eachStep === true;
|
|
5789
|
+
return ({ data, here, deref, visit }) => {
|
|
4970
5790
|
const follow = isRecord(data) && typeof field(data, "follow") === "string" ? String(field(data, "follow")) : "parent";
|
|
4971
5791
|
const wanted = isRecord(data) ? field(data, "type") : void 0;
|
|
4972
5792
|
const fromSelf = isRecord(data) && field(data, "self") === true;
|
|
5793
|
+
const record = eachStep ? visit : void 0;
|
|
4973
5794
|
let at = here;
|
|
4974
5795
|
for (let hops = 0; hops < maxHops; hops += 1) {
|
|
4975
|
-
|
|
5796
|
+
const stepped = hops > 0 || !fromSelf;
|
|
5797
|
+
if (stepped) at = at === void 0 || at === null ? void 0 : deref(at, follow);
|
|
4976
5798
|
if (at === void 0 || at === null) return {
|
|
4977
5799
|
ok: false,
|
|
4978
5800
|
reason: `No node of the type ${String(wanted)} is on the way.`
|
|
@@ -4981,6 +5803,7 @@ function nearestWalker(options = {}) {
|
|
|
4981
5803
|
ok: true,
|
|
4982
5804
|
thing: at
|
|
4983
5805
|
};
|
|
5806
|
+
if (stepped) record?.(follow, at);
|
|
4984
5807
|
}
|
|
4985
5808
|
return {
|
|
4986
5809
|
ok: false,
|
|
@@ -6115,7 +6938,7 @@ const DOWN_NAMES = /* @__PURE__ */ Object.freeze([
|
|
|
6115
6938
|
const FOLD_NAMES = /* @__PURE__ */ Object.freeze(["acc", "item"]);
|
|
6116
6939
|
/**
|
|
6117
6940
|
* This function gives each equation of the tree contract of a kind, with the names that it sees (D42). An equation of
|
|
6118
|
-
* `equations`, a `unit` and a `default` see no names.
|
|
6941
|
+
* `equations`, a `unit` and a `default` see no names. `check` and `factsOf` read the equations with this one function.
|
|
6119
6942
|
*/
|
|
6120
6943
|
function equationsOf(kind) {
|
|
6121
6944
|
const tree = kind.tree;
|
|
@@ -6437,9 +7260,11 @@ function partLazy(above, parent, slot) {
|
|
|
6437
7260
|
*
|
|
6438
7261
|
* - `reads`: each free reference, with the root and the keys of its path. A read under a lazy part has the mark
|
|
6439
7262
|
* `lazy`, for example a read in a branch of `if`. A read after a step document has `exact: false`. A kind that
|
|
6440
|
-
* declares `reads` adds a read of each of its roots at each document of the kind.
|
|
7263
|
+
* declares `reads` adds a read of each of its roots at each document of the kind. The first document of a tree
|
|
7264
|
+
* kind also gets the lazy reads of the equations of its kind (FACTS.EQUATIONS). A read from `self` or from a name
|
|
7265
|
+
* of a rung is not one of them, because it is not free (READS.EQUATION).
|
|
6441
7266
|
* - `side` and `effects`: from the contract of each kind.
|
|
6442
|
-
* - `pure`: each kind declares `pure`, and the document has no effect and is not volatile.
|
|
7267
|
+
* - `pure`: each kind declares `pure`, also in an equation, and the document has no effect and is not volatile.
|
|
6443
7268
|
* - `volatile`: a kind, an operation or a step document declares `volatile`, or the host marks a read.
|
|
6444
7269
|
*
|
|
6445
7270
|
* The hook `facts` of the machine answers for a name that a fallback kind covers, for example an operation. Its
|
|
@@ -6463,7 +7288,6 @@ function partLazy(above, parent, slot) {
|
|
|
6463
7288
|
function factsOf(machine, doc, options = {}) {
|
|
6464
7289
|
const world = machine.world;
|
|
6465
7290
|
const hook = machine.facts;
|
|
6466
|
-
const verb = options.verb ?? "eval";
|
|
6467
7291
|
const reads = [];
|
|
6468
7292
|
const effects = [];
|
|
6469
7293
|
const above = /* @__PURE__ */ new Map();
|
|
@@ -6471,98 +7295,124 @@ function factsOf(machine, doc, options = {}) {
|
|
|
6471
7295
|
let pure = true;
|
|
6472
7296
|
let volatile = false;
|
|
6473
7297
|
let size = 0;
|
|
7298
|
+
let seen;
|
|
6474
7299
|
const note = (isPure, isVolatile) => {
|
|
6475
7300
|
if (isPure !== true) pure = false;
|
|
6476
7301
|
if (isVolatile === true) volatile = true;
|
|
6477
7302
|
};
|
|
6478
|
-
|
|
6479
|
-
size += 1;
|
|
6480
|
-
const up = v.parent === void 0 ? void 0 : above.get(v.parent);
|
|
6481
|
-
const lazy = up !== void 0 && v.parent !== void 0 && (up.lazy || partLazy(up, v.parent, v.slot));
|
|
6482
|
-
const kind = v.kind;
|
|
6483
|
-
if (kind === void 0) {
|
|
6484
|
-
pure = false;
|
|
6485
|
-
above.set(v, {
|
|
6486
|
-
lazy,
|
|
6487
|
-
rules: void 0
|
|
6488
|
-
});
|
|
6489
|
-
return;
|
|
6490
|
-
}
|
|
6491
|
-
const code = codeOf(world, v.declared ? void 0 : v.doc.own, kind, verb, "eval");
|
|
6492
|
-
const answer = code.name === void 0 ? void 0 : hook?.(v.typePath ?? [], code.name);
|
|
6493
|
-
let roots;
|
|
6494
|
-
if (code.described) {
|
|
6495
|
-
note(answer?.pure ?? kind.pure, answer?.volatile ?? kind.volatile);
|
|
6496
|
-
above.set(v, {
|
|
6497
|
-
lazy,
|
|
6498
|
-
rules: answer?.lazy ?? kind.lazy
|
|
6499
|
-
});
|
|
6500
|
-
roots = answer?.reads ?? kind.reads;
|
|
6501
|
-
} else {
|
|
6502
|
-
note(answer?.pure, answer?.volatile);
|
|
6503
|
-
above.set(v, {
|
|
6504
|
-
lazy,
|
|
6505
|
-
rules: answer?.lazy ?? EACH$1
|
|
6506
|
-
});
|
|
6507
|
-
roots = answer?.reads;
|
|
6508
|
-
}
|
|
6509
|
-
for (const root of roots ?? NONE) {
|
|
6510
|
-
const read = {
|
|
6511
|
-
root,
|
|
6512
|
-
keys: NONE,
|
|
6513
|
-
exact: true,
|
|
6514
|
-
lazy,
|
|
6515
|
-
address: false,
|
|
6516
|
-
path: [root],
|
|
6517
|
-
place: tight(v.place),
|
|
6518
|
-
at: tight(v.at)
|
|
6519
|
-
};
|
|
6520
|
-
reads.push(read);
|
|
6521
|
-
if (options.volatile?.(read) === true) volatile = true;
|
|
6522
|
-
}
|
|
6523
|
-
if (kind.side === "run") side = "run";
|
|
6524
|
-
for (const keys of declaredEffects(v)) {
|
|
6525
|
-
const text = keys.join(".");
|
|
6526
|
-
if (!effects.includes(text)) effects.push(text);
|
|
6527
|
-
}
|
|
6528
|
-
if (!isRef(v.doc)) return void 0;
|
|
6529
|
-
const path = v.doc.value;
|
|
6530
|
-
const root = path[0];
|
|
6531
|
-
if (typeof root !== "string" || !ROOTS.includes(root)) return void 0;
|
|
6532
|
-
let exact = true;
|
|
6533
|
-
const keys = [];
|
|
6534
|
-
for (let index = 1; index < path.length; index += 1) {
|
|
6535
|
-
const step = path[index];
|
|
6536
|
-
if (exact && isKey(step)) {
|
|
6537
|
-
keys.push(step);
|
|
6538
|
-
continue;
|
|
6539
|
-
}
|
|
6540
|
-
exact = false;
|
|
6541
|
-
const found = isEnvelope(step) ? lookupKind(world, step.type) : void 0;
|
|
6542
|
-
if (found?.ok !== true) {
|
|
6543
|
-
pure = false;
|
|
6544
|
-
continue;
|
|
6545
|
-
}
|
|
6546
|
-
const walker = codeOf(world, step.own, found.kind, "step", "step");
|
|
6547
|
-
const stepAnswer = walker.name === void 0 ? void 0 : hook?.(found.path, walker.name);
|
|
6548
|
-
if (walker.described) note(stepAnswer?.pure ?? found.kind.pure, stepAnswer?.volatile ?? found.kind.volatile);
|
|
6549
|
-
else note(stepAnswer?.pure, stepAnswer?.volatile);
|
|
6550
|
-
}
|
|
6551
|
-
const first = path[1];
|
|
6552
|
-
if (root === "names" && isKey(first) && v.names.has(String(first))) return void 0;
|
|
6553
|
-
const read = {
|
|
6554
|
-
root,
|
|
6555
|
-
keys: tight(keys),
|
|
6556
|
-
exact,
|
|
6557
|
-
lazy,
|
|
6558
|
-
address: v.address === true,
|
|
6559
|
-
path,
|
|
6560
|
-
place: tight(v.place),
|
|
6561
|
-
at: tight(v.at)
|
|
6562
|
-
};
|
|
7303
|
+
const add = (read) => {
|
|
6563
7304
|
reads.push(read);
|
|
6564
7305
|
if (options.volatile?.(read) === true) volatile = true;
|
|
6565
|
-
}
|
|
7306
|
+
};
|
|
7307
|
+
/**
|
|
7308
|
+
* This function walks a document. Without `node`, it walks the document of the facts. With `node`, it walks an
|
|
7309
|
+
* equation of a tree kind, and each read gets the place of that node: the first document of the kind.
|
|
7310
|
+
*/
|
|
7311
|
+
const scan = (term, walkOptions, verb, node) => {
|
|
7312
|
+
walk(world, term, (v) => {
|
|
7313
|
+
if (node === void 0) size += 1;
|
|
7314
|
+
const up = v.parent === void 0 ? void 0 : above.get(v.parent);
|
|
7315
|
+
const lazy = node !== void 0 || up !== void 0 && v.parent !== void 0 && (up.lazy || partLazy(up, v.parent, v.slot));
|
|
7316
|
+
const place = node ?? v;
|
|
7317
|
+
const kind = v.kind;
|
|
7318
|
+
if (kind === void 0) {
|
|
7319
|
+
pure = false;
|
|
7320
|
+
above.set(v, {
|
|
7321
|
+
lazy,
|
|
7322
|
+
rules: void 0
|
|
7323
|
+
});
|
|
7324
|
+
return;
|
|
7325
|
+
}
|
|
7326
|
+
const code = codeOf(world, v.declared ? void 0 : v.doc.own, kind, verb, "eval");
|
|
7327
|
+
const answer = code.name === void 0 ? void 0 : hook?.(v.typePath ?? [], code.name);
|
|
7328
|
+
let roots;
|
|
7329
|
+
if (code.described) {
|
|
7330
|
+
note(answer?.pure ?? kind.pure, answer?.volatile ?? kind.volatile);
|
|
7331
|
+
above.set(v, {
|
|
7332
|
+
lazy,
|
|
7333
|
+
rules: answer?.lazy ?? kind.lazy
|
|
7334
|
+
});
|
|
7335
|
+
roots = answer?.reads ?? kind.reads;
|
|
7336
|
+
} else {
|
|
7337
|
+
note(answer?.pure, answer?.volatile);
|
|
7338
|
+
above.set(v, {
|
|
7339
|
+
lazy,
|
|
7340
|
+
rules: answer?.lazy ?? EACH$1
|
|
7341
|
+
});
|
|
7342
|
+
roots = answer?.reads;
|
|
7343
|
+
}
|
|
7344
|
+
for (const root of roots ?? NONE) {
|
|
7345
|
+
if (node !== void 0 && root === "self") continue;
|
|
7346
|
+
add({
|
|
7347
|
+
root,
|
|
7348
|
+
keys: NONE,
|
|
7349
|
+
exact: true,
|
|
7350
|
+
lazy,
|
|
7351
|
+
address: false,
|
|
7352
|
+
path: [root],
|
|
7353
|
+
place: tight(place.place),
|
|
7354
|
+
at: tight(place.at)
|
|
7355
|
+
});
|
|
7356
|
+
}
|
|
7357
|
+
if (kind.side === "run") {
|
|
7358
|
+
if (node === void 0) side = "run";
|
|
7359
|
+
else pure = false;
|
|
7360
|
+
}
|
|
7361
|
+
for (const keys of declaredEffects(v)) {
|
|
7362
|
+
if (node !== void 0) {
|
|
7363
|
+
pure = false;
|
|
7364
|
+
continue;
|
|
7365
|
+
}
|
|
7366
|
+
const text = keys.join(".");
|
|
7367
|
+
if (!effects.includes(text)) effects.push(text);
|
|
7368
|
+
}
|
|
7369
|
+
if (kind.tree !== void 0 && !(seen ??= /* @__PURE__ */ new Set()).has(kind)) {
|
|
7370
|
+
seen.add(kind);
|
|
7371
|
+
const of = equationType(kind);
|
|
7372
|
+
for (const { term: equation, names } of equationsOf(kind)) scan(equation, of === void 0 ? { names } : {
|
|
7373
|
+
names,
|
|
7374
|
+
declaredType: of
|
|
7375
|
+
}, "eval", place);
|
|
7376
|
+
}
|
|
7377
|
+
if (!isRef(v.doc)) return void 0;
|
|
7378
|
+
const path = v.doc.value;
|
|
7379
|
+
const root = path[0];
|
|
7380
|
+
if (typeof root !== "string" || !ROOTS.includes(root)) return void 0;
|
|
7381
|
+
if (node !== void 0 && root === "self") return void 0;
|
|
7382
|
+
let exact = true;
|
|
7383
|
+
const keys = [];
|
|
7384
|
+
for (let index = 1; index < path.length; index += 1) {
|
|
7385
|
+
const step = path[index];
|
|
7386
|
+
if (exact && isKey(step)) {
|
|
7387
|
+
keys.push(step);
|
|
7388
|
+
continue;
|
|
7389
|
+
}
|
|
7390
|
+
exact = false;
|
|
7391
|
+
const found = isEnvelope(step) ? lookupKind(world, step.type) : void 0;
|
|
7392
|
+
if (found?.ok !== true) {
|
|
7393
|
+
pure = false;
|
|
7394
|
+
continue;
|
|
7395
|
+
}
|
|
7396
|
+
const walker = codeOf(world, step.own, found.kind, "step", "step");
|
|
7397
|
+
const stepAnswer = walker.name === void 0 ? void 0 : hook?.(found.path, walker.name);
|
|
7398
|
+
if (walker.described) note(stepAnswer?.pure ?? found.kind.pure, stepAnswer?.volatile ?? found.kind.volatile);
|
|
7399
|
+
else note(stepAnswer?.pure, stepAnswer?.volatile);
|
|
7400
|
+
}
|
|
7401
|
+
const first = path[1];
|
|
7402
|
+
if (root === "names" && isKey(first) && v.names.has(String(first))) return void 0;
|
|
7403
|
+
add({
|
|
7404
|
+
root,
|
|
7405
|
+
keys: tight(keys),
|
|
7406
|
+
exact,
|
|
7407
|
+
lazy,
|
|
7408
|
+
address: v.address === true,
|
|
7409
|
+
path,
|
|
7410
|
+
place: tight(place.place),
|
|
7411
|
+
at: tight(place.at)
|
|
7412
|
+
});
|
|
7413
|
+
}, walkOptions);
|
|
7414
|
+
};
|
|
7415
|
+
scan(doc, options.declaredType === void 0 ? {} : { declaredType: options.declaredType }, options.verb ?? "eval", void 0);
|
|
6566
7416
|
return {
|
|
6567
7417
|
reads: tight(reads),
|
|
6568
7418
|
side,
|
|
@@ -6701,7 +7551,7 @@ function domainTypeOf(data, domains) {
|
|
|
6701
7551
|
return domain !== void 0 && codecOf(domain) !== void 0 ? data["domain"] : "unknown";
|
|
6702
7552
|
}
|
|
6703
7553
|
/** This function sets an own data property. Only the key `__proto__` needs `defineProperty`. */
|
|
6704
|
-
function setOwn$
|
|
7554
|
+
function setOwn$3(target, key, value) {
|
|
6705
7555
|
if (key === "__proto__") Object.defineProperty(target, key, {
|
|
6706
7556
|
value,
|
|
6707
7557
|
enumerable: true,
|
|
@@ -6776,7 +7626,7 @@ function encodeTerm(value, encoder) {
|
|
|
6776
7626
|
else {
|
|
6777
7627
|
const fields = {};
|
|
6778
7628
|
const record = value;
|
|
6779
|
-
for (const key of Object.keys(record)) setOwn$
|
|
7629
|
+
for (const key of Object.keys(record)) setOwn$3(fields, key, encodeTerm(record[key], encoder));
|
|
6780
7630
|
out = {
|
|
6781
7631
|
type: "record",
|
|
6782
7632
|
value: fields
|
|
@@ -6872,7 +7722,7 @@ function strictTruthy(value) {
|
|
|
6872
7722
|
* This function sets an own data property. Only the key `__proto__` needs `defineProperty`: an assignment to it
|
|
6873
7723
|
* changes the prototype. An assignment is faster for each other key of a new plain object.
|
|
6874
7724
|
*/
|
|
6875
|
-
function setOwn$
|
|
7725
|
+
function setOwn$2(target, key, value) {
|
|
6876
7726
|
if (key === "__proto__") Object.defineProperty(target, key, {
|
|
6877
7727
|
value,
|
|
6878
7728
|
enumerable: true,
|
|
@@ -7086,11 +7936,28 @@ function paramsOf(value) {
|
|
|
7086
7936
|
}
|
|
7087
7937
|
/**
|
|
7088
7938
|
* This function makes the type rules of the formula kit for one profile (TYPE.SYNTH, D25). Each evaluator of the kit
|
|
7089
|
-
* holds its rule. The rules read the rules of the profile that change a type: `truthy`, `
|
|
7090
|
-
* and `sigs`.
|
|
7939
|
+
* holds its rule. The rules read the rules of the profile that change a type: `truthy`, `truthType`, `logic`,
|
|
7940
|
+
* `otherwise`, `omit` and `sigs`.
|
|
7091
7941
|
*/
|
|
7092
7942
|
function formulaTypes(profile) {
|
|
7093
|
-
const
|
|
7943
|
+
const declared = profile.truthy !== void 0 && profile.truthType !== void 0;
|
|
7944
|
+
const truth = profile.truthy === void 0 ? "bool" : profile.truthType;
|
|
7945
|
+
/**
|
|
7946
|
+
* The need of a test or a term. The check examines a declared type of a truth as a contract, one time. A declared
|
|
7947
|
+
* type with a problem needs nothing, thus one problem gives one issue.
|
|
7948
|
+
*/
|
|
7949
|
+
const truthPart = (input, what) => {
|
|
7950
|
+
if (truth === void 0) return {};
|
|
7951
|
+
if (!declared) return {
|
|
7952
|
+
expect: truth,
|
|
7953
|
+
what
|
|
7954
|
+
};
|
|
7955
|
+
const type = input.contract(truth, "truth type of the profile");
|
|
7956
|
+
return typeIssues(type, input.machine.domains).length > 0 ? {} : {
|
|
7957
|
+
expect: type,
|
|
7958
|
+
what
|
|
7959
|
+
};
|
|
7960
|
+
};
|
|
7094
7961
|
const logicValue = profile.logic === "value";
|
|
7095
7962
|
const otherwise = Object.hasOwn(profile, "otherwise") ? valueType(profile.otherwise) : "null";
|
|
7096
7963
|
const lookup = (name) => {
|
|
@@ -7126,10 +7993,7 @@ function formulaTypes(profile) {
|
|
|
7126
7993
|
};
|
|
7127
7994
|
};
|
|
7128
7995
|
const logic = (word) => ({
|
|
7129
|
-
part: (
|
|
7130
|
-
expect: truth,
|
|
7131
|
-
what: `term ${Number(path[0]) + 1} of ${word}`
|
|
7132
|
-
},
|
|
7996
|
+
part: (input, path) => truthPart(input, `term ${Number(path[0]) + 1} of ${word}`),
|
|
7133
7997
|
synth: (input) => {
|
|
7134
7998
|
if (!logicValue) return "bool";
|
|
7135
7999
|
if (input.paths.length === 0) return { one: [word === "and"] };
|
|
@@ -7178,7 +8042,7 @@ function formulaTypes(profile) {
|
|
|
7178
8042
|
},
|
|
7179
8043
|
synth: (input) => {
|
|
7180
8044
|
const fields = {};
|
|
7181
|
-
for (const path of input.paths) setOwn$
|
|
8045
|
+
for (const path of input.paths) setOwn$2(fields, String(path[0]), input.typeOf(path));
|
|
7182
8046
|
const keys = Object.keys(fields);
|
|
7183
8047
|
return {
|
|
7184
8048
|
record: fields,
|
|
@@ -7201,7 +8065,7 @@ function formulaTypes(profile) {
|
|
|
7201
8065
|
part: (input, path) => {
|
|
7202
8066
|
if (path[0] !== "body") return {};
|
|
7203
8067
|
const names = {};
|
|
7204
|
-
for (const each of input.paths) if (each[0] === "bind" && each.length === 2) setOwn$
|
|
8068
|
+
for (const each of input.paths) if (each[0] === "bind" && each.length === 2) setOwn$2(names, String(each[1]), input.typeOf(each));
|
|
7205
8069
|
return {
|
|
7206
8070
|
names,
|
|
7207
8071
|
...isUnknown(input.expected, input.machine.domains) ? {} : {
|
|
@@ -7217,7 +8081,7 @@ function formulaTypes(profile) {
|
|
|
7217
8081
|
if (path[0] !== "body") return {};
|
|
7218
8082
|
const want = fnOf(input.expected, input.machine.domains);
|
|
7219
8083
|
const names = {};
|
|
7220
|
-
paramsOf(input.doc.value).forEach((name, index) => setOwn$
|
|
8084
|
+
paramsOf(input.doc.value).forEach((name, index) => setOwn$2(names, name, want?.params[index] ?? "unknown"));
|
|
7221
8085
|
return {
|
|
7222
8086
|
names,
|
|
7223
8087
|
...want === void 0 || isUnknown(want.to, input.machine.domains) ? {} : {
|
|
@@ -7273,10 +8137,7 @@ function formulaTypes(profile) {
|
|
|
7273
8137
|
} },
|
|
7274
8138
|
"formula.if": {
|
|
7275
8139
|
part: (input, path) => {
|
|
7276
|
-
if (path[0] === "test") return
|
|
7277
|
-
expect: truth,
|
|
7278
|
-
what: "test of if"
|
|
7279
|
-
};
|
|
8140
|
+
if (path[0] === "test") return truthPart(input, "test of if");
|
|
7280
8141
|
return isUnknown(input.expected, input.machine.domains) ? {} : {
|
|
7281
8142
|
expect: input.expected,
|
|
7282
8143
|
what: `${String(path[0])} branch of if`
|
|
@@ -7350,18 +8211,44 @@ function formulaTypes(profile) {
|
|
|
7350
8211
|
function formulaEvaluators(profile = {}) {
|
|
7351
8212
|
return { ...kitOf(profile).evaluators };
|
|
7352
8213
|
}
|
|
8214
|
+
/** The evaluators of the kit that read the truth of a value. */
|
|
8215
|
+
const TRUTH_READERS = [
|
|
8216
|
+
"formula.if",
|
|
8217
|
+
"formula.and",
|
|
8218
|
+
"formula.or"
|
|
8219
|
+
];
|
|
8220
|
+
/** The declared truth of each evaluator of the kit that reads a truth, for `conforms` (D86). */
|
|
8221
|
+
const truthByCode = /* @__PURE__ */ new WeakMap();
|
|
8222
|
+
/**
|
|
8223
|
+
* This function gives the declared truth of the profile of an evaluator of the kit (D86). Only `formula.if`,
|
|
8224
|
+
* `formula.and` and `formula.or` of a profile with an own `truthy` and a `truthType` have one. The kernel does not
|
|
8225
|
+
* export the function.
|
|
8226
|
+
*
|
|
8227
|
+
* @param code - The evaluator or the runner.
|
|
8228
|
+
* @returns The declared truth, or `undefined`.
|
|
8229
|
+
*/
|
|
8230
|
+
function declaredTruthOf(code) {
|
|
8231
|
+
return (typeof code === "object" || typeof code === "function") && code !== null ? truthByCode.get(code) : void 0;
|
|
8232
|
+
}
|
|
7353
8233
|
/** This function makes new evaluators of the formula kit for the fields of one profile. */
|
|
7354
8234
|
function makeEvaluators(profile) {
|
|
7355
8235
|
const evaluators = formulaCode(profile);
|
|
7356
8236
|
const types = formulaTypes(profile);
|
|
8237
|
+
const { truthy, truthType } = profile;
|
|
8238
|
+
const truth = truthy === void 0 || truthType === void 0 ? void 0 : {
|
|
8239
|
+
truthy,
|
|
8240
|
+
truthType
|
|
8241
|
+
};
|
|
7357
8242
|
const out = {};
|
|
7358
8243
|
for (const name of Object.keys(evaluators)) {
|
|
7359
8244
|
const evaluator = evaluators[name];
|
|
7360
8245
|
const type = Object.hasOwn(types, name) ? types[name] : void 0;
|
|
7361
|
-
|
|
8246
|
+
const made = Object.freeze(type === void 0 ? evaluator : {
|
|
7362
8247
|
compile: evaluator.compile,
|
|
7363
8248
|
type
|
|
7364
8249
|
});
|
|
8250
|
+
if (truth !== void 0 && TRUTH_READERS.includes(name)) truthByCode.set(made, truth);
|
|
8251
|
+
out[name] = made;
|
|
7365
8252
|
}
|
|
7366
8253
|
return Object.freeze(out);
|
|
7367
8254
|
}
|
|
@@ -7371,6 +8258,7 @@ const PROFILE_KEYS = /* @__PURE__ */ Object.freeze([
|
|
|
7371
8258
|
"lazy",
|
|
7372
8259
|
"ext",
|
|
7373
8260
|
"truthy",
|
|
8261
|
+
"truthType",
|
|
7374
8262
|
"otherwise",
|
|
7375
8263
|
"logic",
|
|
7376
8264
|
"omit",
|
|
@@ -7555,7 +8443,7 @@ function formulaCode(profile) {
|
|
|
7555
8443
|
if (!Array.isArray(values)) return values;
|
|
7556
8444
|
const out = {};
|
|
7557
8445
|
keys.forEach((key, index) => {
|
|
7558
|
-
if (values[index] !== SKIPPED) setOwn$
|
|
8446
|
+
if (values[index] !== SKIPPED) setOwn$2(out, key, values[index]);
|
|
7559
8447
|
});
|
|
7560
8448
|
return ok(out);
|
|
7561
8449
|
};
|
|
@@ -7568,7 +8456,7 @@ function formulaCode(profile) {
|
|
|
7568
8456
|
if (omit !== void 0 && omit(result.error)) continue;
|
|
7569
8457
|
return result;
|
|
7570
8458
|
}
|
|
7571
|
-
setOwn$
|
|
8459
|
+
setOwn$2(out, keys[index], result.value);
|
|
7572
8460
|
}
|
|
7573
8461
|
return ok(out);
|
|
7574
8462
|
};
|
|
@@ -7585,7 +8473,7 @@ function formulaCode(profile) {
|
|
|
7585
8473
|
const names = { ...ctx.names };
|
|
7586
8474
|
for (const [name, part] of bindings) {
|
|
7587
8475
|
const result = part.code(ctx, rt);
|
|
7588
|
-
setOwn$
|
|
8476
|
+
setOwn$2(names, name, result.ok ? result.value : new Held(result.error));
|
|
7589
8477
|
}
|
|
7590
8478
|
return body.code({
|
|
7591
8479
|
...ctx,
|
|
@@ -7616,7 +8504,7 @@ function formulaCode(profile) {
|
|
|
7616
8504
|
rt.keepsContext?.(ctx, reads);
|
|
7617
8505
|
const closure = (...args) => {
|
|
7618
8506
|
const scope = { ...ctx.names };
|
|
7619
|
-
names.forEach((name, index) => setOwn$
|
|
8507
|
+
names.forEach((name, index) => setOwn$2(scope, name, args[index]));
|
|
7620
8508
|
const result = body.code({
|
|
7621
8509
|
...ctx,
|
|
7622
8510
|
names: scope
|
|
@@ -7889,7 +8777,8 @@ function formulaMachine(profile = {}, more = {}) {
|
|
|
7889
8777
|
/**
|
|
7890
8778
|
* The standard syntax of the formula kit. It gives the site and the playground a text form. The operators are
|
|
7891
8779
|
* `or`, `and`, `not`, the comparisons, `+ - * / %`, a minus sign and `^`, from the loosest to the tightest. A call
|
|
7892
|
-
* `name(args)` applies the operation `ops.name`. `if(test, then, else)` and `list(items)` make the constructs.
|
|
8780
|
+
* `name(args)` applies the operation `ops.name`. `if(test, then, else)` and `list(items)` make the constructs. The
|
|
8781
|
+
* text `total = 2 + 3` gives the document the header name `total` (D62).
|
|
7893
8782
|
*/
|
|
7894
8783
|
const STD_SYNTAX = /* @__PURE__ */ Object.freeze({
|
|
7895
8784
|
operators: [
|
|
@@ -8019,7 +8908,8 @@ const STD_SYNTAX = /* @__PURE__ */ Object.freeze({
|
|
|
8019
8908
|
}, {
|
|
8020
8909
|
name: "list",
|
|
8021
8910
|
kind: "list"
|
|
8022
|
-
}] }
|
|
8911
|
+
}] },
|
|
8912
|
+
header: { bind: "=" }
|
|
8023
8913
|
});
|
|
8024
8914
|
const NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
8025
8915
|
const NAME_AT = /[A-Za-z_][A-Za-z0-9_]*/y;
|
|
@@ -8037,6 +8927,12 @@ const DEFAULT_ROOTS = [
|
|
|
8037
8927
|
"event",
|
|
8038
8928
|
"caps"
|
|
8039
8929
|
];
|
|
8930
|
+
const FORM_ARGS = [
|
|
8931
|
+
"list",
|
|
8932
|
+
"fields",
|
|
8933
|
+
"named",
|
|
8934
|
+
"literal"
|
|
8935
|
+
];
|
|
8040
8936
|
const compiledProfiles = /* @__PURE__ */ new WeakMap();
|
|
8041
8937
|
function typeOfKind(kind) {
|
|
8042
8938
|
const keys = kind.split(".");
|
|
@@ -8092,6 +8988,37 @@ function compileOps(profile, separator) {
|
|
|
8092
8988
|
opWords
|
|
8093
8989
|
};
|
|
8094
8990
|
}
|
|
8991
|
+
/** This function examines the bind symbol of a header. A text with the symbol after a name was not valid before. */
|
|
8992
|
+
function compileBind(bind, separator, binary) {
|
|
8993
|
+
if (bind === void 0) return void 0;
|
|
8994
|
+
if (!SYMBOL.test(bind) || bind.includes(separator)) throw new TypeError(`The bind symbol "${bind}" is not a group of symbols.`);
|
|
8995
|
+
for (const symbol of binary.keys()) if (bind.startsWith(symbol)) throw new TypeError(`The bind symbol "${bind}" starts with the operator "${symbol}".`);
|
|
8996
|
+
return bind;
|
|
8997
|
+
}
|
|
8998
|
+
/** This function examines the field `wrap` of a call form (K159). A name that is not a field of the form throws. */
|
|
8999
|
+
function compileWrap(given, args) {
|
|
9000
|
+
const wrap = given.wrap;
|
|
9001
|
+
if (wrap === void 0 || wrap === false) return false;
|
|
9002
|
+
if (args === "literal") throw new TypeError(`The call form "${given.name}" with the arguments literal takes no wrap.`);
|
|
9003
|
+
if (wrap === true) return true;
|
|
9004
|
+
if (args === "list") throw new TypeError(`The call form "${given.name}" with the arguments list takes only the wrap true.`);
|
|
9005
|
+
for (const name of wrap) if (given.fields !== void 0 && !given.fields.includes(name)) throw new TypeError(`The call form "${given.name}" has no field ${name} for its wrap.`);
|
|
9006
|
+
return new Set(wrap);
|
|
9007
|
+
}
|
|
9008
|
+
/** This function gives the types of the data envelopes of a profile (K159), or `undefined` without them. */
|
|
9009
|
+
function compileEnvelopes(given) {
|
|
9010
|
+
if (given === void 0) return void 0;
|
|
9011
|
+
const out = {};
|
|
9012
|
+
for (const key of [
|
|
9013
|
+
"number",
|
|
9014
|
+
"text",
|
|
9015
|
+
"bool"
|
|
9016
|
+
]) {
|
|
9017
|
+
const kind = given[key];
|
|
9018
|
+
if (kind !== void 0) out[key] = typeOfKind(kind);
|
|
9019
|
+
}
|
|
9020
|
+
return out;
|
|
9021
|
+
}
|
|
8095
9022
|
function compileProfile(profile) {
|
|
8096
9023
|
const cached = compiledProfiles.get(profile);
|
|
8097
9024
|
if (cached !== void 0) return cached;
|
|
@@ -8106,13 +9033,22 @@ function compileProfile(profile) {
|
|
|
8106
9033
|
const forms = /* @__PURE__ */ new Map();
|
|
8107
9034
|
const formsByKind = /* @__PURE__ */ new Map();
|
|
8108
9035
|
for (const given of calls.forms ?? []) {
|
|
8109
|
-
|
|
9036
|
+
const keys = given.name.split(".");
|
|
9037
|
+
if (!keys.every((key) => NAME.test(key))) throw new TypeError(`The call form "${given.name}" is not a plain name or a path of plain names.`);
|
|
9038
|
+
if (keys.length > 1 && refs.fields === false) throw new TypeError(`The call form "${given.name}" has dots, but the syntax has no field steps.`);
|
|
9039
|
+
if (keys.length > 1 && ops.opWords.has(keys[0])) throw new TypeError(`The call form "${given.name}" starts with the word operator "${keys[0]}".`);
|
|
8110
9040
|
const { type, key } = typeOfKind(given.kind);
|
|
9041
|
+
const args = given.args ?? (given.fields === void 0 ? "list" : "fields");
|
|
9042
|
+
if (!FORM_ARGS.includes(args)) throw new TypeError(`The call form "${given.name}" has the unknown arguments ${String(args)}.`);
|
|
9043
|
+
if (args === "fields" !== (given.fields !== void 0) && args !== "named") throw new TypeError(`The call form "${given.name}" with the arguments ${args} ${args === "fields" ? "needs" : "takes no"} fields.`);
|
|
9044
|
+
if (given.min !== void 0 && (args === "named" || args === "literal")) throw new TypeError(`The call form "${given.name}" with the arguments ${args} takes no min.`);
|
|
8111
9045
|
const form = {
|
|
8112
9046
|
name: given.name,
|
|
8113
9047
|
type,
|
|
9048
|
+
args,
|
|
8114
9049
|
fields: given.fields,
|
|
8115
|
-
min: given.min ?? given.fields?.length ?? 0
|
|
9050
|
+
min: given.min ?? given.fields?.length ?? 0,
|
|
9051
|
+
wrap: compileWrap(given, args)
|
|
8116
9052
|
};
|
|
8117
9053
|
forms.set(form.name, form);
|
|
8118
9054
|
addTo(formsByKind, key, form);
|
|
@@ -8124,6 +9060,7 @@ function compileProfile(profile) {
|
|
|
8124
9060
|
quote: strings === void 0 ? void 0 : strings.quote ?? "\"",
|
|
8125
9061
|
doubled: strings !== void 0 && strings.escape === "double",
|
|
8126
9062
|
words,
|
|
9063
|
+
envelopes: compileEnvelopes(literals.envelopes),
|
|
8127
9064
|
bare: refs.bare ?? "names",
|
|
8128
9065
|
roots,
|
|
8129
9066
|
fields: refs.fields ?? true,
|
|
@@ -8140,6 +9077,7 @@ function compileProfile(profile) {
|
|
|
8140
9077
|
forms,
|
|
8141
9078
|
formsByKind,
|
|
8142
9079
|
separator,
|
|
9080
|
+
bind: compileBind(profile.header?.bind, separator, ops.binary),
|
|
8143
9081
|
reserved: /* @__PURE__ */ new Set([
|
|
8144
9082
|
...words.keys(),
|
|
8145
9083
|
...ops.opWords,
|
|
@@ -8166,6 +9104,37 @@ function opDoc(op, value) {
|
|
|
8166
9104
|
value
|
|
8167
9105
|
};
|
|
8168
9106
|
}
|
|
9107
|
+
/** This function gives the kind of the data envelope of a literal (K159), or `undefined` when the literal has none. */
|
|
9108
|
+
function envelopeOf(syn, value) {
|
|
9109
|
+
const kinds = syn.envelopes;
|
|
9110
|
+
if (kinds === void 0 || !isScalar(value)) return void 0;
|
|
9111
|
+
return typeof value === "string" ? kinds.text : typeof value === "number" ? kinds.number : typeof value === "boolean" ? kinds.bool : void 0;
|
|
9112
|
+
}
|
|
9113
|
+
/** This function gives a literal in a place that takes a document its data envelope (K159). Each other document stays. */
|
|
9114
|
+
function envelop(syn, doc) {
|
|
9115
|
+
const kind = isEnvelope(doc) ? void 0 : envelopeOf(syn, doc);
|
|
9116
|
+
return kind === void 0 ? doc : {
|
|
9117
|
+
type: kind.type,
|
|
9118
|
+
value: doc
|
|
9119
|
+
};
|
|
9120
|
+
}
|
|
9121
|
+
/** This function tells if an argument of a form takes a document (K159). */
|
|
9122
|
+
function wraps(form, key) {
|
|
9123
|
+
return form.wrap === true || form.wrap !== false && form.wrap.has(String(key));
|
|
9124
|
+
}
|
|
9125
|
+
/** This function tells if a value is a literal that JSON writes exactly: a text, a finite number, a truth value or `null`. */
|
|
9126
|
+
function isScalar(value) {
|
|
9127
|
+
return value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value) && !Object.is(value, -0);
|
|
9128
|
+
}
|
|
9129
|
+
function setOwn$1(target, key, value) {
|
|
9130
|
+
if (key === "__proto__") Object.defineProperty(target, key, {
|
|
9131
|
+
value,
|
|
9132
|
+
enumerable: true,
|
|
9133
|
+
writable: true,
|
|
9134
|
+
configurable: true
|
|
9135
|
+
});
|
|
9136
|
+
else target[key] = value;
|
|
9137
|
+
}
|
|
8169
9138
|
const ESCAPES = {
|
|
8170
9139
|
"\"": "\"",
|
|
8171
9140
|
"'": "'",
|
|
@@ -8189,10 +9158,10 @@ var Parser = class {
|
|
|
8189
9158
|
parse() {
|
|
8190
9159
|
this.skip();
|
|
8191
9160
|
if (this.pos >= this.text.length) this.stop("The text is empty.", 0, 0);
|
|
8192
|
-
const doc = this.
|
|
9161
|
+
const doc = this.bound();
|
|
8193
9162
|
this.skip();
|
|
8194
9163
|
if (this.pos < this.text.length) this.stop("The text continues after the expression.", this.pos, this.tokenLength());
|
|
8195
|
-
return doc;
|
|
9164
|
+
return envelop(this.syn, doc);
|
|
8196
9165
|
}
|
|
8197
9166
|
position() {
|
|
8198
9167
|
return this.pos;
|
|
@@ -8214,6 +9183,60 @@ var Parser = class {
|
|
|
8214
9183
|
tokenLength() {
|
|
8215
9184
|
return (this.at(NAME_AT) ?? this.at(DIGITS_AT) ?? "x").length;
|
|
8216
9185
|
}
|
|
9186
|
+
/**
|
|
9187
|
+
* This method reads an expression that can have a header name: `name = expr` (D62). The bind symbol of the profile
|
|
9188
|
+
* follows the name. A longer operator that starts with the symbol wins, for example `==` over `=`.
|
|
9189
|
+
*/
|
|
9190
|
+
bound() {
|
|
9191
|
+
const name = this.bindingName();
|
|
9192
|
+
if (name === void 0) return this.expr(0);
|
|
9193
|
+
this.skip();
|
|
9194
|
+
const start = this.pos;
|
|
9195
|
+
const doc = this.expr(0);
|
|
9196
|
+
if (isEnvelope(doc)) {
|
|
9197
|
+
if (doc.name !== void 0) this.stop(`The document already has the name ${doc.name}.`, start, this.pos - start);
|
|
9198
|
+
return {
|
|
9199
|
+
...doc,
|
|
9200
|
+
name
|
|
9201
|
+
};
|
|
9202
|
+
}
|
|
9203
|
+
const kind = envelopeOf(this.syn, doc);
|
|
9204
|
+
return kind === void 0 ? {
|
|
9205
|
+
type: "lit",
|
|
9206
|
+
value: doc,
|
|
9207
|
+
name
|
|
9208
|
+
} : {
|
|
9209
|
+
type: kind.type,
|
|
9210
|
+
value: doc,
|
|
9211
|
+
name
|
|
9212
|
+
};
|
|
9213
|
+
}
|
|
9214
|
+
/** This method reads a name and the bind symbol after it. Without them, it gives `undefined` and keeps the position. */
|
|
9215
|
+
bindingName() {
|
|
9216
|
+
const bind = this.syn.bind;
|
|
9217
|
+
if (bind === void 0) return void 0;
|
|
9218
|
+
this.skip();
|
|
9219
|
+
const back = this.pos;
|
|
9220
|
+
let name;
|
|
9221
|
+
if (this.text[this.pos] === "`") try {
|
|
9222
|
+
name = this.quoted();
|
|
9223
|
+
} catch (error) {
|
|
9224
|
+
if (!(error instanceof Stop)) throw error;
|
|
9225
|
+
}
|
|
9226
|
+
else {
|
|
9227
|
+
name = this.at(NAME_AT);
|
|
9228
|
+
if (name !== void 0) this.pos += name.length;
|
|
9229
|
+
}
|
|
9230
|
+
if (name !== void 0) {
|
|
9231
|
+
this.skip();
|
|
9232
|
+
const longer = this.syn.binarySymbols.some((symbol) => symbol.length > bind.length && symbol.startsWith(bind) && this.text.startsWith(symbol, this.pos));
|
|
9233
|
+
if (this.text.startsWith(bind, this.pos) && !longer) {
|
|
9234
|
+
this.pos += bind.length;
|
|
9235
|
+
return name;
|
|
9236
|
+
}
|
|
9237
|
+
}
|
|
9238
|
+
this.pos = back;
|
|
9239
|
+
}
|
|
8217
9240
|
/** This method reads an expression by precedence climbing. It stops at an operator below `min`. */
|
|
8218
9241
|
expr(min) {
|
|
8219
9242
|
const start = this.pos;
|
|
@@ -8346,7 +9369,7 @@ var Parser = class {
|
|
|
8346
9369
|
group() {
|
|
8347
9370
|
const open = this.pos;
|
|
8348
9371
|
this.pos += 1;
|
|
8349
|
-
const inner = this.
|
|
9372
|
+
const inner = this.bound();
|
|
8350
9373
|
this.skip();
|
|
8351
9374
|
if (this.text[this.pos] !== ")") {
|
|
8352
9375
|
if (this.pos >= this.text.length) this.stop("A closing parenthesis is missing.", open, 1);
|
|
@@ -8396,6 +9419,11 @@ var Parser = class {
|
|
|
8396
9419
|
}
|
|
8397
9420
|
this.pos += 1;
|
|
8398
9421
|
this.skip();
|
|
9422
|
+
if (this.text[this.pos] === "(") {
|
|
9423
|
+
path.push(this.group());
|
|
9424
|
+
allPlain = false;
|
|
9425
|
+
continue;
|
|
9426
|
+
}
|
|
8399
9427
|
if (this.text[this.pos] === "`") {
|
|
8400
9428
|
path.push(this.quoted());
|
|
8401
9429
|
allPlain = false;
|
|
@@ -8411,7 +9439,7 @@ var Parser = class {
|
|
|
8411
9439
|
const back = this.pos;
|
|
8412
9440
|
this.skip();
|
|
8413
9441
|
if (this.text[this.pos] === "(") {
|
|
8414
|
-
if (!allPlain) this.stop("
|
|
9442
|
+
if (!allPlain) this.stop("Each key of the name of a call is a plain name.", start, back - start);
|
|
8415
9443
|
return this.call(path.join("."), start, back - start);
|
|
8416
9444
|
}
|
|
8417
9445
|
this.pos = back;
|
|
@@ -8421,14 +9449,18 @@ var Parser = class {
|
|
|
8421
9449
|
value: plain && this.syn.roots.has(first) ? path : [this.syn.bare, ...path]
|
|
8422
9450
|
};
|
|
8423
9451
|
}
|
|
8424
|
-
call
|
|
9452
|
+
/** This method reads the arguments of a call in parentheses. `item` reads one argument. */
|
|
9453
|
+
argumentsOf(item) {
|
|
8425
9454
|
const open = this.pos;
|
|
8426
9455
|
const args = [];
|
|
8427
9456
|
this.pos += 1;
|
|
8428
9457
|
this.skip();
|
|
8429
|
-
if (this.text[this.pos] === ")")
|
|
8430
|
-
|
|
8431
|
-
args
|
|
9458
|
+
if (this.text[this.pos] === ")") {
|
|
9459
|
+
this.pos += 1;
|
|
9460
|
+
return args;
|
|
9461
|
+
}
|
|
9462
|
+
for (;;) {
|
|
9463
|
+
args.push(item());
|
|
8432
9464
|
this.skip();
|
|
8433
9465
|
if (this.text.startsWith(this.syn.separator, this.pos)) {
|
|
8434
9466
|
this.pos += this.syn.separator.length;
|
|
@@ -8436,22 +9468,55 @@ var Parser = class {
|
|
|
8436
9468
|
}
|
|
8437
9469
|
if (this.text[this.pos] === ")") {
|
|
8438
9470
|
this.pos += 1;
|
|
8439
|
-
|
|
9471
|
+
return args;
|
|
8440
9472
|
}
|
|
8441
9473
|
if (this.pos >= this.text.length) this.stop("A closing parenthesis is missing.", open, 1);
|
|
8442
9474
|
this.stop(`A "${this.syn.separator}" or a closing parenthesis is missing.`, this.pos, this.tokenLength());
|
|
8443
9475
|
}
|
|
9476
|
+
}
|
|
9477
|
+
/** This method reads the arguments `key: expr` of a form `named` into a record (SYNTAX.RECORD). */
|
|
9478
|
+
namedArguments(form) {
|
|
9479
|
+
const value = {};
|
|
9480
|
+
this.argumentsOf(() => {
|
|
9481
|
+
this.skip();
|
|
9482
|
+
const at = this.pos;
|
|
9483
|
+
let key;
|
|
9484
|
+
if (this.text[at] === "`") key = this.quoted();
|
|
9485
|
+
else {
|
|
9486
|
+
key = this.at(NAME_AT);
|
|
9487
|
+
if (key !== void 0) this.pos += key.length;
|
|
9488
|
+
}
|
|
9489
|
+
const end = this.pos;
|
|
9490
|
+
this.skip();
|
|
9491
|
+
if (key === void 0 || this.text[this.pos] !== ":") this.stop(`Each argument of the form ${form.name} is a key, a ":" and a document.`, at, Math.max(end - at, this.tokenLength()));
|
|
9492
|
+
if (form.fields !== void 0 && !form.fields.includes(key)) this.stop(`The form ${form.name} has no field ${key}.`, at, end - at);
|
|
9493
|
+
if (Object.hasOwn(value, key)) this.stop(`The form ${form.name} has the field ${key} two times.`, at, end - at);
|
|
9494
|
+
this.pos += 1;
|
|
9495
|
+
const arg = this.bound();
|
|
9496
|
+
setOwn$1(value, key, wraps(form, key) ? envelop(this.syn, arg) : arg);
|
|
9497
|
+
});
|
|
9498
|
+
return opDoc(form, value);
|
|
9499
|
+
}
|
|
9500
|
+
call(name, start, nameLength) {
|
|
8444
9501
|
const form = this.syn.forms.get(name);
|
|
9502
|
+
if (form?.args === "named") return this.namedArguments(form);
|
|
9503
|
+
const args = this.argumentsOf(() => this.bound());
|
|
9504
|
+
if (form?.args === "literal") {
|
|
9505
|
+
const [only] = args;
|
|
9506
|
+
if (args.length !== 1 || !isScalar(only)) this.stop(`The form ${name} takes one literal that JSON writes.`, start, this.pos - start);
|
|
9507
|
+
return opDoc(form, only);
|
|
9508
|
+
}
|
|
8445
9509
|
if (form !== void 0) {
|
|
8446
9510
|
const most = form.fields?.length ?? Number.POSITIVE_INFINITY;
|
|
8447
9511
|
if (args.length < form.min || args.length > most) {
|
|
8448
9512
|
const range = most === form.min ? String(most) : most === Number.POSITIVE_INFINITY ? `${String(form.min)} or more` : `${String(form.min)} to ${String(most)}`;
|
|
8449
9513
|
this.stop(`The form ${name} takes ${range} arguments.`, start, this.pos - start);
|
|
8450
9514
|
}
|
|
8451
|
-
if (form.fields === void 0) return opDoc(form, args);
|
|
9515
|
+
if (form.fields === void 0) return opDoc(form, form.wrap === true ? args.map((arg) => envelop(this.syn, arg)) : args);
|
|
8452
9516
|
const value = {};
|
|
8453
9517
|
args.forEach((arg, index) => {
|
|
8454
|
-
|
|
9518
|
+
const key = form.fields?.[index];
|
|
9519
|
+
value[key] = wraps(form, key) ? envelop(this.syn, arg) : arg;
|
|
8455
9520
|
});
|
|
8456
9521
|
return opDoc(form, value);
|
|
8457
9522
|
}
|
|
@@ -8530,8 +9595,10 @@ function literalShape(syn, value) {
|
|
|
8530
9595
|
function plainSegment(syn, name) {
|
|
8531
9596
|
return NAME.test(name) && !syn.reserved.has(name) && !syn.patterns.some((p) => p.whole.test(name));
|
|
8532
9597
|
}
|
|
8533
|
-
function
|
|
9598
|
+
/** This function gives the text of a later step of a path, or the step and its index when the step is a document. */
|
|
9599
|
+
function laterSegment(step, index) {
|
|
8534
9600
|
if (typeof step === "string") return NAME.test(step) ? step : quoteName(step);
|
|
9601
|
+
if (isEnvelope(step)) return [index, step];
|
|
8535
9602
|
return typeof step === "number" && Number.isSafeInteger(step) && step >= 0 ? String(step) : void 0;
|
|
8536
9603
|
}
|
|
8537
9604
|
function refShape(syn, value) {
|
|
@@ -8545,18 +9612,23 @@ function refShape(syn, value) {
|
|
|
8545
9612
|
}
|
|
8546
9613
|
const [root, ...rest] = value;
|
|
8547
9614
|
if ((rest.length > 1 || root !== syn.bare && rest.length > 0) && !syn.fields) return none("This syntax has no field steps.");
|
|
8548
|
-
const later = rest.slice(1).map(laterSegment);
|
|
8549
9615
|
let first;
|
|
8550
|
-
let tail =
|
|
8551
|
-
if (root === syn.bare && typeof rest[0] === "string")
|
|
8552
|
-
|
|
9616
|
+
let tail = [];
|
|
9617
|
+
if (root === syn.bare && typeof rest[0] === "string") {
|
|
9618
|
+
first = plainSegment(syn, rest[0]) ? rest[0] : quoteName(rest[0]);
|
|
9619
|
+
tail = rest.slice(1).map((step, index) => laterSegment(step, index + 2));
|
|
9620
|
+
} else if (typeof root === "string" && syn.roots.has(root) && !syn.patterns.some((p) => p.whole.test(root))) {
|
|
8553
9621
|
first = root;
|
|
8554
|
-
tail = rest.map(laterSegment);
|
|
9622
|
+
tail = rest.map((step, index) => laterSegment(step, index + 1));
|
|
8555
9623
|
}
|
|
8556
9624
|
if (first === void 0 || tail.includes(void 0)) return none("This syntax has no text for the path of the reference.");
|
|
8557
|
-
|
|
9625
|
+
const segments = [first, ...tail];
|
|
9626
|
+
return segments.every((segment) => typeof segment === "string") ? {
|
|
8558
9627
|
tag: "text",
|
|
8559
|
-
text:
|
|
9628
|
+
text: segments.join(".")
|
|
9629
|
+
} : {
|
|
9630
|
+
tag: "path",
|
|
9631
|
+
segments
|
|
8560
9632
|
};
|
|
8561
9633
|
}
|
|
8562
9634
|
function callName(syn, keys) {
|
|
@@ -8565,13 +9637,58 @@ function callName(syn, keys) {
|
|
|
8565
9637
|
if (syn.forms.has(name) || syn.opWords.has(keys[0])) return void 0;
|
|
8566
9638
|
return syn.ops === true || syn.ops.has(name) ? name : void 0;
|
|
8567
9639
|
}
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
|
|
9640
|
+
const WRAPPED = "This syntax reads a literal in this place as its data envelope, thus a bare literal has no text here.";
|
|
9641
|
+
/**
|
|
9642
|
+
* This function gives the text of a document in a place that takes a document (K159). A data envelope of a literal
|
|
9643
|
+
* is the bare literal. A literal without its envelope has no text, because `parse` gives it the envelope. The
|
|
9644
|
+
* function gives `undefined` for each other document.
|
|
9645
|
+
*/
|
|
9646
|
+
function wrappedShape(syn, doc) {
|
|
9647
|
+
if (syn.envelopes === void 0) return void 0;
|
|
9648
|
+
if (!isEnvelope(doc)) return envelopeOf(syn, doc) === void 0 ? void 0 : none(WRAPPED);
|
|
9649
|
+
if (doc.name !== void 0 || doc.own !== void 0 || !Object.hasOwn(doc, "value")) return void 0;
|
|
9650
|
+
const path = typePath(doc.type);
|
|
9651
|
+
if (path === void 0) return void 0;
|
|
9652
|
+
const key = JSON.stringify(path);
|
|
9653
|
+
if (key === JSON.stringify(["lit"])) return envelopeOf(syn, doc.value) === void 0 ? void 0 : none(WRAPPED);
|
|
9654
|
+
if (envelopeOf(syn, doc.value)?.key !== key) return void 0;
|
|
9655
|
+
const literal = literalShape(syn, doc.value);
|
|
9656
|
+
return literal.tag === "text" ? literal : void 0;
|
|
9657
|
+
}
|
|
9658
|
+
function formShape(syn, form, value) {
|
|
9659
|
+
if (form.args === "literal") {
|
|
9660
|
+
if (!isScalar(value)) return void 0;
|
|
9661
|
+
const literal = literalShape(syn, value);
|
|
9662
|
+
return literal.tag === "text" ? {
|
|
9663
|
+
tag: "text",
|
|
9664
|
+
text: `${form.name}(${literal.text})`
|
|
9665
|
+
} : void 0;
|
|
9666
|
+
}
|
|
9667
|
+
if (form.args === "named") {
|
|
9668
|
+
if (!isRecord(value)) return void 0;
|
|
9669
|
+
const keys = Object.keys(value);
|
|
9670
|
+
if (form.fields !== void 0 && !keys.every((key) => form.fields?.includes(key) === true)) return void 0;
|
|
9671
|
+
return {
|
|
8572
9672
|
tag: "call",
|
|
8573
9673
|
name: form.name,
|
|
8574
|
-
args:
|
|
9674
|
+
args: keys.map((key) => [
|
|
9675
|
+
key,
|
|
9676
|
+
value[key],
|
|
9677
|
+
wraps(form, key)
|
|
9678
|
+
]),
|
|
9679
|
+
named: true
|
|
9680
|
+
};
|
|
9681
|
+
}
|
|
9682
|
+
if (form.fields === void 0) {
|
|
9683
|
+
if (!Array.isArray(value) || value.length < form.min) return void 0;
|
|
9684
|
+
return {
|
|
9685
|
+
tag: "call",
|
|
9686
|
+
name: form.name,
|
|
9687
|
+
args: value.map((item, index) => [
|
|
9688
|
+
index,
|
|
9689
|
+
item,
|
|
9690
|
+
wraps(form, index)
|
|
9691
|
+
])
|
|
8575
9692
|
};
|
|
8576
9693
|
}
|
|
8577
9694
|
if (!isRecord(value)) return void 0;
|
|
@@ -8581,7 +9698,11 @@ function formShape(form, value) {
|
|
|
8581
9698
|
return {
|
|
8582
9699
|
tag: "call",
|
|
8583
9700
|
name: form.name,
|
|
8584
|
-
args: present.map((key) => [
|
|
9701
|
+
args: present.map((key) => [
|
|
9702
|
+
key,
|
|
9703
|
+
value[key],
|
|
9704
|
+
wraps(form, key)
|
|
9705
|
+
])
|
|
8585
9706
|
};
|
|
8586
9707
|
}
|
|
8587
9708
|
function opShape(syn, key, terms) {
|
|
@@ -8594,7 +9715,16 @@ function opShape(syn, key, terms) {
|
|
|
8594
9715
|
/** This function gives the shape of a document: a literal or a reference, an operator, a call, or no text. */
|
|
8595
9716
|
function shapeOf(syn, doc) {
|
|
8596
9717
|
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);
|
|
8597
|
-
if (doc.
|
|
9718
|
+
if (doc.own !== void 0) return none("This syntax has no text for the own entries of a document.");
|
|
9719
|
+
if (doc.name !== void 0) {
|
|
9720
|
+
if (syn.bind === void 0) return none("This syntax has no text for the name of a document.");
|
|
9721
|
+
const { name, ...rest } = doc;
|
|
9722
|
+
return {
|
|
9723
|
+
tag: "bind",
|
|
9724
|
+
name,
|
|
9725
|
+
doc: rest
|
|
9726
|
+
};
|
|
9727
|
+
}
|
|
8598
9728
|
const path = typePath(doc.type);
|
|
8599
9729
|
if (path === void 0) return none("This syntax has no text for a computed type.");
|
|
8600
9730
|
const value = doc.value;
|
|
@@ -8615,7 +9745,7 @@ function shapeOf(syn, doc) {
|
|
|
8615
9745
|
const op = Array.isArray(value) ? opShape(syn, key, value) : void 0;
|
|
8616
9746
|
if (op !== void 0) return op;
|
|
8617
9747
|
for (const form of syn.formsByKind.get(key) ?? []) {
|
|
8618
|
-
const shape = formShape(form, value);
|
|
9748
|
+
const shape = formShape(syn, form, value);
|
|
8619
9749
|
if (shape !== void 0) return shape;
|
|
8620
9750
|
}
|
|
8621
9751
|
const called = path[0] === "ops" && Array.isArray(value) ? callName(syn, path.slice(1)) : void 0;
|
|
@@ -8628,7 +9758,7 @@ function shapeOf(syn, doc) {
|
|
|
8628
9758
|
}
|
|
8629
9759
|
function levelOf(syn, doc) {
|
|
8630
9760
|
const shape = shapeOf(syn, doc);
|
|
8631
|
-
return shape.tag === "op" ? shape.op.prec : Number.POSITIVE_INFINITY;
|
|
9761
|
+
return shape.tag === "op" ? shape.op.prec : shape.tag === "bind" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY;
|
|
8632
9762
|
}
|
|
8633
9763
|
/** This function tells if a term needs parentheses in its position, so that `parse` reads the same tree. */
|
|
8634
9764
|
function needsGroup(op, index, level) {
|
|
@@ -8658,18 +9788,42 @@ function printNode(syn, doc, sub) {
|
|
|
8658
9788
|
if (shape.tag === "text") return ok(shape.text);
|
|
8659
9789
|
if (shape.tag === "none") return fail("#VALUE!", shape.reason);
|
|
8660
9790
|
if (shape.tag === "op") return opText(syn, shape.op, shape.terms, sub);
|
|
9791
|
+
if (shape.tag === "bind") {
|
|
9792
|
+
const head = wrappedShape(syn, shape.doc);
|
|
9793
|
+
const inner = head === void 0 ? printNode(syn, shape.doc, sub) : head.tag === "text" ? ok(head.text) : fail("#VALUE!", WRAPPED);
|
|
9794
|
+
return inner.ok ? ok(`${NAME.test(shape.name) ? shape.name : quoteName(shape.name)} ${syn.bind} ${inner.value}`) : inner;
|
|
9795
|
+
}
|
|
9796
|
+
if (shape.tag === "path") {
|
|
9797
|
+
const texts = [];
|
|
9798
|
+
for (const segment of shape.segments) {
|
|
9799
|
+
if (typeof segment === "string") {
|
|
9800
|
+
texts.push(segment);
|
|
9801
|
+
continue;
|
|
9802
|
+
}
|
|
9803
|
+
const result = sub(segment[1], [segment[0]]);
|
|
9804
|
+
if (!result.ok) return result;
|
|
9805
|
+
texts.push(`(${result.value})`);
|
|
9806
|
+
}
|
|
9807
|
+
return ok(texts.join("."));
|
|
9808
|
+
}
|
|
8661
9809
|
const texts = [];
|
|
8662
|
-
for (const [key, arg] of shape.args) {
|
|
8663
|
-
const result = sub(arg, [key]);
|
|
9810
|
+
for (const [key, arg, wrapped] of shape.args) {
|
|
9811
|
+
const result = sub(arg, [key], wrapped);
|
|
8664
9812
|
if (!result.ok) return result;
|
|
8665
|
-
texts.push(result.value);
|
|
9813
|
+
texts.push(shape.named === true ? `${NAME.test(String(key)) ? String(key) : quoteName(String(key))}: ${result.value}` : result.value);
|
|
8666
9814
|
}
|
|
8667
9815
|
return ok(`${shape.name}(${texts.join(`${syn.separator} `)})`);
|
|
8668
9816
|
}
|
|
8669
9817
|
function printTree(syn, doc, place) {
|
|
8670
|
-
const result = printNode(syn, doc, (child, path) => printTree(syn, child, [...place, ...path]));
|
|
9818
|
+
const result = printNode(syn, doc, (child, path, wrapped) => (wrapped === true ? printPlace : printTree)(syn, child, [...place, ...path]));
|
|
8671
9819
|
return result.ok || result.error.place !== void 0 ? result : fail(result.error.code, result.error.reason, { place });
|
|
8672
9820
|
}
|
|
9821
|
+
/** This function writes a document in a place that takes a document, for example the whole text (K159). */
|
|
9822
|
+
function printPlace(syn, doc, place) {
|
|
9823
|
+
const shape = wrappedShape(syn, doc);
|
|
9824
|
+
if (shape === void 0) return printTree(syn, doc, place);
|
|
9825
|
+
return shape.tag === "text" ? ok(shape.text) : fail("#VALUE!", WRAPPED, { place });
|
|
9826
|
+
}
|
|
8673
9827
|
/**
|
|
8674
9828
|
* This function writes a document as text with the rules of a profile (SYNTAX.PRINT). It writes the fewest
|
|
8675
9829
|
* parentheses that keep the tree. `parse` reads the text back to the same document (SYNTAX.ROUNDTRIP).
|
|
@@ -8682,7 +9836,7 @@ function printTree(syn, doc, place) {
|
|
|
8682
9836
|
function print(profile, doc) {
|
|
8683
9837
|
const syn = compileProfile(profile);
|
|
8684
9838
|
try {
|
|
8685
|
-
return
|
|
9839
|
+
return printPlace(syn, doc, []);
|
|
8686
9840
|
} catch (error) {
|
|
8687
9841
|
if (isStackOverflow(error)) return fail("#DEPTH!", "The document is too deep for the stack of the host.", { cause: error });
|
|
8688
9842
|
throw error;
|
|
@@ -8700,7 +9854,9 @@ function syntaxFormat(profile) {
|
|
|
8700
9854
|
const syn = compileProfile(profile);
|
|
8701
9855
|
const code = ({ doc, parts, place }) => {
|
|
8702
9856
|
const byPath = new Map(parts.map((part) => [JSON.stringify(part.path), part]));
|
|
8703
|
-
return printNode(syn, doc, (child, path) => {
|
|
9857
|
+
return printNode(syn, doc, (child, path, wrapped) => {
|
|
9858
|
+
const own = wrapped === true ? wrappedShape(syn, child) : void 0;
|
|
9859
|
+
if (own !== void 0) return own.tag === "text" ? ok(own.text) : fail("#VALUE!", WRAPPED, { place: [...place, ...path] });
|
|
8704
9860
|
const part = byPath.get(JSON.stringify(path));
|
|
8705
9861
|
if (part === void 0) return printTree(syn, child, [...place, ...path]);
|
|
8706
9862
|
const result = part.evaluate();
|
|
@@ -8821,6 +9977,54 @@ function resolveFully(machine, ctx, path, options) {
|
|
|
8821
9977
|
hops: []
|
|
8822
9978
|
};
|
|
8823
9979
|
}
|
|
9980
|
+
/** The text that names a resolution in the error of an exception of the host (READS.RECORD). */
|
|
9981
|
+
const READ_FAILED = "the resolution of a path";
|
|
9982
|
+
/**
|
|
9983
|
+
* This function resolves a path for a document, and records the read when the evaluation has a list (REF.READS,
|
|
9984
|
+
* READS.RECORD). An exception of the host gives the error value of the read, and the exception goes on.
|
|
9985
|
+
*/
|
|
9986
|
+
function readAt(env, ctx, path, options) {
|
|
9987
|
+
const reading = env.reading;
|
|
9988
|
+
if (reading === void 0) return resolveFully(env.machine, ctx, path, options);
|
|
9989
|
+
const free = freeIn(reading.root, ctx, path);
|
|
9990
|
+
let found;
|
|
9991
|
+
try {
|
|
9992
|
+
found = resolveFully(env.machine, ctx, path, options);
|
|
9993
|
+
} catch (error) {
|
|
9994
|
+
reading.list.push({
|
|
9995
|
+
path,
|
|
9996
|
+
reached: thrown(error, READ_FAILED),
|
|
9997
|
+
free
|
|
9998
|
+
});
|
|
9999
|
+
throw error;
|
|
10000
|
+
}
|
|
10001
|
+
reading.list.push(options?.hops === true ? {
|
|
10002
|
+
path,
|
|
10003
|
+
reached: reachedOf(found),
|
|
10004
|
+
free,
|
|
10005
|
+
hops: found.hops
|
|
10006
|
+
} : {
|
|
10007
|
+
path,
|
|
10008
|
+
reached: reachedOf(found),
|
|
10009
|
+
free
|
|
10010
|
+
});
|
|
10011
|
+
return found;
|
|
10012
|
+
}
|
|
10013
|
+
/**
|
|
10014
|
+
* This function tells if a read reads the context at the root of the evaluation (READS.FREE). The reference reads
|
|
10015
|
+
* the rule from SPEC. A name that a kind binds, a `self` that a kind sets and a path without a root are not free. A
|
|
10016
|
+
* name that a kind does not bind keeps its value from the root.
|
|
10017
|
+
*/
|
|
10018
|
+
function freeIn(root, ctx, path) {
|
|
10019
|
+
const head = path[0];
|
|
10020
|
+
if (head === "self") return Object.is(ctx.self, root.self);
|
|
10021
|
+
if (head !== "names") return typeof head === "string" && ROOTS.includes(head);
|
|
10022
|
+
const name = path[1];
|
|
10023
|
+
if (ctx.names === root.names || !isKey(name)) return true;
|
|
10024
|
+
const key = String(name);
|
|
10025
|
+
const bound = Object.hasOwn(ctx.names, key);
|
|
10026
|
+
return bound === Object.hasOwn(root.names, key) && (!bound || Object.is(ctx.names[key], root.names[key]));
|
|
10027
|
+
}
|
|
8824
10028
|
/**
|
|
8825
10029
|
* This function causes an effect when the capabilities permit it (RUN.CAP, CAP.HOOK). It records the effect with its
|
|
8826
10030
|
* address (RUN.ADDRESS) and its outcome (RUN.OUTCOME). The mock answers the effect at once (RUN.MOCK). Without a
|
|
@@ -8975,7 +10179,7 @@ function evalInput(env, seen, at) {
|
|
|
8975
10179
|
place: at.place,
|
|
8976
10180
|
machine: env.machine,
|
|
8977
10181
|
...at.address ? { address: true } : {},
|
|
8978
|
-
resolve: (path, options) =>
|
|
10182
|
+
resolve: (path, options) => readAt(env, at.ctx, path, options),
|
|
8979
10183
|
effect: (kind, payload) => causeFromEvaluator(env, at.ctx, kind, payload, at.place),
|
|
8980
10184
|
fill
|
|
8981
10185
|
};
|
|
@@ -9002,7 +10206,7 @@ function runInput(env, seen, at) {
|
|
|
9002
10206
|
context: at.ctx,
|
|
9003
10207
|
place: at.place,
|
|
9004
10208
|
machine: env.machine,
|
|
9005
|
-
resolve: (path, options) =>
|
|
10209
|
+
resolve: (path, options) => readAt(env, at.ctx, path, options),
|
|
9006
10210
|
effect: (kind, payload) => causeAndWait(env, at.ctx, kind, payload, at.place),
|
|
9007
10211
|
all: (list, options) => together(env, list, options),
|
|
9008
10212
|
fill
|
|
@@ -9031,6 +10235,7 @@ function* together(env, list, options = {}) {
|
|
|
9031
10235
|
outer.alls += 1;
|
|
9032
10236
|
let failed = false;
|
|
9033
10237
|
let started = 0;
|
|
10238
|
+
let before = 0;
|
|
9034
10239
|
const end = (index, result) => {
|
|
9035
10240
|
results[index] = result;
|
|
9036
10241
|
if (failFast && !result.ok) failed = true;
|
|
@@ -9094,10 +10299,11 @@ function* together(env, list, options = {}) {
|
|
|
9094
10299
|
};
|
|
9095
10300
|
startMore();
|
|
9096
10301
|
while (waiting.length > 0 && !failed) {
|
|
9097
|
-
const chosen = env.journal?.next?.(address, waiting.map((waiter) => waiter.index));
|
|
10302
|
+
const chosen = env.journal?.next?.(address, waiting.map((waiter) => waiter.index), before);
|
|
9098
10303
|
const pick = chosen === void 0 ? void 0 : waiting.find((waiter) => waiter.index === chosen);
|
|
9099
10304
|
const resumed = yield pick === void 0 ? Promise.race(waiting.map((waiter) => waiter.next)) : pick.next;
|
|
9100
10305
|
env.journal?.settled?.(address, resumed.waiter.index);
|
|
10306
|
+
before += 1;
|
|
9101
10307
|
waiting.splice(waiting.indexOf(resumed.waiter), 1);
|
|
9102
10308
|
goOn(resumed.waiter.index, resumed.waiter.steps, resumed);
|
|
9103
10309
|
startMore();
|
|
@@ -9124,9 +10330,17 @@ function* together(env, list, options = {}) {
|
|
|
9124
10330
|
* @returns The result.
|
|
9125
10331
|
*/
|
|
9126
10332
|
function reference(machine, doc, context = makeContext(), options = {}) {
|
|
9127
|
-
|
|
10333
|
+
return evaluateRoot(machine, doc, context, options, void 0);
|
|
10334
|
+
}
|
|
10335
|
+
/**
|
|
10336
|
+
* This function evaluates a document from its root. An equation of a tree gives the record of its tree. Thus the
|
|
10337
|
+
* reads of the equation go in the list of the outer evaluation (REF.READS).
|
|
10338
|
+
*/
|
|
10339
|
+
function evaluateRoot(machine, doc, context, options, outer) {
|
|
10340
|
+
const at = rootAt(machine, doc, context, options);
|
|
10341
|
+
const env = envOf(machine, options, false, at.ctx, outer);
|
|
9128
10342
|
try {
|
|
9129
|
-
return within(env, () => evaluateAt(env,
|
|
10343
|
+
return within(env, () => evaluateAt(env, at));
|
|
9130
10344
|
} catch (error) {
|
|
9131
10345
|
return placed(thrown(error, "the reference"), []);
|
|
9132
10346
|
}
|
|
@@ -9147,11 +10361,15 @@ function reference(machine, doc, context = makeContext(), options = {}) {
|
|
|
9147
10361
|
* @returns The result, when the run ends.
|
|
9148
10362
|
*/
|
|
9149
10363
|
function referenceRun(machine, doc, context = makeContext(), options = {}) {
|
|
9150
|
-
const
|
|
9151
|
-
|
|
10364
|
+
const at = rootAt(machine, doc, context, options);
|
|
10365
|
+
const env = envOf(machine, options, true, at.ctx, void 0);
|
|
10366
|
+
return driveRun(env, stepsAt(env, at), options.signal).catch((error) => placed(thrown(error, "the reference"), []));
|
|
9152
10367
|
}
|
|
9153
|
-
/**
|
|
9154
|
-
function
|
|
10368
|
+
/**
|
|
10369
|
+
* This function makes the state of the reference for one evaluation or one run. The record of the reads comes from
|
|
10370
|
+
* the evaluation around it, or from the list of the options.
|
|
10371
|
+
*/
|
|
10372
|
+
function envOf(machine, options, running, root, outer) {
|
|
9155
10373
|
const perform = options.perform;
|
|
9156
10374
|
return {
|
|
9157
10375
|
machine,
|
|
@@ -9166,7 +10384,11 @@ function envOf(machine, options, running) {
|
|
|
9166
10384
|
prefix: "",
|
|
9167
10385
|
counts: /* @__PURE__ */ new Map(),
|
|
9168
10386
|
alls: 0
|
|
9169
|
-
}
|
|
10387
|
+
},
|
|
10388
|
+
reading: outer ?? (options.reads === void 0 ? void 0 : {
|
|
10389
|
+
list: options.reads,
|
|
10390
|
+
root
|
|
10391
|
+
})
|
|
9170
10392
|
};
|
|
9171
10393
|
}
|
|
9172
10394
|
/** This function gives the root position of a document. A context without a world gets the world of the machine. */
|
|
@@ -9241,7 +10463,8 @@ function refNodeOf(thing) {
|
|
|
9241
10463
|
}
|
|
9242
10464
|
/**
|
|
9243
10465
|
* TREE.LADDER: an equation of the kind of a node evaluates in the reference. It gets the node as `self`, the names of
|
|
9244
|
-
* its rung and no capabilities. A bare equation gets the type `of` of the parts of the kind.
|
|
10466
|
+
* its rung and no capabilities. A bare equation gets the type `of` of the parts of the kind. The reads of the
|
|
10467
|
+
* equation go in the record of the evaluation that made the node (REF.READS).
|
|
9245
10468
|
*/
|
|
9246
10469
|
function equationOf(node, term, names) {
|
|
9247
10470
|
const env = node.env;
|
|
@@ -9252,10 +10475,10 @@ function equationOf(node, term, names) {
|
|
|
9252
10475
|
caps: [],
|
|
9253
10476
|
self: node.thing
|
|
9254
10477
|
};
|
|
9255
|
-
return
|
|
10478
|
+
return evaluateRoot(env.machine, term, ctx, {
|
|
9256
10479
|
definitions: env.definitions,
|
|
9257
10480
|
...node.of === void 0 ? {} : { declaredType: node.of }
|
|
9258
|
-
});
|
|
10481
|
+
}, env.reading);
|
|
9259
10482
|
}
|
|
9260
10483
|
function termIn(table, name) {
|
|
9261
10484
|
return table === void 0 || !Object.hasOwn(table, name) ? void 0 : table[name];
|
|
@@ -9501,10 +10724,13 @@ function formulaDefinitions(profile = {}) {
|
|
|
9501
10724
|
}
|
|
9502
10725
|
const head = path[0] === "names" && typeof path[1] === "string" ? path[1] : void 0;
|
|
9503
10726
|
const bound = head === void 0 ? void 0 : own$1(context.names, head);
|
|
9504
|
-
if (bound instanceof Held)
|
|
9505
|
-
|
|
9506
|
-
|
|
9507
|
-
|
|
10727
|
+
if (bound instanceof Held) {
|
|
10728
|
+
walk(["names", head]);
|
|
10729
|
+
return {
|
|
10730
|
+
ok: false,
|
|
10731
|
+
error: bound.error
|
|
10732
|
+
};
|
|
10733
|
+
}
|
|
9508
10734
|
const found = walk(steps);
|
|
9509
10735
|
if (!found.ok) return fail(found.code, found.reason);
|
|
9510
10736
|
if (address === true) return ok(found.thing);
|
|
@@ -9667,18 +10893,21 @@ function valuesOfStrict(parts) {
|
|
|
9667
10893
|
* Its log keeps each effect with its address, its kind, its payload and the outcome that the kernel gives it. The log
|
|
9668
10894
|
* also keeps the order in which the waits of each `all` settle.
|
|
9669
10895
|
*
|
|
10896
|
+
* The recorder gives each call of its journal to the journal of the inner function, for example of a replayer. Thus a
|
|
10897
|
+
* recording of a replay settles the waits in the order of the replayer, and it gives the same log (RUN.REPLAY-ORDER).
|
|
10898
|
+
*
|
|
9670
10899
|
* @param perform - The function of the host. Without it, each effect gives its payload, as in a run without a host.
|
|
9671
10900
|
* @returns The function `perform` for the run, and the log.
|
|
9672
10901
|
*/
|
|
9673
10902
|
function recorder(perform) {
|
|
9674
10903
|
const seen = [];
|
|
9675
10904
|
const settles = [];
|
|
9676
|
-
const journal =
|
|
10905
|
+
const journal = relay(perform, (all, item) => {
|
|
9677
10906
|
settles.push({
|
|
9678
10907
|
all,
|
|
9679
10908
|
item
|
|
9680
10909
|
});
|
|
9681
|
-
}
|
|
10910
|
+
});
|
|
9682
10911
|
const host = (effect) => {
|
|
9683
10912
|
const entry = {
|
|
9684
10913
|
effect,
|
|
@@ -9704,56 +10933,928 @@ function recorder(perform) {
|
|
|
9704
10933
|
};
|
|
9705
10934
|
}
|
|
9706
10935
|
/**
|
|
9707
|
-
* This function makes a replayer: a function `perform` that answers each effect from a log, by its address (RUN.REPLAY). A
|
|
9708
|
-
* recorded error comes again with its code, its reason, its place and its cause. The waits of each `all` settle in the
|
|
9709
|
-
* recorded order. An effect that the log does not have at its address gives `#REPLAY!` (RUN.REPLAY-MISMATCH).
|
|
10936
|
+
* This function makes a replayer: a function `perform` that answers each effect from a log, by its address (RUN.REPLAY). A
|
|
10937
|
+
* recorded error comes again with its code, its reason, its place and its cause. The waits of each `all` settle in the
|
|
10938
|
+
* recorded order. An effect that the log does not have at its address gives `#REPLAY!` (RUN.REPLAY-MISMATCH).
|
|
10939
|
+
*
|
|
10940
|
+
* One replayer serves more than one run, also two runs at the same time (RUN.REPLAY-RUNS). The kernel tells the journal
|
|
10941
|
+
* the number of the waits of each `all` that settled before, thus the replayer keeps no state of a run. When the
|
|
10942
|
+
* recorded item of a wait does not wait in the run, that wait follows the order of the host.
|
|
10943
|
+
*
|
|
10944
|
+
* @param log - The log of a recorder, or of `decodeLog`.
|
|
10945
|
+
* @param options - `wait: false` for an evaluation, and the rule of the host for payloads.
|
|
10946
|
+
* @returns The function `perform` for the replay.
|
|
10947
|
+
*/
|
|
10948
|
+
function replayer(log, options = {}) {
|
|
10949
|
+
const wait = options.wait !== false;
|
|
10950
|
+
const equal = options.equal;
|
|
10951
|
+
const entries = /* @__PURE__ */ new Map();
|
|
10952
|
+
for (const entry of log.effects) entries.set(entry.address, entry);
|
|
10953
|
+
const order = /* @__PURE__ */ new Map();
|
|
10954
|
+
for (const settle of log.settles) {
|
|
10955
|
+
const items = order.get(settle.all);
|
|
10956
|
+
if (items === void 0) order.set(settle.all, [settle.item]);
|
|
10957
|
+
else items.push(settle.item);
|
|
10958
|
+
}
|
|
10959
|
+
let places;
|
|
10960
|
+
const journal = { next: (all, waiting, before) => {
|
|
10961
|
+
const items = order.get(all);
|
|
10962
|
+
if (items === void 0) return void 0;
|
|
10963
|
+
const at = before ?? (places ??= /* @__PURE__ */ new Map()).get(all) ?? 0;
|
|
10964
|
+
const item = items[at];
|
|
10965
|
+
if (item === void 0 || !waiting.includes(item)) return void 0;
|
|
10966
|
+
if (before === void 0) places?.set(all, at + 1);
|
|
10967
|
+
return item;
|
|
10968
|
+
} };
|
|
10969
|
+
const perform = (effect) => {
|
|
10970
|
+
const entry = effect.address === void 0 ? void 0 : entries.get(effect.address);
|
|
10971
|
+
const wrong = mismatch(effect, entry, equal);
|
|
10972
|
+
if (wrong !== void 0 || entry === void 0) throw wrong;
|
|
10973
|
+
const outcome = entry.outcome;
|
|
10974
|
+
const failure = outcome === void 0 ? replayError(effect, `The log has no outcome for the effect ${effect.kind} at ${effect.address ?? ""}: the recorded run stopped before the answer.`) : outcome.ok ? void 0 : new Failure(outcome.error);
|
|
10975
|
+
const value = outcome?.ok === true ? outcome.value : void 0;
|
|
10976
|
+
if (wait && entry.waited) return failure === void 0 ? Promise.resolve(value) : Promise.reject(failure);
|
|
10977
|
+
if (failure !== void 0) throw failure;
|
|
10978
|
+
return value;
|
|
10979
|
+
};
|
|
10980
|
+
return Object.assign(perform, { [JOURNAL]: journal });
|
|
10981
|
+
}
|
|
10982
|
+
/** This function gives the error of an effect that the log does not have at its address, or `undefined`. */
|
|
10983
|
+
function mismatch(effect, entry, equal) {
|
|
10984
|
+
const at = effect.address ?? "";
|
|
10985
|
+
if (entry === void 0) return replayError(effect, `The log has no effect at the address ${at}, and the run asks for the effect ${effect.kind}.`);
|
|
10986
|
+
if (entry.kind !== effect.kind) return replayError(effect, `The log has the effect ${entry.kind} at the address ${at}, and the run asks for the effect ${effect.kind}.`);
|
|
10987
|
+
if (!equalWith(entry.payload, effect.payload, equal)) return replayError(effect, `The effect ${effect.kind} at the address ${at} has another payload than the log.`);
|
|
10988
|
+
}
|
|
10989
|
+
/** This function gives the exception that carries `#REPLAY!` to the kernel, with the place of the effect. */
|
|
10990
|
+
function replayError(effect, reason) {
|
|
10991
|
+
return new Failure(fail("#REPLAY!", reason, { place: effect.place }).error);
|
|
10992
|
+
}
|
|
10993
|
+
/**
|
|
10994
|
+
* This function gives a journal that tells a function of its own each wait that settles. It gives each call to the
|
|
10995
|
+
* journal of the inner function `perform`, when that function has one.
|
|
10996
|
+
*
|
|
10997
|
+
* @param perform - The inner function.
|
|
10998
|
+
* @param settled - The function of its own.
|
|
10999
|
+
* @returns The journal.
|
|
11000
|
+
*/
|
|
11001
|
+
function relay(perform, settled) {
|
|
11002
|
+
const inner = perform === void 0 ? void 0 : perform[JOURNAL];
|
|
11003
|
+
const tell = (all, item) => {
|
|
11004
|
+
settled(all, item);
|
|
11005
|
+
inner?.settled?.(all, item);
|
|
11006
|
+
};
|
|
11007
|
+
if (inner?.next === void 0) return { settled: tell };
|
|
11008
|
+
return {
|
|
11009
|
+
settled: tell,
|
|
11010
|
+
next: (all, waiting, before) => inner.next?.(all, waiting, before)
|
|
11011
|
+
};
|
|
11012
|
+
}
|
|
11013
|
+
/** This function tells if a key is a field of an error value that the JSON form keeps. */
|
|
11014
|
+
function errorField(key) {
|
|
11015
|
+
return key === "code" || key === "reason" || key === "place" || key === "cause";
|
|
11016
|
+
}
|
|
11017
|
+
/** This function tells if a key is a field of an exception that the JSON form keeps in a field of its own, or drops. */
|
|
11018
|
+
function ownField(key) {
|
|
11019
|
+
return key === "name" || key === "message" || key === "stack" || key === "cause";
|
|
11020
|
+
}
|
|
11021
|
+
/** This function tells if a key is a field of the JSON form of an exception. */
|
|
11022
|
+
function exceptionField(key) {
|
|
11023
|
+
return key === "name" || key === "message" || key === "fields" || key === "parts" || key === "dropped" || key === "hidden" || key === "cause" || key === "exception" || key === "loop" || key === "causeParts";
|
|
11024
|
+
}
|
|
11025
|
+
/**
|
|
11026
|
+
* The own fields that are not enumerable and that the JSON form keeps in `hidden` (D77). The standard classes define
|
|
11027
|
+
* them: `errors` of `AggregateError`, `error` and `suppressed` of `SuppressedError`.
|
|
11028
|
+
*/
|
|
11029
|
+
const HIDDEN = /* @__PURE__ */ Object.freeze([
|
|
11030
|
+
"errors",
|
|
11031
|
+
"error",
|
|
11032
|
+
"suppressed"
|
|
11033
|
+
]);
|
|
11034
|
+
/** The result of `readField` for a field that the walk of `causeJson` cannot read. */
|
|
11035
|
+
const MISSING = /* @__PURE__ */ Symbol("missing");
|
|
11036
|
+
/**
|
|
11037
|
+
* This function tells if a value holds a hole of a list at some depth. `encodeValue` writes a hole as `undefined`. Thus
|
|
11038
|
+
* the walk of `causeJson` keeps such a value in parts, and the hole keeps its mark (D82).
|
|
11039
|
+
*/
|
|
11040
|
+
function holdsHole(value) {
|
|
11041
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11042
|
+
const stack = [value];
|
|
11043
|
+
while (stack.length > 0) {
|
|
11044
|
+
const at = stack.pop();
|
|
11045
|
+
if (typeof at !== "object" || at === null || seen.has(at) || !(Array.isArray(at) || isRecord(at))) continue;
|
|
11046
|
+
seen.add(at);
|
|
11047
|
+
if (Array.isArray(at)) for (let item = 0; item < at.length; item += 1) {
|
|
11048
|
+
if (!Object.hasOwn(at, item)) return true;
|
|
11049
|
+
stack.push(at[item]);
|
|
11050
|
+
}
|
|
11051
|
+
else for (const key of Object.keys(at)) stack.push(at[key]);
|
|
11052
|
+
}
|
|
11053
|
+
return false;
|
|
11054
|
+
}
|
|
11055
|
+
/**
|
|
11056
|
+
* This function tells if a list or a plain record holds an exception at some depth (D87). It reads the items as the
|
|
11057
|
+
* walk of `causeJson` does, thus a cause container with an exception gets a form.
|
|
11058
|
+
*/
|
|
11059
|
+
function holdsException(value) {
|
|
11060
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11061
|
+
const stack = [value];
|
|
11062
|
+
while (stack.length > 0) {
|
|
11063
|
+
const at = stack.pop();
|
|
11064
|
+
if (at instanceof Error) return true;
|
|
11065
|
+
if (typeof at !== "object" || at === null || seen.has(at) || !(Array.isArray(at) || isRecord(at))) continue;
|
|
11066
|
+
seen.add(at);
|
|
11067
|
+
if (Array.isArray(at)) {
|
|
11068
|
+
for (let item = 0; item < at.length; item += 1) if (Object.hasOwn(at, item)) stack.push(readField(at, item));
|
|
11069
|
+
} else for (const key of Object.keys(at)) stack.push(readField(at, key));
|
|
11070
|
+
}
|
|
11071
|
+
return false;
|
|
11072
|
+
}
|
|
11073
|
+
/** This function gives the field of a cause for its form after the walk (D76, D87). A cause without a form has no field. */
|
|
11074
|
+
function causeOf(built) {
|
|
11075
|
+
const form = built.form;
|
|
11076
|
+
if (form === void 0) return { names: built.names };
|
|
11077
|
+
if (Array.isArray(form) || "record" in form || "share" in form) return {
|
|
11078
|
+
link: { causeParts: form },
|
|
11079
|
+
names: built.names
|
|
11080
|
+
};
|
|
11081
|
+
return {
|
|
11082
|
+
link: "value" in form ? { cause: form.value } : form,
|
|
11083
|
+
names: built.names
|
|
11084
|
+
};
|
|
11085
|
+
}
|
|
11086
|
+
/** This function gives the term document of a value of an exception, or `undefined` when the value has none. */
|
|
11087
|
+
function termOrNothing(machine, value) {
|
|
11088
|
+
if (holdsHole(value)) return void 0;
|
|
11089
|
+
try {
|
|
11090
|
+
return encodeValue(machine, value);
|
|
11091
|
+
} catch {
|
|
11092
|
+
return;
|
|
11093
|
+
}
|
|
11094
|
+
}
|
|
11095
|
+
/** This function reads a field of a value, or gives `MISSING` when the read throws. */
|
|
11096
|
+
function readField(value, key) {
|
|
11097
|
+
try {
|
|
11098
|
+
return value[key];
|
|
11099
|
+
} catch {
|
|
11100
|
+
return MISSING;
|
|
11101
|
+
}
|
|
11102
|
+
}
|
|
11103
|
+
/** This function gives the forms of the parts with a form, with their keys, in the order of the keys. */
|
|
11104
|
+
function keptOf(keys, built) {
|
|
11105
|
+
const kept = [];
|
|
11106
|
+
for (let place = 0; place < keys.length; place += 1) {
|
|
11107
|
+
const form = built[place]?.form;
|
|
11108
|
+
if (form !== void 0) kept.push([keys[place], form]);
|
|
11109
|
+
}
|
|
11110
|
+
return kept;
|
|
11111
|
+
}
|
|
11112
|
+
/**
|
|
11113
|
+
* This function writes a cause without a term document as JSON (RUN.REPLAY-CAUSES, RUN.REPLAY-HIDDEN, RUN.REPLAY-FIELDS).
|
|
11114
|
+
* The cause is an exception with its hidden fields and its causes, or a container with an exception (D87). A host
|
|
11115
|
+
* makes the exception, thus a part without a term document is a note in `dropped`, not a refusal (D75, D76).
|
|
11116
|
+
*
|
|
11117
|
+
* The walk gives each exception an index in the order of a depth-first walk. It takes the fields in `parts` in the
|
|
11118
|
+
* order of their keys, the hidden fields in the order of `HIDDEN`, then the cause (D78). A list or a plain record
|
|
11119
|
+
* without a term document is a container of parts, with its items in their order (D82). A cause is a container only
|
|
11120
|
+
* when it holds an exception (D87). A value that is an earlier exception of the walk is its index in `loop` (D77).
|
|
11121
|
+
*
|
|
11122
|
+
* A container that an earlier place holds is its index in `share`, also a container that holds itself (D88). The walk is
|
|
11123
|
+
* a loop, thus a long chain does not overflow the stack.
|
|
11124
|
+
*/
|
|
11125
|
+
function causeJson(machine, root) {
|
|
11126
|
+
const items = [];
|
|
11127
|
+
const index = /* @__PURE__ */ new Map();
|
|
11128
|
+
const holders = /* @__PURE__ */ new Map();
|
|
11129
|
+
const tasks = [];
|
|
11130
|
+
const later = (children) => {
|
|
11131
|
+
for (let place = children.length - 1; place >= 0; place -= 1) tasks.push(children[place]);
|
|
11132
|
+
};
|
|
11133
|
+
const hold = (name, value, keep, children) => {
|
|
11134
|
+
const at = items.length;
|
|
11135
|
+
holders.set(value, at);
|
|
11136
|
+
const list = Array.isArray(value);
|
|
11137
|
+
const keys = list ? [] : Object.keys(value);
|
|
11138
|
+
const slots = [];
|
|
11139
|
+
items.push({
|
|
11140
|
+
name,
|
|
11141
|
+
list,
|
|
11142
|
+
keep,
|
|
11143
|
+
keys,
|
|
11144
|
+
slots
|
|
11145
|
+
});
|
|
11146
|
+
const size = list ? value.length : keys.length;
|
|
11147
|
+
for (let place = 0; place < size; place += 1) {
|
|
11148
|
+
const key = list ? place : keys[place];
|
|
11149
|
+
const item = list && !Object.hasOwn(value, place) ? MISSING : readField(value, key);
|
|
11150
|
+
const put = (part) => {
|
|
11151
|
+
slots[place] = part;
|
|
11152
|
+
};
|
|
11153
|
+
children.push({
|
|
11154
|
+
value: item,
|
|
11155
|
+
name: `${name}.${key}`,
|
|
11156
|
+
tried: false,
|
|
11157
|
+
nest: true,
|
|
11158
|
+
keep: false,
|
|
11159
|
+
put
|
|
11160
|
+
});
|
|
11161
|
+
}
|
|
11162
|
+
return at;
|
|
11163
|
+
};
|
|
11164
|
+
const open = (current) => {
|
|
11165
|
+
const at = items.length;
|
|
11166
|
+
index.set(current, index.size);
|
|
11167
|
+
const fields = [];
|
|
11168
|
+
const linked = [];
|
|
11169
|
+
for (const key of Object.keys(current)) {
|
|
11170
|
+
if (ownField(key)) continue;
|
|
11171
|
+
const value = readField(current, key);
|
|
11172
|
+
const term = value === MISSING ? void 0 : termOrNothing(machine, value);
|
|
11173
|
+
if (term === void 0) linked.push([key, value]);
|
|
11174
|
+
else fields.push([key, term]);
|
|
11175
|
+
}
|
|
11176
|
+
const node = {
|
|
11177
|
+
head: {
|
|
11178
|
+
name: String(current.name),
|
|
11179
|
+
message: String(current.message),
|
|
11180
|
+
...fields.length === 0 ? {} : { fields: Object.fromEntries(fields) }
|
|
11181
|
+
},
|
|
11182
|
+
keys: linked.map(([key]) => key),
|
|
11183
|
+
parts: [],
|
|
11184
|
+
hiddenKeys: [],
|
|
11185
|
+
hidden: []
|
|
11186
|
+
};
|
|
11187
|
+
items.push(node);
|
|
11188
|
+
const children = [];
|
|
11189
|
+
for (let place = 0; place < linked.length; place += 1) {
|
|
11190
|
+
const [key, value] = linked[place];
|
|
11191
|
+
const put = (part) => {
|
|
11192
|
+
node.parts[place] = part;
|
|
11193
|
+
};
|
|
11194
|
+
children.push({
|
|
11195
|
+
value,
|
|
11196
|
+
name: key,
|
|
11197
|
+
tried: true,
|
|
11198
|
+
nest: true,
|
|
11199
|
+
keep: false,
|
|
11200
|
+
put
|
|
11201
|
+
});
|
|
11202
|
+
}
|
|
11203
|
+
for (const key of HIDDEN) {
|
|
11204
|
+
if (Object.getOwnPropertyDescriptor(current, key)?.enumerable !== false) continue;
|
|
11205
|
+
const place = node.hiddenKeys.length;
|
|
11206
|
+
node.hiddenKeys.push(key);
|
|
11207
|
+
const value = readField(current, key);
|
|
11208
|
+
const put = (part) => {
|
|
11209
|
+
node.hidden[place] = part;
|
|
11210
|
+
};
|
|
11211
|
+
children.push({
|
|
11212
|
+
value,
|
|
11213
|
+
name: key,
|
|
11214
|
+
tried: false,
|
|
11215
|
+
nest: true,
|
|
11216
|
+
keep: Array.isArray(value),
|
|
11217
|
+
put
|
|
11218
|
+
});
|
|
11219
|
+
}
|
|
11220
|
+
if (Object.hasOwn(current, "cause")) {
|
|
11221
|
+
const put = (part) => {
|
|
11222
|
+
node.cause = part;
|
|
11223
|
+
};
|
|
11224
|
+
children.push({
|
|
11225
|
+
value: current.cause,
|
|
11226
|
+
name: "cause",
|
|
11227
|
+
tried: false,
|
|
11228
|
+
nest: false,
|
|
11229
|
+
keep: false,
|
|
11230
|
+
put
|
|
11231
|
+
});
|
|
11232
|
+
}
|
|
11233
|
+
later(children);
|
|
11234
|
+
return at;
|
|
11235
|
+
};
|
|
11236
|
+
let top = { none: "cause" };
|
|
11237
|
+
tasks.push({
|
|
11238
|
+
value: root,
|
|
11239
|
+
name: "cause",
|
|
11240
|
+
tried: true,
|
|
11241
|
+
nest: false,
|
|
11242
|
+
keep: false,
|
|
11243
|
+
put: (part) => {
|
|
11244
|
+
top = part;
|
|
11245
|
+
}
|
|
11246
|
+
});
|
|
11247
|
+
for (let task = tasks.pop(); task !== void 0; task = tasks.pop()) {
|
|
11248
|
+
const value = task.value;
|
|
11249
|
+
if (value === MISSING) {
|
|
11250
|
+
task.put({ none: task.name });
|
|
11251
|
+
continue;
|
|
11252
|
+
}
|
|
11253
|
+
const loop = index.get(value);
|
|
11254
|
+
if (loop !== void 0) {
|
|
11255
|
+
task.put({ loop });
|
|
11256
|
+
continue;
|
|
11257
|
+
}
|
|
11258
|
+
const term = task.tried || task.keep ? void 0 : termOrNothing(machine, value);
|
|
11259
|
+
if (term !== void 0) task.put({ value: term });
|
|
11260
|
+
else if (value instanceof Error) task.put({ node: open(value) });
|
|
11261
|
+
else if ((Array.isArray(value) || isRecord(value)) && (task.nest || holdsException(value))) {
|
|
11262
|
+
const held = holders.get(value);
|
|
11263
|
+
if (held === void 0) {
|
|
11264
|
+
const children = [];
|
|
11265
|
+
task.put({ holder: hold(task.name, value, task.keep, children) });
|
|
11266
|
+
later(children);
|
|
11267
|
+
} else {
|
|
11268
|
+
if (task.keep) items[held].keep = true;
|
|
11269
|
+
task.put({
|
|
11270
|
+
same: held,
|
|
11271
|
+
name: task.name
|
|
11272
|
+
});
|
|
11273
|
+
}
|
|
11274
|
+
} else task.put({ none: task.name });
|
|
11275
|
+
}
|
|
11276
|
+
const { formed, shares } = formsOf(items);
|
|
11277
|
+
const forms = [];
|
|
11278
|
+
const builtOf = (part) => {
|
|
11279
|
+
if ("none" in part) return { names: [part.none] };
|
|
11280
|
+
if ("node" in part) return {
|
|
11281
|
+
form: { exception: forms[part.node] },
|
|
11282
|
+
names: []
|
|
11283
|
+
};
|
|
11284
|
+
if ("holder" in part) return forms[part.holder];
|
|
11285
|
+
if ("same" in part) return formed[part.same] === true ? {
|
|
11286
|
+
form: { share: shares[part.same] },
|
|
11287
|
+
names: []
|
|
11288
|
+
} : { names: [part.name] };
|
|
11289
|
+
return {
|
|
11290
|
+
form: part,
|
|
11291
|
+
names: []
|
|
11292
|
+
};
|
|
11293
|
+
};
|
|
11294
|
+
for (let at = items.length - 1; at >= 0; at -= 1) {
|
|
11295
|
+
const item = items[at];
|
|
11296
|
+
if ("slots" in item) {
|
|
11297
|
+
if (formed[at] !== true) {
|
|
11298
|
+
forms[at] = { names: [item.name] };
|
|
11299
|
+
continue;
|
|
11300
|
+
}
|
|
11301
|
+
const built = Array.from(item.slots, builtOf);
|
|
11302
|
+
const form = item.list ? built.map((part) => part.form ?? { dropped: true }) : { record: Object.fromEntries(keptOf(item.keys, built)) };
|
|
11303
|
+
forms[at] = {
|
|
11304
|
+
form,
|
|
11305
|
+
names: built.flatMap((part) => part.names)
|
|
11306
|
+
};
|
|
11307
|
+
continue;
|
|
11308
|
+
}
|
|
11309
|
+
const { head, keys, parts, hiddenKeys, hidden, cause } = item;
|
|
11310
|
+
const linked = Array.from(parts, builtOf);
|
|
11311
|
+
const kept = Array.from(hidden, builtOf);
|
|
11312
|
+
const link = cause === void 0 ? { names: [] } : causeOf(builtOf(cause));
|
|
11313
|
+
const dropped = [
|
|
11314
|
+
...linked,
|
|
11315
|
+
...kept,
|
|
11316
|
+
link
|
|
11317
|
+
].flatMap((part) => part.names);
|
|
11318
|
+
const partsOf = keptOf(keys, linked);
|
|
11319
|
+
const hiddenOf = keptOf(hiddenKeys, kept);
|
|
11320
|
+
forms[at] = {
|
|
11321
|
+
...head,
|
|
11322
|
+
...partsOf.length === 0 ? {} : { parts: Object.fromEntries(partsOf) },
|
|
11323
|
+
...dropped.length === 0 ? {} : { dropped },
|
|
11324
|
+
...hiddenOf.length === 0 ? {} : { hidden: Object.fromEntries(hiddenOf) },
|
|
11325
|
+
...link.link
|
|
11326
|
+
};
|
|
11327
|
+
}
|
|
11328
|
+
return causeOf(builtOf(top));
|
|
11329
|
+
}
|
|
11330
|
+
/**
|
|
11331
|
+
* This function tells which containers of the walk of `causeJson` get a form, and it gives each such container its
|
|
11332
|
+
* index for `share` (D88). A container gets a form when it is a hidden list, or when an item has a form. A share has
|
|
11333
|
+
* the form of its container. Thus a cycle of containers without another form has no form. The index counts the
|
|
11334
|
+
* containers with a form in the order of the walk, as `decodeLog` meets them.
|
|
11335
|
+
*/
|
|
11336
|
+
function formsOf(items) {
|
|
11337
|
+
const formed = [];
|
|
11338
|
+
const users = /* @__PURE__ */ new Map();
|
|
11339
|
+
const work = [];
|
|
11340
|
+
for (let at = 0; at < items.length; at += 1) {
|
|
11341
|
+
const item = items[at];
|
|
11342
|
+
if (!("slots" in item)) continue;
|
|
11343
|
+
let seed = item.keep;
|
|
11344
|
+
for (const part of item.slots) {
|
|
11345
|
+
const on = "holder" in part ? part.holder : "same" in part ? part.same : void 0;
|
|
11346
|
+
if (on === void 0) seed ||= !("none" in part);
|
|
11347
|
+
else {
|
|
11348
|
+
const list = users.get(on);
|
|
11349
|
+
if (list === void 0) users.set(on, [at]);
|
|
11350
|
+
else list.push(at);
|
|
11351
|
+
}
|
|
11352
|
+
}
|
|
11353
|
+
if (seed) {
|
|
11354
|
+
formed[at] = true;
|
|
11355
|
+
work.push(at);
|
|
11356
|
+
}
|
|
11357
|
+
}
|
|
11358
|
+
for (let at = work.pop(); at !== void 0; at = work.pop()) for (const user of users.get(at) ?? []) {
|
|
11359
|
+
if (formed[user] === true) continue;
|
|
11360
|
+
formed[user] = true;
|
|
11361
|
+
work.push(user);
|
|
11362
|
+
}
|
|
11363
|
+
const shares = [];
|
|
11364
|
+
let count = 0;
|
|
11365
|
+
for (let at = 0; at < items.length; at += 1) {
|
|
11366
|
+
if (formed[at] !== true) continue;
|
|
11367
|
+
shares[at] = count;
|
|
11368
|
+
count += 1;
|
|
11369
|
+
}
|
|
11370
|
+
return {
|
|
11371
|
+
formed,
|
|
11372
|
+
shares
|
|
11373
|
+
};
|
|
11374
|
+
}
|
|
11375
|
+
/** This function gives the message of an exception, or a short text for another value. */
|
|
11376
|
+
function messageOf(error) {
|
|
11377
|
+
return error instanceof Error ? error.message : describe(error);
|
|
11378
|
+
}
|
|
11379
|
+
/** This function writes a value of a log as a term document, or throws a `TypeError` that names the value. */
|
|
11380
|
+
function termOf(machine, value, what) {
|
|
11381
|
+
try {
|
|
11382
|
+
return encodeValue(machine, value);
|
|
11383
|
+
} catch (error) {
|
|
11384
|
+
throw new TypeError(`encodeLog cannot write ${what()}: ${messageOf(error)}`);
|
|
11385
|
+
}
|
|
11386
|
+
}
|
|
11387
|
+
/** This function writes an error value of a log as JSON, or throws a `TypeError` that names the error. */
|
|
11388
|
+
function errorJson(machine, error, what) {
|
|
11389
|
+
for (const key of Object.keys(error)) if (!errorField(key)) throw new TypeError(`encodeLog cannot write ${what()}: the error has the field ${key}, and the JSON form has no field for it.`);
|
|
11390
|
+
const base = {
|
|
11391
|
+
code: error.code,
|
|
11392
|
+
reason: error.reason,
|
|
11393
|
+
...error.place === void 0 ? {} : { place: error.place }
|
|
11394
|
+
};
|
|
11395
|
+
if (!Object.hasOwn(error, "cause")) return base;
|
|
11396
|
+
const cause = error.cause;
|
|
11397
|
+
try {
|
|
11398
|
+
return {
|
|
11399
|
+
...base,
|
|
11400
|
+
cause: encodeValue(machine, cause)
|
|
11401
|
+
};
|
|
11402
|
+
} catch (failure) {
|
|
11403
|
+
const { link, names } = causeJson(machine, cause);
|
|
11404
|
+
if (link === void 0) throw new TypeError(`encodeLog cannot write the cause of ${what()}: ${messageOf(failure)}`);
|
|
11405
|
+
return {
|
|
11406
|
+
...base,
|
|
11407
|
+
...link,
|
|
11408
|
+
...names.length === 0 ? {} : { dropped: names }
|
|
11409
|
+
};
|
|
11410
|
+
}
|
|
11411
|
+
}
|
|
11412
|
+
/**
|
|
11413
|
+
* This function writes a log as JSON (RUN.REPLAY-JSON, D75). Then `JSON.stringify` gives the text of a golden file.
|
|
11414
|
+
*
|
|
11415
|
+
* - Each payload and each value of an outcome is a term document of `encodeValue`. Thus `NaN`, `-0` and `undefined`
|
|
11416
|
+
* stay exact, and a member of a domain with a codec is `ext.domain`.
|
|
11417
|
+
* - An error keeps its code, its reason, its place and its cause. A cause that `encodeValue` cannot write and that is
|
|
11418
|
+
* an exception keeps its name and its message in the field `exception`.
|
|
11419
|
+
* - An exception also keeps its own fields that `encodeValue` writes, and the chain of its causes (RUN.REPLAY-CAUSES).
|
|
11420
|
+
* The list `dropped` names each other own field. A cause that is an earlier exception of the chain is its index in `loop`.
|
|
11421
|
+
* - An exception keeps the hidden fields of the standard classes in `hidden`, for example `errors` of an
|
|
11422
|
+
* `AggregateError` (RUN.REPLAY-HIDDEN, D77). Each exception in them has the rule of a cause.
|
|
11423
|
+
* - An enumerable field without a term document that is an exception, a list or a plain record is in `parts`
|
|
11424
|
+
* (RUN.REPLAY-FIELDS, D78, D82). An item of a list without a form is the mark `dropped` at its place.
|
|
11425
|
+
* - A cause without a term document that is a list or a plain record with an exception is in `causeParts`
|
|
11426
|
+
* (RUN.REPLAY-CAUSE-PARTS, D87). The rule is the same for the cause of an exception and of an error value.
|
|
11427
|
+
* - A list or a record of parts that an earlier place of one error holds is the part `share` with its index
|
|
11428
|
+
* (RUN.REPLAY-SHARED, D88). This rule also covers a container that holds itself.
|
|
11429
|
+
* - An outcome that the log does not have stays absent.
|
|
11430
|
+
*
|
|
11431
|
+
* @param machine - The machine of the run, or only its domains.
|
|
11432
|
+
* @param log - The log of a recorder.
|
|
11433
|
+
* @returns The JSON form. `decodeLog` gives the log again.
|
|
11434
|
+
* @throws {TypeError} A value has no term document, for example a closure, a host thing, a symbol, or a value that no
|
|
11435
|
+
* domain with a codec holds. The message names the effect and its address. A part of an exception is not a refusal.
|
|
11436
|
+
*/
|
|
11437
|
+
function encodeLog(machine, log) {
|
|
11438
|
+
return {
|
|
11439
|
+
version: 1,
|
|
11440
|
+
effects: log.effects.map((entry) => {
|
|
11441
|
+
const at = (field) => () => `the ${field} of the effect ${entry.kind} at the address ${entry.address}`;
|
|
11442
|
+
const head = {
|
|
11443
|
+
address: entry.address,
|
|
11444
|
+
kind: entry.kind,
|
|
11445
|
+
payload: termOf(machine, entry.payload, at("payload"))
|
|
11446
|
+
};
|
|
11447
|
+
const outcome = entry.outcome;
|
|
11448
|
+
if (outcome === void 0) return {
|
|
11449
|
+
...head,
|
|
11450
|
+
waited: entry.waited
|
|
11451
|
+
};
|
|
11452
|
+
const json = outcome.ok ? {
|
|
11453
|
+
ok: true,
|
|
11454
|
+
value: termOf(machine, outcome.value, at("outcome"))
|
|
11455
|
+
} : {
|
|
11456
|
+
ok: false,
|
|
11457
|
+
error: errorJson(machine, outcome.error, at("outcome"))
|
|
11458
|
+
};
|
|
11459
|
+
return {
|
|
11460
|
+
...head,
|
|
11461
|
+
outcome: json,
|
|
11462
|
+
waited: entry.waited
|
|
11463
|
+
};
|
|
11464
|
+
}),
|
|
11465
|
+
settles: log.settles.map((settle) => ({
|
|
11466
|
+
all: settle.all,
|
|
11467
|
+
item: settle.item
|
|
11468
|
+
}))
|
|
11469
|
+
};
|
|
11470
|
+
}
|
|
11471
|
+
/** This function gives an exception with a name and a message. A standard name gives an instance of its class. */
|
|
11472
|
+
function exception(name, message) {
|
|
11473
|
+
const made = standard(name, message);
|
|
11474
|
+
if (made.name !== name) made.name = name;
|
|
11475
|
+
return made;
|
|
11476
|
+
}
|
|
11477
|
+
/**
|
|
11478
|
+
* This function gives an instance of the standard class with a name, or an `Error`. An `AggregateError` gets an empty
|
|
11479
|
+
* list `errors`, and a `SuppressedError` gets `undefined` for `error` and `suppressed`, as their constructors do.
|
|
11480
|
+
*/
|
|
11481
|
+
function standard(name, message) {
|
|
11482
|
+
switch (name) {
|
|
11483
|
+
case "TypeError": return new TypeError(message);
|
|
11484
|
+
case "RangeError": return new RangeError(message);
|
|
11485
|
+
case "SyntaxError": return new SyntaxError(message);
|
|
11486
|
+
case "ReferenceError": return new ReferenceError(message);
|
|
11487
|
+
case "AggregateError": return new AggregateError([], message);
|
|
11488
|
+
case "SuppressedError": return typeof SuppressedError === "function" ? new SuppressedError(void 0, void 0, message) : new Error(message);
|
|
11489
|
+
default: return new Error(message);
|
|
11490
|
+
}
|
|
11491
|
+
}
|
|
11492
|
+
/** This function gives an exception a field that is not enumerable, as the constructor of `Error` does for `cause`. */
|
|
11493
|
+
function hide(error, key, value) {
|
|
11494
|
+
Object.defineProperty(error, key, {
|
|
11495
|
+
value,
|
|
11496
|
+
writable: true,
|
|
11497
|
+
enumerable: false,
|
|
11498
|
+
configurable: true
|
|
11499
|
+
});
|
|
11500
|
+
}
|
|
11501
|
+
/** This function gives the failed result of a JSON form that `decodeLog` cannot read. */
|
|
11502
|
+
function bad(place, reason) {
|
|
11503
|
+
return fail("#VALUE!", `decodeLog cannot read the log: ${reason}`, { place });
|
|
11504
|
+
}
|
|
11505
|
+
/** This function reads a term document of a log again, with the formula kit and the domains of the machine. */
|
|
11506
|
+
function readTerm(kit, doc, place) {
|
|
11507
|
+
const value = decodeValue(kit, doc);
|
|
11508
|
+
return value.ok ? value : bad(place, `the term document gives ${value.error.code}: ${value.error.reason}`);
|
|
11509
|
+
}
|
|
11510
|
+
/** This function reads the JSON form of an error value again. */
|
|
11511
|
+
function readError(kit, data, place) {
|
|
11512
|
+
if (!isRecord(data) || typeof data["code"] !== "string" || !data["code"].startsWith("#") || typeof data["reason"] !== "string") return bad(place, "an error is a record with a code that starts with # and a reason.");
|
|
11513
|
+
for (const key of Object.keys(data)) if (!errorField(key) && key !== "exception" && key !== "causeParts" && key !== "dropped") return bad(place, `an error has no field ${key}.`);
|
|
11514
|
+
const keys = data["place"];
|
|
11515
|
+
if (keys !== void 0 && !(Array.isArray(keys) && keys.every((key) => isKey(key)))) return bad([...place, "place"], "a place is a list of texts and numbers.");
|
|
11516
|
+
const dropped = data["dropped"];
|
|
11517
|
+
if (dropped !== void 0 && !(Array.isArray(dropped) && dropped.every((name) => typeof name === "string"))) return bad([...place, "dropped"], "the list dropped holds texts.");
|
|
11518
|
+
const error = {
|
|
11519
|
+
code: data["code"],
|
|
11520
|
+
reason: data["reason"]
|
|
11521
|
+
};
|
|
11522
|
+
if (keys !== void 0) error.place = keys;
|
|
11523
|
+
const record = data;
|
|
11524
|
+
const links = LINKS.filter((key) => key !== "loop" && Object.hasOwn(record, key));
|
|
11525
|
+
if (links.length > 1) return bad(place, "an error has a cause, an exception or causeParts, not more than one.");
|
|
11526
|
+
const link = links[0];
|
|
11527
|
+
if (link === "cause") {
|
|
11528
|
+
const cause = readTerm(kit, data["cause"], [...place, "cause"]);
|
|
11529
|
+
if (!cause.ok) return cause;
|
|
11530
|
+
error.cause = cause.value;
|
|
11531
|
+
} else if (link !== void 0) {
|
|
11532
|
+
const read = readCause(kit, link, data[link], [...place, link]);
|
|
11533
|
+
if (!read.ok) return read;
|
|
11534
|
+
error.cause = read.value;
|
|
11535
|
+
}
|
|
11536
|
+
return ok(error);
|
|
11537
|
+
}
|
|
11538
|
+
/**
|
|
11539
|
+
* This function reads the JSON form of a cause again: an exception in `exception`, or a container in `causeParts`
|
|
11540
|
+
* (RUN.REPLAY-CAUSES, RUN.REPLAY-HIDDEN, RUN.REPLAY-FIELDS, D87). The walk has the order of `causeJson`, thus a loop
|
|
11541
|
+
* gives the same exception. A list or a record of parts gives a new list or plain record (D82). A share gives the same
|
|
11542
|
+
* list or record as its earlier place (D88). The walk is a loop, thus a long chain does not overflow the stack.
|
|
11543
|
+
*/
|
|
11544
|
+
function readCause(kit, kind, data, place) {
|
|
11545
|
+
const chain = [];
|
|
11546
|
+
const held = [];
|
|
11547
|
+
const tasks = [];
|
|
11548
|
+
const open = (at, where) => {
|
|
11549
|
+
if (!isRecord(at) || typeof at["name"] !== "string" || typeof at["message"] !== "string" || !Object.keys(at).every(exceptionField)) return bad(where, "an exception is a record with the texts name and message, and only the fields fields, parts, dropped, hidden, cause, exception, loop and causeParts.");
|
|
11550
|
+
const made = exception(at["name"], at["message"]);
|
|
11551
|
+
const fields = at["fields"];
|
|
11552
|
+
if (fields !== void 0) {
|
|
11553
|
+
if (!isRecord(fields)) return bad([...where, "fields"], "the fields of an exception are a record.");
|
|
11554
|
+
for (const key of Object.keys(fields)) {
|
|
11555
|
+
if (ownField(key)) return bad([
|
|
11556
|
+
...where,
|
|
11557
|
+
"fields",
|
|
11558
|
+
key
|
|
11559
|
+
], `the field ${key} of an exception is not in its fields.`);
|
|
11560
|
+
const value = readTerm(kit, fields[key], [
|
|
11561
|
+
...where,
|
|
11562
|
+
"fields",
|
|
11563
|
+
key
|
|
11564
|
+
]);
|
|
11565
|
+
if (!value.ok) return value;
|
|
11566
|
+
Object.defineProperty(made, key, {
|
|
11567
|
+
value: value.value,
|
|
11568
|
+
writable: true,
|
|
11569
|
+
enumerable: true,
|
|
11570
|
+
configurable: true
|
|
11571
|
+
});
|
|
11572
|
+
}
|
|
11573
|
+
}
|
|
11574
|
+
const dropped = at["dropped"];
|
|
11575
|
+
if (dropped !== void 0 && !(Array.isArray(dropped) && dropped.every((name) => typeof name === "string"))) return bad([...where, "dropped"], "the list dropped holds texts.");
|
|
11576
|
+
chain.push(made);
|
|
11577
|
+
const children = [];
|
|
11578
|
+
const taken = (key) => isRecord(fields) && Object.hasOwn(fields, key);
|
|
11579
|
+
const parts = at["parts"];
|
|
11580
|
+
if (parts !== void 0) {
|
|
11581
|
+
if (!isRecord(parts)) return bad([...where, "parts"], "the parts of an exception are a record.");
|
|
11582
|
+
for (const key of Object.keys(parts)) {
|
|
11583
|
+
if (ownField(key) || taken(key)) return bad([
|
|
11584
|
+
...where,
|
|
11585
|
+
"parts",
|
|
11586
|
+
key
|
|
11587
|
+
], `the field ${key} of an exception is not in its parts.`);
|
|
11588
|
+
const show = (value) => {
|
|
11589
|
+
Object.defineProperty(made, key, {
|
|
11590
|
+
value,
|
|
11591
|
+
writable: true,
|
|
11592
|
+
enumerable: true,
|
|
11593
|
+
configurable: true
|
|
11594
|
+
});
|
|
11595
|
+
};
|
|
11596
|
+
const read = slotTask(parts[key], [
|
|
11597
|
+
...where,
|
|
11598
|
+
"parts",
|
|
11599
|
+
key
|
|
11600
|
+
], false, show);
|
|
11601
|
+
if (!read.ok) return read;
|
|
11602
|
+
show(void 0);
|
|
11603
|
+
children.push(read.value);
|
|
11604
|
+
}
|
|
11605
|
+
}
|
|
11606
|
+
const hidden = at["hidden"];
|
|
11607
|
+
if (hidden !== void 0) {
|
|
11608
|
+
if (!isRecord(hidden)) return bad([...where, "hidden"], "the hidden fields of an exception are a record.");
|
|
11609
|
+
for (const key of Object.keys(hidden)) if (!HIDDEN.includes(key) || taken(key) || isRecord(parts) && Object.hasOwn(parts, key)) return bad([
|
|
11610
|
+
...where,
|
|
11611
|
+
"hidden",
|
|
11612
|
+
key
|
|
11613
|
+
], `an exception has no hidden field ${key}.`);
|
|
11614
|
+
for (const key of HIDDEN) {
|
|
11615
|
+
if (!Object.hasOwn(hidden, key)) continue;
|
|
11616
|
+
const read = slotTask(hidden[key], [
|
|
11617
|
+
...where,
|
|
11618
|
+
"hidden",
|
|
11619
|
+
key
|
|
11620
|
+
], false, (value) => hide(made, key, value));
|
|
11621
|
+
if (!read.ok) return read;
|
|
11622
|
+
children.push(read.value);
|
|
11623
|
+
}
|
|
11624
|
+
}
|
|
11625
|
+
const record = at;
|
|
11626
|
+
const links = LINKS.filter((key) => Object.hasOwn(record, key));
|
|
11627
|
+
if (links.length > 1) return bad(where, "an exception has a cause, an exception, a loop or causeParts, not more than one.");
|
|
11628
|
+
const link = links[0];
|
|
11629
|
+
if (link !== void 0) {
|
|
11630
|
+
const read = linkTask(link, record[link], [...where, link], (value) => hide(made, "cause", value));
|
|
11631
|
+
if (!read.ok) return read;
|
|
11632
|
+
children.push(read.value);
|
|
11633
|
+
}
|
|
11634
|
+
for (let index = children.length - 1; index >= 0; index -= 1) tasks.push(children[index]);
|
|
11635
|
+
return ok(made);
|
|
11636
|
+
};
|
|
11637
|
+
let cause;
|
|
11638
|
+
const root = linkTask(kind, data, place, (value) => {
|
|
11639
|
+
cause = value;
|
|
11640
|
+
});
|
|
11641
|
+
if (!root.ok) return root;
|
|
11642
|
+
tasks.push(root.value);
|
|
11643
|
+
for (let task = tasks.pop(); task !== void 0; task = tasks.pop()) {
|
|
11644
|
+
const { kind, data: part, where } = task;
|
|
11645
|
+
if (kind === "dropped") continue;
|
|
11646
|
+
if (kind === "loop") {
|
|
11647
|
+
if (typeof part !== "number" || !Number.isInteger(part) || part < 0 || part >= chain.length) return bad(where, "a loop is the index of an exception of the chain until this one.");
|
|
11648
|
+
task.put(chain[part]);
|
|
11649
|
+
continue;
|
|
11650
|
+
}
|
|
11651
|
+
if (kind === "share") {
|
|
11652
|
+
if (typeof part !== "number" || !Number.isInteger(part) || part < 0 || part >= held.length) return bad(where, "a share is the index of a list or a record of parts of the walk until this one.");
|
|
11653
|
+
task.put(held[part]);
|
|
11654
|
+
continue;
|
|
11655
|
+
}
|
|
11656
|
+
if (kind === "list" || kind === "record") {
|
|
11657
|
+
const items = kind === "list" ? listTasks(part, where, held, task.put) : recordTasks(part, where, held, task.put);
|
|
11658
|
+
if (!items.ok) return items;
|
|
11659
|
+
for (let index = items.value.length - 1; index >= 0; index -= 1) tasks.push(items.value[index]);
|
|
11660
|
+
continue;
|
|
11661
|
+
}
|
|
11662
|
+
const value = kind === "exception" ? open(part, where) : readTerm(kit, part, where);
|
|
11663
|
+
if (!value.ok) return value;
|
|
11664
|
+
task.put(value.value);
|
|
11665
|
+
}
|
|
11666
|
+
return ok(cause);
|
|
11667
|
+
}
|
|
11668
|
+
/**
|
|
11669
|
+
* This function reads the field of a cause: `cause`, `exception`, `loop` or `causeParts`. A value of `causeParts` is a
|
|
11670
|
+
* list of parts, the part `record` or the part `share`, and no other part (D87, D88).
|
|
11671
|
+
*/
|
|
11672
|
+
function linkTask(kind, data, where, put) {
|
|
11673
|
+
if (kind !== "causeParts") return ok({
|
|
11674
|
+
kind,
|
|
11675
|
+
data,
|
|
11676
|
+
where,
|
|
11677
|
+
put
|
|
11678
|
+
});
|
|
11679
|
+
if (Array.isArray(data)) return ok({
|
|
11680
|
+
kind: "list",
|
|
11681
|
+
data,
|
|
11682
|
+
where,
|
|
11683
|
+
put
|
|
11684
|
+
});
|
|
11685
|
+
const key = isRecord(data) && Object.keys(data).length === 1 ? Object.keys(data)[0] : void 0;
|
|
11686
|
+
if (isRecord(data) && (key === "record" || key === "share")) return ok({
|
|
11687
|
+
kind: key,
|
|
11688
|
+
data: data[key],
|
|
11689
|
+
where: [...where, key],
|
|
11690
|
+
put
|
|
11691
|
+
});
|
|
11692
|
+
return bad(where, "a cause of parts is a list of parts, the part record or the part share.");
|
|
11693
|
+
}
|
|
11694
|
+
/**
|
|
11695
|
+
* This function reads one value of a field in `parts`, in `hidden` or in a record of parts. A list is a list of parts,
|
|
11696
|
+
* and another value is one part.
|
|
11697
|
+
*/
|
|
11698
|
+
function slotTask(data, where, item, put) {
|
|
11699
|
+
return Array.isArray(data) ? ok({
|
|
11700
|
+
kind: "list",
|
|
11701
|
+
data,
|
|
11702
|
+
where,
|
|
11703
|
+
put
|
|
11704
|
+
}) : partTask(data, where, item, put);
|
|
11705
|
+
}
|
|
11706
|
+
/**
|
|
11707
|
+
* This function gives a list of parts its length at once, and it gives the tasks of its items. Thus the mark `dropped`
|
|
11708
|
+
* leaves a hole at its place, and each later item keeps its index (D78). The list goes in `held` for a share (D88).
|
|
11709
|
+
*/
|
|
11710
|
+
function listTasks(data, where, held, put) {
|
|
11711
|
+
const items = data;
|
|
11712
|
+
const list = [];
|
|
11713
|
+
list.length = items.length;
|
|
11714
|
+
held.push(list);
|
|
11715
|
+
put(list);
|
|
11716
|
+
const tasks = [];
|
|
11717
|
+
for (let item = 0; item < items.length; item += 1) {
|
|
11718
|
+
const task = slotTask(items[item], [...where, item], true, (value) => {
|
|
11719
|
+
list[item] = value;
|
|
11720
|
+
});
|
|
11721
|
+
if (!task.ok) return task;
|
|
11722
|
+
tasks.push(task.value);
|
|
11723
|
+
}
|
|
11724
|
+
return ok(tasks);
|
|
11725
|
+
}
|
|
11726
|
+
/**
|
|
11727
|
+
* This function gives a record of parts a new plain record (D82). It gives the tasks of its fields in the order of
|
|
11728
|
+
* the keys. Each key gets its place at once, thus the record keeps the order of the keys. The record goes in `held`
|
|
11729
|
+
* for a share (D88).
|
|
11730
|
+
*/
|
|
11731
|
+
function recordTasks(data, where, held, put) {
|
|
11732
|
+
if (!isRecord(data)) return bad(where, "a record of parts is a record.");
|
|
11733
|
+
const record = {};
|
|
11734
|
+
held.push(record);
|
|
11735
|
+
put(record);
|
|
11736
|
+
const tasks = [];
|
|
11737
|
+
for (const key of Object.keys(data)) {
|
|
11738
|
+
const show = (value) => {
|
|
11739
|
+
Object.defineProperty(record, key, {
|
|
11740
|
+
value,
|
|
11741
|
+
writable: true,
|
|
11742
|
+
enumerable: true,
|
|
11743
|
+
configurable: true
|
|
11744
|
+
});
|
|
11745
|
+
};
|
|
11746
|
+
const task = slotTask(data[key], [...where, key], false, show);
|
|
11747
|
+
if (!task.ok) return task;
|
|
11748
|
+
show(void 0);
|
|
11749
|
+
tasks.push(task.value);
|
|
11750
|
+
}
|
|
11751
|
+
return ok(tasks);
|
|
11752
|
+
}
|
|
11753
|
+
/**
|
|
11754
|
+
* This function reads one part: a record with only one of the fields `value`, `exception`, `loop`, `record` and
|
|
11755
|
+
* `share`. An item of a list can also be the mark `{ "dropped": true }`.
|
|
11756
|
+
*/
|
|
11757
|
+
function partTask(data, where, item, put) {
|
|
11758
|
+
const keys = isRecord(data) ? Object.keys(data) : [];
|
|
11759
|
+
const kind = keys[0];
|
|
11760
|
+
const mark = kind === "dropped" && item && isRecord(data) && data[kind] === true;
|
|
11761
|
+
if (!isRecord(data) || keys.length !== 1 || kind === void 0 || !(PARTS$1.includes(kind) || mark)) return bad(where, "a field of parts is a part or a list of parts. A part has only one of the fields value, exception, loop, record and share, or an item is the mark dropped.");
|
|
11762
|
+
return ok({
|
|
11763
|
+
kind,
|
|
11764
|
+
data: data[kind],
|
|
11765
|
+
where: [...where, kind],
|
|
11766
|
+
put
|
|
11767
|
+
});
|
|
11768
|
+
}
|
|
11769
|
+
/** The fields of the JSON form of an exception that give its cause. */
|
|
11770
|
+
const LINKS = /* @__PURE__ */ Object.freeze([
|
|
11771
|
+
"cause",
|
|
11772
|
+
"exception",
|
|
11773
|
+
"loop",
|
|
11774
|
+
"causeParts"
|
|
11775
|
+
]);
|
|
11776
|
+
/** The fields of the JSON form of a part of a field in `parts` or `hidden`. */
|
|
11777
|
+
const PARTS$1 = /* @__PURE__ */ Object.freeze([
|
|
11778
|
+
"value",
|
|
11779
|
+
"exception",
|
|
11780
|
+
"loop",
|
|
11781
|
+
"record",
|
|
11782
|
+
"share"
|
|
11783
|
+
]);
|
|
11784
|
+
/** This function reads the JSON form of one effect again. */
|
|
11785
|
+
function readEntry(kit, data, place) {
|
|
11786
|
+
if (!isRecord(data) || typeof data["address"] !== "string" || typeof data["kind"] !== "string" || typeof data["waited"] !== "boolean" || !Object.hasOwn(data, "payload")) return bad(place, "an effect is a record with the texts address and kind, a payload, and the truth value waited.");
|
|
11787
|
+
for (const key of Object.keys(data)) if (key !== "address" && key !== "kind" && key !== "payload" && key !== "outcome" && key !== "waited") return bad(place, `an effect has no field ${key}.`);
|
|
11788
|
+
const payload = readTerm(kit, data["payload"], [...place, "payload"]);
|
|
11789
|
+
if (!payload.ok) return payload;
|
|
11790
|
+
const head = {
|
|
11791
|
+
address: data["address"],
|
|
11792
|
+
kind: data["kind"],
|
|
11793
|
+
payload: payload.value,
|
|
11794
|
+
waited: data["waited"]
|
|
11795
|
+
};
|
|
11796
|
+
if (!Object.hasOwn(data, "outcome")) return ok(head);
|
|
11797
|
+
const raw = data["outcome"];
|
|
11798
|
+
const at = [...place, "outcome"];
|
|
11799
|
+
if (isRecord(raw) && raw["ok"] === true && Object.hasOwn(raw, "value") && Object.keys(raw).length === 2) {
|
|
11800
|
+
const value = readTerm(kit, raw["value"], [...at, "value"]);
|
|
11801
|
+
return value.ok ? ok({
|
|
11802
|
+
...head,
|
|
11803
|
+
outcome: ok(value.value)
|
|
11804
|
+
}) : value;
|
|
11805
|
+
}
|
|
11806
|
+
if (isRecord(raw) && raw["ok"] === false && Object.keys(raw).length === 2) {
|
|
11807
|
+
const error = readError(kit, raw["error"], [...at, "error"]);
|
|
11808
|
+
return error.ok ? ok({
|
|
11809
|
+
...head,
|
|
11810
|
+
outcome: {
|
|
11811
|
+
ok: false,
|
|
11812
|
+
error: error.value
|
|
11813
|
+
}
|
|
11814
|
+
}) : error;
|
|
11815
|
+
}
|
|
11816
|
+
return bad(at, "an outcome is a record with ok true and a value, or with ok false and an error.");
|
|
11817
|
+
}
|
|
11818
|
+
/**
|
|
11819
|
+
* This function reads the JSON form of a log again (RUN.REPLAY-JSON). It reads each term document with the formula kit
|
|
11820
|
+
* and the domains of the machine. A cause in the field `exception` becomes an exception with that name and message.
|
|
11821
|
+
* The exception gets its fields and the chain of its causes again, also a cycle (RUN.REPLAY-CAUSES). A list gets a
|
|
11822
|
+
* hole at the place of the mark `dropped`, thus each later item keeps its index (D78). Each share of a list or a record
|
|
11823
|
+
* gives the same object (RUN.REPLAY-SHARED).
|
|
9710
11824
|
*
|
|
9711
|
-
*
|
|
9712
|
-
*
|
|
9713
|
-
* @
|
|
9714
|
-
* @param options - `wait: false` for an evaluation.
|
|
9715
|
-
* @returns The function `perform` for the replay.
|
|
11825
|
+
* @param machine - The machine of the run, or only its domains.
|
|
11826
|
+
* @param data - The JSON form, for example from `JSON.parse` of a golden file.
|
|
11827
|
+
* @returns The log for `replayer`, or `#VALUE!` with the place of the first field that it cannot read.
|
|
9716
11828
|
*/
|
|
9717
|
-
function
|
|
9718
|
-
|
|
9719
|
-
const
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
9723
|
-
|
|
9724
|
-
|
|
9725
|
-
|
|
9726
|
-
}
|
|
9727
|
-
const journal = { next: (all, waiting) => {
|
|
9728
|
-
const items = order.get(all);
|
|
9729
|
-
const item = items?.[0];
|
|
9730
|
-
if (items === void 0 || item === void 0 || !waiting.includes(item)) return void 0;
|
|
9731
|
-
items.shift();
|
|
9732
|
-
return item;
|
|
9733
|
-
} };
|
|
9734
|
-
const perform = (effect) => {
|
|
9735
|
-
const entry = effect.address === void 0 ? void 0 : entries.get(effect.address);
|
|
9736
|
-
const wrong = mismatch(effect, entry);
|
|
9737
|
-
if (wrong !== void 0 || entry === void 0) throw wrong;
|
|
9738
|
-
const outcome = entry.outcome;
|
|
9739
|
-
const failure = outcome === void 0 ? replayError(effect, `The log has no outcome for the effect ${effect.kind} at ${effect.address ?? ""}: the recorded run stopped before the answer.`) : outcome.ok ? void 0 : new Failure(outcome.error);
|
|
9740
|
-
const value = outcome?.ok === true ? outcome.value : void 0;
|
|
9741
|
-
if (wait && entry.waited) return failure === void 0 ? Promise.resolve(value) : Promise.reject(failure);
|
|
9742
|
-
if (failure !== void 0) throw failure;
|
|
9743
|
-
return value;
|
|
11829
|
+
function decodeLog(machine, data) {
|
|
11830
|
+
if (!isRecord(data) || data["version"] !== 1) return bad([], "the form is a record with the version 1.");
|
|
11831
|
+
const list = data["effects"];
|
|
11832
|
+
const order = data["settles"];
|
|
11833
|
+
if (!Array.isArray(list) || !Array.isArray(order) || Object.keys(data).length !== 3) return bad([], "the form has only the version and the lists effects and settles.");
|
|
11834
|
+
const domains = machine.domains;
|
|
11835
|
+
const kit = {
|
|
11836
|
+
...formulaMachine(),
|
|
11837
|
+
...domains === void 0 ? {} : { domains }
|
|
9744
11838
|
};
|
|
9745
|
-
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
|
|
9750
|
-
|
|
9751
|
-
|
|
9752
|
-
|
|
9753
|
-
|
|
9754
|
-
|
|
9755
|
-
|
|
9756
|
-
|
|
11839
|
+
const effects = [];
|
|
11840
|
+
for (let index = 0; index < list.length; index += 1) {
|
|
11841
|
+
const entry = readEntry(kit, list[index], ["effects", index]);
|
|
11842
|
+
if (!entry.ok) return entry;
|
|
11843
|
+
effects.push(entry.value);
|
|
11844
|
+
}
|
|
11845
|
+
const settles = [];
|
|
11846
|
+
for (let index = 0; index < order.length; index += 1) {
|
|
11847
|
+
const settle = order[index];
|
|
11848
|
+
if (!isRecord(settle) || typeof settle["all"] !== "string" || !Number.isInteger(settle["item"]) || Object.keys(settle).length !== 2) return bad(["settles", index], "a settle is a record with only the text all and the whole number item.");
|
|
11849
|
+
settles.push({
|
|
11850
|
+
all: settle["all"],
|
|
11851
|
+
item: settle["item"]
|
|
11852
|
+
});
|
|
11853
|
+
}
|
|
11854
|
+
return ok({
|
|
11855
|
+
effects,
|
|
11856
|
+
settles
|
|
11857
|
+
});
|
|
9757
11858
|
}
|
|
9758
11859
|
//#endregion
|
|
9759
11860
|
//#region src/agree.ts
|
|
@@ -9762,7 +11863,8 @@ const DEFAULT_TIERS = /* @__PURE__ */ Object.freeze([
|
|
|
9762
11863
|
"reference",
|
|
9763
11864
|
"traced",
|
|
9764
11865
|
"again",
|
|
9765
|
-
"folds"
|
|
11866
|
+
"folds",
|
|
11867
|
+
"fixed"
|
|
9766
11868
|
]);
|
|
9767
11869
|
/**
|
|
9768
11870
|
* This function tells if two values are the same for `agree`. Lists and plain records compare item by item and field
|
|
@@ -9791,6 +11893,7 @@ function sameInTiers(x, y) {
|
|
|
9791
11893
|
if (x instanceof Held || y instanceof Held) return x instanceof Held && y instanceof Held && errorDiffers(x.error, y.error, false) === void 0;
|
|
9792
11894
|
}
|
|
9793
11895
|
const CLOSURE = /* @__PURE__ */ Symbol.for("coeval.closure");
|
|
11896
|
+
/** This function gives the parameters of a closure of `fn`, or `undefined` for another function. */
|
|
9794
11897
|
function closureParams(fn) {
|
|
9795
11898
|
return fn[CLOSURE]?.params;
|
|
9796
11899
|
}
|
|
@@ -9830,6 +11933,67 @@ function effectsDiffer(a, b, options) {
|
|
|
9830
11933
|
}
|
|
9831
11934
|
}
|
|
9832
11935
|
/**
|
|
11936
|
+
* This function compares the plan with the tier `fixed` (PLAN.FIXED). The tier declares each name of plain data fixed,
|
|
11937
|
+
* with a frozen copy of its value. It evaluates with these copies, thus the folds with fixed names give the result.
|
|
11938
|
+
* Then it evaluates with the context of `agree`, thus the code without the fold gives the result. A fold moves the
|
|
11939
|
+
* calls of the host to the compilation, thus only the result and the effects compare.
|
|
11940
|
+
*/
|
|
11941
|
+
function fixedDiffers(machine, doc, context, result, effects, given, options) {
|
|
11942
|
+
const { perform, ...declared } = given;
|
|
11943
|
+
const held = fixedContext(context);
|
|
11944
|
+
const plan = prepare(machine, doc, {
|
|
11945
|
+
...declared,
|
|
11946
|
+
fixed: { names: held.fixed }
|
|
11947
|
+
});
|
|
11948
|
+
for (const each of [held.context, context]) {
|
|
11949
|
+
const tierEffects = [];
|
|
11950
|
+
const other = plan.evaluate(each, {
|
|
11951
|
+
effects: tierEffects,
|
|
11952
|
+
...perform === void 0 ? {} : { perform }
|
|
11953
|
+
});
|
|
11954
|
+
options.observe?.();
|
|
11955
|
+
if (overflowed(result) || overflowed(other)) continue;
|
|
11956
|
+
const reason = resultDiffers(result, other, options) ?? effectsDiffer(effects, tierEffects, options);
|
|
11957
|
+
if (reason !== void 0) return {
|
|
11958
|
+
other,
|
|
11959
|
+
reason
|
|
11960
|
+
};
|
|
11961
|
+
}
|
|
11962
|
+
}
|
|
11963
|
+
/**
|
|
11964
|
+
* This function gives the context of the tier `fixed`. Each name of plain data gets a deeply frozen copy of its value,
|
|
11965
|
+
* and the record of the fixed names holds the same copy. Each other name keeps its value.
|
|
11966
|
+
*/
|
|
11967
|
+
function fixedContext(context) {
|
|
11968
|
+
const given = context.names;
|
|
11969
|
+
const names = Object.create(Object.getPrototypeOf(given));
|
|
11970
|
+
const fixed = Object.create(null);
|
|
11971
|
+
for (const key of Reflect.ownKeys(given)) {
|
|
11972
|
+
let field = Object.getOwnPropertyDescriptor(given, key);
|
|
11973
|
+
if (typeof key === "string" && "value" in field) {
|
|
11974
|
+
const copy = sharedCopy(field.value);
|
|
11975
|
+
if (copy !== void 0 || field.value === void 0) {
|
|
11976
|
+
Object.defineProperty(fixed, key, {
|
|
11977
|
+
value: copy,
|
|
11978
|
+
enumerable: true
|
|
11979
|
+
});
|
|
11980
|
+
field = {
|
|
11981
|
+
...field,
|
|
11982
|
+
value: copy
|
|
11983
|
+
};
|
|
11984
|
+
}
|
|
11985
|
+
}
|
|
11986
|
+
Object.defineProperty(names, key, field);
|
|
11987
|
+
}
|
|
11988
|
+
return {
|
|
11989
|
+
context: {
|
|
11990
|
+
...context,
|
|
11991
|
+
names
|
|
11992
|
+
},
|
|
11993
|
+
fixed
|
|
11994
|
+
};
|
|
11995
|
+
}
|
|
11996
|
+
/**
|
|
9833
11997
|
* This function evaluates a document with a plan and with other tiers, and compares the results (PLAN.AGREES). Two
|
|
9834
11998
|
* results agree when they have the same value by `sameValue`, or errors with the same code, and the same place when
|
|
9835
11999
|
* both have one. The effects must have the same kinds, places and payloads, in the same order. A language gives its
|
|
@@ -9886,6 +12050,19 @@ function agree(machine, doc, context = makeContext(), options = {}) {
|
|
|
9886
12050
|
});
|
|
9887
12051
|
other = traced.result;
|
|
9888
12052
|
otherEffects = [...traced.effects];
|
|
12053
|
+
} else if (tier === "fixed") {
|
|
12054
|
+
const why = fixedDiffers(machine, doc, context, result, effects, {
|
|
12055
|
+
...declared,
|
|
12056
|
+
...mock
|
|
12057
|
+
}, options);
|
|
12058
|
+
if (why !== void 0) return {
|
|
12059
|
+
ok: false,
|
|
12060
|
+
tier,
|
|
12061
|
+
plan: result,
|
|
12062
|
+
other: why.other,
|
|
12063
|
+
reason: why.reason
|
|
12064
|
+
};
|
|
12065
|
+
continue;
|
|
9889
12066
|
} else if (tier === "folds") {
|
|
9890
12067
|
other = prepare(machine, doc, {
|
|
9891
12068
|
...declared,
|
|
@@ -9950,18 +12127,12 @@ function agree(machine, doc, context = makeContext(), options = {}) {
|
|
|
9950
12127
|
function watched(perform) {
|
|
9951
12128
|
const effects = [];
|
|
9952
12129
|
const settles = [];
|
|
9953
|
-
const
|
|
9954
|
-
const settled = (all, item) => {
|
|
12130
|
+
const journal = relay(perform, (all, item) => {
|
|
9955
12131
|
settles.push({
|
|
9956
12132
|
all,
|
|
9957
12133
|
item
|
|
9958
12134
|
});
|
|
9959
|
-
|
|
9960
|
-
};
|
|
9961
|
-
const journal = inner?.next === void 0 ? { settled } : {
|
|
9962
|
-
settled,
|
|
9963
|
-
next: (all, waiting) => inner.next?.(all, waiting)
|
|
9964
|
-
};
|
|
12135
|
+
});
|
|
9965
12136
|
const host = (effect) => {
|
|
9966
12137
|
effects.push(effect);
|
|
9967
12138
|
return perform(effect);
|
|
@@ -9983,6 +12154,82 @@ function settlesDiffer(a, b) {
|
|
|
9983
12154
|
if (x?.all !== y?.all || x?.item !== y?.item) return `The wait ${index} settles ${settleText(x)} in the plan, and ${settleText(y)} in the tier.`;
|
|
9984
12155
|
}
|
|
9985
12156
|
}
|
|
12157
|
+
/** The roots beside `self` that a value without static reads keeps whole (READS.KEPT). */
|
|
12158
|
+
const KEPT_ROOTS = /* @__PURE__ */ Object.freeze([
|
|
12159
|
+
"names",
|
|
12160
|
+
"world",
|
|
12161
|
+
"event",
|
|
12162
|
+
"caps"
|
|
12163
|
+
]);
|
|
12164
|
+
/**
|
|
12165
|
+
* This function tells if the plan does not know the reads of a run. A value without static reads keeps each root
|
|
12166
|
+
* (READS.KEPT), thus the list holds a kept read of each whole root.
|
|
12167
|
+
*/
|
|
12168
|
+
function readsUnknown(reads) {
|
|
12169
|
+
return KEPT_ROOTS.every((root) => reads.some((read) => read.kept === true && read.path.length === 1 && read.path[0] === root));
|
|
12170
|
+
}
|
|
12171
|
+
/**
|
|
12172
|
+
* This function gives the reads that `agreeRun` compares: the free reads of the resolutions (RUN.AGREES-READS). A
|
|
12173
|
+
* kept read is the bound of the plan for the later reads of a value, and the reference has no such bound.
|
|
12174
|
+
*/
|
|
12175
|
+
function resolutions(reads) {
|
|
12176
|
+
return reads.filter((read) => read.free && read.kept !== true);
|
|
12177
|
+
}
|
|
12178
|
+
function pathText(path) {
|
|
12179
|
+
if (path === void 0) return "no read";
|
|
12180
|
+
try {
|
|
12181
|
+
return JSON.stringify(path);
|
|
12182
|
+
} catch {
|
|
12183
|
+
return path.map(String).join(", ");
|
|
12184
|
+
}
|
|
12185
|
+
}
|
|
12186
|
+
/**
|
|
12187
|
+
* This function gives the rule of a comparison of reads. The rule of the caller answers first. Each tier makes its
|
|
12188
|
+
* own nodes of a tree and its own adopted things. Thus two host things are the same when they have the same label,
|
|
12189
|
+
* and they are nodes or have the same JSON object.
|
|
12190
|
+
*/
|
|
12191
|
+
function sameThing(equal) {
|
|
12192
|
+
return (a, b) => {
|
|
12193
|
+
const told = equal?.(a, b);
|
|
12194
|
+
if (told !== void 0) return told;
|
|
12195
|
+
if (!isHostThing(a) || !isHostThing(b)) return void 0;
|
|
12196
|
+
if (a.label !== b.label) return false;
|
|
12197
|
+
if (a.label.startsWith("node ")) return true;
|
|
12198
|
+
try {
|
|
12199
|
+
return Object.is(a.toJson(), b.toJson());
|
|
12200
|
+
} catch {
|
|
12201
|
+
return false;
|
|
12202
|
+
}
|
|
12203
|
+
};
|
|
12204
|
+
}
|
|
12205
|
+
/** This function gives the difference of what two reads reached, or `undefined` when they agree. */
|
|
12206
|
+
function reachedDiffers(a, b, equal, strict) {
|
|
12207
|
+
if (a.ok && b.ok) return sameValue(a.value, b.value, equal) ? void 0 : "The read reaches another value in the tier.";
|
|
12208
|
+
if (!a.ok && !b.ok) {
|
|
12209
|
+
if (a.error.code !== b.error.code) return `The read gives ${a.error.code} in the plan, and ${b.error.code} in the tier.`;
|
|
12210
|
+
return strict && a.error.reason !== b.error.reason ? `The read gives ${a.error.code} with another reason in the tier: "${b.error.reason}" for "${a.error.reason}".` : void 0;
|
|
12211
|
+
}
|
|
12212
|
+
if (!b.ok) return `The read reaches a value in the plan, and ${b.error.code} in the tier.`;
|
|
12213
|
+
return `The read gives ${a.error.code} in the plan, and a value in the tier.`;
|
|
12214
|
+
}
|
|
12215
|
+
/**
|
|
12216
|
+
* This function gives the first difference of the reads of the plan and of a tier, or `undefined` when they agree
|
|
12217
|
+
* (RUN.AGREES-READS). Only the free reads of the resolutions compare, in their order. When the plan does not know its
|
|
12218
|
+
* reads, the function compares nothing.
|
|
12219
|
+
*/
|
|
12220
|
+
function readsDiffer(plan, tier, options) {
|
|
12221
|
+
if (readsUnknown(plan)) return void 0;
|
|
12222
|
+
const a = resolutions(plan);
|
|
12223
|
+
const b = resolutions(tier);
|
|
12224
|
+
const equal = sameThing(options.equal);
|
|
12225
|
+
for (let index = 0; index < Math.max(a.length, b.length); index += 1) {
|
|
12226
|
+
const x = a[index];
|
|
12227
|
+
const y = b[index];
|
|
12228
|
+
if (x === void 0 || y === void 0 || !sameValue(x.path, y.path, equal)) return `The free read ${index} is ${pathText(x?.path)} in the plan, and ${pathText(y?.path)} in the tier.`;
|
|
12229
|
+
const why = reachedDiffers(x.reached, y.reached, equal, options.strict === true);
|
|
12230
|
+
if (why !== void 0) return `The free read ${index} of ${pathText(x.path)} differs. ${why}`;
|
|
12231
|
+
}
|
|
12232
|
+
}
|
|
9986
12233
|
/**
|
|
9987
12234
|
* This function compares the run of a document in a plan with its run in other tiers (RUN.AGREES). First the plan
|
|
9988
12235
|
* starts the run with a recorder around the host. Then each tier replays the log of that run. These tiers are the
|
|
@@ -9991,6 +12238,13 @@ function settlesDiffer(a, b) {
|
|
|
9991
12238
|
* The rules of `agree` compare the results and the effects, with their addresses and outcomes. A tier must also
|
|
9992
12239
|
* settle the waits of each `all` in the order of the plan, thus a replay keeps the order of the waits.
|
|
9993
12240
|
*
|
|
12241
|
+
* The reference run must also give the free reads of the plan, in their order (RUN.AGREES-READS). Another replay of
|
|
12242
|
+
* the plan records these reads. A kept read does not compare. When the plan keeps each root, its reads are not known,
|
|
12243
|
+
* and no read compares.
|
|
12244
|
+
*
|
|
12245
|
+
* One replayer of the log serves each tier and the replay for the reads (RUN.AGREES-ONE-REPLAYER). The replayer keeps
|
|
12246
|
+
* no state of a run (RUN.REPLAY-RUNS), thus each tier replays the same log as with a replayer of its own.
|
|
12247
|
+
*
|
|
9994
12248
|
* @param machine - The world, the evaluators and the runners.
|
|
9995
12249
|
* @param doc - The document.
|
|
9996
12250
|
* @param context - The names and the capabilities at the root.
|
|
@@ -10014,16 +12268,24 @@ async function agreeRun(machine, doc, context = makeContext(), options = {}) {
|
|
|
10014
12268
|
const result = await plan.run(context, { perform: first.perform });
|
|
10015
12269
|
const saw = observe?.();
|
|
10016
12270
|
const log = recording.log();
|
|
12271
|
+
let planReads;
|
|
12272
|
+
let compared;
|
|
12273
|
+
const replay = replayer(log);
|
|
10017
12274
|
for (const tier of options.tiers ?? DEFAULT_TIERS) {
|
|
10018
|
-
|
|
10019
|
-
const
|
|
12275
|
+
if (tier === "fixed") continue;
|
|
12276
|
+
const tierReplay = watched(replay);
|
|
12277
|
+
const host = { perform: tierReplay.perform };
|
|
10020
12278
|
let other;
|
|
10021
|
-
|
|
10022
|
-
|
|
10023
|
-
|
|
10024
|
-
|
|
10025
|
-
|
|
10026
|
-
|
|
12279
|
+
let tierReads;
|
|
12280
|
+
if (tier === "reference") {
|
|
12281
|
+
tierReads = [];
|
|
12282
|
+
other = await referenceRun(machine, doc, context, {
|
|
12283
|
+
...declared,
|
|
12284
|
+
...host,
|
|
12285
|
+
reads: tierReads,
|
|
12286
|
+
...options.definitions === void 0 ? {} : { definitions: options.definitions }
|
|
12287
|
+
});
|
|
12288
|
+
} else if (tier === "traced") other = (await run(machine, doc, context, {
|
|
10027
12289
|
...declared,
|
|
10028
12290
|
...host
|
|
10029
12291
|
})).result;
|
|
@@ -10035,7 +12297,20 @@ async function agreeRun(machine, doc, context = makeContext(), options = {}) {
|
|
|
10035
12297
|
const compareCalls = observe !== void 0 && tier !== "folds";
|
|
10036
12298
|
const calls = observe?.();
|
|
10037
12299
|
if (overflowed(result) || overflowed(other)) continue;
|
|
10038
|
-
|
|
12300
|
+
let why = resultDiffers(result, other, options) ?? effectsDiffer(first.effects, tierReplay.effects, options) ?? settlesDiffer(first.settles, tierReplay.settles) ?? (compareCalls && !sameValue(saw, calls, options.equal) ? "The host saw other calls in the tier." : void 0);
|
|
12301
|
+
if (why === void 0 && tierReads !== void 0) {
|
|
12302
|
+
if (planReads === void 0) {
|
|
12303
|
+
const reads = [];
|
|
12304
|
+
await plan.run(context, {
|
|
12305
|
+
perform: replay,
|
|
12306
|
+
reads
|
|
12307
|
+
});
|
|
12308
|
+
observe?.();
|
|
12309
|
+
planReads = reads;
|
|
12310
|
+
}
|
|
12311
|
+
why = readsDiffer(planReads, tierReads, options);
|
|
12312
|
+
if (why === void 0 && !readsUnknown(planReads)) compared = resolutions(planReads);
|
|
12313
|
+
}
|
|
10039
12314
|
if (why !== void 0) return {
|
|
10040
12315
|
ok: false,
|
|
10041
12316
|
tier,
|
|
@@ -10044,10 +12319,15 @@ async function agreeRun(machine, doc, context = makeContext(), options = {}) {
|
|
|
10044
12319
|
reason: why
|
|
10045
12320
|
};
|
|
10046
12321
|
}
|
|
10047
|
-
return {
|
|
12322
|
+
return compared === void 0 ? {
|
|
10048
12323
|
ok: true,
|
|
10049
12324
|
result,
|
|
10050
12325
|
effects: first.effects
|
|
12326
|
+
} : {
|
|
12327
|
+
ok: true,
|
|
12328
|
+
result,
|
|
12329
|
+
effects: first.effects,
|
|
12330
|
+
reads: compared
|
|
10051
12331
|
};
|
|
10052
12332
|
}
|
|
10053
12333
|
//#endregion
|
|
@@ -10466,7 +12746,8 @@ const ORDER = [
|
|
|
10466
12746
|
"C7",
|
|
10467
12747
|
"C8",
|
|
10468
12748
|
"local",
|
|
10469
|
-
"agree"
|
|
12749
|
+
"agree",
|
|
12750
|
+
"truth"
|
|
10470
12751
|
];
|
|
10471
12752
|
/** A function in the results of the probe parts. It gives the count of its arguments. */
|
|
10472
12753
|
const PROBE_FN = (...args) => args.length;
|
|
@@ -11344,14 +13625,41 @@ function examine(env, sample, contract) {
|
|
|
11344
13625
|
if (env.runner) for (const scenario of scenariosOf(env, sample, contract, true)) checkShot(env, sample, contract, scenario, shoot(env, code, sample, scenario));
|
|
11345
13626
|
}
|
|
11346
13627
|
/**
|
|
13628
|
+
* This function checks the declared truth of a profile (D86). The function `truthy` must give `true` or `false` for
|
|
13629
|
+
* each member of `truthType`, and it must not throw. The members come from the lists `values` of the domains of the
|
|
13630
|
+
* type. A type without such a list gets the values of the results of the palette that are its members.
|
|
13631
|
+
*/
|
|
13632
|
+
function checkTruth(env, code, probe) {
|
|
13633
|
+
const truth = declaredTruthOf(code);
|
|
13634
|
+
if (truth === void 0) return;
|
|
13635
|
+
const domains = env.machine.domains;
|
|
13636
|
+
if (typeIssues(truth.truthType, domains).length > 0) return;
|
|
13637
|
+
const samples = env.results.flatMap((result) => result.ok ? [result.value] : []);
|
|
13638
|
+
const of = (value) => `the member ${valueText(value)} of the truth type ${typeText(truth.truthType)}`;
|
|
13639
|
+
for (const value of membersOf(truth.truthType, domains, samples)) {
|
|
13640
|
+
let given;
|
|
13641
|
+
try {
|
|
13642
|
+
given = truth.truthy(value);
|
|
13643
|
+
} catch (error) {
|
|
13644
|
+
report(env, "truth", `The function truthy of the profile throws for ${of(value)}: ${describe(error)}`, () => probe(value));
|
|
13645
|
+
return;
|
|
13646
|
+
}
|
|
13647
|
+
if (typeof given === "boolean") continue;
|
|
13648
|
+
report(env, "truth", `The function truthy of the profile gives ${given === void 0 ? "no truth" : valueText(given)} for ${of(value)}. A truth is true or false.`, () => probe(value));
|
|
13649
|
+
return;
|
|
13650
|
+
}
|
|
13651
|
+
}
|
|
13652
|
+
/**
|
|
11347
13653
|
* This function examines one evaluator or runner of a machine against the obligations of host code (K92, D36). It
|
|
11348
13654
|
* starts the code directly, without the kernel, with probe parts and a probe runtime. It generates probe documents from
|
|
11349
13655
|
* the contract of each kind that names the code, and it gives the probe parts chosen and random results.
|
|
11350
13656
|
*
|
|
11351
13657
|
* The checks follow the obligations C1 to C8 of `docs/SEMANTICS.md`. Two more checks follow the premises of the
|
|
11352
13658
|
* simulation of the plan. The check `local` finds state that stays from one call to the next. The check `agree` compares
|
|
11353
|
-
* compiled code with its definition. The
|
|
11354
|
-
*
|
|
13659
|
+
* compiled code with its definition. The finding of an obligation names the first probe that breaks it. An empty list
|
|
13660
|
+
* means that each probe conforms.
|
|
13661
|
+
*
|
|
13662
|
+
* The check `truth` tests the declared truth of a formula profile (D86).
|
|
11355
13663
|
*
|
|
11356
13664
|
* @param machine - The machine with the code and the world of the kinds.
|
|
11357
13665
|
* @param name - The name of the evaluator or of the runner. A runner wins over an evaluator of the same name.
|
|
@@ -11382,14 +13690,34 @@ function conforms(machine, name, options = {}) {
|
|
|
11382
13690
|
found: /* @__PURE__ */ new Map()
|
|
11383
13691
|
};
|
|
11384
13692
|
const types = options.types ?? typesNaming(machine.world, name, verb);
|
|
13693
|
+
let first;
|
|
11385
13694
|
if (types.length === 0) throw new Error(`No kind of the world names ${name} for the verb ${verb}. Give the types of the probe documents in the option types.`);
|
|
11386
13695
|
for (const type of types) {
|
|
11387
13696
|
const found = lookupKind(machine.world, type);
|
|
11388
13697
|
if (!found.ok) throw new Error(`The world has no kind for the type ${typeName(type)}.`);
|
|
11389
13698
|
const contract = contractOf(env, found.kind, found.path, options.caps);
|
|
11390
13699
|
const extra = own(options.values, typeName(type)) ?? own(options.values, found.path.join(".")) ?? [];
|
|
11391
|
-
for (const sample of samplesOf(type, found.path, found.kind, contract, extra))
|
|
13700
|
+
for (const sample of samplesOf(type, found.path, found.kind, contract, extra)) {
|
|
13701
|
+
first ??= sample;
|
|
13702
|
+
examine(env, sample, contract);
|
|
13703
|
+
}
|
|
11392
13704
|
}
|
|
13705
|
+
checkTruth(env, code, (value) => first === void 0 ? {
|
|
13706
|
+
type: typeName(types[0]),
|
|
13707
|
+
doc: {
|
|
13708
|
+
type: types[0],
|
|
13709
|
+
value: null
|
|
13710
|
+
},
|
|
13711
|
+
results: {},
|
|
13712
|
+
waits: [],
|
|
13713
|
+
caps: NO_CAPS,
|
|
13714
|
+
running: runner
|
|
13715
|
+
} : probeOf(first, {
|
|
13716
|
+
results: first.split.parts.map(() => ok(value)),
|
|
13717
|
+
waits: [],
|
|
13718
|
+
caps: NO_CAPS,
|
|
13719
|
+
running: runner
|
|
13720
|
+
}));
|
|
11393
13721
|
return ORDER.filter((obligation) => env.found.has(obligation)).map((obligation) => env.found.get(obligation));
|
|
11394
13722
|
}
|
|
11395
13723
|
//#endregion
|
|
@@ -11494,6 +13822,22 @@ function verifyReads(target, reads, context, options = {}) {
|
|
|
11494
13822
|
read,
|
|
11495
13823
|
now: stop
|
|
11496
13824
|
};
|
|
13825
|
+
if (read.kept === true) {
|
|
13826
|
+
const now = keptAgain(machine, root, read.path);
|
|
13827
|
+
if (now === void 0) return {
|
|
13828
|
+
same: false,
|
|
13829
|
+
index,
|
|
13830
|
+
read,
|
|
13831
|
+
now: fail("#N/A", "The host does not know the value of the kept read yet.")
|
|
13832
|
+
};
|
|
13833
|
+
if (!sameResult(read.reached, now, equal)) return {
|
|
13834
|
+
same: false,
|
|
13835
|
+
index,
|
|
13836
|
+
read,
|
|
13837
|
+
now
|
|
13838
|
+
};
|
|
13839
|
+
continue;
|
|
13840
|
+
}
|
|
11497
13841
|
const asked = read.hops !== void 0;
|
|
11498
13842
|
const now = readAgain(machine, root, read.path, asked);
|
|
11499
13843
|
if (!sameResult(read.reached, now.reached, equal) || read.hops !== void 0 && !sameHops(read.hops, now.hops, equal)) return {
|
|
@@ -11766,7 +14110,7 @@ function cyclic(graph, members) {
|
|
|
11766
14110
|
return false;
|
|
11767
14111
|
}
|
|
11768
14112
|
/** This function gives the names of the nodes of a component, without the node `all`. */
|
|
11769
|
-
function named(graph, members) {
|
|
14113
|
+
function named$1(graph, members) {
|
|
11770
14114
|
const out = [];
|
|
11771
14115
|
for (const member of members) if (member !== graph.all) out.push(graph.ids[member]);
|
|
11772
14116
|
return out;
|
|
@@ -11805,7 +14149,7 @@ function components(graph, options = {}) {
|
|
|
11805
14149
|
const index = indexed(graph, options);
|
|
11806
14150
|
const out = [];
|
|
11807
14151
|
for (const members of strong(index).list) {
|
|
11808
|
-
const names = named(index, members);
|
|
14152
|
+
const names = named$1(index, members);
|
|
11809
14153
|
if (names.length > 0) out.push(names);
|
|
11810
14154
|
}
|
|
11811
14155
|
return out;
|
|
@@ -11912,7 +14256,7 @@ function cycles(graph) {
|
|
|
11912
14256
|
const out = [];
|
|
11913
14257
|
found.list.forEach((members, component) => {
|
|
11914
14258
|
if (!cyclic(every, members)) return;
|
|
11915
|
-
const nodes = named(every, members);
|
|
14259
|
+
const nodes = named$1(every, members);
|
|
11916
14260
|
const certain = inside.get(component) ?? [];
|
|
11917
14261
|
if (certain.length === 1 && certain[0]?.length === nodes.length) {
|
|
11918
14262
|
out.push({
|
|
@@ -11926,7 +14270,7 @@ function cycles(graph) {
|
|
|
11926
14270
|
certain: false
|
|
11927
14271
|
});
|
|
11928
14272
|
for (const part of certain) out.push({
|
|
11929
|
-
nodes: named(eager, part),
|
|
14273
|
+
nodes: named$1(eager, part),
|
|
11930
14274
|
certain: true
|
|
11931
14275
|
});
|
|
11932
14276
|
});
|
|
@@ -12168,10 +14512,12 @@ function schedule(machine, options = {}) {
|
|
|
12168
14512
|
};
|
|
12169
14513
|
/**
|
|
12170
14514
|
* This function gives the state of the current data epoch. Each data epoch has a new host thing at the root
|
|
12171
|
-
* `world`. Thus a read that reaches the whole root changes at each change of data (READS.KEPT).
|
|
14515
|
+
* `world`. Thus a read that reaches the whole root changes at each change of data (READS.KEPT). A kept read of a
|
|
14516
|
+
* closure reads a node through `known`, without a demand (SCHED.CLOSURE).
|
|
12172
14517
|
*/
|
|
12173
14518
|
const current = () => {
|
|
12174
14519
|
if (now !== void 0) return now;
|
|
14520
|
+
const values = /* @__PURE__ */ new Map();
|
|
12175
14521
|
const nodesThing = {
|
|
12176
14522
|
[THING]: true,
|
|
12177
14523
|
label: "nodes",
|
|
@@ -12183,14 +14529,20 @@ function schedule(machine, options = {}) {
|
|
|
12183
14529
|
return result.value;
|
|
12184
14530
|
},
|
|
12185
14531
|
has: (key) => docs.has(String(key)),
|
|
12186
|
-
toJson: record
|
|
14532
|
+
toJson: record,
|
|
14533
|
+
known(key) {
|
|
14534
|
+
const result = values.get(String(key));
|
|
14535
|
+
if (result === void 0) return void 0;
|
|
14536
|
+
if (!result.ok) throw new Failure(result.error);
|
|
14537
|
+
return { thing: result.value };
|
|
14538
|
+
}
|
|
12187
14539
|
};
|
|
12188
14540
|
now = {
|
|
12189
14541
|
context: rootContext(machine, {
|
|
12190
14542
|
...base,
|
|
12191
14543
|
world: nodesThing
|
|
12192
14544
|
}),
|
|
12193
|
-
values
|
|
14545
|
+
values
|
|
12194
14546
|
};
|
|
12195
14547
|
return now;
|
|
12196
14548
|
};
|
|
@@ -12265,6 +14617,406 @@ function schedule(machine, options = {}) {
|
|
|
12265
14617
|
};
|
|
12266
14618
|
}
|
|
12267
14619
|
//#endregion
|
|
14620
|
+
//#region src/meaning.ts
|
|
14621
|
+
/** A list of eight reference types. The generator then gives more references, thus more reads of nodes. */
|
|
14622
|
+
function moreRefs() {
|
|
14623
|
+
return Array.from({ length: 8 }, () => ["ref"]);
|
|
14624
|
+
}
|
|
14625
|
+
/** The default options of the document generator, for `STD_MACHINE`. The kinds `log` and `write` need a capability. */
|
|
14626
|
+
function standardGenerate() {
|
|
14627
|
+
return {
|
|
14628
|
+
maxSize: 16,
|
|
14629
|
+
types: [
|
|
14630
|
+
...typesOf(STD_MACHINE.world).filter((type) => type[0] !== "log" && type[0] !== "write"),
|
|
14631
|
+
...moreRefs(),
|
|
14632
|
+
["if"],
|
|
14633
|
+
["add"]
|
|
14634
|
+
],
|
|
14635
|
+
data: (type, rng) => {
|
|
14636
|
+
const name = type.join(".");
|
|
14637
|
+
if (name === "number") return rng.chance(.9) ? rng.pick([
|
|
14638
|
+
0,
|
|
14639
|
+
1,
|
|
14640
|
+
2,
|
|
14641
|
+
3.5,
|
|
14642
|
+
-1
|
|
14643
|
+
]) : "1";
|
|
14644
|
+
if (name === "text") return rng.chance(.9) ? rng.pick([
|
|
14645
|
+
"",
|
|
14646
|
+
"a",
|
|
14647
|
+
"b"
|
|
14648
|
+
]) : 1;
|
|
14649
|
+
if (name === "bool") return rng.chance(.9) ? rng.chance(.5) : 0;
|
|
14650
|
+
if (name === "nearest") return rng.pick([{ type: "panel" }, "panel"]);
|
|
14651
|
+
}
|
|
14652
|
+
};
|
|
14653
|
+
}
|
|
14654
|
+
/** The default `wire`, for `STD_MACHINE`. A list evaluates each part, also after a failed part. */
|
|
14655
|
+
function standardWire(rng, node) {
|
|
14656
|
+
if (rng.chance(.5)) return {
|
|
14657
|
+
type: "list",
|
|
14658
|
+
value: [node(), node()]
|
|
14659
|
+
};
|
|
14660
|
+
return rng.chance(.5) ? {
|
|
14661
|
+
type: "add",
|
|
14662
|
+
value: [node(), 1]
|
|
14663
|
+
} : node();
|
|
14664
|
+
}
|
|
14665
|
+
/** The default values of the names of the host. Some are equal by structure, and some are host values. */
|
|
14666
|
+
function standardValues() {
|
|
14667
|
+
return [
|
|
14668
|
+
0,
|
|
14669
|
+
1,
|
|
14670
|
+
2,
|
|
14671
|
+
-0,
|
|
14672
|
+
NaN,
|
|
14673
|
+
"two",
|
|
14674
|
+
null,
|
|
14675
|
+
void 0,
|
|
14676
|
+
[1, 2],
|
|
14677
|
+
{ a: 1 },
|
|
14678
|
+
{ a: 2 },
|
|
14679
|
+
{
|
|
14680
|
+
type: "ref",
|
|
14681
|
+
value: ["names", "a"]
|
|
14682
|
+
},
|
|
14683
|
+
new Held({
|
|
14684
|
+
code: "#HELD",
|
|
14685
|
+
reason: "The binding failed."
|
|
14686
|
+
})
|
|
14687
|
+
];
|
|
14688
|
+
}
|
|
14689
|
+
/** The default contexts of the host at the start of a run. */
|
|
14690
|
+
function standardContexts() {
|
|
14691
|
+
return [context({
|
|
14692
|
+
names: {
|
|
14693
|
+
a: 1,
|
|
14694
|
+
b: [1, "two"],
|
|
14695
|
+
x: { a: 1 },
|
|
14696
|
+
n: 2
|
|
14697
|
+
},
|
|
14698
|
+
caps: ["log"]
|
|
14699
|
+
}), context({
|
|
14700
|
+
names: {
|
|
14701
|
+
a: "two",
|
|
14702
|
+
b: 0,
|
|
14703
|
+
x: null
|
|
14704
|
+
},
|
|
14705
|
+
self: { a: 1 },
|
|
14706
|
+
event: { a: 2 }
|
|
14707
|
+
})];
|
|
14708
|
+
}
|
|
14709
|
+
/** This function puts a field on a record, also the field `__proto__`. */
|
|
14710
|
+
function put(target, key, value) {
|
|
14711
|
+
Object.defineProperty(target, key, {
|
|
14712
|
+
value,
|
|
14713
|
+
enumerable: true,
|
|
14714
|
+
writable: true,
|
|
14715
|
+
configurable: true
|
|
14716
|
+
});
|
|
14717
|
+
}
|
|
14718
|
+
/** This function changes each path from `names` to a node into a path from `world`. Some paths read the whole root. */
|
|
14719
|
+
function linked(value, rng, nodes) {
|
|
14720
|
+
if (Array.isArray(value)) {
|
|
14721
|
+
const items = value.map((item) => linked(item, rng, nodes));
|
|
14722
|
+
if (items[0] === "names" && typeof items[1] === "string" && nodes.includes(items[1])) return rng.chance(.03) ? ["world"] : ["world", ...items.slice(1)];
|
|
14723
|
+
return items;
|
|
14724
|
+
}
|
|
14725
|
+
if (typeof value !== "object" || value === null) return value;
|
|
14726
|
+
const out = {};
|
|
14727
|
+
for (const key of Object.keys(value)) put(out, key, linked(value[key], rng, nodes));
|
|
14728
|
+
return out;
|
|
14729
|
+
}
|
|
14730
|
+
/** This function gives new names of the host, with one or two changes. */
|
|
14731
|
+
function change(base, rng, names, values) {
|
|
14732
|
+
const out = { ...base.names };
|
|
14733
|
+
for (let step = 1 + rng.int(2); step > 0; step -= 1) {
|
|
14734
|
+
const name = rng.pick(names);
|
|
14735
|
+
if (rng.chance(.15)) delete out[name];
|
|
14736
|
+
else out[name] = rng.pick(values);
|
|
14737
|
+
}
|
|
14738
|
+
return out;
|
|
14739
|
+
}
|
|
14740
|
+
function shuffle(items, rng) {
|
|
14741
|
+
const out = [...items];
|
|
14742
|
+
for (let index = out.length - 1; index > 0; index -= 1) {
|
|
14743
|
+
const other = rng.int(index + 1);
|
|
14744
|
+
[out[index], out[other]] = [out[other], out[index]];
|
|
14745
|
+
}
|
|
14746
|
+
return out;
|
|
14747
|
+
}
|
|
14748
|
+
/**
|
|
14749
|
+
* This function evaluates each node of a graph from scratch, with the meaning of D31. Each node gets a new plan and
|
|
14750
|
+
* one value. The algorithm of Tarjan over the demands finds the cycles. The function keeps nothing between calls.
|
|
14751
|
+
*/
|
|
14752
|
+
function fromScratch(machine, docs, base, declaredType) {
|
|
14753
|
+
const declared = declaredType === void 0 ? {} : { declaredType };
|
|
14754
|
+
const values = /* @__PURE__ */ new Map();
|
|
14755
|
+
const index = /* @__PURE__ */ new Map();
|
|
14756
|
+
const lowest = /* @__PURE__ */ new Map();
|
|
14757
|
+
const component = [];
|
|
14758
|
+
const evaluating = [];
|
|
14759
|
+
const selfReads = /* @__PURE__ */ new Set();
|
|
14760
|
+
const visit = (id) => {
|
|
14761
|
+
const reader = evaluating[evaluating.length - 1];
|
|
14762
|
+
const link = (to) => {
|
|
14763
|
+
if (reader !== void 0) lowest.set(reader, Math.min(lowest.get(reader), to));
|
|
14764
|
+
};
|
|
14765
|
+
const done = values.get(id);
|
|
14766
|
+
if (done !== void 0) {
|
|
14767
|
+
if (component.includes(id)) link(index.get(id));
|
|
14768
|
+
return done;
|
|
14769
|
+
}
|
|
14770
|
+
if (evaluating.includes(id)) {
|
|
14771
|
+
link(index.get(id));
|
|
14772
|
+
if (reader === id) selfReads.add(id);
|
|
14773
|
+
return cycleOf(id);
|
|
14774
|
+
}
|
|
14775
|
+
if (factsOf(machine, docs.get(id), declared).side === "run") {
|
|
14776
|
+
const refused = runSide(id);
|
|
14777
|
+
values.set(id, refused);
|
|
14778
|
+
return refused;
|
|
14779
|
+
}
|
|
14780
|
+
index.set(id, index.size);
|
|
14781
|
+
lowest.set(id, index.get(id));
|
|
14782
|
+
component.push(id);
|
|
14783
|
+
evaluating.push(id);
|
|
14784
|
+
let result = prepare(machine, docs.get(id), declared).evaluate(ctx);
|
|
14785
|
+
evaluating.pop();
|
|
14786
|
+
if (lowest.get(id) < index.get(id)) result = cycleOf(id);
|
|
14787
|
+
else if (component.splice(component.indexOf(id)).length > 1 || selfReads.has(id)) result = cycleOf(id);
|
|
14788
|
+
values.set(id, result);
|
|
14789
|
+
link(lowest.get(id));
|
|
14790
|
+
return result;
|
|
14791
|
+
};
|
|
14792
|
+
const world = {
|
|
14793
|
+
[THING]: true,
|
|
14794
|
+
label: "nodes",
|
|
14795
|
+
deref: (key) => {
|
|
14796
|
+
if (!docs.has(String(key))) return void 0;
|
|
14797
|
+
const result = visit(String(key));
|
|
14798
|
+
if (!result.ok) throw new Failure(result.error);
|
|
14799
|
+
return result.value;
|
|
14800
|
+
},
|
|
14801
|
+
has: (key) => docs.has(String(key)),
|
|
14802
|
+
toJson: () => {
|
|
14803
|
+
const out = {};
|
|
14804
|
+
for (const id of [...docs.keys()].sort()) {
|
|
14805
|
+
if (factsOf(machine, docs.get(id), declared).side === "run") continue;
|
|
14806
|
+
const result = visit(id);
|
|
14807
|
+
if (!result.ok) throw new Failure(result.error);
|
|
14808
|
+
put(out, id, result.value);
|
|
14809
|
+
}
|
|
14810
|
+
return out;
|
|
14811
|
+
}
|
|
14812
|
+
};
|
|
14813
|
+
const ctx = {
|
|
14814
|
+
...base,
|
|
14815
|
+
world
|
|
14816
|
+
};
|
|
14817
|
+
for (const id of [...docs.keys()].sort()) visit(id);
|
|
14818
|
+
return values;
|
|
14819
|
+
}
|
|
14820
|
+
/** This function names a result for the reason of a disagreement: its error code, or "a value". */
|
|
14821
|
+
function named(result) {
|
|
14822
|
+
return result.ok ? "a value" : result.error.code;
|
|
14823
|
+
}
|
|
14824
|
+
/**
|
|
14825
|
+
* This function examines an engine of a host against an evaluation from scratch (SCHED.AGREES, R4 P4, D58). Each run
|
|
14826
|
+
* makes a new engine and a random graph of named nodes from the document generator. Then it gives random commands:
|
|
14827
|
+
* edits, new nodes, removed nodes, the same document again, new machines and changes of data.
|
|
14828
|
+
*
|
|
14829
|
+
* - Each read command evaluates the whole graph from scratch two times, with the meaning of D31.
|
|
14830
|
+
* - It then demands each node in a random order, or one node, and compares each answer with `sameResult`.
|
|
14831
|
+
* - A read whose answer from scratch differs between the two evaluations is not compared.
|
|
14832
|
+
* - Two errors agree when they have the same code, reason and place. Thus an engine gives the `#CYCLE!` and the
|
|
14833
|
+
* `#REF!` of D31.
|
|
14834
|
+
*
|
|
14835
|
+
* The same seed and the same options give the same commands, thus a disagreement occurs again.
|
|
14836
|
+
*
|
|
14837
|
+
* @param engine - This function makes a new engine for each run, for example `schedule`.
|
|
14838
|
+
* @param options - The machines, the generator, the contexts, the number of runs and the rule for host values.
|
|
14839
|
+
* @returns What the harness reached, or the first read whose answer differs from the answer from scratch.
|
|
14840
|
+
*/
|
|
14841
|
+
function agreeSchedule(engine, options = {}) {
|
|
14842
|
+
const machines = options.machines ?? [STD_MACHINE, { ...STD_MACHINE }];
|
|
14843
|
+
const declaredType = options.declaredType;
|
|
14844
|
+
const generateOptions = options.generate ?? standardGenerate();
|
|
14845
|
+
const value = options.value ?? ((rng) => ({
|
|
14846
|
+
type: "number",
|
|
14847
|
+
value: rng.pick([
|
|
14848
|
+
0,
|
|
14849
|
+
1,
|
|
14850
|
+
2
|
|
14851
|
+
])
|
|
14852
|
+
}));
|
|
14853
|
+
const wire = options.wire ?? standardWire;
|
|
14854
|
+
const wired = options.wired ?? .2;
|
|
14855
|
+
const runs = options.runs ?? true;
|
|
14856
|
+
const nodes = options.nodes ?? [
|
|
14857
|
+
"p",
|
|
14858
|
+
"q",
|
|
14859
|
+
"r",
|
|
14860
|
+
"s",
|
|
14861
|
+
"t"
|
|
14862
|
+
];
|
|
14863
|
+
const hostNames = options.names ?? [
|
|
14864
|
+
"a",
|
|
14865
|
+
"b",
|
|
14866
|
+
"x",
|
|
14867
|
+
"n"
|
|
14868
|
+
];
|
|
14869
|
+
const hostValues = options.values ?? standardValues();
|
|
14870
|
+
const contexts = options.contexts ?? standardContexts();
|
|
14871
|
+
const total = options.count ?? 100;
|
|
14872
|
+
const steps = options.steps ?? 30;
|
|
14873
|
+
const first = options.seed ?? 9000011;
|
|
14874
|
+
const given = options.equal;
|
|
14875
|
+
const equal = (a, b) => {
|
|
14876
|
+
const answer = given?.(a, b);
|
|
14877
|
+
if (answer !== void 0) return answer;
|
|
14878
|
+
if (isClosure(a) || isClosure(b)) return isClosure(a) && isClosure(b) && docEquals(closureParams(a), closureParams(b));
|
|
14879
|
+
};
|
|
14880
|
+
const counts = {
|
|
14881
|
+
reads: 0,
|
|
14882
|
+
compared: 0,
|
|
14883
|
+
unrepeatable: 0,
|
|
14884
|
+
values: 0,
|
|
14885
|
+
cycles: 0,
|
|
14886
|
+
runs: 0,
|
|
14887
|
+
data: 0,
|
|
14888
|
+
empty: 0,
|
|
14889
|
+
edits: 0,
|
|
14890
|
+
additions: 0,
|
|
14891
|
+
removes: 0,
|
|
14892
|
+
same: 0,
|
|
14893
|
+
machines: 0
|
|
14894
|
+
};
|
|
14895
|
+
const declared = declaredType === void 0 ? {} : { declaredType };
|
|
14896
|
+
for (let run = 0; run < total; run += 1) {
|
|
14897
|
+
const seed = first + run;
|
|
14898
|
+
const rng = random(seed);
|
|
14899
|
+
const node = () => ({
|
|
14900
|
+
type: "ref",
|
|
14901
|
+
value: ["world", rng.pick(nodes)]
|
|
14902
|
+
});
|
|
14903
|
+
let at = 0;
|
|
14904
|
+
let machine = machines[0];
|
|
14905
|
+
let made = 0;
|
|
14906
|
+
const make = () => {
|
|
14907
|
+
if (rng.chance(wired)) return wire(rng, node);
|
|
14908
|
+
const roll = rng.int(10);
|
|
14909
|
+
if (roll === 0) return value(rng);
|
|
14910
|
+
const names = roll < 5 ? hostNames : [
|
|
14911
|
+
...hostNames,
|
|
14912
|
+
...nodes,
|
|
14913
|
+
...nodes,
|
|
14914
|
+
...nodes
|
|
14915
|
+
];
|
|
14916
|
+
const doc = linked(generate(machine.world, {
|
|
14917
|
+
...generateOptions,
|
|
14918
|
+
names,
|
|
14919
|
+
seed: seed * 31 + made++
|
|
14920
|
+
}), rng, nodes);
|
|
14921
|
+
return runs && roll === 9 ? {
|
|
14922
|
+
type: "log",
|
|
14923
|
+
value: [doc]
|
|
14924
|
+
} : doc;
|
|
14925
|
+
};
|
|
14926
|
+
const docs = new Map(nodes.map((id) => [id, make()]));
|
|
14927
|
+
const start = contexts[run % contexts.length];
|
|
14928
|
+
let base = {
|
|
14929
|
+
...start,
|
|
14930
|
+
names: { ...start.names }
|
|
14931
|
+
};
|
|
14932
|
+
const host = engine(machine, {
|
|
14933
|
+
...declared,
|
|
14934
|
+
context: base
|
|
14935
|
+
});
|
|
14936
|
+
for (const [id, doc] of docs) host.set(id, doc);
|
|
14937
|
+
for (let step = 0; step < steps; step += 1) {
|
|
14938
|
+
const what = rng.int(12);
|
|
14939
|
+
if (what < 4) {
|
|
14940
|
+
const fresh = fromScratch(machine, docs, base, declaredType);
|
|
14941
|
+
const again = fromScratch(machine, docs, base, declaredType);
|
|
14942
|
+
const ids = shuffle([...docs.keys()], rng);
|
|
14943
|
+
for (const id of rng.chance(.5) ? ids : ids.slice(0, 1)) {
|
|
14944
|
+
const got = host.read(id);
|
|
14945
|
+
const want = fresh.get(id);
|
|
14946
|
+
counts.reads += 1;
|
|
14947
|
+
if (!sameResult(want, again.get(id), equal)) {
|
|
14948
|
+
counts.unrepeatable += 1;
|
|
14949
|
+
continue;
|
|
14950
|
+
}
|
|
14951
|
+
counts.compared += 1;
|
|
14952
|
+
if (sameResult(want, cycleOf(id))) counts.cycles += 1;
|
|
14953
|
+
else if (sameResult(want, runSide(id))) counts.runs += 1;
|
|
14954
|
+
else if (want.ok) counts.values += 1;
|
|
14955
|
+
if (!sameResult(got, want, equal)) {
|
|
14956
|
+
const reason = got.ok === want.ok && !got.ok ? `The engine and the evaluation from scratch give two different errors ${named(got)} and ${named(want)} for the node ${id}.` : `The engine gives ${named(got)} for the node ${id}, and the evaluation from scratch gives ${named(want)}.`;
|
|
14957
|
+
return {
|
|
14958
|
+
ok: false,
|
|
14959
|
+
seed,
|
|
14960
|
+
step,
|
|
14961
|
+
node: id,
|
|
14962
|
+
engine: got,
|
|
14963
|
+
scratch: want,
|
|
14964
|
+
docs: new Map(docs),
|
|
14965
|
+
context: base,
|
|
14966
|
+
machine,
|
|
14967
|
+
reason,
|
|
14968
|
+
counts: { ...counts }
|
|
14969
|
+
};
|
|
14970
|
+
}
|
|
14971
|
+
}
|
|
14972
|
+
} else if (what < 6) {
|
|
14973
|
+
base = {
|
|
14974
|
+
...base,
|
|
14975
|
+
names: change(base, rng, hostNames, hostValues)
|
|
14976
|
+
};
|
|
14977
|
+
host.changeData(base);
|
|
14978
|
+
counts.data += 1;
|
|
14979
|
+
} else if (what === 6) {
|
|
14980
|
+
host.changeData(base);
|
|
14981
|
+
counts.empty += 1;
|
|
14982
|
+
} else if (what < 9) {
|
|
14983
|
+
const id = rng.pick(nodes);
|
|
14984
|
+
const doc = make();
|
|
14985
|
+
docs.set(id, doc);
|
|
14986
|
+
host.set(id, doc);
|
|
14987
|
+
counts.edits += 1;
|
|
14988
|
+
} else if (what === 9) {
|
|
14989
|
+
const absent = nodes.filter((id) => !docs.has(id));
|
|
14990
|
+
if (absent.length > 0 && (docs.size <= 2 || rng.chance(.65))) {
|
|
14991
|
+
const id = rng.pick(absent);
|
|
14992
|
+
const doc = make();
|
|
14993
|
+
docs.set(id, doc);
|
|
14994
|
+
host.set(id, doc);
|
|
14995
|
+
counts.additions += 1;
|
|
14996
|
+
} else {
|
|
14997
|
+
const id = rng.pick([...docs.keys()]);
|
|
14998
|
+
docs.delete(id);
|
|
14999
|
+
host.remove(id);
|
|
15000
|
+
counts.removes += 1;
|
|
15001
|
+
}
|
|
15002
|
+
} else if (what === 10) {
|
|
15003
|
+
const id = rng.pick([...docs.keys()]);
|
|
15004
|
+
host.set(id, docs.get(id));
|
|
15005
|
+
counts.same += 1;
|
|
15006
|
+
} else if (host.changeMachine !== void 0) {
|
|
15007
|
+
at = (at + 1) % machines.length;
|
|
15008
|
+
machine = machines[at];
|
|
15009
|
+
host.changeMachine(machine);
|
|
15010
|
+
counts.machines += 1;
|
|
15011
|
+
}
|
|
15012
|
+
}
|
|
15013
|
+
}
|
|
15014
|
+
return {
|
|
15015
|
+
ok: true,
|
|
15016
|
+
counts
|
|
15017
|
+
};
|
|
15018
|
+
}
|
|
15019
|
+
//#endregion
|
|
12268
15020
|
//#region src/providers.ts
|
|
12269
15021
|
/** The deepest schema that `typeFromSchema` reads. A deeper schema reads as `unknown`. */
|
|
12270
15022
|
const MAX_SCHEMA_DEPTH = 256;
|
|
@@ -12575,6 +15327,6 @@ function typeFromSamples(values, options = {}) {
|
|
|
12575
15327
|
return samplesAt(values, options.closed === true, 0);
|
|
12576
15328
|
}
|
|
12577
15329
|
//#endregion
|
|
12578
|
-
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, agreeRun, ask, attributeTrace, capKeys, check, checkLatent, checkSteps, checkWorld, classify, components, conforms, context, cycles, dataEdges, decodeDomain, decodeValue, derefStep, describe, docEquals, durable, edgesOf, effectEdges, effectsOf, effectsOfVerb, 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, referenceRun, 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 };
|
|
15330
|
+
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, WORLD_SYNTAX, agree, agreeRun, agreeSchedule, ask, attributeTrace, capKeys, check, checkLatent, checkSteps, checkWorld, classify, components, conforms, context, cycles, dataEdges, decodeDomain, decodeLog, decodeValue, derefStep, describe, docEquals, durable, edgesOf, effectEdges, effectsOf, effectsOfVerb, encodeLog, 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, placeOf, placed, planner, prepare, print, random, recorder, ref, reference, referenceRun, 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 };
|
|
12579
15331
|
|
|
12580
15332
|
//# sourceMappingURL=index.js.map
|