@mark1russell7/coeval 0.16.0 → 0.18.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 +545 -233
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2619 -383
- package/build/index.js.map +1 -1
- package/package.json +3 -2
- package/src/agree.ts +128 -16
- package/src/conform.ts +75 -5
- package/src/deref.ts +76 -11
- package/src/evaluate.ts +151 -21
- package/src/facts.ts +113 -78
- package/src/formula.ts +67 -11
- package/src/index.ts +23 -1
- package/src/merge.ts +49 -14
- package/src/reference.ts +110 -15
- package/src/replay.ts +1052 -18
- package/src/static.ts +2 -2
- package/src/stdlib.ts +55 -2
- package/src/syntax.ts +357 -52
- package/src/types.ts +520 -17
package/build/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/doc.ts
|
|
2
2
|
/** The keys that an envelope can have. */
|
|
3
3
|
const ENVELOPE_KEYS = [
|
|
4
4
|
"type",
|
|
@@ -240,8 +240,7 @@ function equalWith(a, b, rule) {
|
|
|
240
240
|
}
|
|
241
241
|
return true;
|
|
242
242
|
}
|
|
243
|
-
|
|
244
|
-
//#region src/errors.ts
|
|
243
|
+
// src/errors.ts
|
|
245
244
|
/**
|
|
246
245
|
* This function makes a result with a value.
|
|
247
246
|
*
|
|
@@ -354,8 +353,7 @@ function describe(value) {
|
|
|
354
353
|
return "(a value without a text)";
|
|
355
354
|
}
|
|
356
355
|
}
|
|
357
|
-
|
|
358
|
-
//#region src/world.ts
|
|
356
|
+
// src/world.ts
|
|
359
357
|
/**
|
|
360
358
|
* This function gives the type of a bare equation of a kind (D42). It is the type `of` of the shape of its parts, or
|
|
361
359
|
* else the type `of` of the shape `rest` of a record. The plan, the reference and `check` read it with this one function.
|
|
@@ -568,8 +566,7 @@ function kindDocument(name, kind) {
|
|
|
568
566
|
function looksLikeKind(value) {
|
|
569
567
|
return isRecord(value) && isRecord(field(value, "verbs"));
|
|
570
568
|
}
|
|
571
|
-
|
|
572
|
-
//#region src/types.ts
|
|
569
|
+
// src/types.ts
|
|
573
570
|
/**
|
|
574
571
|
* The domains of the kernel. `unknown` and `never` are not domains: they are the top and the bottom of the types.
|
|
575
572
|
* A machine that does not name one of these domains gets it from this table.
|
|
@@ -577,18 +574,22 @@ function looksLikeKind(value) {
|
|
|
577
574
|
const STD_DOMAINS = {
|
|
578
575
|
null: {
|
|
579
576
|
is: (value) => value === null,
|
|
577
|
+
values: [null],
|
|
580
578
|
summary: "The value null."
|
|
581
579
|
},
|
|
582
580
|
undefined: {
|
|
583
581
|
is: (value) => value === void 0,
|
|
582
|
+
values: [void 0],
|
|
584
583
|
summary: "The value undefined. A document holds it as the extension ext.undefined."
|
|
585
584
|
},
|
|
586
585
|
bool: {
|
|
587
586
|
is: (value) => typeof value === "boolean",
|
|
587
|
+
values: [true, false],
|
|
588
588
|
summary: "A truth value: true or false."
|
|
589
589
|
},
|
|
590
590
|
number: {
|
|
591
591
|
is: (value) => typeof value === "number",
|
|
592
|
+
samples: [0, -1],
|
|
592
593
|
summary: "A number of the host, also NaN, an infinity and -0."
|
|
593
594
|
},
|
|
594
595
|
text: {
|
|
@@ -598,6 +599,12 @@ const STD_DOMAINS = {
|
|
|
598
599
|
};
|
|
599
600
|
/** The deepest term that a value type can have. A deeper term reads as `unknown`, thus a check stays total. */
|
|
600
601
|
const MAX_TYPE_DEPTH = 256;
|
|
602
|
+
/** 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. */
|
|
603
|
+
const MAX_FINITE_VALUES = 64;
|
|
604
|
+
/** The most domains of covers that the cover rule of `fits` tests for one type (TYPE.FITS-COVER). A larger test gives `false`. */
|
|
605
|
+
const MAX_COVER_STEPS = 64;
|
|
606
|
+
/** The most records that one split of `fits` builds, with each split inside its work (TYPE.FITS-PATH, D89). A larger split gives `false`. */
|
|
607
|
+
const MAX_SPLIT_RECORDS = 64;
|
|
601
608
|
/** The keys that each term permits beside its own key. */
|
|
602
609
|
const TERM_KEYS = {
|
|
603
610
|
list: ["list"],
|
|
@@ -620,7 +627,7 @@ function domainOf(name, domains) {
|
|
|
620
627
|
return typeof found === "object" && found !== null && typeof found.is === "function" ? found : void 0;
|
|
621
628
|
}
|
|
622
629
|
/** This function gives the term key of a record, when the record has exactly one term key and no other key. */
|
|
623
|
-
function termOf$
|
|
630
|
+
function termOf$2(term) {
|
|
624
631
|
const keys = Object.keys(term);
|
|
625
632
|
const own = keys.filter((key) => TERMS.includes(key));
|
|
626
633
|
if (own.length !== 1) return void 0;
|
|
@@ -638,7 +645,7 @@ function formOf(type, domains) {
|
|
|
638
645
|
};
|
|
639
646
|
}
|
|
640
647
|
if (!isRecord(type)) return TOP;
|
|
641
|
-
const term = termOf$
|
|
648
|
+
const term = termOf$2(type);
|
|
642
649
|
if (term === "list") return {
|
|
643
650
|
t: "list",
|
|
644
651
|
item: type["list"]
|
|
@@ -745,39 +752,419 @@ function member(value, type, domains) {
|
|
|
745
752
|
return memberAt(value, type, domains, 0);
|
|
746
753
|
}
|
|
747
754
|
/** 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) {
|
|
755
|
+
function recordFits(a, b, domains, depth, gradual, budget) {
|
|
749
756
|
if (b.closed && (!a.closed || Object.keys(a.fields).some((key) => !Object.hasOwn(b.fields, key)))) return false;
|
|
750
757
|
for (const key of Object.keys(b.fields)) {
|
|
751
758
|
const want = b.fields[key];
|
|
752
759
|
const needed = !b.optional.has(key);
|
|
753
760
|
if (Object.hasOwn(a.fields, key)) {
|
|
754
761
|
if (needed && a.optional.has(key)) return false;
|
|
755
|
-
if (!fitsAt(a.fields[key], want, domains, depth, gradual)) return false;
|
|
762
|
+
if (!fitsAt(a.fields[key], want, domains, depth, gradual, true, budget)) return false;
|
|
756
763
|
} else if (needed) return false;
|
|
757
764
|
else if (!a.closed && formOf(want, domains).t !== "top") return false;
|
|
758
765
|
}
|
|
759
766
|
return true;
|
|
760
767
|
}
|
|
768
|
+
/** This function adds a value to a list of distinct values, by `docEquals`. */
|
|
769
|
+
function addValue(out, value) {
|
|
770
|
+
if (!out.some((item) => docEquals(item, value))) out.push(value);
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* This function gives the values of a base type with a finite list. It reads the list of the first domain of the line
|
|
774
|
+
* 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.
|
|
775
|
+
*/
|
|
776
|
+
function baseValues(name, domains) {
|
|
777
|
+
const domain = domainOf(name, domains);
|
|
778
|
+
if (domain === void 0) return void 0;
|
|
779
|
+
const line = Array.isArray(domain.values) || !Array.isArray(domain.under) ? [name] : lineOf(name, domains);
|
|
780
|
+
for (const each of line) {
|
|
781
|
+
const values = domainOf(each, domains)?.values;
|
|
782
|
+
if (!Array.isArray(values) || values.length > MAX_FINITE_VALUES) continue;
|
|
783
|
+
const out = [];
|
|
784
|
+
for (let index = 0; index < values.length; index += 1) if (inDomain(values[index], name, domains)) addValue(out, values[index]);
|
|
785
|
+
return out;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
/** This function gives a copy of a record with one more field. */
|
|
789
|
+
function withField(record, key, value) {
|
|
790
|
+
const out = {};
|
|
791
|
+
for (const name of Object.keys(record)) setOwn$6(out, name, record[name]);
|
|
792
|
+
setOwn$6(out, key, value);
|
|
793
|
+
return out;
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* This function gives the members of a record type, when they are few. A record with a needed field without members
|
|
797
|
+
* has no members. Only a closed record with a finite list for each field has a finite list.
|
|
798
|
+
*/
|
|
799
|
+
function recordValues(form, domains, depth) {
|
|
800
|
+
let out = [{}];
|
|
801
|
+
let finite = form.closed;
|
|
802
|
+
for (const key of Object.keys(form.fields)) {
|
|
803
|
+
const values = valuesOf$1(form.fields[key], domains, depth);
|
|
804
|
+
const optional = form.optional.has(key);
|
|
805
|
+
if (values === void 0) {
|
|
806
|
+
finite = false;
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
809
|
+
if (values.length === 0 && !optional) return [];
|
|
810
|
+
if (!finite) continue;
|
|
811
|
+
if (out.length * (values.length + (optional ? 1 : 0)) > MAX_FINITE_VALUES) {
|
|
812
|
+
finite = false;
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
out = out.flatMap((record) => [...optional ? [record] : [], ...values.map((value) => withField(record, key, value))]);
|
|
816
|
+
}
|
|
817
|
+
return finite ? out : void 0;
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* This function gives a list of the members of a type, when the type has at most 64 members (TYPE.FITS-FINITE). Each
|
|
821
|
+
* other type gives `undefined`. Such a type is `unknown`, a base type without a finite list, or a function type. A
|
|
822
|
+
* list that can hold an item and an open record with members are also such types.
|
|
823
|
+
*/
|
|
824
|
+
function valuesOf$1(type, domains, depth) {
|
|
825
|
+
if (depth > MAX_TYPE_DEPTH) return void 0;
|
|
826
|
+
const form = formOf(type, domains);
|
|
827
|
+
const next = depth + 1;
|
|
828
|
+
switch (form.t) {
|
|
829
|
+
case "bottom": return [];
|
|
830
|
+
case "base": return baseValues(form.name, domains);
|
|
831
|
+
case "list": return valuesOf$1(form.item, domains, next)?.length === 0 ? [[]] : void 0;
|
|
832
|
+
case "record": return recordValues(form, domains, next);
|
|
833
|
+
case "one":
|
|
834
|
+
case "union": {
|
|
835
|
+
const out = [];
|
|
836
|
+
const items = form.t === "one" ? form.values : form.members;
|
|
837
|
+
for (const item of items) {
|
|
838
|
+
const values = form.t === "one" ? [item] : valuesOf$1(item, domains, next);
|
|
839
|
+
if (values === void 0) return void 0;
|
|
840
|
+
for (const value of values) addValue(out, value);
|
|
841
|
+
if (out.length > MAX_FINITE_VALUES) return void 0;
|
|
842
|
+
}
|
|
843
|
+
return out;
|
|
844
|
+
}
|
|
845
|
+
default: return;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
/** This function gives the members of a type at one depth, for `membersOf`. */
|
|
849
|
+
function membersAt(type, domains, samples, depth) {
|
|
850
|
+
const finite = valuesOf$1(type, domains, depth);
|
|
851
|
+
if (finite !== void 0) return finite;
|
|
852
|
+
const form = formOf(type, domains);
|
|
853
|
+
if (form.t !== "union" || depth > MAX_TYPE_DEPTH) return samples.filter((sample) => member(sample, type, domains));
|
|
854
|
+
const out = [];
|
|
855
|
+
for (const item of form.members) for (const value of membersAt(item, domains, samples, depth + 1)) addValue(out, value);
|
|
856
|
+
return out;
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* This function gives members of a type for a test of a declaration (D86). A type with at most 64 members gives each
|
|
860
|
+
* member, from the lists `values` of its domains (TYPE.FITS-FINITE). A union gives the members of each of its types.
|
|
861
|
+
* Each other type gives the samples that are its members. The samples of the domains come after the given samples (D99).
|
|
862
|
+
* The kernel does not export the function.
|
|
863
|
+
*
|
|
864
|
+
* @param type - The type.
|
|
865
|
+
* @param domains - The domains of the machine.
|
|
866
|
+
* @param samples - The values to test when the type has no full list of members.
|
|
867
|
+
* @returns Distinct members of the type.
|
|
868
|
+
*/
|
|
869
|
+
function membersOf(type, domains, samples) {
|
|
870
|
+
return membersAt(type, domains, withDomainSamples(samples, domains), 0);
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* This function adds the samples of the domains to a palette (D99). It reads the list `samples` of each domain of the
|
|
874
|
+
* table and of each standard domain that the table does not replace. The result holds each value one time.
|
|
875
|
+
*/
|
|
876
|
+
function withDomainSamples(palette, domains) {
|
|
877
|
+
const out = [];
|
|
878
|
+
for (const value of palette) addValue(out, value);
|
|
879
|
+
const names = /* @__PURE__ */ new Set([...Object.keys(STD_DOMAINS), ...domains === void 0 ? [] : Object.keys(domains)]);
|
|
880
|
+
for (const name of names) {
|
|
881
|
+
const samples = domainOf(name, domains)?.samples;
|
|
882
|
+
if (Array.isArray(samples)) for (const value of samples) addValue(out, value);
|
|
883
|
+
}
|
|
884
|
+
return out;
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* This function finds the false covers of a language from samples (D94). A cover is false when a sample of the covered
|
|
888
|
+
* domain is in no domain of the cover. The function tests each valid cover of each domain of the table. It ignores a
|
|
889
|
+
* cover that does not count by the line rule of D85, because `fits` ignores it too. It also ignores a sample outside
|
|
890
|
+
* the covered domain.
|
|
891
|
+
*
|
|
892
|
+
* The samples of the domains come after the given samples (D99). `conforms` and a test of `fits` use the function. The
|
|
893
|
+
* kernel does not export it.
|
|
894
|
+
*
|
|
895
|
+
* @param domains - The domains of the language, by their names.
|
|
896
|
+
* @param samples - The samples of each covered domain, by the name of the domain.
|
|
897
|
+
* @returns Each sample that breaks a cover, in the order of the domains, the covers and the samples.
|
|
898
|
+
*/
|
|
899
|
+
function falseCovers(domains, samples) {
|
|
900
|
+
const out = [];
|
|
901
|
+
for (const [name, domain] of Object.entries(domains)) {
|
|
902
|
+
const covers = domain.covers;
|
|
903
|
+
if (!Array.isArray(covers)) continue;
|
|
904
|
+
const values = withDomainSamples((Object.hasOwn(samples, name) ? samples[name] : void 0) ?? [], domains);
|
|
905
|
+
for (const cover of covers) {
|
|
906
|
+
if (!validCover(name, cover, domains)) continue;
|
|
907
|
+
for (const value of values) if (member(value, name, domains) && !cover.some((part) => member(value, part, domains))) out.push({
|
|
908
|
+
domain: name,
|
|
909
|
+
cover,
|
|
910
|
+
value
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
return out;
|
|
915
|
+
}
|
|
916
|
+
/** This function holds the rules of the shapes of `fits`: base types, lists, records and functions. */
|
|
917
|
+
function shapeFits(a, b, domains, next, gradual, budget) {
|
|
918
|
+
if (a.t === "base") return b.t === "base" && lineOf(a.name, domains).has(b.name);
|
|
919
|
+
if (a.t === "list") return b.t === "list" && fitsAt(a.item, b.item, domains, next, gradual, true, budget);
|
|
920
|
+
if (a.t === "record") return b.t === "record" && recordFits(a, b, domains, next, gradual, budget);
|
|
921
|
+
if (a.t !== "fn" || b.t !== "fn" || a.params.length > b.params.length) return false;
|
|
922
|
+
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);
|
|
923
|
+
}
|
|
761
924
|
/**
|
|
762
925
|
* 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
926
|
* `fits`. Without `gradual`, `unknown` is only the top: it fits only `unknown`. The join uses this strict form.
|
|
927
|
+
*
|
|
928
|
+
* When the rules of the shapes refuse a type with few members, the finite rule tests each member (TYPE.FITS-FINITE).
|
|
929
|
+
* The cover rule and the split rule come after it. Without `finite`, the call skips these three rules. The members of
|
|
930
|
+
* a union in `expected` get such calls, because the call for the union tests `found` against the full union.
|
|
931
|
+
*
|
|
932
|
+
* 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
933
|
*/
|
|
765
|
-
function fitsAt(found, expected, domains, depth, gradual = true) {
|
|
934
|
+
function fitsAt(found, expected, domains, depth, gradual = true, finite = true, budget) {
|
|
766
935
|
if (depth > MAX_TYPE_DEPTH) return true;
|
|
767
936
|
const a = formOf(found, domains);
|
|
768
937
|
const b = formOf(expected, domains);
|
|
769
938
|
const next = depth + 1;
|
|
770
939
|
if (b.t === "top" || a.t === "bottom") return true;
|
|
771
940
|
if (a.t === "top") return gradual;
|
|
772
|
-
if (a.t === "union") return a.members.every((item) => fitsAt(item, expected, domains, next, gradual));
|
|
941
|
+
if (a.t === "union") return a.members.every((item) => fitsAt(item, expected, domains, next, gradual, true, budget));
|
|
773
942
|
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
|
-
|
|
943
|
+
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;
|
|
944
|
+
if (!finite || a.t === "fn") return false;
|
|
945
|
+
const values = valuesOf$1(found, domains, next);
|
|
946
|
+
if (values !== void 0) return values.every((value) => memberAt(value, expected, domains, next));
|
|
947
|
+
if (a.t === "base") return (b.t === "union" || b.t === "one") && lineCovered(a.name, expected, domains, depth, gradual, void 0);
|
|
948
|
+
return a.t === "record" && (b.t === "union" || b.t === "one" && a.closed) && splitFits(found, a, expected, domains, depth, gradual, budget);
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* This function is the cover rule of `fits` (TYPE.FITS-COVER, D85). A base type fits when a domain of its line has a
|
|
952
|
+
* cover, and each domain of the cover fits `expected`. The function makes the state at the first domain with covers.
|
|
953
|
+
*/
|
|
954
|
+
function lineCovered(name, expected, domains, depth, gradual, state) {
|
|
955
|
+
let now = state;
|
|
956
|
+
for (const each of lineOf(name, domains)) {
|
|
957
|
+
if (!Array.isArray(domainOf(each, domains)?.covers)) continue;
|
|
958
|
+
now ??= {
|
|
959
|
+
held: /* @__PURE__ */ new Map(),
|
|
960
|
+
left: MAX_COVER_STEPS
|
|
961
|
+
};
|
|
962
|
+
if (covered(each, expected, domains, depth, gradual, now)) return true;
|
|
963
|
+
}
|
|
964
|
+
return false;
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* This function tells if a cover of one domain holds the domain in `expected`. A cover that is empty, or that names
|
|
968
|
+
* the domain or a name that is not under it, does not count. Each domain of a cover costs one step, and the rule
|
|
969
|
+
* stops after 64 steps.
|
|
970
|
+
*/
|
|
971
|
+
function covered(name, expected, domains, depth, gradual, state) {
|
|
972
|
+
const known = state.held.get(name);
|
|
973
|
+
if (known !== void 0) return known;
|
|
974
|
+
state.held.set(name, false);
|
|
975
|
+
const covers = domainOf(name, domains)?.covers;
|
|
976
|
+
if (!Array.isArray(covers)) return false;
|
|
977
|
+
for (const cover of covers) {
|
|
978
|
+
if (!Array.isArray(cover) || cover.length === 0) continue;
|
|
979
|
+
let held = true;
|
|
980
|
+
for (const part of cover) {
|
|
981
|
+
state.left -= 1;
|
|
982
|
+
if (state.left < 0) return false;
|
|
983
|
+
if (typeof part !== "string" || part === name || !lineOf(part, domains).has(name) || !partFits(part, expected, domains, depth, gradual, state)) {
|
|
984
|
+
held = false;
|
|
985
|
+
break;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
if (held) {
|
|
989
|
+
state.held.set(name, true);
|
|
990
|
+
return true;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
return false;
|
|
994
|
+
}
|
|
995
|
+
/** 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. */
|
|
996
|
+
function partFits(part, expected, domains, depth, gradual, state) {
|
|
997
|
+
if (fitsAt(part, expected, domains, depth, gradual, false)) return true;
|
|
998
|
+
const values = valuesOf$1(part, domains, depth + 1);
|
|
999
|
+
if (values !== void 0) return values.every((value) => memberAt(value, expected, domains, depth + 1));
|
|
1000
|
+
return lineCovered(part, expected, domains, depth, gradual, state);
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* This function gives the fields that the split rule can split, in the order of the split (TYPE.FITS-PATH, D89). The
|
|
1004
|
+
* 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
|
|
1005
|
+
* 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
|
|
1006
|
+
* cases is a split. An optional field also has the absent case.
|
|
1007
|
+
*
|
|
1008
|
+
* The type splits follow each value split, in the same order (D90). A base type with a valid cover in its line splits
|
|
1009
|
+
* into the domains of a cover, and it tries each valid cover (D102). A union without a finite list splits into its
|
|
1010
|
+
* members. The prefix is the path of `a`.
|
|
1011
|
+
*/
|
|
1012
|
+
function splitsOf(a, domains, depth, prefix = []) {
|
|
1013
|
+
const out = [];
|
|
1014
|
+
const later = [];
|
|
1015
|
+
let level = [{
|
|
1016
|
+
path: prefix,
|
|
1017
|
+
form: a
|
|
1018
|
+
}];
|
|
1019
|
+
for (let at = depth + 1; level.length > 0 && at <= MAX_TYPE_DEPTH; at += 1) {
|
|
1020
|
+
const deeper = [];
|
|
1021
|
+
for (const { path, form } of level) for (const key of Object.keys(form.fields)) {
|
|
1022
|
+
const values = valuesOf$1(form.fields[key], domains, at);
|
|
1023
|
+
const optional = form.optional.has(key);
|
|
1024
|
+
if (values !== void 0) {
|
|
1025
|
+
if (values.length + (optional ? 1 : 0) > 1) out.push({
|
|
1026
|
+
path: [...path, key],
|
|
1027
|
+
values,
|
|
1028
|
+
optional
|
|
1029
|
+
});
|
|
1030
|
+
continue;
|
|
1031
|
+
}
|
|
1032
|
+
const inner = formOf(form.fields[key], domains);
|
|
1033
|
+
if (inner.t === "record") deeper.push({
|
|
1034
|
+
path: [...path, key],
|
|
1035
|
+
form: inner
|
|
1036
|
+
});
|
|
1037
|
+
else if (inner.t === "union") later.push({
|
|
1038
|
+
path: [...path, key],
|
|
1039
|
+
types: inner.members,
|
|
1040
|
+
optional
|
|
1041
|
+
});
|
|
1042
|
+
else if (inner.t === "base") {
|
|
1043
|
+
const covers = coversOf(inner.name, domains, false);
|
|
1044
|
+
if (covers.length > 0) later.push({
|
|
1045
|
+
path: [...path, key],
|
|
1046
|
+
covers,
|
|
1047
|
+
optional
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
level = deeper;
|
|
1052
|
+
}
|
|
1053
|
+
return later.length === 0 ? out : [...out, ...later];
|
|
1054
|
+
}
|
|
1055
|
+
/** 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. */
|
|
1056
|
+
function validCover(name, cover, domains) {
|
|
1057
|
+
return Array.isArray(cover) && cover.length > 0 && cover.every((part) => typeof part === "string" && part !== name && lineOf(part, domains).has(name));
|
|
1058
|
+
}
|
|
1059
|
+
/**
|
|
1060
|
+
* This function gives the valid covers of a base type for a type split (D90, D102). The order is the order of the line,
|
|
1061
|
+
* then the order of the covers of each domain. With `own`, only the domain of the type itself counts.
|
|
1062
|
+
*/
|
|
1063
|
+
function coversOf(name, domains, own) {
|
|
1064
|
+
const out = [];
|
|
1065
|
+
for (const each of own ? [name] : lineOf(name, domains)) {
|
|
1066
|
+
const covers = domainOf(each, domains)?.covers;
|
|
1067
|
+
if (!Array.isArray(covers)) continue;
|
|
1068
|
+
for (const cover of covers) if (validCover(each, cover, domains)) out.push(cover);
|
|
1069
|
+
}
|
|
1070
|
+
return out;
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* 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
|
|
1074
|
+
* splits by its members. A base type splits by a cover of its own domain, because the covers of its parents hold it
|
|
1075
|
+
* already. A union splits by its members, and a record splits by the paths into its fields.
|
|
1076
|
+
*/
|
|
1077
|
+
function againOf(type, path, domains, depth) {
|
|
1078
|
+
const at = depth + path.length;
|
|
1079
|
+
const values = valuesOf$1(type, domains, at);
|
|
1080
|
+
if (values !== void 0) return values.length > 1 ? [{
|
|
1081
|
+
path,
|
|
1082
|
+
values,
|
|
1083
|
+
optional: false
|
|
1084
|
+
}] : [];
|
|
1085
|
+
const form = formOf(type, domains);
|
|
1086
|
+
if (form.t === "record") return splitsOf(form, domains, at, path);
|
|
1087
|
+
if (form.t === "union") return [{
|
|
1088
|
+
path,
|
|
1089
|
+
types: form.members,
|
|
1090
|
+
optional: false
|
|
1091
|
+
}];
|
|
1092
|
+
const covers = form.t === "base" ? coversOf(form.name, domains, true) : [];
|
|
1093
|
+
return covers.length === 0 ? [] : [{
|
|
1094
|
+
path,
|
|
1095
|
+
covers,
|
|
1096
|
+
optional: false
|
|
1097
|
+
}];
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* This function gives a copy of a record type with one field narrowed at a path. A case type replaces the field. The
|
|
1101
|
+
* absent case removes the field of a closed record, and it gives an optional field of type `never` in an open record.
|
|
1102
|
+
*/
|
|
1103
|
+
function narrowAt(type, path, at, narrowed, domains) {
|
|
1104
|
+
const form = formOf(type, domains);
|
|
1105
|
+
const key = path[at];
|
|
1106
|
+
if (form.t !== "record" || key === void 0) return type;
|
|
1107
|
+
const last = at + 1 === path.length;
|
|
1108
|
+
const fields = {};
|
|
1109
|
+
for (const name of Object.keys(form.fields)) if (name !== key) setOwn$6(fields, name, form.fields[name]);
|
|
1110
|
+
else if (!last) setOwn$6(fields, name, narrowAt(form.fields[name], path, at + 1, narrowed, domains));
|
|
1111
|
+
else if (narrowed !== void 0) setOwn$6(fields, name, narrowed);
|
|
1112
|
+
else if (!form.closed) setOwn$6(fields, name, "never");
|
|
1113
|
+
const optional = [...form.optional].filter((name) => !last || name !== key || narrowed === void 0 && !form.closed);
|
|
1114
|
+
return {
|
|
1115
|
+
record: fields,
|
|
1116
|
+
...optional.length === 0 ? {} : { optional },
|
|
1117
|
+
...form.closed ? { closed: true } : {}
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* This function is the split rule of `fits` (TYPE.FITS-FINITE, D84, TYPE.FITS-PATH, D89). A record type fits when each
|
|
1122
|
+
* record of its split fits. A split takes the first field in the order of `splitsOf`. Each member of the field gives a
|
|
1123
|
+
* record with the field narrowed to that member. An optional field also gives a record without the field. A type split
|
|
1124
|
+
* narrows the field to each case type instead (TYPE.FITS-SPLIT-TYPE, D90).
|
|
1125
|
+
*
|
|
1126
|
+
* A record of the split that does not fit splits the next such field. A record of a type split first splits its case
|
|
1127
|
+
* again, at the same path. One budget of 64 records serves the outer split and each split inside its work, at each
|
|
1128
|
+
* depth. Thus nested splits do not multiply the work.
|
|
1129
|
+
*
|
|
1130
|
+
* A cover split tries each valid cover of its field until one fits (D102). Each try takes from the same budget.
|
|
1131
|
+
*/
|
|
1132
|
+
function splitFits(found, a, expected, domains, depth, gradual, budget) {
|
|
1133
|
+
const splits = splitsOf(a, domains, depth);
|
|
1134
|
+
if (splits.length === 0) return false;
|
|
1135
|
+
return casesFit(found, a.closed, splits, 0, expected, domains, depth, gradual, budget ?? { left: MAX_SPLIT_RECORDS });
|
|
1136
|
+
}
|
|
1137
|
+
/** 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. */
|
|
1138
|
+
function casesFit(type, closed, splits, index, expected, domains, depth, gradual, budget) {
|
|
1139
|
+
const split = splits[index];
|
|
1140
|
+
if (split === void 0) return false;
|
|
1141
|
+
if (split.covers !== void 0) {
|
|
1142
|
+
for (const cover of split.covers) {
|
|
1143
|
+
const tried = [...splits];
|
|
1144
|
+
tried[index] = {
|
|
1145
|
+
path: split.path,
|
|
1146
|
+
types: cover,
|
|
1147
|
+
optional: split.optional
|
|
1148
|
+
};
|
|
1149
|
+
if (casesFit(type, closed, tried, index, expected, domains, depth, gradual, budget)) return true;
|
|
1150
|
+
}
|
|
1151
|
+
return false;
|
|
1152
|
+
}
|
|
1153
|
+
const cases = (split.values?.map((value) => ({ one: [value] })) ?? split.types ?? []).map((each) => narrowAt(type, split.path, 0, each, domains));
|
|
1154
|
+
if (split.optional) cases.push(narrowAt(type, split.path, 0, void 0, domains));
|
|
1155
|
+
for (const [at, record] of cases.entries()) {
|
|
1156
|
+
budget.left -= 1;
|
|
1157
|
+
if (budget.left < 0) return false;
|
|
1158
|
+
if (fitsAt(record, expected, domains, depth, gradual, false, budget)) continue;
|
|
1159
|
+
const values = closed ? valuesOf$1(record, domains, depth + 1) : void 0;
|
|
1160
|
+
if (values !== void 0) {
|
|
1161
|
+
if (values.every((value) => memberAt(value, expected, domains, depth + 1))) continue;
|
|
1162
|
+
return false;
|
|
1163
|
+
}
|
|
1164
|
+
const again = split.types !== void 0 && at < split.types.length ? againOf(split.types[at], split.path, domains, depth) : [];
|
|
1165
|
+
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;
|
|
1166
|
+
}
|
|
1167
|
+
return true;
|
|
781
1168
|
}
|
|
782
1169
|
/**
|
|
783
1170
|
* This function tells if each value of the type `found` can be where the type `expected` must be (TYPE.FITS). Each
|
|
@@ -794,9 +1181,35 @@ function fitsAt(found, expected, domains, depth, gradual = true) {
|
|
|
794
1181
|
* field is `unknown`.
|
|
795
1182
|
* - A function fits when it has the same or fewer parameters, each expected parameter fits its parameter, and its
|
|
796
1183
|
* result fits.
|
|
797
|
-
*
|
|
798
|
-
*
|
|
799
|
-
*
|
|
1184
|
+
* - The finite rule comes after these rules (TYPE.FITS-FINITE). A type with at most 64 members fits when each member
|
|
1185
|
+
* is a member of `expected`. Thus `bool` fits `{ "one": [true, false] }`.
|
|
1186
|
+
* - A base type has few members when its domain or a parent declares `values`, as `null`, `undefined` and `bool` do.
|
|
1187
|
+
* A union, a `one`, a closed record of such types and a list of `never` also have few members.
|
|
1188
|
+
* - The cover rule comes next (TYPE.FITS-COVER, D85). A base type fits a union or a `one` when each domain of a cover
|
|
1189
|
+
* fits. The cover is in `covers` of a domain of its line. Each domain of a cover is under the covered
|
|
1190
|
+
* domain. Thus a cover of `number` by `neg` and `nonneg` gives `number` a fit against `neg | nonneg`.
|
|
1191
|
+
* - The split rule comes last (D84). An open record, or a closed record with more than 64 members, splits a field with
|
|
1192
|
+
* few members over a union. A closed record also splits over a `one`. Each record of the split narrows the field to
|
|
1193
|
+
* one member, and each record must fit.
|
|
1194
|
+
* Thus `{ "record": { "a": "bool" } }` fits a union of a record for each value of `a`.
|
|
1195
|
+
* - A split also narrows a field of a record in a field, by its path (TYPE.FITS-PATH, D89). The fields of the record
|
|
1196
|
+
* split first, then the paths of each next depth, in the order of the keys. A path goes into a record without a
|
|
1197
|
+
* finite list. Thus `{a: {x: bool}}` fits the union of `{a: {x: true}}` and `{a: {x: false}}`.
|
|
1198
|
+
* - A type split comes after each value split (TYPE.FITS-SPLIT-TYPE, D90). A field of a base type with a valid cover in
|
|
1199
|
+
* its line splits into the domains of a cover. A field of a union without a finite list splits into its
|
|
1200
|
+
* members. A case that does not fit splits again at its path, by the same rules. A base type there uses only the
|
|
1201
|
+
* covers of its own domain. Thus `{a: number}` fits `{a: neg} | {a: nonneg}` when `neg` and `nonneg` cover `number`.
|
|
1202
|
+
* - A field with more than one valid cover tries each cover until one fits (TYPE.FITS-SPLIT-COVERS, D102). The order
|
|
1203
|
+
* is the order of the line, then the order of the covers of each domain.
|
|
1204
|
+
*
|
|
1205
|
+
* The relation is sound: a member of `found` is a member of `expected` when no `unknown` is in the types
|
|
1206
|
+
* (TYPE.FITS-SOUND). The finite rule trusts `values`, and the cover rule trusts `covers`. Thus a list without a member
|
|
1207
|
+
* or a false cover makes the relation unsound for that domain.
|
|
1208
|
+
*
|
|
1209
|
+
* The relation is not complete. A split builds at most 64 records, with each split inside its work at each depth. The
|
|
1210
|
+
* failed tries of covers take from these 64 records too. A domain without `values` gets no finite rule, and
|
|
1211
|
+
* neither does a type with more than 64 members that is not a record. The cover rule tests at most 64 domains of
|
|
1212
|
+
* covers. The function does not throw.
|
|
800
1213
|
*
|
|
801
1214
|
* @param found - The type that a part has.
|
|
802
1215
|
* @param expected - The type that the position needs.
|
|
@@ -807,7 +1220,7 @@ function fits(found, expected, domains) {
|
|
|
807
1220
|
return fitsAt(found, expected, domains, 0);
|
|
808
1221
|
}
|
|
809
1222
|
/** This function sets an own field of a new record. Only the key `__proto__` needs `defineProperty`. */
|
|
810
|
-
function setOwn$
|
|
1223
|
+
function setOwn$6(target, key, value) {
|
|
811
1224
|
if (key === "__proto__") Object.defineProperty(target, key, {
|
|
812
1225
|
value,
|
|
813
1226
|
enumerable: true,
|
|
@@ -826,10 +1239,10 @@ function joinRecords(a, b, domains, depth) {
|
|
|
826
1239
|
const inA = Object.hasOwn(a.fields, key);
|
|
827
1240
|
const inB = Object.hasOwn(b.fields, key);
|
|
828
1241
|
if (inA && inB) {
|
|
829
|
-
setOwn$
|
|
1242
|
+
setOwn$6(fields, key, joinAt(a.fields[key], b.fields[key], domains, depth));
|
|
830
1243
|
if (a.optional.has(key) || b.optional.has(key)) optional.push(key);
|
|
831
1244
|
} else if (closed) {
|
|
832
|
-
setOwn$
|
|
1245
|
+
setOwn$6(fields, key, inA ? a.fields[key] : b.fields[key]);
|
|
833
1246
|
optional.push(key);
|
|
834
1247
|
}
|
|
835
1248
|
}
|
|
@@ -932,7 +1345,7 @@ function valueText$1(value) {
|
|
|
932
1345
|
function textAt(type, depth, wrap) {
|
|
933
1346
|
if (depth > MAX_TYPE_DEPTH) return "unknown";
|
|
934
1347
|
if (typeof type === "string") return type;
|
|
935
|
-
const term = isRecord(type) ? termOf$
|
|
1348
|
+
const term = isRecord(type) ? termOf$2(type) : void 0;
|
|
936
1349
|
if (!isRecord(type) || term === void 0) return "unknown";
|
|
937
1350
|
const next = depth + 1;
|
|
938
1351
|
const group = (items) => items.length === 0 ? "never" : items.length > 1 && wrap ? `(${items.join(" | ")})` : items.join(" | ");
|
|
@@ -1155,7 +1568,7 @@ function typeIssues(type, domains) {
|
|
|
1155
1568
|
if (here !== "unknown" && here !== "never" && domainOf(here, domains) === void 0) issue(`The machine has no domain ${here}.`);
|
|
1156
1569
|
continue;
|
|
1157
1570
|
}
|
|
1158
|
-
const term = isRecord(here) ? termOf$
|
|
1571
|
+
const term = isRecord(here) ? termOf$2(here) : void 0;
|
|
1159
1572
|
if (!isRecord(here) || term === void 0) {
|
|
1160
1573
|
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
1574
|
continue;
|
|
@@ -1211,8 +1624,7 @@ function typeIssues(type, domains) {
|
|
|
1211
1624
|
}
|
|
1212
1625
|
return issues;
|
|
1213
1626
|
}
|
|
1214
|
-
|
|
1215
|
-
//#region src/merge.ts
|
|
1627
|
+
// src/merge.ts
|
|
1216
1628
|
/** The exception of a merge with conflicts. It lists each conflict of the merge, not only the first. */
|
|
1217
1629
|
var MergeError = class extends Error {
|
|
1218
1630
|
conflicts;
|
|
@@ -1233,7 +1645,7 @@ function keyOf(path) {
|
|
|
1233
1645
|
return JSON.stringify(path.map(String));
|
|
1234
1646
|
}
|
|
1235
1647
|
/** This function sets an own data property. An assignment to the key `__proto__` changes the prototype. */
|
|
1236
|
-
function setOwn$
|
|
1648
|
+
function setOwn$5(target, key, value) {
|
|
1237
1649
|
if (key === "__proto__") Object.defineProperty(target, key, {
|
|
1238
1650
|
value,
|
|
1239
1651
|
enumerable: true,
|
|
@@ -1250,7 +1662,7 @@ function defined(value) {
|
|
|
1250
1662
|
if (Array.isArray(value)) return value.map(defined);
|
|
1251
1663
|
if (!isRecord(value)) return value;
|
|
1252
1664
|
const out = {};
|
|
1253
|
-
for (const key of Object.keys(value)) if (value[key] !== void 0) setOwn$
|
|
1665
|
+
for (const key of Object.keys(value)) if (value[key] !== void 0) setOwn$5(out, key, defined(value[key]));
|
|
1254
1666
|
return out;
|
|
1255
1667
|
}
|
|
1256
1668
|
/** Two values are the same when they are equal by `docEquals`. A function and a host value are equal only to themselves. */
|
|
@@ -1259,23 +1671,39 @@ function same(a, b) {
|
|
|
1259
1671
|
}
|
|
1260
1672
|
/**
|
|
1261
1673
|
* 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
|
|
1674
|
+
* parents, summary, steps, codec, list of values, samples and covers. Thus two languages that give one test under one name give one
|
|
1675
|
+
* domain. Two codecs are the same when they have the same functions `encode` and `decode` (D80).
|
|
1263
1676
|
*/
|
|
1264
1677
|
function sameDomain(a, b) {
|
|
1265
|
-
|
|
1678
|
+
if (a === b) return true;
|
|
1679
|
+
const fields = {
|
|
1680
|
+
is: a.is === b.is,
|
|
1681
|
+
under: same(a.under ?? [], b.under ?? []),
|
|
1682
|
+
summary: a.summary === b.summary,
|
|
1683
|
+
steps: same(a.steps ?? {}, b.steps ?? {}),
|
|
1684
|
+
codec: same(a.codec ?? null, b.codec ?? null),
|
|
1685
|
+
values: same(a.values ?? null, b.values ?? null),
|
|
1686
|
+
samples: same(a.samples ?? null, b.samples ?? null),
|
|
1687
|
+
covers: same(a.covers ?? [], b.covers ?? [])
|
|
1688
|
+
};
|
|
1689
|
+
return Object.values(fields).every(Boolean);
|
|
1266
1690
|
}
|
|
1267
1691
|
/**
|
|
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
|
|
1271
|
-
*
|
|
1692
|
+
* This function makes a part of a merge that adds parents, steps or covers to a domain of an earlier part
|
|
1693
|
+
* (TYPE.PARENTS). An example is a domain of another language. The part reads the domain from the merge before it
|
|
1694
|
+
* (`prev`). It gives a copy with the same test, summary, codec, list of values and samples, and it names
|
|
1695
|
+
* `["domains", name]` in `replaces` (D17).
|
|
1696
|
+
* A domain of the kernel that the earlier parts do not name comes from `STD_DOMAINS`.
|
|
1697
|
+
*
|
|
1698
|
+
* An extension does not change the codec. Thus each member keeps its document (D80).
|
|
1272
1699
|
*
|
|
1273
1700
|
* A new parent adds its test to the domain (TYPE.MEMBER). Thus `fits` stays sound, but a value that fails the test of
|
|
1274
1701
|
* the parent is no longer a member. The language that adds the parent keeps the signatures of the other language
|
|
1275
|
-
* true. A
|
|
1702
|
+
* true. A new parent of a domain in a cover of another domain can make that cover false (D85). A name without a
|
|
1703
|
+
* domain gives an empty part, and `typeIssues` reports each use of that name.
|
|
1276
1704
|
*
|
|
1277
1705
|
* @param name - The name of the domain.
|
|
1278
|
-
* @param more - The parents and the
|
|
1706
|
+
* @param more - The parents, the steps and the covers to add.
|
|
1279
1707
|
* @returns The part, as a function of the merge before it.
|
|
1280
1708
|
*/
|
|
1281
1709
|
function extendDomain(name, more) {
|
|
@@ -1288,14 +1716,22 @@ function extendDomain(name, more) {
|
|
|
1288
1716
|
...found.steps,
|
|
1289
1717
|
...more.steps
|
|
1290
1718
|
};
|
|
1291
|
-
const
|
|
1719
|
+
const covers = [...found.covers ?? []];
|
|
1720
|
+
for (const cover of more.covers ?? []) if (!covers.some((each) => same(each, cover))) covers.push([...cover]);
|
|
1721
|
+
const fields = {
|
|
1292
1722
|
is: found.is,
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1723
|
+
under: under.length === 0 ? void 0 : under,
|
|
1724
|
+
summary: found.summary,
|
|
1725
|
+
steps: Object.keys(steps).length === 0 ? void 0 : steps,
|
|
1726
|
+
codec: found.codec,
|
|
1727
|
+
values: found.values,
|
|
1728
|
+
samples: found.samples,
|
|
1729
|
+
covers: covers.length === 0 ? void 0 : covers
|
|
1296
1730
|
};
|
|
1731
|
+
const domain = {};
|
|
1732
|
+
for (const [key, value] of Object.entries(fields)) if (value !== void 0) domain[key] = value;
|
|
1297
1733
|
const domains = {};
|
|
1298
|
-
setOwn$
|
|
1734
|
+
setOwn$5(domains, name, domain);
|
|
1299
1735
|
return {
|
|
1300
1736
|
domains,
|
|
1301
1737
|
replaces: [["domains", name]]
|
|
@@ -1345,10 +1781,10 @@ function mergeLeaf(m, table, key, later, path, part, replaces, equal, reason) {
|
|
|
1345
1781
|
if (later === void 0) return;
|
|
1346
1782
|
const earlier = own$2(table, key);
|
|
1347
1783
|
if (earlier === void 0) {
|
|
1348
|
-
setOwn$
|
|
1784
|
+
setOwn$5(table, key, later);
|
|
1349
1785
|
m.give(path, part);
|
|
1350
1786
|
} else if (replaces.has(keyOf(path))) {
|
|
1351
|
-
setOwn$
|
|
1787
|
+
setOwn$5(table, key, later);
|
|
1352
1788
|
m.replace(path, part);
|
|
1353
1789
|
} else if (!equal(earlier, later)) m.clash(path, part, reason);
|
|
1354
1790
|
}
|
|
@@ -1369,9 +1805,9 @@ function mergeEntries(m, into, from, path, part, replaces) {
|
|
|
1369
1805
|
const at = [...path, key];
|
|
1370
1806
|
const earlier = own$2(into, key);
|
|
1371
1807
|
if (earlier === void 0) {
|
|
1372
|
-
setOwn$
|
|
1808
|
+
setOwn$5(into, key, later);
|
|
1373
1809
|
m.give(at, part);
|
|
1374
|
-
} else setOwn$
|
|
1810
|
+
} else setOwn$5(into, key, mergeEntry(m, earlier, later, at, part, replaces));
|
|
1375
1811
|
}
|
|
1376
1812
|
}
|
|
1377
1813
|
/** This function merges two entries at one path. Two namespaces merge key by key, and their fallbacks merge too. */
|
|
@@ -1524,8 +1960,8 @@ function mergeCode(m, draft, part, index, replaces) {
|
|
|
1524
1960
|
const runnerPath = ["runners", name];
|
|
1525
1961
|
if (replaces.has(keyOf(evaluatorPath)) || replaces.has(keyOf(runnerPath))) {
|
|
1526
1962
|
if (evaluator === void 0) delete draft.evaluators[name];
|
|
1527
|
-
else setOwn$
|
|
1528
|
-
if (runner !== void 0) setOwn$
|
|
1963
|
+
else setOwn$5(draft.evaluators, name, evaluator);
|
|
1964
|
+
if (runner !== void 0) setOwn$5(draft.runners ??= {}, name, runner);
|
|
1529
1965
|
else if (draft.runners !== void 0) delete draft.runners[name];
|
|
1530
1966
|
m.replace(evaluatorPath, index);
|
|
1531
1967
|
m.replace(runnerPath, index);
|
|
@@ -1648,7 +2084,7 @@ function removeEntry(entries, path) {
|
|
|
1648
2084
|
}
|
|
1649
2085
|
const out = { ...entries };
|
|
1650
2086
|
if (next === void 0) delete out[key];
|
|
1651
|
-
else setOwn$
|
|
2087
|
+
else setOwn$5(out, key, next);
|
|
1652
2088
|
return out;
|
|
1653
2089
|
}
|
|
1654
2090
|
/**
|
|
@@ -1734,8 +2170,7 @@ function restrictMachine(machine, paths) {
|
|
|
1734
2170
|
...tables.domains === void 0 ? {} : { domains: tables.domains }
|
|
1735
2171
|
};
|
|
1736
2172
|
}
|
|
1737
|
-
|
|
1738
|
-
//#region src/ladder.ts
|
|
2173
|
+
// src/ladder.ts
|
|
1739
2174
|
/** The rungs of the ladder, in the order that a question visits them. */
|
|
1740
2175
|
const RUNGS = [
|
|
1741
2176
|
"own",
|
|
@@ -1903,8 +2338,7 @@ function ask(world, doc, verb, options = {}) {
|
|
|
1903
2338
|
steps
|
|
1904
2339
|
};
|
|
1905
2340
|
}
|
|
1906
|
-
|
|
1907
|
-
//#region src/deref.ts
|
|
2341
|
+
// src/deref.ts
|
|
1908
2342
|
/**
|
|
1909
2343
|
* The brand of a host thing: an object of the host that answers the steps of a path itself. The brand is a
|
|
1910
2344
|
* global symbol, thus two copies of the kernel in one program know the things of each other.
|
|
@@ -2043,11 +2477,12 @@ function derefStep(nav, thing, key) {
|
|
|
2043
2477
|
const derefs = nav.derefs;
|
|
2044
2478
|
const answer = derefs === void 0 ? void 0 : answerOf(nav.world, ownEntries(envelope), kindFor(nav.world, envelope.type), "deref", (name) => codeIn(derefs, name));
|
|
2045
2479
|
if (answer !== void 0) {
|
|
2046
|
-
const
|
|
2047
|
-
|
|
2480
|
+
const code = derefs[answer.name];
|
|
2481
|
+
const reached = code?.(envelope, key);
|
|
2482
|
+
return reached !== void 0 || code?.has?.(envelope, key) === true ? {
|
|
2048
2483
|
thing: reached,
|
|
2049
2484
|
by: answer.rung
|
|
2050
|
-
};
|
|
2485
|
+
} : void 0;
|
|
2051
2486
|
}
|
|
2052
2487
|
const reached = plainStep(envelope.value, key);
|
|
2053
2488
|
return reached === NOTHING ? void 0 : {
|
|
@@ -2285,6 +2720,25 @@ function resolve(nav, ctx, path, options = {}) {
|
|
|
2285
2720
|
}
|
|
2286
2721
|
return resolveFully$1(nav, ctx, path, options.hops === false ? void 0 : [], void 0);
|
|
2287
2722
|
}
|
|
2723
|
+
/** The functions of the last navigator that gave a walker its input. A walk with the same navigator makes no new functions. */
|
|
2724
|
+
let lastSteps;
|
|
2725
|
+
/**
|
|
2726
|
+
* This function gives the functions `deref` and `has` of the input of a walker. They depend only on the navigator,
|
|
2727
|
+
* thus the function keeps the functions of the last navigator (K170).
|
|
2728
|
+
*/
|
|
2729
|
+
function walkSteps(nav) {
|
|
2730
|
+
if (lastSteps !== void 0 && lastSteps.nav === nav) return lastSteps;
|
|
2731
|
+
const adopt = nav.adopt;
|
|
2732
|
+
lastSteps = {
|
|
2733
|
+
nav,
|
|
2734
|
+
deref: adopt === void 0 ? (thing, key) => derefStep(nav, thing, key)?.thing : (thing, key) => {
|
|
2735
|
+
const reached = derefStep(nav, thing, key);
|
|
2736
|
+
return reached === void 0 ? void 0 : adopt(reached.thing);
|
|
2737
|
+
},
|
|
2738
|
+
has: (thing, key) => derefStep(nav, thing, key) !== void 0
|
|
2739
|
+
};
|
|
2740
|
+
return lastSteps;
|
|
2741
|
+
}
|
|
2288
2742
|
/**
|
|
2289
2743
|
* This function resolves a path with the full algorithm of `resolve`.
|
|
2290
2744
|
*
|
|
@@ -2379,20 +2833,19 @@ function resolveFully$1(nav, ctx, path, hops, known) {
|
|
|
2379
2833
|
const walker = given === void 0 ? walkerOf(nav, step) : given ?? void 0;
|
|
2380
2834
|
if (walker === void 0) return failed("#STEP?", "A step is a key, or a document of a kind with the verb step.");
|
|
2381
2835
|
const envelope = step;
|
|
2382
|
-
const deref = (
|
|
2383
|
-
const reached = derefStep(nav, thing, key);
|
|
2384
|
-
return reached === void 0 ? void 0 : adopt(reached.thing);
|
|
2385
|
-
};
|
|
2836
|
+
const { deref, has } = walkSteps(nav);
|
|
2386
2837
|
const result = walker(hops === void 0 ? {
|
|
2387
2838
|
data: envelope.value,
|
|
2388
2839
|
here,
|
|
2389
2840
|
context: ctx,
|
|
2390
|
-
deref
|
|
2841
|
+
deref,
|
|
2842
|
+
has
|
|
2391
2843
|
} : {
|
|
2392
2844
|
data: envelope.value,
|
|
2393
2845
|
here,
|
|
2394
2846
|
context: ctx,
|
|
2395
2847
|
deref,
|
|
2848
|
+
has,
|
|
2396
2849
|
visit: (key, thing) => hops.push({
|
|
2397
2850
|
step: key,
|
|
2398
2851
|
thing,
|
|
@@ -2429,8 +2882,7 @@ function resolveFully$1(nav, ctx, path, hops, known) {
|
|
|
2429
2882
|
function valueOfThing(thing) {
|
|
2430
2883
|
return isHostThing(thing) ? thing.toJson() : thing;
|
|
2431
2884
|
}
|
|
2432
|
-
|
|
2433
|
-
//#region src/classify.ts
|
|
2885
|
+
// src/classify.ts
|
|
2434
2886
|
function isStrictSuperset(larger, smaller) {
|
|
2435
2887
|
return larger.length > smaller.length && smaller.every((name) => larger.includes(name));
|
|
2436
2888
|
}
|
|
@@ -2468,8 +2920,7 @@ function classify(shapes, value) {
|
|
|
2468
2920
|
function tag(value, kind) {
|
|
2469
2921
|
return typed(kind, value);
|
|
2470
2922
|
}
|
|
2471
|
-
|
|
2472
|
-
//#region src/parts.ts
|
|
2923
|
+
// src/parts.ts
|
|
2473
2924
|
var Bad = class {
|
|
2474
2925
|
reason;
|
|
2475
2926
|
at;
|
|
@@ -2479,7 +2930,7 @@ var Bad = class {
|
|
|
2479
2930
|
}
|
|
2480
2931
|
};
|
|
2481
2932
|
/** This function sets an own data property. Only the key `__proto__` needs `defineProperty`. */
|
|
2482
|
-
function setOwn$
|
|
2933
|
+
function setOwn$4(target, key, value) {
|
|
2483
2934
|
if (key === "__proto__") Object.defineProperty(target, key, {
|
|
2484
2935
|
value,
|
|
2485
2936
|
enumerable: true,
|
|
@@ -2493,7 +2944,7 @@ function hasSlots(template) {
|
|
|
2493
2944
|
return template.t !== "data";
|
|
2494
2945
|
}
|
|
2495
2946
|
/** This function gives the shape of a part with the default type `of` and the value type `expect` of its container. */
|
|
2496
|
-
function termOf(of, expect) {
|
|
2947
|
+
function termOf$1(of, expect) {
|
|
2497
2948
|
return {
|
|
2498
2949
|
shape: "term",
|
|
2499
2950
|
...of === void 0 ? {} : { of },
|
|
@@ -2602,7 +3053,7 @@ function splitParts(shape, value) {
|
|
|
2602
3053
|
const twice = list.findIndex((child, index) => list.slice(0, index).some((before) => docEquals(before, child)));
|
|
2603
3054
|
if (twice !== -1) throw new Bad("The items of the list must be different.", [...path, twice]);
|
|
2604
3055
|
}
|
|
2605
|
-
const each = spec.item ?? termOf(spec.of, spec.expect);
|
|
3056
|
+
const each = spec.item ?? termOf$1(spec.of, spec.expect);
|
|
2606
3057
|
const items = list.map((child, index) => walk(each, child, [...path, index]));
|
|
2607
3058
|
return isData(each) ? {
|
|
2608
3059
|
t: "data",
|
|
@@ -2614,14 +3065,14 @@ function splitParts(shape, value) {
|
|
|
2614
3065
|
}
|
|
2615
3066
|
if (!isRecord(item)) throw new Bad("The value must be a record.", path);
|
|
2616
3067
|
if (spec.shape === "map") {
|
|
2617
|
-
const each = spec.item ?? termOf(spec.of, spec.expect);
|
|
3068
|
+
const each = spec.item ?? termOf$1(spec.of, spec.expect);
|
|
2618
3069
|
return {
|
|
2619
3070
|
t: "record",
|
|
2620
3071
|
fields: Object.keys(item).map((key) => [key, walk(each, item[key], [...path, key])])
|
|
2621
3072
|
};
|
|
2622
3073
|
}
|
|
2623
3074
|
for (const name of spec.required ?? []) if (!Object.hasOwn(item, name)) throw new Bad(`The field ${name} is missing.`, path);
|
|
2624
|
-
const named = Array.isArray(spec.fields) ? Object.fromEntries(spec.fields.map((name) => [name, termOf(spec.of, spec.expect)])) : spec.fields;
|
|
3075
|
+
const named = Array.isArray(spec.fields) ? Object.fromEntries(spec.fields.map((name) => [name, termOf$1(spec.of, spec.expect)])) : spec.fields;
|
|
2625
3076
|
const fields = [];
|
|
2626
3077
|
for (const key of Object.keys(item)) {
|
|
2627
3078
|
if (spec.closed === true && !Object.hasOwn(named, key)) throw new Bad(`The field ${key} is not a field of this kind.`, [...path, key]);
|
|
@@ -2651,7 +3102,7 @@ function splitParts(shape, value) {
|
|
|
2651
3102
|
const out = {};
|
|
2652
3103
|
for (const [key, child] of node.fields) {
|
|
2653
3104
|
const kept = strip(child);
|
|
2654
|
-
if (kept !== void 0) setOwn$
|
|
3105
|
+
if (kept !== void 0) setOwn$4(out, key, kept);
|
|
2655
3106
|
}
|
|
2656
3107
|
return out;
|
|
2657
3108
|
};
|
|
@@ -2660,7 +3111,7 @@ function splitParts(shape, value) {
|
|
|
2660
3111
|
if (node.t === "slot") return handles[node.index];
|
|
2661
3112
|
if (node.t === "list") return node.items.map((child) => fillWith(child, handles));
|
|
2662
3113
|
const out = {};
|
|
2663
|
-
for (const [key, child] of node.fields) setOwn$
|
|
3114
|
+
for (const [key, child] of node.fields) setOwn$4(out, key, fillWith(child, handles));
|
|
2664
3115
|
return out;
|
|
2665
3116
|
};
|
|
2666
3117
|
return {
|
|
@@ -2803,8 +3254,7 @@ function scopesOf(rules, value, slots) {
|
|
|
2803
3254
|
later
|
|
2804
3255
|
};
|
|
2805
3256
|
}
|
|
2806
|
-
|
|
2807
|
-
//#region src/evaluate.ts
|
|
3257
|
+
// src/evaluate.ts
|
|
2808
3258
|
/**
|
|
2809
3259
|
* This function makes a context with names and capabilities. The other roots are empty, and an evaluation sets
|
|
2810
3260
|
* the world.
|
|
@@ -2918,10 +3368,19 @@ const GRANT = /* @__PURE__ */ Symbol("coeval.grant");
|
|
|
2918
3368
|
/**
|
|
2919
3369
|
* The key of the epoch of a grant (CAP.NARROW-KEPT). The epoch is the list of capabilities that the host gave the root
|
|
2920
3370
|
* of the evaluation of the grant. A spread of a context keeps its epoch, as it keeps its grant. An
|
|
2921
|
-
* effect and a resolution read a context of another epoch as a context without a grant.
|
|
3371
|
+
* effect and a resolution read a context of another epoch as a context without a grant. The root has no field of
|
|
3372
|
+
* the epoch, because its grant is the list of the host (`epochOf`).
|
|
2922
3373
|
*/
|
|
2923
3374
|
const EPOCH = /* @__PURE__ */ Symbol("coeval.epoch");
|
|
2924
3375
|
/**
|
|
3376
|
+
* This function gives the epoch of the grant of a context (CAP.NARROW-KEPT). A context without the field of the epoch
|
|
3377
|
+
* 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
|
|
3378
|
+
* one field fewer, and each evaluation makes it faster (K154).
|
|
3379
|
+
*/
|
|
3380
|
+
function epochOf(ctx) {
|
|
3381
|
+
return ctx[EPOCH] ?? ctx[GRANT];
|
|
3382
|
+
}
|
|
3383
|
+
/**
|
|
2925
3384
|
* This function gives the capabilities that a context and its grant both permit, the meet `intersectCaps` (CAP.MEET).
|
|
2926
3385
|
* A context whose grant is its own list gives that list after one comparison. A context without a grant gives none.
|
|
2927
3386
|
*/
|
|
@@ -2941,10 +3400,36 @@ function granted(ctx) {
|
|
|
2941
3400
|
return {
|
|
2942
3401
|
...ctx,
|
|
2943
3402
|
caps,
|
|
2944
|
-
[GRANT]: caps
|
|
3403
|
+
[GRANT]: caps,
|
|
3404
|
+
[EPOCH]: epochOf(ctx)
|
|
2945
3405
|
};
|
|
2946
3406
|
}
|
|
2947
3407
|
/**
|
|
3408
|
+
* This function tells if a list of capabilities is equal to the epoch of the evaluation (CAP.EPOCH-EQUAL). An equal list
|
|
3409
|
+
* has the same length, and at each index the same text or the same keys in the same order. The runtime uses it only
|
|
3410
|
+
* when the two lists are not the same object. Thus a host that gives a new copy of its list for each evaluation keeps
|
|
3411
|
+
* the grant of a kept context.
|
|
3412
|
+
*/
|
|
3413
|
+
function sameCaps(caps, epoch) {
|
|
3414
|
+
if (caps === void 0 || caps.length !== epoch.length) return false;
|
|
3415
|
+
for (let index = 0; index < caps.length; index += 1) {
|
|
3416
|
+
const a = caps[index];
|
|
3417
|
+
const b = epoch[index];
|
|
3418
|
+
if (a === b) continue;
|
|
3419
|
+
if (typeof a === "string" || typeof b === "string" || a.length !== b.length) return false;
|
|
3420
|
+
for (let key = 0; key < a.length; key += 1) if (!Object.is(a[key], b[key])) return false;
|
|
3421
|
+
}
|
|
3422
|
+
return true;
|
|
3423
|
+
}
|
|
3424
|
+
/**
|
|
3425
|
+
* This function tells if a context has the epoch of the evaluation: the same list, or an equal list (CAP.NARROW-KEPT,
|
|
3426
|
+
* CAP.EPOCH-EQUAL). The comparison of the entries happens only when the two lists are not the same object.
|
|
3427
|
+
*/
|
|
3428
|
+
function inEpoch(ctx, epoch) {
|
|
3429
|
+
const own = epochOf(ctx);
|
|
3430
|
+
return own === epoch || sameCaps(own, epoch);
|
|
3431
|
+
}
|
|
3432
|
+
/**
|
|
2948
3433
|
* This function gives a context of another epoch the epoch of the evaluation, without capabilities (CAP.NARROW-KEPT).
|
|
2949
3434
|
* Thus the context reads as a context without a grant. The kernel cannot know the document of the context in this
|
|
2950
3435
|
* evaluation.
|
|
@@ -2962,16 +3447,26 @@ function ungranted(ctx, epoch) {
|
|
|
2962
3447
|
* 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
|
|
2963
3448
|
* `caps` and a walker of a step document get the capabilities of the document from a widened context.
|
|
2964
3449
|
* A context that the code narrowed keeps its own list. A context whose list does not change stays the same object.
|
|
2965
|
-
*
|
|
3450
|
+
*
|
|
3451
|
+
* A context of another epoch has no capabilities (CAP.NARROW-KEPT). An equal list of the host is the same epoch
|
|
3452
|
+
* (CAP.EPOCH-EQUAL). The first test answers for a context of the epoch whose grant is its list, for example the root.
|
|
3453
|
+
* The other contexts go to `readableOther`, thus the hot part of the function stays small (K154).
|
|
2966
3454
|
*/
|
|
2967
3455
|
function readable(ctx, epoch) {
|
|
2968
|
-
|
|
3456
|
+
const grant = ctx[GRANT];
|
|
3457
|
+
if (grant === ctx.caps && (ctx[EPOCH] ?? grant) === epoch) return ctx;
|
|
3458
|
+
return readableOther(ctx, epoch);
|
|
3459
|
+
}
|
|
3460
|
+
/** This function gives the result of `readable` for each other context: a context of another epoch or a narrowed context. */
|
|
3461
|
+
function readableOther(ctx, epoch) {
|
|
3462
|
+
if (!inEpoch(ctx, epoch)) return ctx.caps.length === 0 ? ctx : ungranted(ctx, epoch);
|
|
2969
3463
|
const caps = grantedCaps(ctx);
|
|
2970
3464
|
if (caps === ctx.caps || caps.length === 0 && ctx.caps.length === 0) return ctx;
|
|
2971
3465
|
return {
|
|
2972
3466
|
...ctx,
|
|
2973
3467
|
caps,
|
|
2974
|
-
[GRANT]: caps
|
|
3468
|
+
[GRANT]: caps,
|
|
3469
|
+
[EPOCH]: epochOf(ctx)
|
|
2975
3470
|
};
|
|
2976
3471
|
}
|
|
2977
3472
|
/**
|
|
@@ -2981,8 +3476,8 @@ function readable(ctx, epoch) {
|
|
|
2981
3476
|
*/
|
|
2982
3477
|
function handed(parent, next) {
|
|
2983
3478
|
const ctx = narrow(parent, next);
|
|
2984
|
-
const epoch = parent
|
|
2985
|
-
if (ctx[GRANT] === ctx.caps && ctx
|
|
3479
|
+
const epoch = epochOf(parent);
|
|
3480
|
+
if (ctx[GRANT] === ctx.caps && epochOf(ctx) === epoch) return ctx;
|
|
2986
3481
|
return {
|
|
2987
3482
|
...ctx,
|
|
2988
3483
|
[GRANT]: ctx.caps,
|
|
@@ -2990,7 +3485,7 @@ function handed(parent, next) {
|
|
|
2990
3485
|
};
|
|
2991
3486
|
}
|
|
2992
3487
|
/** The text that names a resolution in the error of an exception of the host. */
|
|
2993
|
-
const READ_FAILED = "the resolution of a path";
|
|
3488
|
+
const READ_FAILED$1 = "the resolution of a path";
|
|
2994
3489
|
/**
|
|
2995
3490
|
* This function gives what a resolution reached as a result: the thing, or the error with its code and its reason.
|
|
2996
3491
|
* The record of a read and `verifyReads` share it.
|
|
@@ -3019,7 +3514,7 @@ function readAgain(nav, ctx, path, hops) {
|
|
|
3019
3514
|
hops: found.hops
|
|
3020
3515
|
} : { reached: reachedOf(found) };
|
|
3021
3516
|
} catch (error) {
|
|
3022
|
-
return { reached: thrown(error, READ_FAILED) };
|
|
3517
|
+
return { reached: thrown(error, READ_FAILED$1) };
|
|
3023
3518
|
}
|
|
3024
3519
|
}
|
|
3025
3520
|
/**
|
|
@@ -3124,7 +3619,7 @@ function keptPath(nav, root, path) {
|
|
|
3124
3619
|
} catch (error) {
|
|
3125
3620
|
return {
|
|
3126
3621
|
path: path.slice(0, index + 1),
|
|
3127
|
-
reached: thrown(error, READ_FAILED)
|
|
3622
|
+
reached: thrown(error, READ_FAILED$1)
|
|
3128
3623
|
};
|
|
3129
3624
|
}
|
|
3130
3625
|
if (step === void 0) return end(index);
|
|
@@ -3160,7 +3655,7 @@ function keptAgain(nav, ctx, path) {
|
|
|
3160
3655
|
try {
|
|
3161
3656
|
step = here.known(path[index]);
|
|
3162
3657
|
} catch (error) {
|
|
3163
|
-
return thrown(error, READ_FAILED);
|
|
3658
|
+
return thrown(error, READ_FAILED$1);
|
|
3164
3659
|
}
|
|
3165
3660
|
if (step === void 0) return void 0;
|
|
3166
3661
|
known = true;
|
|
@@ -3197,6 +3692,11 @@ var Session = class {
|
|
|
3197
3692
|
list;
|
|
3198
3693
|
tick = 0;
|
|
3199
3694
|
nextId = 0;
|
|
3695
|
+
/**
|
|
3696
|
+
* The count of the drives without a wait that are active at this time. In a run, an effect under such a drive does
|
|
3697
|
+
* not reach the host (RUN.HELD).
|
|
3698
|
+
*/
|
|
3699
|
+
held = 0;
|
|
3200
3700
|
current;
|
|
3201
3701
|
holder;
|
|
3202
3702
|
/**
|
|
@@ -3255,7 +3755,7 @@ var Session = class {
|
|
|
3255
3755
|
} catch (error) {
|
|
3256
3756
|
reading.list.push({
|
|
3257
3757
|
path,
|
|
3258
|
-
reached: thrown(error, READ_FAILED),
|
|
3758
|
+
reached: thrown(error, READ_FAILED$1),
|
|
3259
3759
|
free
|
|
3260
3760
|
});
|
|
3261
3761
|
throw error;
|
|
@@ -3343,10 +3843,10 @@ var Session = class {
|
|
|
3343
3843
|
* This function checks the capability of an effect, and records the effect with its address (RUN.ADDRESS). The
|
|
3344
3844
|
* context keeps only the capabilities of its grant, thus compiled code cannot widen the context of its own effect
|
|
3345
3845
|
* (CAP.NARROW-CODE). A context of another epoch has no capabilities, thus code cannot keep a context for a later
|
|
3346
|
-
* evaluation with fewer capabilities (CAP.NARROW-KEPT).
|
|
3846
|
+
* evaluation with fewer capabilities (CAP.NARROW-KEPT). An equal list of the host is the same epoch (CAP.EPOCH-EQUAL).
|
|
3347
3847
|
*/
|
|
3348
3848
|
record(given, kind, payload, place) {
|
|
3349
|
-
const ctx = given
|
|
3849
|
+
const ctx = inEpoch(given, this.epoch) ? granted(given) : ungranted(given, this.epoch);
|
|
3350
3850
|
const keys = typeof kind === "string" ? kind.split(".") : kind;
|
|
3351
3851
|
const text = keys.join(".");
|
|
3352
3852
|
if (!this.permitted(ctx, keys)) return fail("#CAP!", `The context has no capability for the effect ${text}.`, { place });
|
|
@@ -3404,6 +3904,11 @@ var Session = class {
|
|
|
3404
3904
|
if ("ok" in made) return made;
|
|
3405
3905
|
const perform = this.perform;
|
|
3406
3906
|
if (!this.running || perform === void 0) return this.answer(made, place);
|
|
3907
|
+
if (this.held > 0) {
|
|
3908
|
+
made.outcome = fail("#CALC!", heldReason(made.kind), { place });
|
|
3909
|
+
yield HELD;
|
|
3910
|
+
return made.outcome;
|
|
3911
|
+
}
|
|
3407
3912
|
let done;
|
|
3408
3913
|
try {
|
|
3409
3914
|
done = perform(made);
|
|
@@ -3429,6 +3934,7 @@ var Session = class {
|
|
|
3429
3934
|
let failed = false;
|
|
3430
3935
|
const active = [];
|
|
3431
3936
|
let next = 0;
|
|
3937
|
+
let before = 0;
|
|
3432
3938
|
const settle = (index, result) => {
|
|
3433
3939
|
results[index] = result;
|
|
3434
3940
|
if (failFast && !result.ok) failed = true;
|
|
@@ -3492,10 +3998,11 @@ var Session = class {
|
|
|
3492
3998
|
};
|
|
3493
3999
|
start();
|
|
3494
4000
|
while (active.length > 0 && !failed) {
|
|
3495
|
-
const chosen = journal?.next?.(address, active.map((item) => item.index));
|
|
4001
|
+
const chosen = journal?.next?.(address, active.map((item) => item.index), before);
|
|
3496
4002
|
const pick = chosen === void 0 ? void 0 : active.find((item) => item.index === chosen);
|
|
3497
4003
|
const settled = yield pick === void 0 ? Promise.race(active.map((item) => item.pending)) : pick.pending;
|
|
3498
4004
|
journal?.settled?.(address, settled.item.index);
|
|
4005
|
+
before += 1;
|
|
3499
4006
|
active.splice(active.indexOf(settled.item), 1);
|
|
3500
4007
|
advance(settled.item.index, settled.item.steps, settled);
|
|
3501
4008
|
start();
|
|
@@ -3581,6 +4088,18 @@ function holderNode() {
|
|
|
3581
4088
|
function waitedReason(kindText) {
|
|
3582
4089
|
return `${kindText} waits for effects, and its parent cannot wait. Give the parent a runner.`;
|
|
3583
4090
|
}
|
|
4091
|
+
/**
|
|
4092
|
+
* This function gives the reason of an effect that a run does not give to the host: the parent of its runner cannot
|
|
4093
|
+
* wait (RUN.HELD). The plan and the reference give the same reason.
|
|
4094
|
+
*
|
|
4095
|
+
* @param kind - The kind of the effect.
|
|
4096
|
+
* @returns The reason.
|
|
4097
|
+
*/
|
|
4098
|
+
function heldReason(kind) {
|
|
4099
|
+
return `The parent of the runner cannot wait, thus the host does not get the effect ${typeof kind === "string" ? kind : kind.join(".")}.`;
|
|
4100
|
+
}
|
|
4101
|
+
/** The wait of an effect that a run does not give to the host. A drive without a wait stops at it (RUN.HELD). */
|
|
4102
|
+
const HELD = /* @__PURE__ */ Promise.resolve(void 0);
|
|
3584
4103
|
/** This function drives steps that cannot wait. A step that waits stops the steps, and gives `#CALC!`. */
|
|
3585
4104
|
function drive$2(steps, kindText) {
|
|
3586
4105
|
const step = steps.next();
|
|
@@ -4184,7 +4703,8 @@ function gated(rules, part) {
|
|
|
4184
4703
|
return {
|
|
4185
4704
|
...ctx,
|
|
4186
4705
|
caps,
|
|
4187
|
-
[GRANT]: caps
|
|
4706
|
+
[GRANT]: caps,
|
|
4707
|
+
[EPOCH]: epochOf(ctx)
|
|
4188
4708
|
};
|
|
4189
4709
|
};
|
|
4190
4710
|
return {
|
|
@@ -4208,10 +4728,19 @@ function placedRun(inner, place, name) {
|
|
|
4208
4728
|
/**
|
|
4209
4729
|
* This function gives the code of a runner under a parent that cannot wait: its steps, driven without a wait. An
|
|
4210
4730
|
* evaluation and a run both drive them (RUN.DRIVE-NESTED). Only a step that really waits gives `#CALC!`, at the place
|
|
4211
|
-
* of the runner, where the error started (RUN.WAIT).
|
|
4731
|
+
* of the runner, where the error started (RUN.WAIT). In a run, an effect under the runner does not reach the host,
|
|
4732
|
+
* because the parent cannot wait for its answer (RUN.HELD).
|
|
4212
4733
|
*/
|
|
4213
4734
|
function runnerCode(run, place, kindText) {
|
|
4214
|
-
return (ctx, rt) =>
|
|
4735
|
+
return (ctx, rt) => {
|
|
4736
|
+
if (!rt.running || !(rt instanceof Session)) return placed(drive$2(run(ctx, rt), kindText), place);
|
|
4737
|
+
rt.held += 1;
|
|
4738
|
+
try {
|
|
4739
|
+
return placed(drive$2(run(ctx, rt), kindText), place);
|
|
4740
|
+
} finally {
|
|
4741
|
+
rt.held -= 1;
|
|
4742
|
+
}
|
|
4743
|
+
};
|
|
4215
4744
|
}
|
|
4216
4745
|
/** This function gives the steps of a run of an evaluator. The code runs at the first step, and it does not wait. */
|
|
4217
4746
|
function runOf(code) {
|
|
@@ -4223,6 +4752,30 @@ function runOf(code) {
|
|
|
4223
4752
|
function fillOf(split, doc) {
|
|
4224
4753
|
return (items) => split.parts.length === 0 ? doc.value : split.fill(items);
|
|
4225
4754
|
}
|
|
4755
|
+
/**
|
|
4756
|
+
* The key of the runtime on the input of an evaluator. Only the kernel reads it. The node of a tree evaluates its
|
|
4757
|
+
* equations in the evaluation of this runtime, thus their reads go in its list (READS.EQUATION). A copy of the input
|
|
4758
|
+
* with a spread keeps the key.
|
|
4759
|
+
*/
|
|
4760
|
+
const RUNTIME = /* @__PURE__ */ Symbol("coeval.runtime");
|
|
4761
|
+
/**
|
|
4762
|
+
* This function evaluates a plan inside the evaluation of another runtime, for example an equation of a tree inside
|
|
4763
|
+
* the evaluation of its tree (READS.EQUATION). The plan gets its own runtime, as in `plan.evaluate`. The reads of the
|
|
4764
|
+
* plan go in the record of the reads of `outer`. The context at the root of `outer` tells which reads are free
|
|
4765
|
+
* (READS.FREE). Without such a record, the function gives `plan.evaluate(context)`.
|
|
4766
|
+
*
|
|
4767
|
+
* @param plan - The plan.
|
|
4768
|
+
* @param context - The context of the plan.
|
|
4769
|
+
* @param outer - The runtime of the evaluation around the plan, or `undefined`.
|
|
4770
|
+
* @returns The result.
|
|
4771
|
+
*/
|
|
4772
|
+
function evaluateWithin(plan, context, outer) {
|
|
4773
|
+
const reading = outer instanceof Session ? outer.reading : void 0;
|
|
4774
|
+
if (reading === void 0 || !(plan instanceof CompiledPlan)) return plan.evaluate(context);
|
|
4775
|
+
const machine = plan.machine;
|
|
4776
|
+
const ctx = rootContext(machine, context);
|
|
4777
|
+
return plan.code(ctx, new Session(machine, ctx.caps, false, plan.traced, void 0, void 0, void 0, reading));
|
|
4778
|
+
}
|
|
4226
4779
|
function interpret(fn, input, fill) {
|
|
4227
4780
|
const { kind, typePath, doc, data, place, machine } = input;
|
|
4228
4781
|
const address = input.address === true ? { address: true } : {};
|
|
@@ -4249,7 +4802,8 @@ function interpret(fn, input, fill) {
|
|
|
4249
4802
|
...address,
|
|
4250
4803
|
resolve: (path, options) => rt.resolve(ctx, path, options),
|
|
4251
4804
|
effect: (effectKind, payload) => rt.effect(ctx, effectKind, payload, place),
|
|
4252
|
-
fill: refill
|
|
4805
|
+
fill: refill,
|
|
4806
|
+
[RUNTIME]: rt
|
|
4253
4807
|
});
|
|
4254
4808
|
};
|
|
4255
4809
|
}
|
|
@@ -4285,18 +4839,27 @@ function interpretRun(fn, input, fill) {
|
|
|
4285
4839
|
/**
|
|
4286
4840
|
* This function gives the context at the root of an evaluation. It is the context of the host, with the world of
|
|
4287
4841
|
* the machine when the context has none. The host gives the capabilities, thus the root has the grant of each one
|
|
4288
|
-
* (CAP.NARROW-CODE). The list of the host is also the epoch of the grant (CAP.NARROW-KEPT).
|
|
4289
|
-
* `verifyReads` share it.
|
|
4842
|
+
* (CAP.NARROW-CODE). The list of the host is also the epoch of the grant (CAP.NARROW-KEPT). The root has no field of
|
|
4843
|
+
* the epoch, thus it has the shape of a context before K120 (K154). An evaluation and `verifyReads` share it.
|
|
4844
|
+
*
|
|
4845
|
+
* The function reads the epoch of the context one time, and one test keeps a context that is already a root (K169).
|
|
4290
4846
|
*
|
|
4291
4847
|
* @param machine - The machine.
|
|
4292
4848
|
* @param ctx - The context of the host.
|
|
4293
4849
|
* @returns The context at the root.
|
|
4294
4850
|
*/
|
|
4295
4851
|
function rootContext(machine, ctx) {
|
|
4296
|
-
|
|
4852
|
+
const epoch = ctx[EPOCH];
|
|
4853
|
+
if (ctx.world !== null && ctx[GRANT] === ctx.caps && (epoch === void 0 || epoch === ctx.caps)) return ctx;
|
|
4854
|
+
const world = ctx.world === null ? worldThing(machine.world) : ctx.world;
|
|
4855
|
+
if (epoch === void 0) return {
|
|
4856
|
+
...ctx,
|
|
4857
|
+
world,
|
|
4858
|
+
[GRANT]: ctx.caps
|
|
4859
|
+
};
|
|
4297
4860
|
return {
|
|
4298
4861
|
...ctx,
|
|
4299
|
-
world
|
|
4862
|
+
world,
|
|
4300
4863
|
[GRANT]: ctx.caps,
|
|
4301
4864
|
[EPOCH]: ctx.caps
|
|
4302
4865
|
};
|
|
@@ -4518,8 +5081,7 @@ function flatten(trace) {
|
|
|
4518
5081
|
function failures(trace) {
|
|
4519
5082
|
return flatten(trace).filter((node) => !node.result.ok && !node.children.some((child) => !child.result.ok && child.result.error === node.result.error)).map((node) => node.result.error);
|
|
4520
5083
|
}
|
|
4521
|
-
|
|
4522
|
-
//#region src/stdlib.ts
|
|
5084
|
+
// src/stdlib.ts
|
|
4523
5085
|
/**
|
|
4524
5086
|
* The world of the examples: small kinds for numbers, lists, conditions, names, sequences and effects.
|
|
4525
5087
|
* Each kind names an evaluator for `eval` and a view for `view`. The site draws each document with its view.
|
|
@@ -4736,6 +5298,158 @@ const STD_WORLD = {
|
|
|
4736
5298
|
},
|
|
4737
5299
|
defaults: { view: "view.card" }
|
|
4738
5300
|
};
|
|
5301
|
+
/**
|
|
5302
|
+
* The text syntax of the standard world (K162, D64). The syntax kit (D41) reads it, thus a host of `STD_WORLD` does
|
|
5303
|
+
* not write its own profile. The operators `>`, `+` and `*` make `greater`, `add` and `multiply`. A call form makes
|
|
5304
|
+
* each other kind, and `ref` uses the reference syntax of the kit.
|
|
5305
|
+
*
|
|
5306
|
+
* A literal in a place that takes a document gets its data envelope (K159, D65). Such a place is a part of `if`,
|
|
5307
|
+
* `list`, `seq` or `pure`, the value of `write`, or the whole text. The document after a header name is such a place
|
|
5308
|
+
* too. Thus `list(1, "a")` evaluates to `[1, "a"]`. The forms `add`, `multiply` and `greater` write an operator document that its operator
|
|
5309
|
+
* does not write, for example `add(5)` (K163).
|
|
5310
|
+
*
|
|
5311
|
+
* A host with more kinds spreads this profile and adds its forms to `calls.forms`. `STD_SYNTAX` stays the profile of
|
|
5312
|
+
* the formula kit.
|
|
5313
|
+
*/
|
|
5314
|
+
const WORLD_SYNTAX = /* @__PURE__ */ Object.freeze({
|
|
5315
|
+
operators: [
|
|
5316
|
+
{
|
|
5317
|
+
symbol: ">",
|
|
5318
|
+
fixity: "infix",
|
|
5319
|
+
prec: 4,
|
|
5320
|
+
assoc: "none",
|
|
5321
|
+
kind: "greater"
|
|
5322
|
+
},
|
|
5323
|
+
{
|
|
5324
|
+
symbol: "+",
|
|
5325
|
+
fixity: "infix",
|
|
5326
|
+
prec: 5,
|
|
5327
|
+
assoc: "flat",
|
|
5328
|
+
kind: "add"
|
|
5329
|
+
},
|
|
5330
|
+
{
|
|
5331
|
+
symbol: "*",
|
|
5332
|
+
fixity: "infix",
|
|
5333
|
+
prec: 6,
|
|
5334
|
+
assoc: "flat",
|
|
5335
|
+
kind: "multiply"
|
|
5336
|
+
}
|
|
5337
|
+
],
|
|
5338
|
+
calls: {
|
|
5339
|
+
ops: [],
|
|
5340
|
+
forms: [
|
|
5341
|
+
{
|
|
5342
|
+
name: "if",
|
|
5343
|
+
kind: "if",
|
|
5344
|
+
fields: [
|
|
5345
|
+
"test",
|
|
5346
|
+
"then",
|
|
5347
|
+
"else"
|
|
5348
|
+
],
|
|
5349
|
+
min: 2,
|
|
5350
|
+
wrap: true
|
|
5351
|
+
},
|
|
5352
|
+
{
|
|
5353
|
+
name: "list",
|
|
5354
|
+
kind: "list",
|
|
5355
|
+
wrap: true
|
|
5356
|
+
},
|
|
5357
|
+
{
|
|
5358
|
+
name: "seq",
|
|
5359
|
+
kind: "seq",
|
|
5360
|
+
wrap: true
|
|
5361
|
+
},
|
|
5362
|
+
{
|
|
5363
|
+
name: "pure",
|
|
5364
|
+
kind: "pure",
|
|
5365
|
+
min: 1,
|
|
5366
|
+
wrap: true
|
|
5367
|
+
},
|
|
5368
|
+
{
|
|
5369
|
+
name: "log",
|
|
5370
|
+
kind: "log",
|
|
5371
|
+
min: 1
|
|
5372
|
+
},
|
|
5373
|
+
{
|
|
5374
|
+
name: "write",
|
|
5375
|
+
kind: "write",
|
|
5376
|
+
fields: ["key", "value"],
|
|
5377
|
+
wrap: ["value"]
|
|
5378
|
+
},
|
|
5379
|
+
{
|
|
5380
|
+
name: "math.sum",
|
|
5381
|
+
kind: "math.sum"
|
|
5382
|
+
},
|
|
5383
|
+
{
|
|
5384
|
+
name: "math.product",
|
|
5385
|
+
kind: "math.product"
|
|
5386
|
+
},
|
|
5387
|
+
{
|
|
5388
|
+
name: "number",
|
|
5389
|
+
kind: "number",
|
|
5390
|
+
args: "literal"
|
|
5391
|
+
},
|
|
5392
|
+
{
|
|
5393
|
+
name: "text",
|
|
5394
|
+
kind: "text",
|
|
5395
|
+
args: "literal"
|
|
5396
|
+
},
|
|
5397
|
+
{
|
|
5398
|
+
name: "bool",
|
|
5399
|
+
kind: "bool",
|
|
5400
|
+
args: "literal"
|
|
5401
|
+
},
|
|
5402
|
+
{
|
|
5403
|
+
name: "panel",
|
|
5404
|
+
kind: "panel",
|
|
5405
|
+
args: "named",
|
|
5406
|
+
fields: [
|
|
5407
|
+
"width",
|
|
5408
|
+
"height",
|
|
5409
|
+
"children"
|
|
5410
|
+
]
|
|
5411
|
+
},
|
|
5412
|
+
{
|
|
5413
|
+
name: "box",
|
|
5414
|
+
kind: "box",
|
|
5415
|
+
args: "named",
|
|
5416
|
+
fields: [
|
|
5417
|
+
"width",
|
|
5418
|
+
"height",
|
|
5419
|
+
"children"
|
|
5420
|
+
]
|
|
5421
|
+
},
|
|
5422
|
+
{
|
|
5423
|
+
name: "nearest",
|
|
5424
|
+
kind: "nearest",
|
|
5425
|
+
args: "named",
|
|
5426
|
+
fields: [
|
|
5427
|
+
"type",
|
|
5428
|
+
"follow",
|
|
5429
|
+
"self"
|
|
5430
|
+
]
|
|
5431
|
+
},
|
|
5432
|
+
{
|
|
5433
|
+
name: "add",
|
|
5434
|
+
kind: "add"
|
|
5435
|
+
},
|
|
5436
|
+
{
|
|
5437
|
+
name: "multiply",
|
|
5438
|
+
kind: "multiply"
|
|
5439
|
+
},
|
|
5440
|
+
{
|
|
5441
|
+
name: "greater",
|
|
5442
|
+
kind: "greater"
|
|
5443
|
+
}
|
|
5444
|
+
]
|
|
5445
|
+
},
|
|
5446
|
+
literals: { envelopes: {
|
|
5447
|
+
number: "number",
|
|
5448
|
+
text: "text",
|
|
5449
|
+
bool: "bool"
|
|
5450
|
+
} },
|
|
5451
|
+
header: { bind: "=" }
|
|
5452
|
+
});
|
|
4739
5453
|
function isNumber(value) {
|
|
4740
5454
|
return typeof value === "number" && Number.isFinite(value);
|
|
4741
5455
|
}
|
|
@@ -4914,6 +5628,7 @@ function plainOf(value, path) {
|
|
|
4914
5628
|
*/
|
|
4915
5629
|
function treeThing(input, build, outer) {
|
|
4916
5630
|
const { parts, context, kind: type, place, machine } = input;
|
|
5631
|
+
const runtime = input[RUNTIME];
|
|
4917
5632
|
const { tree, of } = contractOf$1(machine, input.typePath);
|
|
4918
5633
|
const above = stateOf(outer);
|
|
4919
5634
|
const up = above !== void 0 && above.build === build ? above : void 0;
|
|
@@ -5015,12 +5730,12 @@ function treeThing(input, build, outer) {
|
|
|
5015
5730
|
read,
|
|
5016
5731
|
equation(term, names) {
|
|
5017
5732
|
if (machine === void 0) return fail("#NOEVAL", "The evaluation of an equation needs the machine.");
|
|
5018
|
-
return equationPlan(machine, of, term)
|
|
5733
|
+
return evaluateWithin(equationPlan(machine, of, term), {
|
|
5019
5734
|
...context,
|
|
5020
5735
|
names,
|
|
5021
5736
|
caps: NO_CAPS$1,
|
|
5022
5737
|
self: node
|
|
5023
|
-
});
|
|
5738
|
+
}, runtime);
|
|
5024
5739
|
},
|
|
5025
5740
|
childNames(child) {
|
|
5026
5741
|
const children = read("children");
|
|
@@ -5267,8 +5982,7 @@ const STD_MACHINE = {
|
|
|
5267
5982
|
};
|
|
5268
5983
|
/** The capabilities of the examples at the root: the two effects of the standard world. */
|
|
5269
5984
|
const STD_CAPS = ["log", "write"];
|
|
5270
|
-
|
|
5271
|
-
//#region src/order.ts
|
|
5985
|
+
// src/order.ts
|
|
5272
5986
|
/**
|
|
5273
5987
|
* This function finds the references that a sequence cannot resolve. A step can read only the steps before it.
|
|
5274
5988
|
*
|
|
@@ -5364,8 +6078,7 @@ function layers(steps) {
|
|
|
5364
6078
|
}
|
|
5365
6079
|
return result;
|
|
5366
6080
|
}
|
|
5367
|
-
|
|
5368
|
-
//#region src/context.ts
|
|
6081
|
+
// src/context.ts
|
|
5369
6082
|
/** The parts of a context. A term can reach outside itself only through these parts. */
|
|
5370
6083
|
const PARTS = [
|
|
5371
6084
|
"self",
|
|
@@ -5653,8 +6366,7 @@ const CONSTRUCTS = [
|
|
|
5653
6366
|
}
|
|
5654
6367
|
}
|
|
5655
6368
|
];
|
|
5656
|
-
|
|
5657
|
-
//#region src/typing.ts
|
|
6369
|
+
// src/typing.ts
|
|
5658
6370
|
/**
|
|
5659
6371
|
* The value type of the root `caps`: a list of capabilities. A capability is a text, or a list of keys. The kernel
|
|
5660
6372
|
* narrows the capabilities for each part, thus the host does not give this type.
|
|
@@ -5881,8 +6593,7 @@ function typePass(machine, nodes, verb, expect, rootTypes = {}) {
|
|
|
5881
6593
|
places
|
|
5882
6594
|
};
|
|
5883
6595
|
}
|
|
5884
|
-
|
|
5885
|
-
//#region src/static.ts
|
|
6596
|
+
// src/static.ts
|
|
5886
6597
|
/** The names of a walk without names. A walk does not change a set at any time: it makes a new set to add names. */
|
|
5887
6598
|
const NO_NAMES = /* @__PURE__ */ new Set();
|
|
5888
6599
|
/** This function gives the options of a walk without the names of the host. */
|
|
@@ -6310,7 +7021,7 @@ const DOWN_NAMES = /* @__PURE__ */ Object.freeze([
|
|
|
6310
7021
|
const FOLD_NAMES = /* @__PURE__ */ Object.freeze(["acc", "item"]);
|
|
6311
7022
|
/**
|
|
6312
7023
|
* This function gives each equation of the tree contract of a kind, with the names that it sees (D42). An equation of
|
|
6313
|
-
* `equations`, a `unit` and a `default` see no names.
|
|
7024
|
+
* `equations`, a `unit` and a `default` see no names. `check` and `factsOf` read the equations with this one function.
|
|
6314
7025
|
*/
|
|
6315
7026
|
function equationsOf(kind) {
|
|
6316
7027
|
const tree = kind.tree;
|
|
@@ -6564,8 +7275,7 @@ function checkAll(machine, doc, options, nodes, seen = /* @__PURE__ */ new Set()
|
|
|
6564
7275
|
}
|
|
6565
7276
|
return issues;
|
|
6566
7277
|
}
|
|
6567
|
-
|
|
6568
|
-
//#region src/facts.ts
|
|
7278
|
+
// src/facts.ts
|
|
6569
7279
|
/** The lazy rules that cover each part: the rules of a document whose evaluator the contract does not describe. */
|
|
6570
7280
|
const EACH$1 = "each";
|
|
6571
7281
|
/** An empty list. One frozen list serves each empty list of the facts, thus a store of many facts keeps less (PLAN.LEAN). */
|
|
@@ -6632,9 +7342,11 @@ function partLazy(above, parent, slot) {
|
|
|
6632
7342
|
*
|
|
6633
7343
|
* - `reads`: each free reference, with the root and the keys of its path. A read under a lazy part has the mark
|
|
6634
7344
|
* `lazy`, for example a read in a branch of `if`. A read after a step document has `exact: false`. A kind that
|
|
6635
|
-
* declares `reads` adds a read of each of its roots at each document of the kind.
|
|
7345
|
+
* declares `reads` adds a read of each of its roots at each document of the kind. The first document of a tree
|
|
7346
|
+
* kind also gets the lazy reads of the equations of its kind (FACTS.EQUATIONS). A read from `self` or from a name
|
|
7347
|
+
* of a rung is not one of them, because it is not free (READS.EQUATION).
|
|
6636
7348
|
* - `side` and `effects`: from the contract of each kind.
|
|
6637
|
-
* - `pure`: each kind declares `pure`, and the document has no effect and is not volatile.
|
|
7349
|
+
* - `pure`: each kind declares `pure`, also in an equation, and the document has no effect and is not volatile.
|
|
6638
7350
|
* - `volatile`: a kind, an operation or a step document declares `volatile`, or the host marks a read.
|
|
6639
7351
|
*
|
|
6640
7352
|
* The hook `facts` of the machine answers for a name that a fallback kind covers, for example an operation. Its
|
|
@@ -6658,7 +7370,6 @@ function partLazy(above, parent, slot) {
|
|
|
6658
7370
|
function factsOf(machine, doc, options = {}) {
|
|
6659
7371
|
const world = machine.world;
|
|
6660
7372
|
const hook = machine.facts;
|
|
6661
|
-
const verb = options.verb ?? "eval";
|
|
6662
7373
|
const reads = [];
|
|
6663
7374
|
const effects = [];
|
|
6664
7375
|
const above = /* @__PURE__ */ new Map();
|
|
@@ -6666,98 +7377,124 @@ function factsOf(machine, doc, options = {}) {
|
|
|
6666
7377
|
let pure = true;
|
|
6667
7378
|
let volatile = false;
|
|
6668
7379
|
let size = 0;
|
|
7380
|
+
let seen;
|
|
6669
7381
|
const note = (isPure, isVolatile) => {
|
|
6670
7382
|
if (isPure !== true) pure = false;
|
|
6671
7383
|
if (isVolatile === true) volatile = true;
|
|
6672
7384
|
};
|
|
6673
|
-
|
|
6674
|
-
size += 1;
|
|
6675
|
-
const up = v.parent === void 0 ? void 0 : above.get(v.parent);
|
|
6676
|
-
const lazy = up !== void 0 && v.parent !== void 0 && (up.lazy || partLazy(up, v.parent, v.slot));
|
|
6677
|
-
const kind = v.kind;
|
|
6678
|
-
if (kind === void 0) {
|
|
6679
|
-
pure = false;
|
|
6680
|
-
above.set(v, {
|
|
6681
|
-
lazy,
|
|
6682
|
-
rules: void 0
|
|
6683
|
-
});
|
|
6684
|
-
return;
|
|
6685
|
-
}
|
|
6686
|
-
const code = codeOf(world, v.declared ? void 0 : v.doc.own, kind, verb, "eval");
|
|
6687
|
-
const answer = code.name === void 0 ? void 0 : hook?.(v.typePath ?? [], code.name);
|
|
6688
|
-
let roots;
|
|
6689
|
-
if (code.described) {
|
|
6690
|
-
note(answer?.pure ?? kind.pure, answer?.volatile ?? kind.volatile);
|
|
6691
|
-
above.set(v, {
|
|
6692
|
-
lazy,
|
|
6693
|
-
rules: answer?.lazy ?? kind.lazy
|
|
6694
|
-
});
|
|
6695
|
-
roots = answer?.reads ?? kind.reads;
|
|
6696
|
-
} else {
|
|
6697
|
-
note(answer?.pure, answer?.volatile);
|
|
6698
|
-
above.set(v, {
|
|
6699
|
-
lazy,
|
|
6700
|
-
rules: answer?.lazy ?? EACH$1
|
|
6701
|
-
});
|
|
6702
|
-
roots = answer?.reads;
|
|
6703
|
-
}
|
|
6704
|
-
for (const root of roots ?? NONE) {
|
|
6705
|
-
const read = {
|
|
6706
|
-
root,
|
|
6707
|
-
keys: NONE,
|
|
6708
|
-
exact: true,
|
|
6709
|
-
lazy,
|
|
6710
|
-
address: false,
|
|
6711
|
-
path: [root],
|
|
6712
|
-
place: tight(v.place),
|
|
6713
|
-
at: tight(v.at)
|
|
6714
|
-
};
|
|
6715
|
-
reads.push(read);
|
|
6716
|
-
if (options.volatile?.(read) === true) volatile = true;
|
|
6717
|
-
}
|
|
6718
|
-
if (kind.side === "run") side = "run";
|
|
6719
|
-
for (const keys of declaredEffects(v)) {
|
|
6720
|
-
const text = keys.join(".");
|
|
6721
|
-
if (!effects.includes(text)) effects.push(text);
|
|
6722
|
-
}
|
|
6723
|
-
if (!isRef(v.doc)) return void 0;
|
|
6724
|
-
const path = v.doc.value;
|
|
6725
|
-
const root = path[0];
|
|
6726
|
-
if (typeof root !== "string" || !ROOTS.includes(root)) return void 0;
|
|
6727
|
-
let exact = true;
|
|
6728
|
-
const keys = [];
|
|
6729
|
-
for (let index = 1; index < path.length; index += 1) {
|
|
6730
|
-
const step = path[index];
|
|
6731
|
-
if (exact && isKey(step)) {
|
|
6732
|
-
keys.push(step);
|
|
6733
|
-
continue;
|
|
6734
|
-
}
|
|
6735
|
-
exact = false;
|
|
6736
|
-
const found = isEnvelope(step) ? lookupKind(world, step.type) : void 0;
|
|
6737
|
-
if (found?.ok !== true) {
|
|
6738
|
-
pure = false;
|
|
6739
|
-
continue;
|
|
6740
|
-
}
|
|
6741
|
-
const walker = codeOf(world, step.own, found.kind, "step", "step");
|
|
6742
|
-
const stepAnswer = walker.name === void 0 ? void 0 : hook?.(found.path, walker.name);
|
|
6743
|
-
if (walker.described) note(stepAnswer?.pure ?? found.kind.pure, stepAnswer?.volatile ?? found.kind.volatile);
|
|
6744
|
-
else note(stepAnswer?.pure, stepAnswer?.volatile);
|
|
6745
|
-
}
|
|
6746
|
-
const first = path[1];
|
|
6747
|
-
if (root === "names" && isKey(first) && v.names.has(String(first))) return void 0;
|
|
6748
|
-
const read = {
|
|
6749
|
-
root,
|
|
6750
|
-
keys: tight(keys),
|
|
6751
|
-
exact,
|
|
6752
|
-
lazy,
|
|
6753
|
-
address: v.address === true,
|
|
6754
|
-
path,
|
|
6755
|
-
place: tight(v.place),
|
|
6756
|
-
at: tight(v.at)
|
|
6757
|
-
};
|
|
7385
|
+
const add = (read) => {
|
|
6758
7386
|
reads.push(read);
|
|
6759
7387
|
if (options.volatile?.(read) === true) volatile = true;
|
|
6760
|
-
}
|
|
7388
|
+
};
|
|
7389
|
+
/**
|
|
7390
|
+
* This function walks a document. Without `node`, it walks the document of the facts. With `node`, it walks an
|
|
7391
|
+
* equation of a tree kind, and each read gets the place of that node: the first document of the kind.
|
|
7392
|
+
*/
|
|
7393
|
+
const scan = (term, walkOptions, verb, node) => {
|
|
7394
|
+
walk(world, term, (v) => {
|
|
7395
|
+
if (node === void 0) size += 1;
|
|
7396
|
+
const up = v.parent === void 0 ? void 0 : above.get(v.parent);
|
|
7397
|
+
const lazy = node !== void 0 || up !== void 0 && v.parent !== void 0 && (up.lazy || partLazy(up, v.parent, v.slot));
|
|
7398
|
+
const place = node ?? v;
|
|
7399
|
+
const kind = v.kind;
|
|
7400
|
+
if (kind === void 0) {
|
|
7401
|
+
pure = false;
|
|
7402
|
+
above.set(v, {
|
|
7403
|
+
lazy,
|
|
7404
|
+
rules: void 0
|
|
7405
|
+
});
|
|
7406
|
+
return;
|
|
7407
|
+
}
|
|
7408
|
+
const code = codeOf(world, v.declared ? void 0 : v.doc.own, kind, verb, "eval");
|
|
7409
|
+
const answer = code.name === void 0 ? void 0 : hook?.(v.typePath ?? [], code.name);
|
|
7410
|
+
let roots;
|
|
7411
|
+
if (code.described) {
|
|
7412
|
+
note(answer?.pure ?? kind.pure, answer?.volatile ?? kind.volatile);
|
|
7413
|
+
above.set(v, {
|
|
7414
|
+
lazy,
|
|
7415
|
+
rules: answer?.lazy ?? kind.lazy
|
|
7416
|
+
});
|
|
7417
|
+
roots = answer?.reads ?? kind.reads;
|
|
7418
|
+
} else {
|
|
7419
|
+
note(answer?.pure, answer?.volatile);
|
|
7420
|
+
above.set(v, {
|
|
7421
|
+
lazy,
|
|
7422
|
+
rules: answer?.lazy ?? EACH$1
|
|
7423
|
+
});
|
|
7424
|
+
roots = answer?.reads;
|
|
7425
|
+
}
|
|
7426
|
+
for (const root of roots ?? NONE) {
|
|
7427
|
+
if (node !== void 0 && root === "self") continue;
|
|
7428
|
+
add({
|
|
7429
|
+
root,
|
|
7430
|
+
keys: NONE,
|
|
7431
|
+
exact: true,
|
|
7432
|
+
lazy,
|
|
7433
|
+
address: false,
|
|
7434
|
+
path: [root],
|
|
7435
|
+
place: tight(place.place),
|
|
7436
|
+
at: tight(place.at)
|
|
7437
|
+
});
|
|
7438
|
+
}
|
|
7439
|
+
if (kind.side === "run") {
|
|
7440
|
+
if (node === void 0) side = "run";
|
|
7441
|
+
else pure = false;
|
|
7442
|
+
}
|
|
7443
|
+
for (const keys of declaredEffects(v)) {
|
|
7444
|
+
if (node !== void 0) {
|
|
7445
|
+
pure = false;
|
|
7446
|
+
continue;
|
|
7447
|
+
}
|
|
7448
|
+
const text = keys.join(".");
|
|
7449
|
+
if (!effects.includes(text)) effects.push(text);
|
|
7450
|
+
}
|
|
7451
|
+
if (kind.tree !== void 0 && !(seen ??= /* @__PURE__ */ new Set()).has(kind)) {
|
|
7452
|
+
seen.add(kind);
|
|
7453
|
+
const of = equationType(kind);
|
|
7454
|
+
for (const { term: equation, names } of equationsOf(kind)) scan(equation, of === void 0 ? { names } : {
|
|
7455
|
+
names,
|
|
7456
|
+
declaredType: of
|
|
7457
|
+
}, "eval", place);
|
|
7458
|
+
}
|
|
7459
|
+
if (!isRef(v.doc)) return void 0;
|
|
7460
|
+
const path = v.doc.value;
|
|
7461
|
+
const root = path[0];
|
|
7462
|
+
if (typeof root !== "string" || !ROOTS.includes(root)) return void 0;
|
|
7463
|
+
if (node !== void 0 && root === "self") return void 0;
|
|
7464
|
+
let exact = true;
|
|
7465
|
+
const keys = [];
|
|
7466
|
+
for (let index = 1; index < path.length; index += 1) {
|
|
7467
|
+
const step = path[index];
|
|
7468
|
+
if (exact && isKey(step)) {
|
|
7469
|
+
keys.push(step);
|
|
7470
|
+
continue;
|
|
7471
|
+
}
|
|
7472
|
+
exact = false;
|
|
7473
|
+
const found = isEnvelope(step) ? lookupKind(world, step.type) : void 0;
|
|
7474
|
+
if (found?.ok !== true) {
|
|
7475
|
+
pure = false;
|
|
7476
|
+
continue;
|
|
7477
|
+
}
|
|
7478
|
+
const walker = codeOf(world, step.own, found.kind, "step", "step");
|
|
7479
|
+
const stepAnswer = walker.name === void 0 ? void 0 : hook?.(found.path, walker.name);
|
|
7480
|
+
if (walker.described) note(stepAnswer?.pure ?? found.kind.pure, stepAnswer?.volatile ?? found.kind.volatile);
|
|
7481
|
+
else note(stepAnswer?.pure, stepAnswer?.volatile);
|
|
7482
|
+
}
|
|
7483
|
+
const first = path[1];
|
|
7484
|
+
if (root === "names" && isKey(first) && v.names.has(String(first))) return void 0;
|
|
7485
|
+
add({
|
|
7486
|
+
root,
|
|
7487
|
+
keys: tight(keys),
|
|
7488
|
+
exact,
|
|
7489
|
+
lazy,
|
|
7490
|
+
address: v.address === true,
|
|
7491
|
+
path,
|
|
7492
|
+
place: tight(place.place),
|
|
7493
|
+
at: tight(place.at)
|
|
7494
|
+
});
|
|
7495
|
+
}, walkOptions);
|
|
7496
|
+
};
|
|
7497
|
+
scan(doc, options.declaredType === void 0 ? {} : { declaredType: options.declaredType }, options.verb ?? "eval", void 0);
|
|
6761
7498
|
return {
|
|
6762
7499
|
reads: tight(reads),
|
|
6763
7500
|
side,
|
|
@@ -6767,8 +7504,7 @@ function factsOf(machine, doc, options = {}) {
|
|
|
6767
7504
|
size
|
|
6768
7505
|
};
|
|
6769
7506
|
}
|
|
6770
|
-
|
|
6771
|
-
//#region src/effects.ts
|
|
7507
|
+
// src/effects.ts
|
|
6772
7508
|
/** Each capability: one capability with no keys. It permits each effect, because each kind starts with no keys (CAP.KEYS). */
|
|
6773
7509
|
const EACH = /* @__PURE__ */ Object.freeze([[]]);
|
|
6774
7510
|
/**
|
|
@@ -6848,8 +7584,7 @@ function checkLatent(machine, declared, body, options = {}) {
|
|
|
6848
7584
|
}
|
|
6849
7585
|
return issues;
|
|
6850
7586
|
}
|
|
6851
|
-
|
|
6852
|
-
//#region src/codec.ts
|
|
7587
|
+
// src/codec.ts
|
|
6853
7588
|
/** This function gives the codec of a domain, or `undefined` when the domain has no codec. */
|
|
6854
7589
|
function codecOf(domain) {
|
|
6855
7590
|
const codec = domain.codec;
|
|
@@ -6896,7 +7631,7 @@ function domainTypeOf(data, domains) {
|
|
|
6896
7631
|
return domain !== void 0 && codecOf(domain) !== void 0 ? data["domain"] : "unknown";
|
|
6897
7632
|
}
|
|
6898
7633
|
/** This function sets an own data property. Only the key `__proto__` needs `defineProperty`. */
|
|
6899
|
-
function setOwn$
|
|
7634
|
+
function setOwn$3(target, key, value) {
|
|
6900
7635
|
if (key === "__proto__") Object.defineProperty(target, key, {
|
|
6901
7636
|
value,
|
|
6902
7637
|
enumerable: true,
|
|
@@ -6971,7 +7706,7 @@ function encodeTerm(value, encoder) {
|
|
|
6971
7706
|
else {
|
|
6972
7707
|
const fields = {};
|
|
6973
7708
|
const record = value;
|
|
6974
|
-
for (const key of Object.keys(record)) setOwn$
|
|
7709
|
+
for (const key of Object.keys(record)) setOwn$3(fields, key, encodeTerm(record[key], encoder));
|
|
6975
7710
|
out = {
|
|
6976
7711
|
type: "record",
|
|
6977
7712
|
value: fields
|
|
@@ -7020,8 +7755,7 @@ function encodeValue(machine, value) {
|
|
|
7020
7755
|
function decodeValue(machine, doc) {
|
|
7021
7756
|
return prepare(machine, doc, { declaredType: "lit" }).evaluate(makeContext());
|
|
7022
7757
|
}
|
|
7023
|
-
|
|
7024
|
-
//#region src/formula.ts
|
|
7758
|
+
// src/formula.ts
|
|
7025
7759
|
/** The mark of a field that the profile leaves out of a record. */
|
|
7026
7760
|
const SKIPPED = /* @__PURE__ */ Symbol("skipped");
|
|
7027
7761
|
/** The value of a name whose binding failed. A reference to the name gives the error. */
|
|
@@ -7067,7 +7801,7 @@ function strictTruthy(value) {
|
|
|
7067
7801
|
* This function sets an own data property. Only the key `__proto__` needs `defineProperty`: an assignment to it
|
|
7068
7802
|
* changes the prototype. An assignment is faster for each other key of a new plain object.
|
|
7069
7803
|
*/
|
|
7070
|
-
function setOwn$
|
|
7804
|
+
function setOwn$2(target, key, value) {
|
|
7071
7805
|
if (key === "__proto__") Object.defineProperty(target, key, {
|
|
7072
7806
|
value,
|
|
7073
7807
|
enumerable: true,
|
|
@@ -7281,11 +8015,28 @@ function paramsOf(value) {
|
|
|
7281
8015
|
}
|
|
7282
8016
|
/**
|
|
7283
8017
|
* This function makes the type rules of the formula kit for one profile (TYPE.SYNTH, D25). Each evaluator of the kit
|
|
7284
|
-
* holds its rule. The rules read the rules of the profile that change a type: `truthy`, `
|
|
7285
|
-
* and `
|
|
8018
|
+
* holds its rule. The rules read the rules of the profile that change a type: `truthy`, `truthType`, `logic`,
|
|
8019
|
+
* `otherwise`, `omit`, `sigs` and `extTypes`.
|
|
7286
8020
|
*/
|
|
7287
8021
|
function formulaTypes(profile) {
|
|
7288
|
-
const
|
|
8022
|
+
const declared = profile.truthy !== void 0 && profile.truthType !== void 0;
|
|
8023
|
+
const truth = profile.truthy === void 0 ? "bool" : profile.truthType;
|
|
8024
|
+
/**
|
|
8025
|
+
* The need of a test or a term. The check examines a declared type of a truth as a contract, one time. A declared
|
|
8026
|
+
* type with a problem needs nothing, thus one problem gives one issue.
|
|
8027
|
+
*/
|
|
8028
|
+
const truthPart = (input, what) => {
|
|
8029
|
+
if (truth === void 0) return {};
|
|
8030
|
+
if (!declared) return {
|
|
8031
|
+
expect: truth,
|
|
8032
|
+
what
|
|
8033
|
+
};
|
|
8034
|
+
const type = input.contract(truth, "truth type of the profile");
|
|
8035
|
+
return typeIssues(type, input.machine.domains).length > 0 ? {} : {
|
|
8036
|
+
expect: type,
|
|
8037
|
+
what
|
|
8038
|
+
};
|
|
8039
|
+
};
|
|
7289
8040
|
const logicValue = profile.logic === "value";
|
|
7290
8041
|
const otherwise = Object.hasOwn(profile, "otherwise") ? valueType(profile.otherwise) : "null";
|
|
7291
8042
|
const lookup = (name) => {
|
|
@@ -7321,10 +8072,7 @@ function formulaTypes(profile) {
|
|
|
7321
8072
|
};
|
|
7322
8073
|
};
|
|
7323
8074
|
const logic = (word) => ({
|
|
7324
|
-
part: (
|
|
7325
|
-
expect: truth,
|
|
7326
|
-
what: `term ${Number(path[0]) + 1} of ${word}`
|
|
7327
|
-
},
|
|
8075
|
+
part: (input, path) => truthPart(input, `term ${Number(path[0]) + 1} of ${word}`),
|
|
7328
8076
|
synth: (input) => {
|
|
7329
8077
|
if (!logicValue) return "bool";
|
|
7330
8078
|
if (input.paths.length === 0) return { one: [word === "and"] };
|
|
@@ -7373,7 +8121,7 @@ function formulaTypes(profile) {
|
|
|
7373
8121
|
},
|
|
7374
8122
|
synth: (input) => {
|
|
7375
8123
|
const fields = {};
|
|
7376
|
-
for (const path of input.paths) setOwn$
|
|
8124
|
+
for (const path of input.paths) setOwn$2(fields, String(path[0]), input.typeOf(path));
|
|
7377
8125
|
const keys = Object.keys(fields);
|
|
7378
8126
|
return {
|
|
7379
8127
|
record: fields,
|
|
@@ -7396,7 +8144,7 @@ function formulaTypes(profile) {
|
|
|
7396
8144
|
part: (input, path) => {
|
|
7397
8145
|
if (path[0] !== "body") return {};
|
|
7398
8146
|
const names = {};
|
|
7399
|
-
for (const each of input.paths) if (each[0] === "bind" && each.length === 2) setOwn$
|
|
8147
|
+
for (const each of input.paths) if (each[0] === "bind" && each.length === 2) setOwn$2(names, String(each[1]), input.typeOf(each));
|
|
7400
8148
|
return {
|
|
7401
8149
|
names,
|
|
7402
8150
|
...isUnknown(input.expected, input.machine.domains) ? {} : {
|
|
@@ -7412,7 +8160,7 @@ function formulaTypes(profile) {
|
|
|
7412
8160
|
if (path[0] !== "body") return {};
|
|
7413
8161
|
const want = fnOf(input.expected, input.machine.domains);
|
|
7414
8162
|
const names = {};
|
|
7415
|
-
paramsOf(input.doc.value).forEach((name, index) => setOwn$
|
|
8163
|
+
paramsOf(input.doc.value).forEach((name, index) => setOwn$2(names, name, want?.params[index] ?? "unknown"));
|
|
7416
8164
|
return {
|
|
7417
8165
|
names,
|
|
7418
8166
|
...want === void 0 || isUnknown(want.to, input.machine.domains) ? {} : {
|
|
@@ -7468,10 +8216,7 @@ function formulaTypes(profile) {
|
|
|
7468
8216
|
} },
|
|
7469
8217
|
"formula.if": {
|
|
7470
8218
|
part: (input, path) => {
|
|
7471
|
-
if (path[0] === "test") return
|
|
7472
|
-
expect: truth,
|
|
7473
|
-
what: "test of if"
|
|
7474
|
-
};
|
|
8219
|
+
if (path[0] === "test") return truthPart(input, "test of if");
|
|
7475
8220
|
return isUnknown(input.expected, input.machine.domains) ? {} : {
|
|
7476
8221
|
expect: input.expected,
|
|
7477
8222
|
what: `${String(path[0])} branch of if`
|
|
@@ -7525,7 +8270,13 @@ function formulaTypes(profile) {
|
|
|
7525
8270
|
},
|
|
7526
8271
|
"formula.ext": { synth: (input) => {
|
|
7527
8272
|
const name = nameOf(input);
|
|
7528
|
-
if (profile.ext !== void 0 && Object.hasOwn(profile.ext, name))
|
|
8273
|
+
if (profile.ext !== void 0 && Object.hasOwn(profile.ext, name)) {
|
|
8274
|
+
const types = profile.extTypes;
|
|
8275
|
+
const declared = types !== void 0 && Object.hasOwn(types, name) ? types[name] : void 0;
|
|
8276
|
+
if (declared === void 0) return "unknown";
|
|
8277
|
+
const type = input.contract(declared, `type of ext.${name} in extTypes`);
|
|
8278
|
+
return typeIssues(type, input.machine.domains).length > 0 ? "unknown" : type;
|
|
8279
|
+
}
|
|
7529
8280
|
if (name === "undefined") return "undefined";
|
|
7530
8281
|
if (name === "number") return "number";
|
|
7531
8282
|
return name === "domain" ? domainTypeOf(input.doc.value, input.machine.domains) : "unknown";
|
|
@@ -7545,18 +8296,44 @@ function formulaTypes(profile) {
|
|
|
7545
8296
|
function formulaEvaluators(profile = {}) {
|
|
7546
8297
|
return { ...kitOf(profile).evaluators };
|
|
7547
8298
|
}
|
|
8299
|
+
/** The evaluators of the kit that read the truth of a value. */
|
|
8300
|
+
const TRUTH_READERS = [
|
|
8301
|
+
"formula.if",
|
|
8302
|
+
"formula.and",
|
|
8303
|
+
"formula.or"
|
|
8304
|
+
];
|
|
8305
|
+
/** The declared truth of each evaluator of the kit that reads a truth, for `conforms` (D86). */
|
|
8306
|
+
const truthByCode = /* @__PURE__ */ new WeakMap();
|
|
8307
|
+
/**
|
|
8308
|
+
* This function gives the declared truth of the profile of an evaluator of the kit (D86). Only `formula.if`,
|
|
8309
|
+
* `formula.and` and `formula.or` of a profile with an own `truthy` and a `truthType` have one. The kernel does not
|
|
8310
|
+
* export the function.
|
|
8311
|
+
*
|
|
8312
|
+
* @param code - The evaluator or the runner.
|
|
8313
|
+
* @returns The declared truth, or `undefined`.
|
|
8314
|
+
*/
|
|
8315
|
+
function declaredTruthOf(code) {
|
|
8316
|
+
return (typeof code === "object" || typeof code === "function") && code !== null ? truthByCode.get(code) : void 0;
|
|
8317
|
+
}
|
|
7548
8318
|
/** This function makes new evaluators of the formula kit for the fields of one profile. */
|
|
7549
8319
|
function makeEvaluators(profile) {
|
|
7550
8320
|
const evaluators = formulaCode(profile);
|
|
7551
8321
|
const types = formulaTypes(profile);
|
|
8322
|
+
const { truthy, truthType } = profile;
|
|
8323
|
+
const truth = truthy === void 0 || truthType === void 0 ? void 0 : {
|
|
8324
|
+
truthy,
|
|
8325
|
+
truthType
|
|
8326
|
+
};
|
|
7552
8327
|
const out = {};
|
|
7553
8328
|
for (const name of Object.keys(evaluators)) {
|
|
7554
8329
|
const evaluator = evaluators[name];
|
|
7555
8330
|
const type = Object.hasOwn(types, name) ? types[name] : void 0;
|
|
7556
|
-
|
|
8331
|
+
const made = Object.freeze(type === void 0 ? evaluator : {
|
|
7557
8332
|
compile: evaluator.compile,
|
|
7558
8333
|
type
|
|
7559
8334
|
});
|
|
8335
|
+
if (truth !== void 0 && TRUTH_READERS.includes(name)) truthByCode.set(made, truth);
|
|
8336
|
+
out[name] = made;
|
|
7560
8337
|
}
|
|
7561
8338
|
return Object.freeze(out);
|
|
7562
8339
|
}
|
|
@@ -7565,7 +8342,9 @@ const PROFILE_KEYS = /* @__PURE__ */ Object.freeze([
|
|
|
7565
8342
|
"ops",
|
|
7566
8343
|
"lazy",
|
|
7567
8344
|
"ext",
|
|
8345
|
+
"extTypes",
|
|
7568
8346
|
"truthy",
|
|
8347
|
+
"truthType",
|
|
7569
8348
|
"otherwise",
|
|
7570
8349
|
"logic",
|
|
7571
8350
|
"omit",
|
|
@@ -7750,7 +8529,7 @@ function formulaCode(profile) {
|
|
|
7750
8529
|
if (!Array.isArray(values)) return values;
|
|
7751
8530
|
const out = {};
|
|
7752
8531
|
keys.forEach((key, index) => {
|
|
7753
|
-
if (values[index] !== SKIPPED) setOwn$
|
|
8532
|
+
if (values[index] !== SKIPPED) setOwn$2(out, key, values[index]);
|
|
7754
8533
|
});
|
|
7755
8534
|
return ok(out);
|
|
7756
8535
|
};
|
|
@@ -7763,7 +8542,7 @@ function formulaCode(profile) {
|
|
|
7763
8542
|
if (omit !== void 0 && omit(result.error)) continue;
|
|
7764
8543
|
return result;
|
|
7765
8544
|
}
|
|
7766
|
-
setOwn$
|
|
8545
|
+
setOwn$2(out, keys[index], result.value);
|
|
7767
8546
|
}
|
|
7768
8547
|
return ok(out);
|
|
7769
8548
|
};
|
|
@@ -7780,7 +8559,7 @@ function formulaCode(profile) {
|
|
|
7780
8559
|
const names = { ...ctx.names };
|
|
7781
8560
|
for (const [name, part] of bindings) {
|
|
7782
8561
|
const result = part.code(ctx, rt);
|
|
7783
|
-
setOwn$
|
|
8562
|
+
setOwn$2(names, name, result.ok ? result.value : new Held(result.error));
|
|
7784
8563
|
}
|
|
7785
8564
|
return body.code({
|
|
7786
8565
|
...ctx,
|
|
@@ -7811,7 +8590,7 @@ function formulaCode(profile) {
|
|
|
7811
8590
|
rt.keepsContext?.(ctx, reads);
|
|
7812
8591
|
const closure = (...args) => {
|
|
7813
8592
|
const scope = { ...ctx.names };
|
|
7814
|
-
names.forEach((name, index) => setOwn$
|
|
8593
|
+
names.forEach((name, index) => setOwn$2(scope, name, args[index]));
|
|
7815
8594
|
const result = body.code({
|
|
7816
8595
|
...ctx,
|
|
7817
8596
|
names: scope
|
|
@@ -8079,12 +8858,12 @@ function formulaMachine(profile = {}, more = {}) {
|
|
|
8079
8858
|
facts: kit.facts
|
|
8080
8859
|
}, more);
|
|
8081
8860
|
}
|
|
8082
|
-
|
|
8083
|
-
//#region src/syntax.ts
|
|
8861
|
+
// src/syntax.ts
|
|
8084
8862
|
/**
|
|
8085
8863
|
* The standard syntax of the formula kit. It gives the site and the playground a text form. The operators are
|
|
8086
8864
|
* `or`, `and`, `not`, the comparisons, `+ - * / %`, a minus sign and `^`, from the loosest to the tightest. A call
|
|
8087
|
-
* `name(args)` applies the operation `ops.name`. `if(test, then, else)` and `list(items)` make the constructs.
|
|
8865
|
+
* `name(args)` applies the operation `ops.name`. `if(test, then, else)` and `list(items)` make the constructs. The
|
|
8866
|
+
* text `total = 2 + 3` gives the document the header name `total` (D62).
|
|
8088
8867
|
*/
|
|
8089
8868
|
const STD_SYNTAX = /* @__PURE__ */ Object.freeze({
|
|
8090
8869
|
operators: [
|
|
@@ -8214,7 +8993,8 @@ const STD_SYNTAX = /* @__PURE__ */ Object.freeze({
|
|
|
8214
8993
|
}, {
|
|
8215
8994
|
name: "list",
|
|
8216
8995
|
kind: "list"
|
|
8217
|
-
}] }
|
|
8996
|
+
}] },
|
|
8997
|
+
header: { bind: "=" }
|
|
8218
8998
|
});
|
|
8219
8999
|
const NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
8220
9000
|
const NAME_AT = /[A-Za-z_][A-Za-z0-9_]*/y;
|
|
@@ -8232,6 +9012,12 @@ const DEFAULT_ROOTS = [
|
|
|
8232
9012
|
"event",
|
|
8233
9013
|
"caps"
|
|
8234
9014
|
];
|
|
9015
|
+
const FORM_ARGS = [
|
|
9016
|
+
"list",
|
|
9017
|
+
"fields",
|
|
9018
|
+
"named",
|
|
9019
|
+
"literal"
|
|
9020
|
+
];
|
|
8235
9021
|
const compiledProfiles = /* @__PURE__ */ new WeakMap();
|
|
8236
9022
|
function typeOfKind(kind) {
|
|
8237
9023
|
const keys = kind.split(".");
|
|
@@ -8287,6 +9073,37 @@ function compileOps(profile, separator) {
|
|
|
8287
9073
|
opWords
|
|
8288
9074
|
};
|
|
8289
9075
|
}
|
|
9076
|
+
/** This function examines the bind symbol of a header. A text with the symbol after a name was not valid before. */
|
|
9077
|
+
function compileBind(bind, separator, binary) {
|
|
9078
|
+
if (bind === void 0) return void 0;
|
|
9079
|
+
if (!SYMBOL.test(bind) || bind.includes(separator)) throw new TypeError(`The bind symbol "${bind}" is not a group of symbols.`);
|
|
9080
|
+
for (const symbol of binary.keys()) if (bind.startsWith(symbol)) throw new TypeError(`The bind symbol "${bind}" starts with the operator "${symbol}".`);
|
|
9081
|
+
return bind;
|
|
9082
|
+
}
|
|
9083
|
+
/** This function examines the field `wrap` of a call form (K159). A name that is not a field of the form throws. */
|
|
9084
|
+
function compileWrap(given, args) {
|
|
9085
|
+
const wrap = given.wrap;
|
|
9086
|
+
if (wrap === void 0 || wrap === false) return false;
|
|
9087
|
+
if (args === "literal") throw new TypeError(`The call form "${given.name}" with the arguments literal takes no wrap.`);
|
|
9088
|
+
if (wrap === true) return true;
|
|
9089
|
+
if (args === "list") throw new TypeError(`The call form "${given.name}" with the arguments list takes only the wrap true.`);
|
|
9090
|
+
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.`);
|
|
9091
|
+
return new Set(wrap);
|
|
9092
|
+
}
|
|
9093
|
+
/** This function gives the types of the data envelopes of a profile (K159), or `undefined` without them. */
|
|
9094
|
+
function compileEnvelopes(given) {
|
|
9095
|
+
if (given === void 0) return void 0;
|
|
9096
|
+
const out = {};
|
|
9097
|
+
for (const key of [
|
|
9098
|
+
"number",
|
|
9099
|
+
"text",
|
|
9100
|
+
"bool"
|
|
9101
|
+
]) {
|
|
9102
|
+
const kind = given[key];
|
|
9103
|
+
if (kind !== void 0) out[key] = typeOfKind(kind);
|
|
9104
|
+
}
|
|
9105
|
+
return out;
|
|
9106
|
+
}
|
|
8290
9107
|
function compileProfile(profile) {
|
|
8291
9108
|
const cached = compiledProfiles.get(profile);
|
|
8292
9109
|
if (cached !== void 0) return cached;
|
|
@@ -8301,13 +9118,22 @@ function compileProfile(profile) {
|
|
|
8301
9118
|
const forms = /* @__PURE__ */ new Map();
|
|
8302
9119
|
const formsByKind = /* @__PURE__ */ new Map();
|
|
8303
9120
|
for (const given of calls.forms ?? []) {
|
|
8304
|
-
|
|
9121
|
+
const keys = given.name.split(".");
|
|
9122
|
+
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.`);
|
|
9123
|
+
if (keys.length > 1 && refs.fields === false) throw new TypeError(`The call form "${given.name}" has dots, but the syntax has no field steps.`);
|
|
9124
|
+
if (keys.length > 1 && ops.opWords.has(keys[0])) throw new TypeError(`The call form "${given.name}" starts with the word operator "${keys[0]}".`);
|
|
8305
9125
|
const { type, key } = typeOfKind(given.kind);
|
|
9126
|
+
const args = given.args ?? (given.fields === void 0 ? "list" : "fields");
|
|
9127
|
+
if (!FORM_ARGS.includes(args)) throw new TypeError(`The call form "${given.name}" has the unknown arguments ${String(args)}.`);
|
|
9128
|
+
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.`);
|
|
9129
|
+
if (given.min !== void 0 && (args === "named" || args === "literal")) throw new TypeError(`The call form "${given.name}" with the arguments ${args} takes no min.`);
|
|
8306
9130
|
const form = {
|
|
8307
9131
|
name: given.name,
|
|
8308
9132
|
type,
|
|
9133
|
+
args,
|
|
8309
9134
|
fields: given.fields,
|
|
8310
|
-
min: given.min ?? given.fields?.length ?? 0
|
|
9135
|
+
min: given.min ?? given.fields?.length ?? 0,
|
|
9136
|
+
wrap: compileWrap(given, args)
|
|
8311
9137
|
};
|
|
8312
9138
|
forms.set(form.name, form);
|
|
8313
9139
|
addTo(formsByKind, key, form);
|
|
@@ -8319,6 +9145,7 @@ function compileProfile(profile) {
|
|
|
8319
9145
|
quote: strings === void 0 ? void 0 : strings.quote ?? "\"",
|
|
8320
9146
|
doubled: strings !== void 0 && strings.escape === "double",
|
|
8321
9147
|
words,
|
|
9148
|
+
envelopes: compileEnvelopes(literals.envelopes),
|
|
8322
9149
|
bare: refs.bare ?? "names",
|
|
8323
9150
|
roots,
|
|
8324
9151
|
fields: refs.fields ?? true,
|
|
@@ -8335,6 +9162,7 @@ function compileProfile(profile) {
|
|
|
8335
9162
|
forms,
|
|
8336
9163
|
formsByKind,
|
|
8337
9164
|
separator,
|
|
9165
|
+
bind: compileBind(profile.header?.bind, separator, ops.binary),
|
|
8338
9166
|
reserved: /* @__PURE__ */ new Set([
|
|
8339
9167
|
...words.keys(),
|
|
8340
9168
|
...ops.opWords,
|
|
@@ -8361,6 +9189,37 @@ function opDoc(op, value) {
|
|
|
8361
9189
|
value
|
|
8362
9190
|
};
|
|
8363
9191
|
}
|
|
9192
|
+
/** This function gives the kind of the data envelope of a literal (K159), or `undefined` when the literal has none. */
|
|
9193
|
+
function envelopeOf(syn, value) {
|
|
9194
|
+
const kinds = syn.envelopes;
|
|
9195
|
+
if (kinds === void 0 || !isScalar(value)) return void 0;
|
|
9196
|
+
return typeof value === "string" ? kinds.text : typeof value === "number" ? kinds.number : typeof value === "boolean" ? kinds.bool : void 0;
|
|
9197
|
+
}
|
|
9198
|
+
/** This function gives a literal in a place that takes a document its data envelope (K159). Each other document stays. */
|
|
9199
|
+
function envelop(syn, doc) {
|
|
9200
|
+
const kind = isEnvelope(doc) ? void 0 : envelopeOf(syn, doc);
|
|
9201
|
+
return kind === void 0 ? doc : {
|
|
9202
|
+
type: kind.type,
|
|
9203
|
+
value: doc
|
|
9204
|
+
};
|
|
9205
|
+
}
|
|
9206
|
+
/** This function tells if an argument of a form takes a document (K159). */
|
|
9207
|
+
function wraps(form, key) {
|
|
9208
|
+
return form.wrap === true || form.wrap !== false && form.wrap.has(String(key));
|
|
9209
|
+
}
|
|
9210
|
+
/** This function tells if a value is a literal that JSON writes exactly: a text, a finite number, a truth value or `null`. */
|
|
9211
|
+
function isScalar(value) {
|
|
9212
|
+
return value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value) && !Object.is(value, -0);
|
|
9213
|
+
}
|
|
9214
|
+
function setOwn$1(target, key, value) {
|
|
9215
|
+
if (key === "__proto__") Object.defineProperty(target, key, {
|
|
9216
|
+
value,
|
|
9217
|
+
enumerable: true,
|
|
9218
|
+
writable: true,
|
|
9219
|
+
configurable: true
|
|
9220
|
+
});
|
|
9221
|
+
else target[key] = value;
|
|
9222
|
+
}
|
|
8364
9223
|
const ESCAPES = {
|
|
8365
9224
|
"\"": "\"",
|
|
8366
9225
|
"'": "'",
|
|
@@ -8384,10 +9243,10 @@ var Parser = class {
|
|
|
8384
9243
|
parse() {
|
|
8385
9244
|
this.skip();
|
|
8386
9245
|
if (this.pos >= this.text.length) this.stop("The text is empty.", 0, 0);
|
|
8387
|
-
const doc = this.
|
|
9246
|
+
const doc = this.bound();
|
|
8388
9247
|
this.skip();
|
|
8389
9248
|
if (this.pos < this.text.length) this.stop("The text continues after the expression.", this.pos, this.tokenLength());
|
|
8390
|
-
return doc;
|
|
9249
|
+
return envelop(this.syn, doc);
|
|
8391
9250
|
}
|
|
8392
9251
|
position() {
|
|
8393
9252
|
return this.pos;
|
|
@@ -8409,6 +9268,60 @@ var Parser = class {
|
|
|
8409
9268
|
tokenLength() {
|
|
8410
9269
|
return (this.at(NAME_AT) ?? this.at(DIGITS_AT) ?? "x").length;
|
|
8411
9270
|
}
|
|
9271
|
+
/**
|
|
9272
|
+
* This method reads an expression that can have a header name: `name = expr` (D62). The bind symbol of the profile
|
|
9273
|
+
* follows the name. A longer operator that starts with the symbol wins, for example `==` over `=`.
|
|
9274
|
+
*/
|
|
9275
|
+
bound() {
|
|
9276
|
+
const name = this.bindingName();
|
|
9277
|
+
if (name === void 0) return this.expr(0);
|
|
9278
|
+
this.skip();
|
|
9279
|
+
const start = this.pos;
|
|
9280
|
+
const doc = this.expr(0);
|
|
9281
|
+
if (isEnvelope(doc)) {
|
|
9282
|
+
if (doc.name !== void 0) this.stop(`The document already has the name ${doc.name}.`, start, this.pos - start);
|
|
9283
|
+
return {
|
|
9284
|
+
...doc,
|
|
9285
|
+
name
|
|
9286
|
+
};
|
|
9287
|
+
}
|
|
9288
|
+
const kind = envelopeOf(this.syn, doc);
|
|
9289
|
+
return kind === void 0 ? {
|
|
9290
|
+
type: "lit",
|
|
9291
|
+
value: doc,
|
|
9292
|
+
name
|
|
9293
|
+
} : {
|
|
9294
|
+
type: kind.type,
|
|
9295
|
+
value: doc,
|
|
9296
|
+
name
|
|
9297
|
+
};
|
|
9298
|
+
}
|
|
9299
|
+
/** This method reads a name and the bind symbol after it. Without them, it gives `undefined` and keeps the position. */
|
|
9300
|
+
bindingName() {
|
|
9301
|
+
const bind = this.syn.bind;
|
|
9302
|
+
if (bind === void 0) return void 0;
|
|
9303
|
+
this.skip();
|
|
9304
|
+
const back = this.pos;
|
|
9305
|
+
let name;
|
|
9306
|
+
if (this.text[this.pos] === "`") try {
|
|
9307
|
+
name = this.quoted();
|
|
9308
|
+
} catch (error) {
|
|
9309
|
+
if (!(error instanceof Stop)) throw error;
|
|
9310
|
+
}
|
|
9311
|
+
else {
|
|
9312
|
+
name = this.at(NAME_AT);
|
|
9313
|
+
if (name !== void 0) this.pos += name.length;
|
|
9314
|
+
}
|
|
9315
|
+
if (name !== void 0) {
|
|
9316
|
+
this.skip();
|
|
9317
|
+
const longer = this.syn.binarySymbols.some((symbol) => symbol.length > bind.length && symbol.startsWith(bind) && this.text.startsWith(symbol, this.pos));
|
|
9318
|
+
if (this.text.startsWith(bind, this.pos) && !longer) {
|
|
9319
|
+
this.pos += bind.length;
|
|
9320
|
+
return name;
|
|
9321
|
+
}
|
|
9322
|
+
}
|
|
9323
|
+
this.pos = back;
|
|
9324
|
+
}
|
|
8412
9325
|
/** This method reads an expression by precedence climbing. It stops at an operator below `min`. */
|
|
8413
9326
|
expr(min) {
|
|
8414
9327
|
const start = this.pos;
|
|
@@ -8541,7 +9454,7 @@ var Parser = class {
|
|
|
8541
9454
|
group() {
|
|
8542
9455
|
const open = this.pos;
|
|
8543
9456
|
this.pos += 1;
|
|
8544
|
-
const inner = this.
|
|
9457
|
+
const inner = this.bound();
|
|
8545
9458
|
this.skip();
|
|
8546
9459
|
if (this.text[this.pos] !== ")") {
|
|
8547
9460
|
if (this.pos >= this.text.length) this.stop("A closing parenthesis is missing.", open, 1);
|
|
@@ -8591,6 +9504,11 @@ var Parser = class {
|
|
|
8591
9504
|
}
|
|
8592
9505
|
this.pos += 1;
|
|
8593
9506
|
this.skip();
|
|
9507
|
+
if (this.text[this.pos] === "(") {
|
|
9508
|
+
path.push(this.group());
|
|
9509
|
+
allPlain = false;
|
|
9510
|
+
continue;
|
|
9511
|
+
}
|
|
8594
9512
|
if (this.text[this.pos] === "`") {
|
|
8595
9513
|
path.push(this.quoted());
|
|
8596
9514
|
allPlain = false;
|
|
@@ -8606,7 +9524,7 @@ var Parser = class {
|
|
|
8606
9524
|
const back = this.pos;
|
|
8607
9525
|
this.skip();
|
|
8608
9526
|
if (this.text[this.pos] === "(") {
|
|
8609
|
-
if (!allPlain) this.stop("
|
|
9527
|
+
if (!allPlain) this.stop("Each key of the name of a call is a plain name.", start, back - start);
|
|
8610
9528
|
return this.call(path.join("."), start, back - start);
|
|
8611
9529
|
}
|
|
8612
9530
|
this.pos = back;
|
|
@@ -8616,14 +9534,18 @@ var Parser = class {
|
|
|
8616
9534
|
value: plain && this.syn.roots.has(first) ? path : [this.syn.bare, ...path]
|
|
8617
9535
|
};
|
|
8618
9536
|
}
|
|
8619
|
-
call
|
|
9537
|
+
/** This method reads the arguments of a call in parentheses. `item` reads one argument. */
|
|
9538
|
+
argumentsOf(item) {
|
|
8620
9539
|
const open = this.pos;
|
|
8621
9540
|
const args = [];
|
|
8622
9541
|
this.pos += 1;
|
|
8623
9542
|
this.skip();
|
|
8624
|
-
if (this.text[this.pos] === ")")
|
|
8625
|
-
|
|
8626
|
-
args
|
|
9543
|
+
if (this.text[this.pos] === ")") {
|
|
9544
|
+
this.pos += 1;
|
|
9545
|
+
return args;
|
|
9546
|
+
}
|
|
9547
|
+
for (;;) {
|
|
9548
|
+
args.push(item());
|
|
8627
9549
|
this.skip();
|
|
8628
9550
|
if (this.text.startsWith(this.syn.separator, this.pos)) {
|
|
8629
9551
|
this.pos += this.syn.separator.length;
|
|
@@ -8631,22 +9553,55 @@ var Parser = class {
|
|
|
8631
9553
|
}
|
|
8632
9554
|
if (this.text[this.pos] === ")") {
|
|
8633
9555
|
this.pos += 1;
|
|
8634
|
-
|
|
9556
|
+
return args;
|
|
8635
9557
|
}
|
|
8636
9558
|
if (this.pos >= this.text.length) this.stop("A closing parenthesis is missing.", open, 1);
|
|
8637
9559
|
this.stop(`A "${this.syn.separator}" or a closing parenthesis is missing.`, this.pos, this.tokenLength());
|
|
8638
9560
|
}
|
|
9561
|
+
}
|
|
9562
|
+
/** This method reads the arguments `key: expr` of a form `named` into a record (SYNTAX.RECORD). */
|
|
9563
|
+
namedArguments(form) {
|
|
9564
|
+
const value = {};
|
|
9565
|
+
this.argumentsOf(() => {
|
|
9566
|
+
this.skip();
|
|
9567
|
+
const at = this.pos;
|
|
9568
|
+
let key;
|
|
9569
|
+
if (this.text[at] === "`") key = this.quoted();
|
|
9570
|
+
else {
|
|
9571
|
+
key = this.at(NAME_AT);
|
|
9572
|
+
if (key !== void 0) this.pos += key.length;
|
|
9573
|
+
}
|
|
9574
|
+
const end = this.pos;
|
|
9575
|
+
this.skip();
|
|
9576
|
+
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()));
|
|
9577
|
+
if (form.fields !== void 0 && !form.fields.includes(key)) this.stop(`The form ${form.name} has no field ${key}.`, at, end - at);
|
|
9578
|
+
if (Object.hasOwn(value, key)) this.stop(`The form ${form.name} has the field ${key} two times.`, at, end - at);
|
|
9579
|
+
this.pos += 1;
|
|
9580
|
+
const arg = this.bound();
|
|
9581
|
+
setOwn$1(value, key, wraps(form, key) ? envelop(this.syn, arg) : arg);
|
|
9582
|
+
});
|
|
9583
|
+
return opDoc(form, value);
|
|
9584
|
+
}
|
|
9585
|
+
call(name, start, nameLength) {
|
|
8639
9586
|
const form = this.syn.forms.get(name);
|
|
9587
|
+
if (form?.args === "named") return this.namedArguments(form);
|
|
9588
|
+
const args = this.argumentsOf(() => this.bound());
|
|
9589
|
+
if (form?.args === "literal") {
|
|
9590
|
+
const [only] = args;
|
|
9591
|
+
if (args.length !== 1 || !isScalar(only)) this.stop(`The form ${name} takes one literal that JSON writes.`, start, this.pos - start);
|
|
9592
|
+
return opDoc(form, only);
|
|
9593
|
+
}
|
|
8640
9594
|
if (form !== void 0) {
|
|
8641
9595
|
const most = form.fields?.length ?? Number.POSITIVE_INFINITY;
|
|
8642
9596
|
if (args.length < form.min || args.length > most) {
|
|
8643
9597
|
const range = most === form.min ? String(most) : most === Number.POSITIVE_INFINITY ? `${String(form.min)} or more` : `${String(form.min)} to ${String(most)}`;
|
|
8644
9598
|
this.stop(`The form ${name} takes ${range} arguments.`, start, this.pos - start);
|
|
8645
9599
|
}
|
|
8646
|
-
if (form.fields === void 0) return opDoc(form, args);
|
|
9600
|
+
if (form.fields === void 0) return opDoc(form, form.wrap === true ? args.map((arg) => envelop(this.syn, arg)) : args);
|
|
8647
9601
|
const value = {};
|
|
8648
9602
|
args.forEach((arg, index) => {
|
|
8649
|
-
|
|
9603
|
+
const key = form.fields?.[index];
|
|
9604
|
+
value[key] = wraps(form, key) ? envelop(this.syn, arg) : arg;
|
|
8650
9605
|
});
|
|
8651
9606
|
return opDoc(form, value);
|
|
8652
9607
|
}
|
|
@@ -8725,8 +9680,10 @@ function literalShape(syn, value) {
|
|
|
8725
9680
|
function plainSegment(syn, name) {
|
|
8726
9681
|
return NAME.test(name) && !syn.reserved.has(name) && !syn.patterns.some((p) => p.whole.test(name));
|
|
8727
9682
|
}
|
|
8728
|
-
function
|
|
9683
|
+
/** This function gives the text of a later step of a path, or the step and its index when the step is a document. */
|
|
9684
|
+
function laterSegment(step, index) {
|
|
8729
9685
|
if (typeof step === "string") return NAME.test(step) ? step : quoteName(step);
|
|
9686
|
+
if (isEnvelope(step)) return [index, step];
|
|
8730
9687
|
return typeof step === "number" && Number.isSafeInteger(step) && step >= 0 ? String(step) : void 0;
|
|
8731
9688
|
}
|
|
8732
9689
|
function refShape(syn, value) {
|
|
@@ -8740,18 +9697,23 @@ function refShape(syn, value) {
|
|
|
8740
9697
|
}
|
|
8741
9698
|
const [root, ...rest] = value;
|
|
8742
9699
|
if ((rest.length > 1 || root !== syn.bare && rest.length > 0) && !syn.fields) return none("This syntax has no field steps.");
|
|
8743
|
-
const later = rest.slice(1).map(laterSegment);
|
|
8744
9700
|
let first;
|
|
8745
|
-
let tail =
|
|
8746
|
-
if (root === syn.bare && typeof rest[0] === "string")
|
|
8747
|
-
|
|
9701
|
+
let tail = [];
|
|
9702
|
+
if (root === syn.bare && typeof rest[0] === "string") {
|
|
9703
|
+
first = plainSegment(syn, rest[0]) ? rest[0] : quoteName(rest[0]);
|
|
9704
|
+
tail = rest.slice(1).map((step, index) => laterSegment(step, index + 2));
|
|
9705
|
+
} else if (typeof root === "string" && syn.roots.has(root) && !syn.patterns.some((p) => p.whole.test(root))) {
|
|
8748
9706
|
first = root;
|
|
8749
|
-
tail = rest.map(laterSegment);
|
|
9707
|
+
tail = rest.map((step, index) => laterSegment(step, index + 1));
|
|
8750
9708
|
}
|
|
8751
9709
|
if (first === void 0 || tail.includes(void 0)) return none("This syntax has no text for the path of the reference.");
|
|
8752
|
-
|
|
9710
|
+
const segments = [first, ...tail];
|
|
9711
|
+
return segments.every((segment) => typeof segment === "string") ? {
|
|
8753
9712
|
tag: "text",
|
|
8754
|
-
text:
|
|
9713
|
+
text: segments.join(".")
|
|
9714
|
+
} : {
|
|
9715
|
+
tag: "path",
|
|
9716
|
+
segments
|
|
8755
9717
|
};
|
|
8756
9718
|
}
|
|
8757
9719
|
function callName(syn, keys) {
|
|
@@ -8760,13 +9722,58 @@ function callName(syn, keys) {
|
|
|
8760
9722
|
if (syn.forms.has(name) || syn.opWords.has(keys[0])) return void 0;
|
|
8761
9723
|
return syn.ops === true || syn.ops.has(name) ? name : void 0;
|
|
8762
9724
|
}
|
|
8763
|
-
|
|
9725
|
+
const WRAPPED = "This syntax reads a literal in this place as its data envelope, thus a bare literal has no text here.";
|
|
9726
|
+
/**
|
|
9727
|
+
* This function gives the text of a document in a place that takes a document (K159). A data envelope of a literal
|
|
9728
|
+
* is the bare literal. A literal without its envelope has no text, because `parse` gives it the envelope. The
|
|
9729
|
+
* function gives `undefined` for each other document.
|
|
9730
|
+
*/
|
|
9731
|
+
function wrappedShape(syn, doc) {
|
|
9732
|
+
if (syn.envelopes === void 0) return void 0;
|
|
9733
|
+
if (!isEnvelope(doc)) return envelopeOf(syn, doc) === void 0 ? void 0 : none(WRAPPED);
|
|
9734
|
+
if (doc.name !== void 0 || doc.own !== void 0 || !Object.hasOwn(doc, "value")) return void 0;
|
|
9735
|
+
const path = typePath(doc.type);
|
|
9736
|
+
if (path === void 0) return void 0;
|
|
9737
|
+
const key = JSON.stringify(path);
|
|
9738
|
+
if (key === JSON.stringify(["lit"])) return envelopeOf(syn, doc.value) === void 0 ? void 0 : none(WRAPPED);
|
|
9739
|
+
if (envelopeOf(syn, doc.value)?.key !== key) return void 0;
|
|
9740
|
+
const literal = literalShape(syn, doc.value);
|
|
9741
|
+
return literal.tag === "text" ? literal : void 0;
|
|
9742
|
+
}
|
|
9743
|
+
function formShape(syn, form, value) {
|
|
9744
|
+
if (form.args === "literal") {
|
|
9745
|
+
if (!isScalar(value)) return void 0;
|
|
9746
|
+
const literal = literalShape(syn, value);
|
|
9747
|
+
return literal.tag === "text" ? {
|
|
9748
|
+
tag: "text",
|
|
9749
|
+
text: `${form.name}(${literal.text})`
|
|
9750
|
+
} : void 0;
|
|
9751
|
+
}
|
|
9752
|
+
if (form.args === "named") {
|
|
9753
|
+
if (!isRecord(value)) return void 0;
|
|
9754
|
+
const keys = Object.keys(value);
|
|
9755
|
+
if (form.fields !== void 0 && !keys.every((key) => form.fields?.includes(key) === true)) return void 0;
|
|
9756
|
+
return {
|
|
9757
|
+
tag: "call",
|
|
9758
|
+
name: form.name,
|
|
9759
|
+
args: keys.map((key) => [
|
|
9760
|
+
key,
|
|
9761
|
+
value[key],
|
|
9762
|
+
wraps(form, key)
|
|
9763
|
+
]),
|
|
9764
|
+
named: true
|
|
9765
|
+
};
|
|
9766
|
+
}
|
|
8764
9767
|
if (form.fields === void 0) {
|
|
8765
9768
|
if (!Array.isArray(value) || value.length < form.min) return void 0;
|
|
8766
9769
|
return {
|
|
8767
9770
|
tag: "call",
|
|
8768
9771
|
name: form.name,
|
|
8769
|
-
args: value.map((item, index) => [
|
|
9772
|
+
args: value.map((item, index) => [
|
|
9773
|
+
index,
|
|
9774
|
+
item,
|
|
9775
|
+
wraps(form, index)
|
|
9776
|
+
])
|
|
8770
9777
|
};
|
|
8771
9778
|
}
|
|
8772
9779
|
if (!isRecord(value)) return void 0;
|
|
@@ -8776,7 +9783,11 @@ function formShape(form, value) {
|
|
|
8776
9783
|
return {
|
|
8777
9784
|
tag: "call",
|
|
8778
9785
|
name: form.name,
|
|
8779
|
-
args: present.map((key) => [
|
|
9786
|
+
args: present.map((key) => [
|
|
9787
|
+
key,
|
|
9788
|
+
value[key],
|
|
9789
|
+
wraps(form, key)
|
|
9790
|
+
])
|
|
8780
9791
|
};
|
|
8781
9792
|
}
|
|
8782
9793
|
function opShape(syn, key, terms) {
|
|
@@ -8789,7 +9800,16 @@ function opShape(syn, key, terms) {
|
|
|
8789
9800
|
/** This function gives the shape of a document: a literal or a reference, an operator, a call, or no text. */
|
|
8790
9801
|
function shapeOf(syn, doc) {
|
|
8791
9802
|
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);
|
|
8792
|
-
if (doc.
|
|
9803
|
+
if (doc.own !== void 0) return none("This syntax has no text for the own entries of a document.");
|
|
9804
|
+
if (doc.name !== void 0) {
|
|
9805
|
+
if (syn.bind === void 0) return none("This syntax has no text for the name of a document.");
|
|
9806
|
+
const { name, ...rest } = doc;
|
|
9807
|
+
return {
|
|
9808
|
+
tag: "bind",
|
|
9809
|
+
name,
|
|
9810
|
+
doc: rest
|
|
9811
|
+
};
|
|
9812
|
+
}
|
|
8793
9813
|
const path = typePath(doc.type);
|
|
8794
9814
|
if (path === void 0) return none("This syntax has no text for a computed type.");
|
|
8795
9815
|
const value = doc.value;
|
|
@@ -8810,7 +9830,7 @@ function shapeOf(syn, doc) {
|
|
|
8810
9830
|
const op = Array.isArray(value) ? opShape(syn, key, value) : void 0;
|
|
8811
9831
|
if (op !== void 0) return op;
|
|
8812
9832
|
for (const form of syn.formsByKind.get(key) ?? []) {
|
|
8813
|
-
const shape = formShape(form, value);
|
|
9833
|
+
const shape = formShape(syn, form, value);
|
|
8814
9834
|
if (shape !== void 0) return shape;
|
|
8815
9835
|
}
|
|
8816
9836
|
const called = path[0] === "ops" && Array.isArray(value) ? callName(syn, path.slice(1)) : void 0;
|
|
@@ -8823,7 +9843,7 @@ function shapeOf(syn, doc) {
|
|
|
8823
9843
|
}
|
|
8824
9844
|
function levelOf(syn, doc) {
|
|
8825
9845
|
const shape = shapeOf(syn, doc);
|
|
8826
|
-
return shape.tag === "op" ? shape.op.prec : Number.POSITIVE_INFINITY;
|
|
9846
|
+
return shape.tag === "op" ? shape.op.prec : shape.tag === "bind" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY;
|
|
8827
9847
|
}
|
|
8828
9848
|
/** This function tells if a term needs parentheses in its position, so that `parse` reads the same tree. */
|
|
8829
9849
|
function needsGroup(op, index, level) {
|
|
@@ -8853,18 +9873,42 @@ function printNode(syn, doc, sub) {
|
|
|
8853
9873
|
if (shape.tag === "text") return ok(shape.text);
|
|
8854
9874
|
if (shape.tag === "none") return fail("#VALUE!", shape.reason);
|
|
8855
9875
|
if (shape.tag === "op") return opText(syn, shape.op, shape.terms, sub);
|
|
9876
|
+
if (shape.tag === "bind") {
|
|
9877
|
+
const head = wrappedShape(syn, shape.doc);
|
|
9878
|
+
const inner = head === void 0 ? printNode(syn, shape.doc, sub) : head.tag === "text" ? ok(head.text) : fail("#VALUE!", WRAPPED);
|
|
9879
|
+
return inner.ok ? ok(`${NAME.test(shape.name) ? shape.name : quoteName(shape.name)} ${syn.bind} ${inner.value}`) : inner;
|
|
9880
|
+
}
|
|
9881
|
+
if (shape.tag === "path") {
|
|
9882
|
+
const texts = [];
|
|
9883
|
+
for (const segment of shape.segments) {
|
|
9884
|
+
if (typeof segment === "string") {
|
|
9885
|
+
texts.push(segment);
|
|
9886
|
+
continue;
|
|
9887
|
+
}
|
|
9888
|
+
const result = sub(segment[1], [segment[0]]);
|
|
9889
|
+
if (!result.ok) return result;
|
|
9890
|
+
texts.push(`(${result.value})`);
|
|
9891
|
+
}
|
|
9892
|
+
return ok(texts.join("."));
|
|
9893
|
+
}
|
|
8856
9894
|
const texts = [];
|
|
8857
|
-
for (const [key, arg] of shape.args) {
|
|
8858
|
-
const result = sub(arg, [key]);
|
|
9895
|
+
for (const [key, arg, wrapped] of shape.args) {
|
|
9896
|
+
const result = sub(arg, [key], wrapped);
|
|
8859
9897
|
if (!result.ok) return result;
|
|
8860
|
-
texts.push(result.value);
|
|
9898
|
+
texts.push(shape.named === true ? `${NAME.test(String(key)) ? String(key) : quoteName(String(key))}: ${result.value}` : result.value);
|
|
8861
9899
|
}
|
|
8862
9900
|
return ok(`${shape.name}(${texts.join(`${syn.separator} `)})`);
|
|
8863
9901
|
}
|
|
8864
9902
|
function printTree(syn, doc, place) {
|
|
8865
|
-
const result = printNode(syn, doc, (child, path) => printTree(syn, child, [...place, ...path]));
|
|
9903
|
+
const result = printNode(syn, doc, (child, path, wrapped) => (wrapped === true ? printPlace : printTree)(syn, child, [...place, ...path]));
|
|
8866
9904
|
return result.ok || result.error.place !== void 0 ? result : fail(result.error.code, result.error.reason, { place });
|
|
8867
9905
|
}
|
|
9906
|
+
/** This function writes a document in a place that takes a document, for example the whole text (K159). */
|
|
9907
|
+
function printPlace(syn, doc, place) {
|
|
9908
|
+
const shape = wrappedShape(syn, doc);
|
|
9909
|
+
if (shape === void 0) return printTree(syn, doc, place);
|
|
9910
|
+
return shape.tag === "text" ? ok(shape.text) : fail("#VALUE!", WRAPPED, { place });
|
|
9911
|
+
}
|
|
8868
9912
|
/**
|
|
8869
9913
|
* This function writes a document as text with the rules of a profile (SYNTAX.PRINT). It writes the fewest
|
|
8870
9914
|
* parentheses that keep the tree. `parse` reads the text back to the same document (SYNTAX.ROUNDTRIP).
|
|
@@ -8877,7 +9921,7 @@ function printTree(syn, doc, place) {
|
|
|
8877
9921
|
function print(profile, doc) {
|
|
8878
9922
|
const syn = compileProfile(profile);
|
|
8879
9923
|
try {
|
|
8880
|
-
return
|
|
9924
|
+
return printPlace(syn, doc, []);
|
|
8881
9925
|
} catch (error) {
|
|
8882
9926
|
if (isStackOverflow(error)) return fail("#DEPTH!", "The document is too deep for the stack of the host.", { cause: error });
|
|
8883
9927
|
throw error;
|
|
@@ -8895,7 +9939,9 @@ function syntaxFormat(profile) {
|
|
|
8895
9939
|
const syn = compileProfile(profile);
|
|
8896
9940
|
const code = ({ doc, parts, place }) => {
|
|
8897
9941
|
const byPath = new Map(parts.map((part) => [JSON.stringify(part.path), part]));
|
|
8898
|
-
return printNode(syn, doc, (child, path) => {
|
|
9942
|
+
return printNode(syn, doc, (child, path, wrapped) => {
|
|
9943
|
+
const own = wrapped === true ? wrappedShape(syn, child) : void 0;
|
|
9944
|
+
if (own !== void 0) return own.tag === "text" ? ok(own.text) : fail("#VALUE!", WRAPPED, { place: [...place, ...path] });
|
|
8899
9945
|
const part = byPath.get(JSON.stringify(path));
|
|
8900
9946
|
if (part === void 0) return printTree(syn, child, [...place, ...path]);
|
|
8901
9947
|
const result = part.evaluate();
|
|
@@ -8908,8 +9954,7 @@ function syntaxFormat(profile) {
|
|
|
8908
9954
|
evaluators: { "syntax.format": code }
|
|
8909
9955
|
};
|
|
8910
9956
|
}
|
|
8911
|
-
|
|
8912
|
-
//#region src/reference.ts
|
|
9957
|
+
// src/reference.ts
|
|
8913
9958
|
function own$1(table, name) {
|
|
8914
9959
|
return table !== void 0 && Object.hasOwn(table, name) ? table[name] : void 0;
|
|
8915
9960
|
}
|
|
@@ -9016,6 +10061,54 @@ function resolveFully(machine, ctx, path, options) {
|
|
|
9016
10061
|
hops: []
|
|
9017
10062
|
};
|
|
9018
10063
|
}
|
|
10064
|
+
/** The text that names a resolution in the error of an exception of the host (READS.RECORD). */
|
|
10065
|
+
const READ_FAILED = "the resolution of a path";
|
|
10066
|
+
/**
|
|
10067
|
+
* This function resolves a path for a document, and records the read when the evaluation has a list (REF.READS,
|
|
10068
|
+
* READS.RECORD). An exception of the host gives the error value of the read, and the exception goes on.
|
|
10069
|
+
*/
|
|
10070
|
+
function readAt(env, ctx, path, options) {
|
|
10071
|
+
const reading = env.reading;
|
|
10072
|
+
if (reading === void 0) return resolveFully(env.machine, ctx, path, options);
|
|
10073
|
+
const free = freeIn(reading.root, ctx, path);
|
|
10074
|
+
let found;
|
|
10075
|
+
try {
|
|
10076
|
+
found = resolveFully(env.machine, ctx, path, options);
|
|
10077
|
+
} catch (error) {
|
|
10078
|
+
reading.list.push({
|
|
10079
|
+
path,
|
|
10080
|
+
reached: thrown(error, READ_FAILED),
|
|
10081
|
+
free
|
|
10082
|
+
});
|
|
10083
|
+
throw error;
|
|
10084
|
+
}
|
|
10085
|
+
reading.list.push(options?.hops === true ? {
|
|
10086
|
+
path,
|
|
10087
|
+
reached: reachedOf(found),
|
|
10088
|
+
free,
|
|
10089
|
+
hops: found.hops
|
|
10090
|
+
} : {
|
|
10091
|
+
path,
|
|
10092
|
+
reached: reachedOf(found),
|
|
10093
|
+
free
|
|
10094
|
+
});
|
|
10095
|
+
return found;
|
|
10096
|
+
}
|
|
10097
|
+
/**
|
|
10098
|
+
* This function tells if a read reads the context at the root of the evaluation (READS.FREE). The reference reads
|
|
10099
|
+
* 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
|
|
10100
|
+
* name that a kind does not bind keeps its value from the root.
|
|
10101
|
+
*/
|
|
10102
|
+
function freeIn(root, ctx, path) {
|
|
10103
|
+
const head = path[0];
|
|
10104
|
+
if (head === "self") return Object.is(ctx.self, root.self);
|
|
10105
|
+
if (head !== "names") return typeof head === "string" && ROOTS.includes(head);
|
|
10106
|
+
const name = path[1];
|
|
10107
|
+
if (ctx.names === root.names || !isKey(name)) return true;
|
|
10108
|
+
const key = String(name);
|
|
10109
|
+
const bound = Object.hasOwn(ctx.names, key);
|
|
10110
|
+
return bound === Object.hasOwn(root.names, key) && (!bound || Object.is(ctx.names[key], root.names[key]));
|
|
10111
|
+
}
|
|
9019
10112
|
/**
|
|
9020
10113
|
* This function causes an effect when the capabilities permit it (RUN.CAP, CAP.HOOK). It records the effect with its
|
|
9021
10114
|
* address (RUN.ADDRESS) and its outcome (RUN.OUTCOME). The mock answers the effect at once (RUN.MOCK). Without a
|
|
@@ -9047,6 +10140,11 @@ function* causeAndWait(env, ctx, kind, payload, place) {
|
|
|
9047
10140
|
if ("ok" in effect) return effect;
|
|
9048
10141
|
const perform = env.perform;
|
|
9049
10142
|
if (perform === void 0) return effect.outcome = ok(payload);
|
|
10143
|
+
if (env.held > 0) {
|
|
10144
|
+
effect.outcome = fail("#CALC!", heldReason(kind), { place });
|
|
10145
|
+
yield Promise.resolve(void 0);
|
|
10146
|
+
return effect.outcome;
|
|
10147
|
+
}
|
|
9050
10148
|
let given;
|
|
9051
10149
|
try {
|
|
9052
10150
|
given = perform(effect);
|
|
@@ -9116,7 +10214,15 @@ function evaluateSeen(env, seen, at) {
|
|
|
9116
10214
|
const { chosen } = seen;
|
|
9117
10215
|
const { place } = at;
|
|
9118
10216
|
if ("gap" in chosen) return fail("#NOEVAL", chosen.gap, { place });
|
|
9119
|
-
if ("runner" in chosen)
|
|
10217
|
+
if ("runner" in chosen) {
|
|
10218
|
+
if (!env.running) return drive$1(runSeen(env, seen, at, chosen.runner), place, seen.kindText);
|
|
10219
|
+
env.held += 1;
|
|
10220
|
+
try {
|
|
10221
|
+
return drive$1(runSeen(env, seen, at, chosen.runner), place, seen.kindText);
|
|
10222
|
+
} finally {
|
|
10223
|
+
env.held -= 1;
|
|
10224
|
+
}
|
|
10225
|
+
}
|
|
9120
10226
|
const input = evalInput(env, seen, at);
|
|
9121
10227
|
try {
|
|
9122
10228
|
return placed(chosen.evaluator(input), place);
|
|
@@ -9170,7 +10276,7 @@ function evalInput(env, seen, at) {
|
|
|
9170
10276
|
place: at.place,
|
|
9171
10277
|
machine: env.machine,
|
|
9172
10278
|
...at.address ? { address: true } : {},
|
|
9173
|
-
resolve: (path, options) =>
|
|
10279
|
+
resolve: (path, options) => readAt(env, at.ctx, path, options),
|
|
9174
10280
|
effect: (kind, payload) => causeFromEvaluator(env, at.ctx, kind, payload, at.place),
|
|
9175
10281
|
fill
|
|
9176
10282
|
};
|
|
@@ -9197,7 +10303,7 @@ function runInput(env, seen, at) {
|
|
|
9197
10303
|
context: at.ctx,
|
|
9198
10304
|
place: at.place,
|
|
9199
10305
|
machine: env.machine,
|
|
9200
|
-
resolve: (path, options) =>
|
|
10306
|
+
resolve: (path, options) => readAt(env, at.ctx, path, options),
|
|
9201
10307
|
effect: (kind, payload) => causeAndWait(env, at.ctx, kind, payload, at.place),
|
|
9202
10308
|
all: (list, options) => together(env, list, options),
|
|
9203
10309
|
fill
|
|
@@ -9226,6 +10332,7 @@ function* together(env, list, options = {}) {
|
|
|
9226
10332
|
outer.alls += 1;
|
|
9227
10333
|
let failed = false;
|
|
9228
10334
|
let started = 0;
|
|
10335
|
+
let before = 0;
|
|
9229
10336
|
const end = (index, result) => {
|
|
9230
10337
|
results[index] = result;
|
|
9231
10338
|
if (failFast && !result.ok) failed = true;
|
|
@@ -9289,10 +10396,11 @@ function* together(env, list, options = {}) {
|
|
|
9289
10396
|
};
|
|
9290
10397
|
startMore();
|
|
9291
10398
|
while (waiting.length > 0 && !failed) {
|
|
9292
|
-
const chosen = env.journal?.next?.(address, waiting.map((waiter) => waiter.index));
|
|
10399
|
+
const chosen = env.journal?.next?.(address, waiting.map((waiter) => waiter.index), before);
|
|
9293
10400
|
const pick = chosen === void 0 ? void 0 : waiting.find((waiter) => waiter.index === chosen);
|
|
9294
10401
|
const resumed = yield pick === void 0 ? Promise.race(waiting.map((waiter) => waiter.next)) : pick.next;
|
|
9295
10402
|
env.journal?.settled?.(address, resumed.waiter.index);
|
|
10403
|
+
before += 1;
|
|
9296
10404
|
waiting.splice(waiting.indexOf(resumed.waiter), 1);
|
|
9297
10405
|
goOn(resumed.waiter.index, resumed.waiter.steps, resumed);
|
|
9298
10406
|
startMore();
|
|
@@ -9319,9 +10427,17 @@ function* together(env, list, options = {}) {
|
|
|
9319
10427
|
* @returns The result.
|
|
9320
10428
|
*/
|
|
9321
10429
|
function reference(machine, doc, context = makeContext(), options = {}) {
|
|
9322
|
-
|
|
10430
|
+
return evaluateRoot(machine, doc, context, options, void 0);
|
|
10431
|
+
}
|
|
10432
|
+
/**
|
|
10433
|
+
* This function evaluates a document from its root. An equation of a tree gives the record of its tree. Thus the
|
|
10434
|
+
* reads of the equation go in the list of the outer evaluation (REF.READS).
|
|
10435
|
+
*/
|
|
10436
|
+
function evaluateRoot(machine, doc, context, options, outer) {
|
|
10437
|
+
const at = rootAt(machine, doc, context, options);
|
|
10438
|
+
const env = envOf(machine, options, false, at.ctx, outer);
|
|
9323
10439
|
try {
|
|
9324
|
-
return within(env, () => evaluateAt(env,
|
|
10440
|
+
return within(env, () => evaluateAt(env, at));
|
|
9325
10441
|
} catch (error) {
|
|
9326
10442
|
return placed(thrown(error, "the reference"), []);
|
|
9327
10443
|
}
|
|
@@ -9342,11 +10458,15 @@ function reference(machine, doc, context = makeContext(), options = {}) {
|
|
|
9342
10458
|
* @returns The result, when the run ends.
|
|
9343
10459
|
*/
|
|
9344
10460
|
function referenceRun(machine, doc, context = makeContext(), options = {}) {
|
|
9345
|
-
const
|
|
9346
|
-
|
|
10461
|
+
const at = rootAt(machine, doc, context, options);
|
|
10462
|
+
const env = envOf(machine, options, true, at.ctx, void 0);
|
|
10463
|
+
return driveRun(env, stepsAt(env, at), options.signal).catch((error) => placed(thrown(error, "the reference"), []));
|
|
9347
10464
|
}
|
|
9348
|
-
/**
|
|
9349
|
-
function
|
|
10465
|
+
/**
|
|
10466
|
+
* This function makes the state of the reference for one evaluation or one run. The record of the reads comes from
|
|
10467
|
+
* the evaluation around it, or from the list of the options.
|
|
10468
|
+
*/
|
|
10469
|
+
function envOf(machine, options, running, root, outer) {
|
|
9350
10470
|
const perform = options.perform;
|
|
9351
10471
|
return {
|
|
9352
10472
|
machine,
|
|
@@ -9361,7 +10481,12 @@ function envOf(machine, options, running) {
|
|
|
9361
10481
|
prefix: "",
|
|
9362
10482
|
counts: /* @__PURE__ */ new Map(),
|
|
9363
10483
|
alls: 0
|
|
9364
|
-
}
|
|
10484
|
+
},
|
|
10485
|
+
held: 0,
|
|
10486
|
+
reading: outer ?? (options.reads === void 0 ? void 0 : {
|
|
10487
|
+
list: options.reads,
|
|
10488
|
+
root
|
|
10489
|
+
})
|
|
9365
10490
|
};
|
|
9366
10491
|
}
|
|
9367
10492
|
/** This function gives the root position of a document. A context without a world gets the world of the machine. */
|
|
@@ -9436,7 +10561,8 @@ function refNodeOf(thing) {
|
|
|
9436
10561
|
}
|
|
9437
10562
|
/**
|
|
9438
10563
|
* TREE.LADDER: an equation of the kind of a node evaluates in the reference. It gets the node as `self`, the names of
|
|
9439
|
-
* its rung and no capabilities. A bare equation gets the type `of` of the parts of the kind.
|
|
10564
|
+
* its rung and no capabilities. A bare equation gets the type `of` of the parts of the kind. The reads of the
|
|
10565
|
+
* equation go in the record of the evaluation that made the node (REF.READS).
|
|
9440
10566
|
*/
|
|
9441
10567
|
function equationOf(node, term, names) {
|
|
9442
10568
|
const env = node.env;
|
|
@@ -9447,10 +10573,10 @@ function equationOf(node, term, names) {
|
|
|
9447
10573
|
caps: [],
|
|
9448
10574
|
self: node.thing
|
|
9449
10575
|
};
|
|
9450
|
-
return
|
|
10576
|
+
return evaluateRoot(env.machine, term, ctx, {
|
|
9451
10577
|
definitions: env.definitions,
|
|
9452
10578
|
...node.of === void 0 ? {} : { declaredType: node.of }
|
|
9453
|
-
});
|
|
10579
|
+
}, env.reading);
|
|
9454
10580
|
}
|
|
9455
10581
|
function termIn(table, name) {
|
|
9456
10582
|
return table === void 0 || !Object.hasOwn(table, name) ? void 0 : table[name];
|
|
@@ -9696,10 +10822,13 @@ function formulaDefinitions(profile = {}) {
|
|
|
9696
10822
|
}
|
|
9697
10823
|
const head = path[0] === "names" && typeof path[1] === "string" ? path[1] : void 0;
|
|
9698
10824
|
const bound = head === void 0 ? void 0 : own$1(context.names, head);
|
|
9699
|
-
if (bound instanceof Held)
|
|
9700
|
-
|
|
9701
|
-
|
|
9702
|
-
|
|
10825
|
+
if (bound instanceof Held) {
|
|
10826
|
+
walk(["names", head]);
|
|
10827
|
+
return {
|
|
10828
|
+
ok: false,
|
|
10829
|
+
error: bound.error
|
|
10830
|
+
};
|
|
10831
|
+
}
|
|
9703
10832
|
const found = walk(steps);
|
|
9704
10833
|
if (!found.ok) return fail(found.code, found.reason);
|
|
9705
10834
|
if (address === true) return ok(found.thing);
|
|
@@ -9855,25 +10984,27 @@ function valuesOfStrict(parts) {
|
|
|
9855
10984
|
}
|
|
9856
10985
|
return { values };
|
|
9857
10986
|
}
|
|
9858
|
-
|
|
9859
|
-
//#region src/replay.ts
|
|
10987
|
+
// src/replay.ts
|
|
9860
10988
|
/**
|
|
9861
10989
|
* This function makes a recorder (RUN.RECORD). The recorder gives each effect to the function `perform` of the host.
|
|
9862
10990
|
* Its log keeps each effect with its address, its kind, its payload and the outcome that the kernel gives it. The log
|
|
9863
10991
|
* also keeps the order in which the waits of each `all` settle.
|
|
9864
10992
|
*
|
|
10993
|
+
* The recorder gives each call of its journal to the journal of the inner function, for example of a replayer. Thus a
|
|
10994
|
+
* recording of a replay settles the waits in the order of the replayer, and it gives the same log (RUN.REPLAY-ORDER).
|
|
10995
|
+
*
|
|
9865
10996
|
* @param perform - The function of the host. Without it, each effect gives its payload, as in a run without a host.
|
|
9866
10997
|
* @returns The function `perform` for the run, and the log.
|
|
9867
10998
|
*/
|
|
9868
10999
|
function recorder(perform) {
|
|
9869
11000
|
const seen = [];
|
|
9870
11001
|
const settles = [];
|
|
9871
|
-
const journal =
|
|
11002
|
+
const journal = relay(perform, (all, item) => {
|
|
9872
11003
|
settles.push({
|
|
9873
11004
|
all,
|
|
9874
11005
|
item
|
|
9875
11006
|
});
|
|
9876
|
-
}
|
|
11007
|
+
});
|
|
9877
11008
|
const host = (effect) => {
|
|
9878
11009
|
const entry = {
|
|
9879
11010
|
effect,
|
|
@@ -9903,14 +11034,17 @@ function recorder(perform) {
|
|
|
9903
11034
|
* recorded error comes again with its code, its reason, its place and its cause. The waits of each `all` settle in the
|
|
9904
11035
|
* recorded order. An effect that the log does not have at its address gives `#REPLAY!` (RUN.REPLAY-MISMATCH).
|
|
9905
11036
|
*
|
|
9906
|
-
*
|
|
11037
|
+
* One replayer serves more than one run, also two runs at the same time (RUN.REPLAY-RUNS). The kernel tells the journal
|
|
11038
|
+
* the number of the waits of each `all` that settled before, thus the replayer keeps no state of a run. When the
|
|
11039
|
+
* recorded item of a wait does not wait in the run, that wait follows the order of the host.
|
|
9907
11040
|
*
|
|
9908
|
-
* @param log - The log of a recorder
|
|
9909
|
-
* @param options - `wait: false` for an evaluation.
|
|
11041
|
+
* @param log - The log of a recorder, or of `decodeLog`.
|
|
11042
|
+
* @param options - `wait: false` for an evaluation, and the rule of the host for payloads.
|
|
9910
11043
|
* @returns The function `perform` for the replay.
|
|
9911
11044
|
*/
|
|
9912
11045
|
function replayer(log, options = {}) {
|
|
9913
11046
|
const wait = options.wait !== false;
|
|
11047
|
+
const equal = options.equal;
|
|
9914
11048
|
const entries = /* @__PURE__ */ new Map();
|
|
9915
11049
|
for (const entry of log.effects) entries.set(entry.address, entry);
|
|
9916
11050
|
const order = /* @__PURE__ */ new Map();
|
|
@@ -9919,16 +11053,19 @@ function replayer(log, options = {}) {
|
|
|
9919
11053
|
if (items === void 0) order.set(settle.all, [settle.item]);
|
|
9920
11054
|
else items.push(settle.item);
|
|
9921
11055
|
}
|
|
9922
|
-
|
|
11056
|
+
let places;
|
|
11057
|
+
const journal = { next: (all, waiting, before) => {
|
|
9923
11058
|
const items = order.get(all);
|
|
9924
|
-
|
|
9925
|
-
|
|
9926
|
-
items
|
|
11059
|
+
if (items === void 0) return void 0;
|
|
11060
|
+
const at = before ?? (places ??= /* @__PURE__ */ new Map()).get(all) ?? 0;
|
|
11061
|
+
const item = items[at];
|
|
11062
|
+
if (item === void 0 || !waiting.includes(item)) return void 0;
|
|
11063
|
+
if (before === void 0) places?.set(all, at + 1);
|
|
9927
11064
|
return item;
|
|
9928
11065
|
} };
|
|
9929
11066
|
const perform = (effect) => {
|
|
9930
11067
|
const entry = effect.address === void 0 ? void 0 : entries.get(effect.address);
|
|
9931
|
-
const wrong = mismatch(effect, entry);
|
|
11068
|
+
const wrong = mismatch(effect, entry, equal);
|
|
9932
11069
|
if (wrong !== void 0 || entry === void 0) throw wrong;
|
|
9933
11070
|
const outcome = entry.outcome;
|
|
9934
11071
|
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);
|
|
@@ -9940,59 +11077,998 @@ function replayer(log, options = {}) {
|
|
|
9940
11077
|
return Object.assign(perform, { [JOURNAL]: journal });
|
|
9941
11078
|
}
|
|
9942
11079
|
/** This function gives the error of an effect that the log does not have at its address, or `undefined`. */
|
|
9943
|
-
function mismatch(effect, entry) {
|
|
11080
|
+
function mismatch(effect, entry, equal) {
|
|
9944
11081
|
const at = effect.address ?? "";
|
|
9945
11082
|
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}.`);
|
|
9946
11083
|
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}.`);
|
|
9947
|
-
if (!
|
|
11084
|
+
if (!equalWith(entry.payload, effect.payload, equal)) return replayError(effect, `The effect ${effect.kind} at the address ${at} has another payload than the log.`);
|
|
9948
11085
|
}
|
|
9949
11086
|
/** This function gives the exception that carries `#REPLAY!` to the kernel, with the place of the effect. */
|
|
9950
11087
|
function replayError(effect, reason) {
|
|
9951
11088
|
return new Failure(fail("#REPLAY!", reason, { place: effect.place }).error);
|
|
9952
11089
|
}
|
|
9953
|
-
//#endregion
|
|
9954
|
-
//#region src/agree.ts
|
|
9955
|
-
/** The tiers that `agree` compares when its options name none. */
|
|
9956
|
-
const DEFAULT_TIERS = /* @__PURE__ */ Object.freeze([
|
|
9957
|
-
"reference",
|
|
9958
|
-
"traced",
|
|
9959
|
-
"again",
|
|
9960
|
-
"folds",
|
|
9961
|
-
"fixed"
|
|
9962
|
-
]);
|
|
9963
11090
|
/**
|
|
9964
|
-
* This function
|
|
9965
|
-
*
|
|
9966
|
-
* `0`, and `NaN` is `NaN`. The identity of a list or of a plain record is not compared.
|
|
9967
|
-
*
|
|
9968
|
-
* Two closures of `fn` are the same when they have the same parameters. Two `Held` values are the same when their
|
|
9969
|
-
* errors have the same code and place. Each other object is the same only as itself, unless the rule of the caller
|
|
9970
|
-
* tells otherwise.
|
|
11091
|
+
* This function gives a journal that tells a function of its own each wait that settles. It gives each call to the
|
|
11092
|
+
* journal of the inner function `perform`, when that function has one.
|
|
9971
11093
|
*
|
|
9972
|
-
*
|
|
9973
|
-
*
|
|
9974
|
-
*
|
|
9975
|
-
*
|
|
9976
|
-
* @param a - The first value.
|
|
9977
|
-
* @param b - The second value.
|
|
9978
|
-
* @param equal - The rule of the caller for other values. It answers first, and `undefined` gives the rules above.
|
|
9979
|
-
* @returns `true` when the values are the same.
|
|
11094
|
+
* @param perform - The inner function.
|
|
11095
|
+
* @param settled - The function of its own.
|
|
11096
|
+
* @returns The journal.
|
|
9980
11097
|
*/
|
|
9981
|
-
function
|
|
9982
|
-
|
|
11098
|
+
function relay(perform, settled) {
|
|
11099
|
+
const inner = perform === void 0 ? void 0 : perform[JOURNAL];
|
|
11100
|
+
const tell = (all, item) => {
|
|
11101
|
+
settled(all, item);
|
|
11102
|
+
inner?.settled?.(all, item);
|
|
11103
|
+
};
|
|
11104
|
+
if (inner?.next === void 0) return { settled: tell };
|
|
11105
|
+
return {
|
|
11106
|
+
settled: tell,
|
|
11107
|
+
next: (all, waiting, before) => inner.next?.(all, waiting, before)
|
|
11108
|
+
};
|
|
9983
11109
|
}
|
|
9984
|
-
/**
|
|
9985
|
-
function
|
|
9986
|
-
|
|
9987
|
-
if (x instanceof Held || y instanceof Held) return x instanceof Held && y instanceof Held && errorDiffers(x.error, y.error, false) === void 0;
|
|
11110
|
+
/** This function tells if a key is a field of an error value that the JSON form keeps. */
|
|
11111
|
+
function errorField(key) {
|
|
11112
|
+
return key === "code" || key === "reason" || key === "place" || key === "cause";
|
|
9988
11113
|
}
|
|
9989
|
-
|
|
9990
|
-
|
|
9991
|
-
|
|
9992
|
-
return fn[CLOSURE]?.params;
|
|
11114
|
+
/** 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. */
|
|
11115
|
+
function ownField(key) {
|
|
11116
|
+
return key === "name" || key === "message" || key === "stack" || key === "cause";
|
|
9993
11117
|
}
|
|
9994
|
-
function
|
|
9995
|
-
|
|
11118
|
+
/** This function tells if a key is a field of the JSON form of an exception. */
|
|
11119
|
+
function exceptionField(key) {
|
|
11120
|
+
return key === "name" || key === "message" || key === "fields" || key === "parts" || key === "dropped" || key === "hidden" || key === "cause" || key === "exception" || key === "loop" || key === "causeParts";
|
|
11121
|
+
}
|
|
11122
|
+
/**
|
|
11123
|
+
* The own fields that are not enumerable and that the JSON form keeps in `hidden` (D77). The standard classes define
|
|
11124
|
+
* them: `errors` of `AggregateError`, `error` and `suppressed` of `SuppressedError`.
|
|
11125
|
+
*/
|
|
11126
|
+
const HIDDEN = /* @__PURE__ */ Object.freeze([
|
|
11127
|
+
"errors",
|
|
11128
|
+
"error",
|
|
11129
|
+
"suppressed"
|
|
11130
|
+
]);
|
|
11131
|
+
/** The result of `readField` for a field that the walk of `causeJson` cannot read. */
|
|
11132
|
+
const MISSING = /* @__PURE__ */ Symbol("missing");
|
|
11133
|
+
/**
|
|
11134
|
+
* This function tells if a value holds a hole of a list at some depth. `encodeValue` writes a hole as `undefined`. Thus
|
|
11135
|
+
* the walk of `causeJson` keeps such a value in parts, and the hole keeps its mark (D82).
|
|
11136
|
+
*/
|
|
11137
|
+
function holdsHole(value) {
|
|
11138
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11139
|
+
const stack = [value];
|
|
11140
|
+
while (stack.length > 0) {
|
|
11141
|
+
const at = stack.pop();
|
|
11142
|
+
if (typeof at !== "object" || at === null || seen.has(at) || !(Array.isArray(at) || isRecord(at))) continue;
|
|
11143
|
+
seen.add(at);
|
|
11144
|
+
if (Array.isArray(at)) for (let item = 0; item < at.length; item += 1) {
|
|
11145
|
+
if (!Object.hasOwn(at, item)) return true;
|
|
11146
|
+
stack.push(at[item]);
|
|
11147
|
+
}
|
|
11148
|
+
else for (const key of Object.keys(at)) stack.push(at[key]);
|
|
11149
|
+
}
|
|
11150
|
+
return false;
|
|
11151
|
+
}
|
|
11152
|
+
/**
|
|
11153
|
+
* This function gives the lists and the plain records that the walk of `causeJson` keeps as containers of parts, also
|
|
11154
|
+
* with a term document (D101). Such a container is at two places of the walk, or it holds such a container at some depth.
|
|
11155
|
+
* It reads the values as the walk does: the fields of an exception, its hidden fields and its cause, and the items of a
|
|
11156
|
+
* container. A term document gives a copy at each place, thus a shared container cannot be one.
|
|
11157
|
+
*/
|
|
11158
|
+
function sharedOf(root) {
|
|
11159
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11160
|
+
const twice = [];
|
|
11161
|
+
const owners = /* @__PURE__ */ new Map();
|
|
11162
|
+
const stack = [[root, void 0]];
|
|
11163
|
+
for (let next = stack.pop(); next !== void 0; next = stack.pop()) {
|
|
11164
|
+
const [at, owner] = next;
|
|
11165
|
+
if (typeof at !== "object" || at === null) continue;
|
|
11166
|
+
const container = Array.isArray(at) || isRecord(at);
|
|
11167
|
+
if (!container && !(at instanceof Error)) continue;
|
|
11168
|
+
if (container && owner !== void 0) {
|
|
11169
|
+
const list = owners.get(at);
|
|
11170
|
+
if (list === void 0) owners.set(at, [owner]);
|
|
11171
|
+
else list.push(owner);
|
|
11172
|
+
}
|
|
11173
|
+
if (seen.has(at)) {
|
|
11174
|
+
if (container) twice.push(at);
|
|
11175
|
+
continue;
|
|
11176
|
+
}
|
|
11177
|
+
seen.add(at);
|
|
11178
|
+
if (Array.isArray(at)) {
|
|
11179
|
+
for (let item = 0; item < at.length; item += 1) if (Object.hasOwn(at, item)) stack.push([readField(at, item), at]);
|
|
11180
|
+
} else if (container) for (const key of Object.keys(at)) stack.push([readField(at, key), at]);
|
|
11181
|
+
else {
|
|
11182
|
+
for (const key of Object.keys(at)) if (!ownField(key)) stack.push([readField(at, key), void 0]);
|
|
11183
|
+
for (const key of HIDDEN) if (Object.getOwnPropertyDescriptor(at, key)?.enumerable === false) stack.push([readField(at, key), void 0]);
|
|
11184
|
+
if (Object.hasOwn(at, "cause")) stack.push([readField(at, "cause"), void 0]);
|
|
11185
|
+
}
|
|
11186
|
+
}
|
|
11187
|
+
const shared = /* @__PURE__ */ new Set();
|
|
11188
|
+
for (let at = twice.pop(); at !== void 0; at = twice.pop()) {
|
|
11189
|
+
if (shared.has(at)) continue;
|
|
11190
|
+
shared.add(at);
|
|
11191
|
+
for (const owner of owners.get(at) ?? []) twice.push(owner);
|
|
11192
|
+
}
|
|
11193
|
+
return shared;
|
|
11194
|
+
}
|
|
11195
|
+
/**
|
|
11196
|
+
* This function tells if the walk of `causeJson` keeps a value with a term document as a container of parts (D101). The
|
|
11197
|
+
* value is in the set of `sharedOf`. A member of a domain stays a term document, because its codec makes it again.
|
|
11198
|
+
*/
|
|
11199
|
+
function wholeOf(shared, value, term) {
|
|
11200
|
+
if (typeof value !== "object" || value === null || !shared.has(value)) return false;
|
|
11201
|
+
const type = isRecord(term) ? term["type"] : void 0;
|
|
11202
|
+
return !(Array.isArray(type) && type[0] === "ext" && type[1] === "domain");
|
|
11203
|
+
}
|
|
11204
|
+
/**
|
|
11205
|
+
* This function tells if a list or a plain record holds an exception at some depth (D87). It reads the items as the
|
|
11206
|
+
* walk of `causeJson` does, thus a container cause of an error value with an exception gets a form.
|
|
11207
|
+
*/
|
|
11208
|
+
function holdsException(value) {
|
|
11209
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11210
|
+
const stack = [value];
|
|
11211
|
+
while (stack.length > 0) {
|
|
11212
|
+
const at = stack.pop();
|
|
11213
|
+
if (at instanceof Error) return true;
|
|
11214
|
+
if (typeof at !== "object" || at === null || seen.has(at) || !(Array.isArray(at) || isRecord(at))) continue;
|
|
11215
|
+
seen.add(at);
|
|
11216
|
+
if (Array.isArray(at)) {
|
|
11217
|
+
for (let item = 0; item < at.length; item += 1) if (Object.hasOwn(at, item)) stack.push(readField(at, item));
|
|
11218
|
+
} else for (const key of Object.keys(at)) stack.push(readField(at, key));
|
|
11219
|
+
}
|
|
11220
|
+
return false;
|
|
11221
|
+
}
|
|
11222
|
+
/** This function gives the field of a cause for its form after the walk (D76, D87). A cause without a form has no field. */
|
|
11223
|
+
function causeOf(built) {
|
|
11224
|
+
const form = built.form;
|
|
11225
|
+
if (form === void 0) return { names: built.names };
|
|
11226
|
+
if (Array.isArray(form) || "record" in form || "share" in form) return {
|
|
11227
|
+
link: { causeParts: form },
|
|
11228
|
+
names: built.names
|
|
11229
|
+
};
|
|
11230
|
+
return {
|
|
11231
|
+
link: "value" in form ? { cause: form.value } : form,
|
|
11232
|
+
names: built.names
|
|
11233
|
+
};
|
|
11234
|
+
}
|
|
11235
|
+
/** This function gives the term document of a value of an exception, or `undefined` when the value has none. */
|
|
11236
|
+
function termOrNothing(machine, value) {
|
|
11237
|
+
if (holdsHole(value)) return void 0;
|
|
11238
|
+
try {
|
|
11239
|
+
return encodeValue(machine, value);
|
|
11240
|
+
} catch {
|
|
11241
|
+
return;
|
|
11242
|
+
}
|
|
11243
|
+
}
|
|
11244
|
+
/** This function reads a field of a value, or gives `MISSING` when the read throws. */
|
|
11245
|
+
function readField(value, key) {
|
|
11246
|
+
try {
|
|
11247
|
+
return value[key];
|
|
11248
|
+
} catch {
|
|
11249
|
+
return MISSING;
|
|
11250
|
+
}
|
|
11251
|
+
}
|
|
11252
|
+
/** This function gives the forms of the parts with a form, with their keys, in the order of the keys. */
|
|
11253
|
+
function keptOf(keys, built) {
|
|
11254
|
+
const kept = [];
|
|
11255
|
+
for (let place = 0; place < keys.length; place += 1) {
|
|
11256
|
+
const form = built[place]?.form;
|
|
11257
|
+
if (form !== void 0) kept.push([keys[place], form]);
|
|
11258
|
+
}
|
|
11259
|
+
return kept;
|
|
11260
|
+
}
|
|
11261
|
+
/**
|
|
11262
|
+
* This function writes a cause without a term document as JSON (RUN.REPLAY-CAUSES, RUN.REPLAY-HIDDEN, RUN.REPLAY-FIELDS).
|
|
11263
|
+
* The cause is an exception with its hidden fields and its causes, or a container with an exception (D87). A host
|
|
11264
|
+
* makes the exception, thus a part without a term document is a note in `dropped`, not a refusal (D75, D76).
|
|
11265
|
+
*
|
|
11266
|
+
* The walk gives each exception an index in the order of a depth-first walk. It takes the fields in `parts` in the
|
|
11267
|
+
* order of their keys, the hidden fields in the order of `HIDDEN`, then the cause (D78). A list or a plain record
|
|
11268
|
+
* without a term document is a container of parts, with its items in their order (D82). The cause of an error value
|
|
11269
|
+
* is a container only when it holds an exception (D87). The cause of an exception is a container as a field is (D100).
|
|
11270
|
+
* A value that is an earlier exception of the walk is its index in `loop` (D77).
|
|
11271
|
+
*
|
|
11272
|
+
* A container that an earlier place holds is its index in `share`, also a container that holds itself (D88). A container
|
|
11273
|
+
* of `sharedOf` is a container of parts also with a term document, thus each share gives one object (D101). The value
|
|
11274
|
+
* `tried` marks a root that `encodeValue` cannot write. The walk is a loop, thus a long chain does not overflow the stack.
|
|
11275
|
+
*/
|
|
11276
|
+
function causeJson(machine, root, shared, tried) {
|
|
11277
|
+
const items = [];
|
|
11278
|
+
const index = /* @__PURE__ */ new Map();
|
|
11279
|
+
const holders = /* @__PURE__ */ new Map();
|
|
11280
|
+
const tasks = [];
|
|
11281
|
+
const later = (children) => {
|
|
11282
|
+
for (let place = children.length - 1; place >= 0; place -= 1) tasks.push(children[place]);
|
|
11283
|
+
};
|
|
11284
|
+
const hold = (name, value, keep, children) => {
|
|
11285
|
+
const at = items.length;
|
|
11286
|
+
holders.set(value, at);
|
|
11287
|
+
const list = Array.isArray(value);
|
|
11288
|
+
const keys = list ? [] : Object.keys(value);
|
|
11289
|
+
const slots = [];
|
|
11290
|
+
items.push({
|
|
11291
|
+
name,
|
|
11292
|
+
list,
|
|
11293
|
+
keep,
|
|
11294
|
+
keys,
|
|
11295
|
+
slots
|
|
11296
|
+
});
|
|
11297
|
+
const size = list ? value.length : keys.length;
|
|
11298
|
+
for (let place = 0; place < size; place += 1) {
|
|
11299
|
+
const key = list ? place : keys[place];
|
|
11300
|
+
const item = list && !Object.hasOwn(value, place) ? MISSING : readField(value, key);
|
|
11301
|
+
const put = (part) => {
|
|
11302
|
+
slots[place] = part;
|
|
11303
|
+
};
|
|
11304
|
+
children.push({
|
|
11305
|
+
value: item,
|
|
11306
|
+
name: `${name}.${key}`,
|
|
11307
|
+
tried: false,
|
|
11308
|
+
nest: true,
|
|
11309
|
+
keep: false,
|
|
11310
|
+
put
|
|
11311
|
+
});
|
|
11312
|
+
}
|
|
11313
|
+
return at;
|
|
11314
|
+
};
|
|
11315
|
+
const open = (current) => {
|
|
11316
|
+
const at = items.length;
|
|
11317
|
+
index.set(current, index.size);
|
|
11318
|
+
const fields = [];
|
|
11319
|
+
const linked = [];
|
|
11320
|
+
for (const key of Object.keys(current)) {
|
|
11321
|
+
if (ownField(key)) continue;
|
|
11322
|
+
const value = readField(current, key);
|
|
11323
|
+
const term = value === MISSING ? void 0 : termOrNothing(machine, value);
|
|
11324
|
+
if (term === void 0) linked.push([
|
|
11325
|
+
key,
|
|
11326
|
+
value,
|
|
11327
|
+
true
|
|
11328
|
+
]);
|
|
11329
|
+
else if (wholeOf(shared, value, term)) linked.push([
|
|
11330
|
+
key,
|
|
11331
|
+
value,
|
|
11332
|
+
false
|
|
11333
|
+
]);
|
|
11334
|
+
else fields.push([key, term]);
|
|
11335
|
+
}
|
|
11336
|
+
const node = {
|
|
11337
|
+
head: {
|
|
11338
|
+
name: String(current.name),
|
|
11339
|
+
message: String(current.message),
|
|
11340
|
+
...fields.length === 0 ? {} : { fields: Object.fromEntries(fields) }
|
|
11341
|
+
},
|
|
11342
|
+
keys: linked.map(([key]) => key),
|
|
11343
|
+
parts: [],
|
|
11344
|
+
hiddenKeys: [],
|
|
11345
|
+
hidden: []
|
|
11346
|
+
};
|
|
11347
|
+
items.push(node);
|
|
11348
|
+
const children = [];
|
|
11349
|
+
for (let place = 0; place < linked.length; place += 1) {
|
|
11350
|
+
const [key, value, without] = linked[place];
|
|
11351
|
+
const put = (part) => {
|
|
11352
|
+
node.parts[place] = part;
|
|
11353
|
+
};
|
|
11354
|
+
children.push({
|
|
11355
|
+
value,
|
|
11356
|
+
name: key,
|
|
11357
|
+
tried: without,
|
|
11358
|
+
nest: true,
|
|
11359
|
+
keep: false,
|
|
11360
|
+
put
|
|
11361
|
+
});
|
|
11362
|
+
}
|
|
11363
|
+
for (const key of HIDDEN) {
|
|
11364
|
+
if (Object.getOwnPropertyDescriptor(current, key)?.enumerable !== false) continue;
|
|
11365
|
+
const place = node.hiddenKeys.length;
|
|
11366
|
+
node.hiddenKeys.push(key);
|
|
11367
|
+
const value = readField(current, key);
|
|
11368
|
+
const put = (part) => {
|
|
11369
|
+
node.hidden[place] = part;
|
|
11370
|
+
};
|
|
11371
|
+
children.push({
|
|
11372
|
+
value,
|
|
11373
|
+
name: key,
|
|
11374
|
+
tried: false,
|
|
11375
|
+
nest: true,
|
|
11376
|
+
keep: Array.isArray(value),
|
|
11377
|
+
put
|
|
11378
|
+
});
|
|
11379
|
+
}
|
|
11380
|
+
if (Object.hasOwn(current, "cause")) {
|
|
11381
|
+
const put = (part) => {
|
|
11382
|
+
node.cause = part;
|
|
11383
|
+
};
|
|
11384
|
+
children.push({
|
|
11385
|
+
value: current.cause,
|
|
11386
|
+
name: "cause",
|
|
11387
|
+
tried: false,
|
|
11388
|
+
nest: true,
|
|
11389
|
+
keep: false,
|
|
11390
|
+
put
|
|
11391
|
+
});
|
|
11392
|
+
}
|
|
11393
|
+
later(children);
|
|
11394
|
+
return at;
|
|
11395
|
+
};
|
|
11396
|
+
let top = { none: "cause" };
|
|
11397
|
+
tasks.push({
|
|
11398
|
+
value: root,
|
|
11399
|
+
name: "cause",
|
|
11400
|
+
tried,
|
|
11401
|
+
nest: false,
|
|
11402
|
+
keep: false,
|
|
11403
|
+
put: (part) => {
|
|
11404
|
+
top = part;
|
|
11405
|
+
}
|
|
11406
|
+
});
|
|
11407
|
+
for (let task = tasks.pop(); task !== void 0; task = tasks.pop()) {
|
|
11408
|
+
const value = task.value;
|
|
11409
|
+
if (value === MISSING) {
|
|
11410
|
+
task.put({ none: task.name });
|
|
11411
|
+
continue;
|
|
11412
|
+
}
|
|
11413
|
+
const loop = index.get(value);
|
|
11414
|
+
if (loop !== void 0) {
|
|
11415
|
+
task.put({ loop });
|
|
11416
|
+
continue;
|
|
11417
|
+
}
|
|
11418
|
+
const container = Array.isArray(value) || isRecord(value);
|
|
11419
|
+
const held = container ? holders.get(value) : void 0;
|
|
11420
|
+
if (held !== void 0) {
|
|
11421
|
+
if (task.keep) items[held].keep = true;
|
|
11422
|
+
task.put({
|
|
11423
|
+
same: held,
|
|
11424
|
+
name: task.name
|
|
11425
|
+
});
|
|
11426
|
+
continue;
|
|
11427
|
+
}
|
|
11428
|
+
const term = task.tried || task.keep ? void 0 : termOrNothing(machine, value);
|
|
11429
|
+
const whole = term !== void 0 && wholeOf(shared, value, term);
|
|
11430
|
+
if (term !== void 0 && !whole) task.put({ value: term });
|
|
11431
|
+
else if (value instanceof Error) task.put({ node: open(value) });
|
|
11432
|
+
else if (container && (whole || task.nest || holdsException(value))) {
|
|
11433
|
+
const children = [];
|
|
11434
|
+
task.put({ holder: hold(task.name, value, task.keep || whole, children) });
|
|
11435
|
+
later(children);
|
|
11436
|
+
} else task.put({ none: task.name });
|
|
11437
|
+
}
|
|
11438
|
+
const { formed, shares } = formsOf(items);
|
|
11439
|
+
const forms = [];
|
|
11440
|
+
const builtOf = (part) => {
|
|
11441
|
+
if ("none" in part) return { names: [part.none] };
|
|
11442
|
+
if ("node" in part) return {
|
|
11443
|
+
form: { exception: forms[part.node] },
|
|
11444
|
+
names: []
|
|
11445
|
+
};
|
|
11446
|
+
if ("holder" in part) return forms[part.holder];
|
|
11447
|
+
if ("same" in part) return formed[part.same] === true ? {
|
|
11448
|
+
form: { share: shares[part.same] },
|
|
11449
|
+
names: []
|
|
11450
|
+
} : { names: [part.name] };
|
|
11451
|
+
return {
|
|
11452
|
+
form: part,
|
|
11453
|
+
names: []
|
|
11454
|
+
};
|
|
11455
|
+
};
|
|
11456
|
+
for (let at = items.length - 1; at >= 0; at -= 1) {
|
|
11457
|
+
const item = items[at];
|
|
11458
|
+
if ("slots" in item) {
|
|
11459
|
+
if (formed[at] !== true) {
|
|
11460
|
+
forms[at] = { names: [item.name] };
|
|
11461
|
+
continue;
|
|
11462
|
+
}
|
|
11463
|
+
const built = Array.from(item.slots, builtOf);
|
|
11464
|
+
const form = item.list ? built.map((part) => part.form ?? { dropped: true }) : { record: Object.fromEntries(keptOf(item.keys, built)) };
|
|
11465
|
+
forms[at] = {
|
|
11466
|
+
form,
|
|
11467
|
+
names: built.flatMap((part) => part.names)
|
|
11468
|
+
};
|
|
11469
|
+
continue;
|
|
11470
|
+
}
|
|
11471
|
+
const { head, keys, parts, hiddenKeys, hidden, cause } = item;
|
|
11472
|
+
const linked = Array.from(parts, builtOf);
|
|
11473
|
+
const kept = Array.from(hidden, builtOf);
|
|
11474
|
+
const link = cause === void 0 ? { names: [] } : causeOf(builtOf(cause));
|
|
11475
|
+
const dropped = [
|
|
11476
|
+
...linked,
|
|
11477
|
+
...kept,
|
|
11478
|
+
link
|
|
11479
|
+
].flatMap((part) => part.names);
|
|
11480
|
+
const partsOf = keptOf(keys, linked);
|
|
11481
|
+
const hiddenOf = keptOf(hiddenKeys, kept);
|
|
11482
|
+
forms[at] = {
|
|
11483
|
+
...head,
|
|
11484
|
+
...partsOf.length === 0 ? {} : { parts: Object.fromEntries(partsOf) },
|
|
11485
|
+
...dropped.length === 0 ? {} : { dropped },
|
|
11486
|
+
...hiddenOf.length === 0 ? {} : { hidden: Object.fromEntries(hiddenOf) },
|
|
11487
|
+
...link.link
|
|
11488
|
+
};
|
|
11489
|
+
}
|
|
11490
|
+
return causeOf(builtOf(top));
|
|
11491
|
+
}
|
|
11492
|
+
/**
|
|
11493
|
+
* This function tells which containers of the walk of `causeJson` get a form, and it gives each such container its
|
|
11494
|
+
* 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
|
|
11495
|
+
* the form of its container. Thus a cycle of containers without another form has no form. The index counts the
|
|
11496
|
+
* containers with a form in the order of the walk, as `decodeLog` meets them.
|
|
11497
|
+
*/
|
|
11498
|
+
function formsOf(items) {
|
|
11499
|
+
const formed = [];
|
|
11500
|
+
const users = /* @__PURE__ */ new Map();
|
|
11501
|
+
const work = [];
|
|
11502
|
+
for (let at = 0; at < items.length; at += 1) {
|
|
11503
|
+
const item = items[at];
|
|
11504
|
+
if (!("slots" in item)) continue;
|
|
11505
|
+
let seed = item.keep;
|
|
11506
|
+
for (const part of item.slots) {
|
|
11507
|
+
const on = "holder" in part ? part.holder : "same" in part ? part.same : void 0;
|
|
11508
|
+
if (on === void 0) seed ||= !("none" in part);
|
|
11509
|
+
else {
|
|
11510
|
+
const list = users.get(on);
|
|
11511
|
+
if (list === void 0) users.set(on, [at]);
|
|
11512
|
+
else list.push(at);
|
|
11513
|
+
}
|
|
11514
|
+
}
|
|
11515
|
+
if (seed) {
|
|
11516
|
+
formed[at] = true;
|
|
11517
|
+
work.push(at);
|
|
11518
|
+
}
|
|
11519
|
+
}
|
|
11520
|
+
for (let at = work.pop(); at !== void 0; at = work.pop()) for (const user of users.get(at) ?? []) {
|
|
11521
|
+
if (formed[user] === true) continue;
|
|
11522
|
+
formed[user] = true;
|
|
11523
|
+
work.push(user);
|
|
11524
|
+
}
|
|
11525
|
+
const shares = [];
|
|
11526
|
+
let count = 0;
|
|
11527
|
+
for (let at = 0; at < items.length; at += 1) {
|
|
11528
|
+
if (formed[at] !== true) continue;
|
|
11529
|
+
shares[at] = count;
|
|
11530
|
+
count += 1;
|
|
11531
|
+
}
|
|
11532
|
+
return {
|
|
11533
|
+
formed,
|
|
11534
|
+
shares
|
|
11535
|
+
};
|
|
11536
|
+
}
|
|
11537
|
+
/** This function gives the message of an exception, or a short text for another value. */
|
|
11538
|
+
function messageOf(error) {
|
|
11539
|
+
return error instanceof Error ? error.message : describe(error);
|
|
11540
|
+
}
|
|
11541
|
+
/** This function writes a value of a log as a term document, or throws a `TypeError` that names the value. */
|
|
11542
|
+
function termOf(machine, value, what) {
|
|
11543
|
+
try {
|
|
11544
|
+
return encodeValue(machine, value);
|
|
11545
|
+
} catch (error) {
|
|
11546
|
+
throw new TypeError(`encodeLog cannot write ${what()}: ${messageOf(error)}`);
|
|
11547
|
+
}
|
|
11548
|
+
}
|
|
11549
|
+
/** This function writes an error value of a log as JSON, or throws a `TypeError` that names the error. */
|
|
11550
|
+
function errorJson(machine, error, what) {
|
|
11551
|
+
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.`);
|
|
11552
|
+
const base = {
|
|
11553
|
+
code: error.code,
|
|
11554
|
+
reason: error.reason,
|
|
11555
|
+
...error.place === void 0 ? {} : { place: error.place }
|
|
11556
|
+
};
|
|
11557
|
+
if (!Object.hasOwn(error, "cause")) return base;
|
|
11558
|
+
const cause = error.cause;
|
|
11559
|
+
const shared = sharedOf(cause);
|
|
11560
|
+
let term;
|
|
11561
|
+
let failure;
|
|
11562
|
+
try {
|
|
11563
|
+
term = encodeValue(machine, cause);
|
|
11564
|
+
} catch (thrown) {
|
|
11565
|
+
failure = thrown;
|
|
11566
|
+
}
|
|
11567
|
+
if (term !== void 0 && !wholeOf(shared, cause, term)) return {
|
|
11568
|
+
...base,
|
|
11569
|
+
cause: term
|
|
11570
|
+
};
|
|
11571
|
+
const { link, names } = causeJson(machine, cause, shared, term === void 0);
|
|
11572
|
+
if (link === void 0) throw new TypeError(`encodeLog cannot write the cause of ${what()}: ${messageOf(failure)}`);
|
|
11573
|
+
return {
|
|
11574
|
+
...base,
|
|
11575
|
+
...link,
|
|
11576
|
+
...names.length === 0 ? {} : { dropped: names }
|
|
11577
|
+
};
|
|
11578
|
+
}
|
|
11579
|
+
/**
|
|
11580
|
+
* This function writes a log as JSON (RUN.REPLAY-JSON, D75). Then `JSON.stringify` gives the text of a golden file.
|
|
11581
|
+
*
|
|
11582
|
+
* - Each payload and each value of an outcome is a term document of `encodeValue`. Thus `NaN`, `-0` and `undefined`
|
|
11583
|
+
* stay exact, and a member of a domain with a codec is `ext.domain`.
|
|
11584
|
+
* - An error keeps its code, its reason, its place and its cause. A cause that `encodeValue` cannot write and that is
|
|
11585
|
+
* an exception keeps its name and its message in the field `exception`.
|
|
11586
|
+
* - An exception also keeps its own fields that `encodeValue` writes, and the chain of its causes (RUN.REPLAY-CAUSES).
|
|
11587
|
+
* The list `dropped` names each other own field. A cause that is an earlier exception of the chain is its index in `loop`.
|
|
11588
|
+
* - An exception keeps the hidden fields of the standard classes in `hidden`, for example `errors` of an
|
|
11589
|
+
* `AggregateError` (RUN.REPLAY-HIDDEN, D77). Each exception in them has the rule of a cause.
|
|
11590
|
+
* - An enumerable field without a term document that is an exception, a list or a plain record is in `parts`
|
|
11591
|
+
* (RUN.REPLAY-FIELDS, D78, D82). An item of a list without a form is the mark `dropped` at its place.
|
|
11592
|
+
* - A cause without a term document that is a list or a plain record with an exception is in `causeParts`
|
|
11593
|
+
* (RUN.REPLAY-CAUSE-PARTS, D87). The rule is the same for the cause of an exception and of an error value.
|
|
11594
|
+
* - The cause of an exception is also in `causeParts` without an exception in it, when an item has a form
|
|
11595
|
+
* (RUN.REPLAY-CAUSE-ITEMS, D100). Each item without a form is a name in `dropped`, for example `cause.send`.
|
|
11596
|
+
* - A list or a record of parts that an earlier place of one error holds is the part `share` with its index
|
|
11597
|
+
* (RUN.REPLAY-SHARED, D88). This rule also covers a container that holds itself.
|
|
11598
|
+
* - A list or a plain record with a term document that two places of one error share is a container of parts
|
|
11599
|
+
* (RUN.REPLAY-SHARED-VALUES, D101). Each later place is a share. A member of a domain stays a term document.
|
|
11600
|
+
* - An outcome that the log does not have stays absent.
|
|
11601
|
+
*
|
|
11602
|
+
* @param machine - The machine of the run, or only its domains.
|
|
11603
|
+
* @param log - The log of a recorder.
|
|
11604
|
+
* @returns The JSON form. `decodeLog` gives the log again.
|
|
11605
|
+
* @throws {TypeError} A value has no term document, for example a closure, a host thing, a symbol, or a value that no
|
|
11606
|
+
* domain with a codec holds. The message names the effect and its address. A part of an exception is not a refusal.
|
|
11607
|
+
*/
|
|
11608
|
+
function encodeLog(machine, log) {
|
|
11609
|
+
return {
|
|
11610
|
+
version: 1,
|
|
11611
|
+
effects: log.effects.map((entry) => {
|
|
11612
|
+
const at = (field) => () => `the ${field} of the effect ${entry.kind} at the address ${entry.address}`;
|
|
11613
|
+
const head = {
|
|
11614
|
+
address: entry.address,
|
|
11615
|
+
kind: entry.kind,
|
|
11616
|
+
payload: termOf(machine, entry.payload, at("payload"))
|
|
11617
|
+
};
|
|
11618
|
+
const outcome = entry.outcome;
|
|
11619
|
+
if (outcome === void 0) return {
|
|
11620
|
+
...head,
|
|
11621
|
+
waited: entry.waited
|
|
11622
|
+
};
|
|
11623
|
+
const json = outcome.ok ? {
|
|
11624
|
+
ok: true,
|
|
11625
|
+
value: termOf(machine, outcome.value, at("outcome"))
|
|
11626
|
+
} : {
|
|
11627
|
+
ok: false,
|
|
11628
|
+
error: errorJson(machine, outcome.error, at("outcome"))
|
|
11629
|
+
};
|
|
11630
|
+
return {
|
|
11631
|
+
...head,
|
|
11632
|
+
outcome: json,
|
|
11633
|
+
waited: entry.waited
|
|
11634
|
+
};
|
|
11635
|
+
}),
|
|
11636
|
+
settles: log.settles.map((settle) => ({
|
|
11637
|
+
all: settle.all,
|
|
11638
|
+
item: settle.item
|
|
11639
|
+
}))
|
|
11640
|
+
};
|
|
11641
|
+
}
|
|
11642
|
+
/** This function gives an exception with a name and a message. A standard name gives an instance of its class. */
|
|
11643
|
+
function exception(name, message) {
|
|
11644
|
+
const made = standard(name, message);
|
|
11645
|
+
if (made.name !== name) made.name = name;
|
|
11646
|
+
return made;
|
|
11647
|
+
}
|
|
11648
|
+
/**
|
|
11649
|
+
* This function gives an instance of the standard class with a name, or an `Error`. An `AggregateError` gets an empty
|
|
11650
|
+
* list `errors`, and a `SuppressedError` gets `undefined` for `error` and `suppressed`, as their constructors do.
|
|
11651
|
+
*/
|
|
11652
|
+
function standard(name, message) {
|
|
11653
|
+
switch (name) {
|
|
11654
|
+
case "TypeError": return new TypeError(message);
|
|
11655
|
+
case "RangeError": return new RangeError(message);
|
|
11656
|
+
case "SyntaxError": return new SyntaxError(message);
|
|
11657
|
+
case "ReferenceError": return new ReferenceError(message);
|
|
11658
|
+
case "AggregateError": return new AggregateError([], message);
|
|
11659
|
+
case "SuppressedError": return typeof SuppressedError === "function" ? new SuppressedError(void 0, void 0, message) : new Error(message);
|
|
11660
|
+
default: return new Error(message);
|
|
11661
|
+
}
|
|
11662
|
+
}
|
|
11663
|
+
/** This function gives an exception a field that is not enumerable, as the constructor of `Error` does for `cause`. */
|
|
11664
|
+
function hide(error, key, value) {
|
|
11665
|
+
Object.defineProperty(error, key, {
|
|
11666
|
+
value,
|
|
11667
|
+
writable: true,
|
|
11668
|
+
enumerable: false,
|
|
11669
|
+
configurable: true
|
|
11670
|
+
});
|
|
11671
|
+
}
|
|
11672
|
+
/** This function gives the failed result of a JSON form that `decodeLog` cannot read. */
|
|
11673
|
+
function bad(place, reason) {
|
|
11674
|
+
return fail("#VALUE!", `decodeLog cannot read the log: ${reason}`, { place });
|
|
11675
|
+
}
|
|
11676
|
+
/** This function reads a term document of a log again, with the formula kit and the domains of the machine. */
|
|
11677
|
+
function readTerm(kit, doc, place) {
|
|
11678
|
+
const value = decodeValue(kit, doc);
|
|
11679
|
+
return value.ok ? value : bad(place, `the term document gives ${value.error.code}: ${value.error.reason}`);
|
|
11680
|
+
}
|
|
11681
|
+
/** This function reads the JSON form of an error value again. */
|
|
11682
|
+
function readError(kit, data, place) {
|
|
11683
|
+
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.");
|
|
11684
|
+
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}.`);
|
|
11685
|
+
const keys = data["place"];
|
|
11686
|
+
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.");
|
|
11687
|
+
const dropped = data["dropped"];
|
|
11688
|
+
if (dropped !== void 0 && !(Array.isArray(dropped) && dropped.every((name) => typeof name === "string"))) return bad([...place, "dropped"], "the list dropped holds texts.");
|
|
11689
|
+
const error = {
|
|
11690
|
+
code: data["code"],
|
|
11691
|
+
reason: data["reason"]
|
|
11692
|
+
};
|
|
11693
|
+
if (keys !== void 0) error.place = keys;
|
|
11694
|
+
const record = data;
|
|
11695
|
+
const links = LINKS.filter((key) => key !== "loop" && Object.hasOwn(record, key));
|
|
11696
|
+
if (links.length > 1) return bad(place, "an error has a cause, an exception or causeParts, not more than one.");
|
|
11697
|
+
const link = links[0];
|
|
11698
|
+
if (link === "cause") {
|
|
11699
|
+
const cause = readTerm(kit, data["cause"], [...place, "cause"]);
|
|
11700
|
+
if (!cause.ok) return cause;
|
|
11701
|
+
error.cause = cause.value;
|
|
11702
|
+
} else if (link !== void 0) {
|
|
11703
|
+
const read = readCause(kit, link, data[link], [...place, link]);
|
|
11704
|
+
if (!read.ok) return read;
|
|
11705
|
+
error.cause = read.value;
|
|
11706
|
+
}
|
|
11707
|
+
return ok(error);
|
|
11708
|
+
}
|
|
11709
|
+
/**
|
|
11710
|
+
* This function reads the JSON form of a cause again: an exception in `exception`, or a container in `causeParts`
|
|
11711
|
+
* (RUN.REPLAY-CAUSES, RUN.REPLAY-HIDDEN, RUN.REPLAY-FIELDS, D87). The walk has the order of `causeJson`, thus a loop
|
|
11712
|
+
* gives the same exception. A list or a record of parts gives a new list or plain record (D82). A share gives the same
|
|
11713
|
+
* list or record as its earlier place (D88). The walk is a loop, thus a long chain does not overflow the stack.
|
|
11714
|
+
*/
|
|
11715
|
+
function readCause(kit, kind, data, place) {
|
|
11716
|
+
const chain = [];
|
|
11717
|
+
const held = [];
|
|
11718
|
+
const tasks = [];
|
|
11719
|
+
const open = (at, where) => {
|
|
11720
|
+
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.");
|
|
11721
|
+
const made = exception(at["name"], at["message"]);
|
|
11722
|
+
const fields = at["fields"];
|
|
11723
|
+
if (fields !== void 0) {
|
|
11724
|
+
if (!isRecord(fields)) return bad([...where, "fields"], "the fields of an exception are a record.");
|
|
11725
|
+
for (const key of Object.keys(fields)) {
|
|
11726
|
+
if (ownField(key)) return bad([
|
|
11727
|
+
...where,
|
|
11728
|
+
"fields",
|
|
11729
|
+
key
|
|
11730
|
+
], `the field ${key} of an exception is not in its fields.`);
|
|
11731
|
+
const value = readTerm(kit, fields[key], [
|
|
11732
|
+
...where,
|
|
11733
|
+
"fields",
|
|
11734
|
+
key
|
|
11735
|
+
]);
|
|
11736
|
+
if (!value.ok) return value;
|
|
11737
|
+
Object.defineProperty(made, key, {
|
|
11738
|
+
value: value.value,
|
|
11739
|
+
writable: true,
|
|
11740
|
+
enumerable: true,
|
|
11741
|
+
configurable: true
|
|
11742
|
+
});
|
|
11743
|
+
}
|
|
11744
|
+
}
|
|
11745
|
+
const dropped = at["dropped"];
|
|
11746
|
+
if (dropped !== void 0 && !(Array.isArray(dropped) && dropped.every((name) => typeof name === "string"))) return bad([...where, "dropped"], "the list dropped holds texts.");
|
|
11747
|
+
chain.push(made);
|
|
11748
|
+
const children = [];
|
|
11749
|
+
const taken = (key) => isRecord(fields) && Object.hasOwn(fields, key);
|
|
11750
|
+
const parts = at["parts"];
|
|
11751
|
+
if (parts !== void 0) {
|
|
11752
|
+
if (!isRecord(parts)) return bad([...where, "parts"], "the parts of an exception are a record.");
|
|
11753
|
+
for (const key of Object.keys(parts)) {
|
|
11754
|
+
if (ownField(key) || taken(key)) return bad([
|
|
11755
|
+
...where,
|
|
11756
|
+
"parts",
|
|
11757
|
+
key
|
|
11758
|
+
], `the field ${key} of an exception is not in its parts.`);
|
|
11759
|
+
const show = (value) => {
|
|
11760
|
+
Object.defineProperty(made, key, {
|
|
11761
|
+
value,
|
|
11762
|
+
writable: true,
|
|
11763
|
+
enumerable: true,
|
|
11764
|
+
configurable: true
|
|
11765
|
+
});
|
|
11766
|
+
};
|
|
11767
|
+
const read = slotTask(parts[key], [
|
|
11768
|
+
...where,
|
|
11769
|
+
"parts",
|
|
11770
|
+
key
|
|
11771
|
+
], false, show);
|
|
11772
|
+
if (!read.ok) return read;
|
|
11773
|
+
show(void 0);
|
|
11774
|
+
children.push(read.value);
|
|
11775
|
+
}
|
|
11776
|
+
}
|
|
11777
|
+
const hidden = at["hidden"];
|
|
11778
|
+
if (hidden !== void 0) {
|
|
11779
|
+
if (!isRecord(hidden)) return bad([...where, "hidden"], "the hidden fields of an exception are a record.");
|
|
11780
|
+
for (const key of Object.keys(hidden)) if (!HIDDEN.includes(key) || taken(key) || isRecord(parts) && Object.hasOwn(parts, key)) return bad([
|
|
11781
|
+
...where,
|
|
11782
|
+
"hidden",
|
|
11783
|
+
key
|
|
11784
|
+
], `an exception has no hidden field ${key}.`);
|
|
11785
|
+
for (const key of HIDDEN) {
|
|
11786
|
+
if (!Object.hasOwn(hidden, key)) continue;
|
|
11787
|
+
const read = slotTask(hidden[key], [
|
|
11788
|
+
...where,
|
|
11789
|
+
"hidden",
|
|
11790
|
+
key
|
|
11791
|
+
], false, (value) => hide(made, key, value));
|
|
11792
|
+
if (!read.ok) return read;
|
|
11793
|
+
children.push(read.value);
|
|
11794
|
+
}
|
|
11795
|
+
}
|
|
11796
|
+
const record = at;
|
|
11797
|
+
const links = LINKS.filter((key) => Object.hasOwn(record, key));
|
|
11798
|
+
if (links.length > 1) return bad(where, "an exception has a cause, an exception, a loop or causeParts, not more than one.");
|
|
11799
|
+
const link = links[0];
|
|
11800
|
+
if (link !== void 0) {
|
|
11801
|
+
const read = linkTask(link, record[link], [...where, link], (value) => hide(made, "cause", value));
|
|
11802
|
+
if (!read.ok) return read;
|
|
11803
|
+
children.push(read.value);
|
|
11804
|
+
}
|
|
11805
|
+
for (let index = children.length - 1; index >= 0; index -= 1) tasks.push(children[index]);
|
|
11806
|
+
return ok(made);
|
|
11807
|
+
};
|
|
11808
|
+
let cause;
|
|
11809
|
+
const root = linkTask(kind, data, place, (value) => {
|
|
11810
|
+
cause = value;
|
|
11811
|
+
});
|
|
11812
|
+
if (!root.ok) return root;
|
|
11813
|
+
tasks.push(root.value);
|
|
11814
|
+
for (let task = tasks.pop(); task !== void 0; task = tasks.pop()) {
|
|
11815
|
+
const { kind, data: part, where } = task;
|
|
11816
|
+
if (kind === "dropped") continue;
|
|
11817
|
+
if (kind === "loop") {
|
|
11818
|
+
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.");
|
|
11819
|
+
task.put(chain[part]);
|
|
11820
|
+
continue;
|
|
11821
|
+
}
|
|
11822
|
+
if (kind === "share") {
|
|
11823
|
+
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.");
|
|
11824
|
+
task.put(held[part]);
|
|
11825
|
+
continue;
|
|
11826
|
+
}
|
|
11827
|
+
if (kind === "list" || kind === "record") {
|
|
11828
|
+
const items = kind === "list" ? listTasks(part, where, held, task.put) : recordTasks(part, where, held, task.put);
|
|
11829
|
+
if (!items.ok) return items;
|
|
11830
|
+
for (let index = items.value.length - 1; index >= 0; index -= 1) tasks.push(items.value[index]);
|
|
11831
|
+
continue;
|
|
11832
|
+
}
|
|
11833
|
+
const value = kind === "exception" ? open(part, where) : readTerm(kit, part, where);
|
|
11834
|
+
if (!value.ok) return value;
|
|
11835
|
+
task.put(value.value);
|
|
11836
|
+
}
|
|
11837
|
+
return ok(cause);
|
|
11838
|
+
}
|
|
11839
|
+
/**
|
|
11840
|
+
* This function reads the field of a cause: `cause`, `exception`, `loop` or `causeParts`. A value of `causeParts` is a
|
|
11841
|
+
* list of parts, the part `record` or the part `share`, and no other part (D87, D88).
|
|
11842
|
+
*/
|
|
11843
|
+
function linkTask(kind, data, where, put) {
|
|
11844
|
+
if (kind !== "causeParts") return ok({
|
|
11845
|
+
kind,
|
|
11846
|
+
data,
|
|
11847
|
+
where,
|
|
11848
|
+
put
|
|
11849
|
+
});
|
|
11850
|
+
if (Array.isArray(data)) return ok({
|
|
11851
|
+
kind: "list",
|
|
11852
|
+
data,
|
|
11853
|
+
where,
|
|
11854
|
+
put
|
|
11855
|
+
});
|
|
11856
|
+
const key = isRecord(data) && Object.keys(data).length === 1 ? Object.keys(data)[0] : void 0;
|
|
11857
|
+
if (isRecord(data) && (key === "record" || key === "share")) return ok({
|
|
11858
|
+
kind: key,
|
|
11859
|
+
data: data[key],
|
|
11860
|
+
where: [...where, key],
|
|
11861
|
+
put
|
|
11862
|
+
});
|
|
11863
|
+
return bad(where, "a cause of parts is a list of parts, the part record or the part share.");
|
|
11864
|
+
}
|
|
11865
|
+
/**
|
|
11866
|
+
* 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,
|
|
11867
|
+
* and another value is one part.
|
|
11868
|
+
*/
|
|
11869
|
+
function slotTask(data, where, item, put) {
|
|
11870
|
+
return Array.isArray(data) ? ok({
|
|
11871
|
+
kind: "list",
|
|
11872
|
+
data,
|
|
11873
|
+
where,
|
|
11874
|
+
put
|
|
11875
|
+
}) : partTask(data, where, item, put);
|
|
11876
|
+
}
|
|
11877
|
+
/**
|
|
11878
|
+
* This function gives a list of parts its length at once, and it gives the tasks of its items. Thus the mark `dropped`
|
|
11879
|
+
* leaves a hole at its place, and each later item keeps its index (D78). The list goes in `held` for a share (D88).
|
|
11880
|
+
*/
|
|
11881
|
+
function listTasks(data, where, held, put) {
|
|
11882
|
+
const items = data;
|
|
11883
|
+
const list = [];
|
|
11884
|
+
list.length = items.length;
|
|
11885
|
+
held.push(list);
|
|
11886
|
+
put(list);
|
|
11887
|
+
const tasks = [];
|
|
11888
|
+
for (let item = 0; item < items.length; item += 1) {
|
|
11889
|
+
const task = slotTask(items[item], [...where, item], true, (value) => {
|
|
11890
|
+
list[item] = value;
|
|
11891
|
+
});
|
|
11892
|
+
if (!task.ok) return task;
|
|
11893
|
+
tasks.push(task.value);
|
|
11894
|
+
}
|
|
11895
|
+
return ok(tasks);
|
|
11896
|
+
}
|
|
11897
|
+
/**
|
|
11898
|
+
* This function gives a record of parts a new plain record (D82). It gives the tasks of its fields in the order of
|
|
11899
|
+
* the keys. Each key gets its place at once, thus the record keeps the order of the keys. The record goes in `held`
|
|
11900
|
+
* for a share (D88).
|
|
11901
|
+
*/
|
|
11902
|
+
function recordTasks(data, where, held, put) {
|
|
11903
|
+
if (!isRecord(data)) return bad(where, "a record of parts is a record.");
|
|
11904
|
+
const record = {};
|
|
11905
|
+
held.push(record);
|
|
11906
|
+
put(record);
|
|
11907
|
+
const tasks = [];
|
|
11908
|
+
for (const key of Object.keys(data)) {
|
|
11909
|
+
const show = (value) => {
|
|
11910
|
+
Object.defineProperty(record, key, {
|
|
11911
|
+
value,
|
|
11912
|
+
writable: true,
|
|
11913
|
+
enumerable: true,
|
|
11914
|
+
configurable: true
|
|
11915
|
+
});
|
|
11916
|
+
};
|
|
11917
|
+
const task = slotTask(data[key], [...where, key], false, show);
|
|
11918
|
+
if (!task.ok) return task;
|
|
11919
|
+
show(void 0);
|
|
11920
|
+
tasks.push(task.value);
|
|
11921
|
+
}
|
|
11922
|
+
return ok(tasks);
|
|
11923
|
+
}
|
|
11924
|
+
/**
|
|
11925
|
+
* This function reads one part: a record with only one of the fields `value`, `exception`, `loop`, `record` and
|
|
11926
|
+
* `share`. An item of a list can also be the mark `{ "dropped": true }`.
|
|
11927
|
+
*/
|
|
11928
|
+
function partTask(data, where, item, put) {
|
|
11929
|
+
const keys = isRecord(data) ? Object.keys(data) : [];
|
|
11930
|
+
const kind = keys[0];
|
|
11931
|
+
const mark = kind === "dropped" && item && isRecord(data) && data[kind] === true;
|
|
11932
|
+
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.");
|
|
11933
|
+
return ok({
|
|
11934
|
+
kind,
|
|
11935
|
+
data: data[kind],
|
|
11936
|
+
where: [...where, kind],
|
|
11937
|
+
put
|
|
11938
|
+
});
|
|
11939
|
+
}
|
|
11940
|
+
/** The fields of the JSON form of an exception that give its cause. */
|
|
11941
|
+
const LINKS = /* @__PURE__ */ Object.freeze([
|
|
11942
|
+
"cause",
|
|
11943
|
+
"exception",
|
|
11944
|
+
"loop",
|
|
11945
|
+
"causeParts"
|
|
11946
|
+
]);
|
|
11947
|
+
/** The fields of the JSON form of a part of a field in `parts` or `hidden`. */
|
|
11948
|
+
const PARTS$1 = /* @__PURE__ */ Object.freeze([
|
|
11949
|
+
"value",
|
|
11950
|
+
"exception",
|
|
11951
|
+
"loop",
|
|
11952
|
+
"record",
|
|
11953
|
+
"share"
|
|
11954
|
+
]);
|
|
11955
|
+
/** This function reads the JSON form of one effect again. */
|
|
11956
|
+
function readEntry(kit, data, place) {
|
|
11957
|
+
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.");
|
|
11958
|
+
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}.`);
|
|
11959
|
+
const payload = readTerm(kit, data["payload"], [...place, "payload"]);
|
|
11960
|
+
if (!payload.ok) return payload;
|
|
11961
|
+
const head = {
|
|
11962
|
+
address: data["address"],
|
|
11963
|
+
kind: data["kind"],
|
|
11964
|
+
payload: payload.value,
|
|
11965
|
+
waited: data["waited"]
|
|
11966
|
+
};
|
|
11967
|
+
if (!Object.hasOwn(data, "outcome")) return ok(head);
|
|
11968
|
+
const raw = data["outcome"];
|
|
11969
|
+
const at = [...place, "outcome"];
|
|
11970
|
+
if (isRecord(raw) && raw["ok"] === true && Object.hasOwn(raw, "value") && Object.keys(raw).length === 2) {
|
|
11971
|
+
const value = readTerm(kit, raw["value"], [...at, "value"]);
|
|
11972
|
+
return value.ok ? ok({
|
|
11973
|
+
...head,
|
|
11974
|
+
outcome: ok(value.value)
|
|
11975
|
+
}) : value;
|
|
11976
|
+
}
|
|
11977
|
+
if (isRecord(raw) && raw["ok"] === false && Object.keys(raw).length === 2) {
|
|
11978
|
+
const error = readError(kit, raw["error"], [...at, "error"]);
|
|
11979
|
+
return error.ok ? ok({
|
|
11980
|
+
...head,
|
|
11981
|
+
outcome: {
|
|
11982
|
+
ok: false,
|
|
11983
|
+
error: error.value
|
|
11984
|
+
}
|
|
11985
|
+
}) : error;
|
|
11986
|
+
}
|
|
11987
|
+
return bad(at, "an outcome is a record with ok true and a value, or with ok false and an error.");
|
|
11988
|
+
}
|
|
11989
|
+
/**
|
|
11990
|
+
* This function reads the JSON form of a log again (RUN.REPLAY-JSON). It reads each term document with the formula kit
|
|
11991
|
+
* and the domains of the machine. A cause in the field `exception` becomes an exception with that name and message.
|
|
11992
|
+
* The exception gets its fields and the chain of its causes again, also a cycle (RUN.REPLAY-CAUSES). A list gets a
|
|
11993
|
+
* hole at the place of the mark `dropped`, thus each later item keeps its index (D78). Each share of a list or a record
|
|
11994
|
+
* gives the same object (RUN.REPLAY-SHARED).
|
|
11995
|
+
*
|
|
11996
|
+
* @param machine - The machine of the run, or only its domains.
|
|
11997
|
+
* @param data - The JSON form, for example from `JSON.parse` of a golden file.
|
|
11998
|
+
* @returns The log for `replayer`, or `#VALUE!` with the place of the first field that it cannot read.
|
|
11999
|
+
*/
|
|
12000
|
+
function decodeLog(machine, data) {
|
|
12001
|
+
if (!isRecord(data) || data["version"] !== 1) return bad([], "the form is a record with the version 1.");
|
|
12002
|
+
const list = data["effects"];
|
|
12003
|
+
const order = data["settles"];
|
|
12004
|
+
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.");
|
|
12005
|
+
const domains = machine.domains;
|
|
12006
|
+
const kit = {
|
|
12007
|
+
...formulaMachine(),
|
|
12008
|
+
...domains === void 0 ? {} : { domains }
|
|
12009
|
+
};
|
|
12010
|
+
const effects = [];
|
|
12011
|
+
for (let index = 0; index < list.length; index += 1) {
|
|
12012
|
+
const entry = readEntry(kit, list[index], ["effects", index]);
|
|
12013
|
+
if (!entry.ok) return entry;
|
|
12014
|
+
effects.push(entry.value);
|
|
12015
|
+
}
|
|
12016
|
+
const settles = [];
|
|
12017
|
+
for (let index = 0; index < order.length; index += 1) {
|
|
12018
|
+
const settle = order[index];
|
|
12019
|
+
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.");
|
|
12020
|
+
settles.push({
|
|
12021
|
+
all: settle["all"],
|
|
12022
|
+
item: settle["item"]
|
|
12023
|
+
});
|
|
12024
|
+
}
|
|
12025
|
+
return ok({
|
|
12026
|
+
effects,
|
|
12027
|
+
settles
|
|
12028
|
+
});
|
|
12029
|
+
}
|
|
12030
|
+
// src/agree.ts
|
|
12031
|
+
/** The tiers that `agree` compares when its options name none. */
|
|
12032
|
+
const DEFAULT_TIERS = /* @__PURE__ */ Object.freeze([
|
|
12033
|
+
"reference",
|
|
12034
|
+
"traced",
|
|
12035
|
+
"again",
|
|
12036
|
+
"folds",
|
|
12037
|
+
"fixed"
|
|
12038
|
+
]);
|
|
12039
|
+
/**
|
|
12040
|
+
* This function tells if two values are the same for `agree`. Lists and plain records compare item by item and field
|
|
12041
|
+
* by field, and the order of the fields does not matter. Each other leaf compares with `Object.is`, thus `-0` is not
|
|
12042
|
+
* `0`, and `NaN` is `NaN`. The identity of a list or of a plain record is not compared.
|
|
12043
|
+
*
|
|
12044
|
+
* Two closures of `fn` are the same when they have the same parameters. Two `Held` values are the same when their
|
|
12045
|
+
* errors have the same code and place. Each other object is the same only as itself, unless the rule of the caller
|
|
12046
|
+
* tells otherwise.
|
|
12047
|
+
*
|
|
12048
|
+
* `docEquals` and `sameResult` use the same walk (D20). `docEquals` has no rules for closures, for `Held` values and
|
|
12049
|
+
* for a rule of the caller. `sameResult` is for an early cutoff, thus a closure is the same there only as itself, and
|
|
12050
|
+
* two errors also need the same reason.
|
|
12051
|
+
*
|
|
12052
|
+
* @param a - The first value.
|
|
12053
|
+
* @param b - The second value.
|
|
12054
|
+
* @param equal - The rule of the caller for other values. It answers first, and `undefined` gives the rules above.
|
|
12055
|
+
* @returns `true` when the values are the same.
|
|
12056
|
+
*/
|
|
12057
|
+
function sameValue(a, b, equal) {
|
|
12058
|
+
return equalWith(a, b, equal === void 0 ? sameInTiers : (x, y) => equal(x, y) ?? sameInTiers(x, y));
|
|
12059
|
+
}
|
|
12060
|
+
/** The rules of `sameValue` for the values that two tiers make again: closures and held errors. */
|
|
12061
|
+
function sameInTiers(x, y) {
|
|
12062
|
+
if (isClosure(x) || isClosure(y)) return isClosure(x) && isClosure(y) && docEquals(closureParams(x), closureParams(y));
|
|
12063
|
+
if (x instanceof Held || y instanceof Held) return x instanceof Held && y instanceof Held && errorDiffers(x.error, y.error, false) === void 0;
|
|
12064
|
+
}
|
|
12065
|
+
const CLOSURE = /* @__PURE__ */ Symbol.for("coeval.closure");
|
|
12066
|
+
/** This function gives the parameters of a closure of `fn`, or `undefined` for another function. */
|
|
12067
|
+
function closureParams(fn) {
|
|
12068
|
+
return fn[CLOSURE]?.params;
|
|
12069
|
+
}
|
|
12070
|
+
function codeAndPlace(error) {
|
|
12071
|
+
return error.place === void 0 ? error.code : `${error.code} at [${error.place.join(", ")}]`;
|
|
9996
12072
|
}
|
|
9997
12073
|
/** This function gives the difference of two errors, or `undefined` when they agree. */
|
|
9998
12074
|
function errorDiffers(a, b, strict) {
|
|
@@ -10221,18 +12297,12 @@ function agree(machine, doc, context = makeContext(), options = {}) {
|
|
|
10221
12297
|
function watched(perform) {
|
|
10222
12298
|
const effects = [];
|
|
10223
12299
|
const settles = [];
|
|
10224
|
-
const
|
|
10225
|
-
const settled = (all, item) => {
|
|
12300
|
+
const journal = relay(perform, (all, item) => {
|
|
10226
12301
|
settles.push({
|
|
10227
12302
|
all,
|
|
10228
12303
|
item
|
|
10229
12304
|
});
|
|
10230
|
-
|
|
10231
|
-
};
|
|
10232
|
-
const journal = inner?.next === void 0 ? { settled } : {
|
|
10233
|
-
settled,
|
|
10234
|
-
next: (all, waiting) => inner.next?.(all, waiting)
|
|
10235
|
-
};
|
|
12305
|
+
});
|
|
10236
12306
|
const host = (effect) => {
|
|
10237
12307
|
effects.push(effect);
|
|
10238
12308
|
return perform(effect);
|
|
@@ -10254,6 +12324,82 @@ function settlesDiffer(a, b) {
|
|
|
10254
12324
|
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.`;
|
|
10255
12325
|
}
|
|
10256
12326
|
}
|
|
12327
|
+
/** The roots beside `self` that a value without static reads keeps whole (READS.KEPT). */
|
|
12328
|
+
const KEPT_ROOTS = /* @__PURE__ */ Object.freeze([
|
|
12329
|
+
"names",
|
|
12330
|
+
"world",
|
|
12331
|
+
"event",
|
|
12332
|
+
"caps"
|
|
12333
|
+
]);
|
|
12334
|
+
/**
|
|
12335
|
+
* This function tells if the plan does not know the reads of a run. A value without static reads keeps each root
|
|
12336
|
+
* (READS.KEPT), thus the list holds a kept read of each whole root.
|
|
12337
|
+
*/
|
|
12338
|
+
function readsUnknown(reads) {
|
|
12339
|
+
return KEPT_ROOTS.every((root) => reads.some((read) => read.kept === true && read.path.length === 1 && read.path[0] === root));
|
|
12340
|
+
}
|
|
12341
|
+
/**
|
|
12342
|
+
* This function gives the reads that `agreeRun` compares: the free reads of the resolutions (RUN.AGREES-READS). A
|
|
12343
|
+
* kept read is the bound of the plan for the later reads of a value, and the reference has no such bound.
|
|
12344
|
+
*/
|
|
12345
|
+
function resolutions(reads) {
|
|
12346
|
+
return reads.filter((read) => read.free && read.kept !== true);
|
|
12347
|
+
}
|
|
12348
|
+
function pathText(path) {
|
|
12349
|
+
if (path === void 0) return "no read";
|
|
12350
|
+
try {
|
|
12351
|
+
return JSON.stringify(path);
|
|
12352
|
+
} catch {
|
|
12353
|
+
return path.map(String).join(", ");
|
|
12354
|
+
}
|
|
12355
|
+
}
|
|
12356
|
+
/**
|
|
12357
|
+
* This function gives the rule of a comparison of reads. The rule of the caller answers first. Each tier makes its
|
|
12358
|
+
* own nodes of a tree and its own adopted things. Thus two host things are the same when they have the same label,
|
|
12359
|
+
* and they are nodes or have the same JSON object.
|
|
12360
|
+
*/
|
|
12361
|
+
function sameThing(equal) {
|
|
12362
|
+
return (a, b) => {
|
|
12363
|
+
const told = equal?.(a, b);
|
|
12364
|
+
if (told !== void 0) return told;
|
|
12365
|
+
if (!isHostThing(a) || !isHostThing(b)) return void 0;
|
|
12366
|
+
if (a.label !== b.label) return false;
|
|
12367
|
+
if (a.label.startsWith("node ")) return true;
|
|
12368
|
+
try {
|
|
12369
|
+
return Object.is(a.toJson(), b.toJson());
|
|
12370
|
+
} catch {
|
|
12371
|
+
return false;
|
|
12372
|
+
}
|
|
12373
|
+
};
|
|
12374
|
+
}
|
|
12375
|
+
/** This function gives the difference of what two reads reached, or `undefined` when they agree. */
|
|
12376
|
+
function reachedDiffers(a, b, equal, strict) {
|
|
12377
|
+
if (a.ok && b.ok) return sameValue(a.value, b.value, equal) ? void 0 : "The read reaches another value in the tier.";
|
|
12378
|
+
if (!a.ok && !b.ok) {
|
|
12379
|
+
if (a.error.code !== b.error.code) return `The read gives ${a.error.code} in the plan, and ${b.error.code} in the tier.`;
|
|
12380
|
+
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;
|
|
12381
|
+
}
|
|
12382
|
+
if (!b.ok) return `The read reaches a value in the plan, and ${b.error.code} in the tier.`;
|
|
12383
|
+
return `The read gives ${a.error.code} in the plan, and a value in the tier.`;
|
|
12384
|
+
}
|
|
12385
|
+
/**
|
|
12386
|
+
* This function gives the first difference of the reads of the plan and of a tier, or `undefined` when they agree
|
|
12387
|
+
* (RUN.AGREES-READS). Only the free reads of the resolutions compare, in their order. When the plan does not know its
|
|
12388
|
+
* reads, the function compares nothing.
|
|
12389
|
+
*/
|
|
12390
|
+
function readsDiffer(plan, tier, options) {
|
|
12391
|
+
if (readsUnknown(plan)) return void 0;
|
|
12392
|
+
const a = resolutions(plan);
|
|
12393
|
+
const b = resolutions(tier);
|
|
12394
|
+
const equal = sameThing(options.equal);
|
|
12395
|
+
for (let index = 0; index < Math.max(a.length, b.length); index += 1) {
|
|
12396
|
+
const x = a[index];
|
|
12397
|
+
const y = b[index];
|
|
12398
|
+
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.`;
|
|
12399
|
+
const why = reachedDiffers(x.reached, y.reached, equal, options.strict === true);
|
|
12400
|
+
if (why !== void 0) return `The free read ${index} of ${pathText(x.path)} differs. ${why}`;
|
|
12401
|
+
}
|
|
12402
|
+
}
|
|
10257
12403
|
/**
|
|
10258
12404
|
* This function compares the run of a document in a plan with its run in other tiers (RUN.AGREES). First the plan
|
|
10259
12405
|
* starts the run with a recorder around the host. Then each tier replays the log of that run. These tiers are the
|
|
@@ -10262,6 +12408,13 @@ function settlesDiffer(a, b) {
|
|
|
10262
12408
|
* The rules of `agree` compare the results and the effects, with their addresses and outcomes. A tier must also
|
|
10263
12409
|
* settle the waits of each `all` in the order of the plan, thus a replay keeps the order of the waits.
|
|
10264
12410
|
*
|
|
12411
|
+
* The reference run must also give the free reads of the plan, in their order (RUN.AGREES-READS). Another replay of
|
|
12412
|
+
* the plan records these reads. A kept read does not compare. When the plan keeps each root, its reads are not known,
|
|
12413
|
+
* and no read compares.
|
|
12414
|
+
*
|
|
12415
|
+
* One replayer of the log serves each tier and the replay for the reads (RUN.AGREES-ONE-REPLAYER). The replayer keeps
|
|
12416
|
+
* no state of a run (RUN.REPLAY-RUNS), thus each tier replays the same log as with a replayer of its own.
|
|
12417
|
+
*
|
|
10265
12418
|
* @param machine - The world, the evaluators and the runners.
|
|
10266
12419
|
* @param doc - The document.
|
|
10267
12420
|
* @param context - The names and the capabilities at the root.
|
|
@@ -10285,17 +12438,24 @@ async function agreeRun(machine, doc, context = makeContext(), options = {}) {
|
|
|
10285
12438
|
const result = await plan.run(context, { perform: first.perform });
|
|
10286
12439
|
const saw = observe?.();
|
|
10287
12440
|
const log = recording.log();
|
|
12441
|
+
let planReads;
|
|
12442
|
+
let compared;
|
|
12443
|
+
const replay = replayer(log);
|
|
10288
12444
|
for (const tier of options.tiers ?? DEFAULT_TIERS) {
|
|
10289
12445
|
if (tier === "fixed") continue;
|
|
10290
|
-
const
|
|
10291
|
-
const host = { perform:
|
|
12446
|
+
const tierReplay = watched(replay);
|
|
12447
|
+
const host = { perform: tierReplay.perform };
|
|
10292
12448
|
let other;
|
|
10293
|
-
|
|
10294
|
-
|
|
10295
|
-
|
|
10296
|
-
|
|
10297
|
-
|
|
10298
|
-
|
|
12449
|
+
let tierReads;
|
|
12450
|
+
if (tier === "reference") {
|
|
12451
|
+
tierReads = [];
|
|
12452
|
+
other = await referenceRun(machine, doc, context, {
|
|
12453
|
+
...declared,
|
|
12454
|
+
...host,
|
|
12455
|
+
reads: tierReads,
|
|
12456
|
+
...options.definitions === void 0 ? {} : { definitions: options.definitions }
|
|
12457
|
+
});
|
|
12458
|
+
} else if (tier === "traced") other = (await run(machine, doc, context, {
|
|
10299
12459
|
...declared,
|
|
10300
12460
|
...host
|
|
10301
12461
|
})).result;
|
|
@@ -10307,7 +12467,20 @@ async function agreeRun(machine, doc, context = makeContext(), options = {}) {
|
|
|
10307
12467
|
const compareCalls = observe !== void 0 && tier !== "folds";
|
|
10308
12468
|
const calls = observe?.();
|
|
10309
12469
|
if (overflowed(result) || overflowed(other)) continue;
|
|
10310
|
-
|
|
12470
|
+
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);
|
|
12471
|
+
if (why === void 0 && tierReads !== void 0) {
|
|
12472
|
+
if (planReads === void 0) {
|
|
12473
|
+
const reads = [];
|
|
12474
|
+
await plan.run(context, {
|
|
12475
|
+
perform: replay,
|
|
12476
|
+
reads
|
|
12477
|
+
});
|
|
12478
|
+
observe?.();
|
|
12479
|
+
planReads = reads;
|
|
12480
|
+
}
|
|
12481
|
+
why = readsDiffer(planReads, tierReads, options);
|
|
12482
|
+
if (why === void 0 && !readsUnknown(planReads)) compared = resolutions(planReads);
|
|
12483
|
+
}
|
|
10311
12484
|
if (why !== void 0) return {
|
|
10312
12485
|
ok: false,
|
|
10313
12486
|
tier,
|
|
@@ -10316,14 +12489,18 @@ async function agreeRun(machine, doc, context = makeContext(), options = {}) {
|
|
|
10316
12489
|
reason: why
|
|
10317
12490
|
};
|
|
10318
12491
|
}
|
|
10319
|
-
return {
|
|
12492
|
+
return compared === void 0 ? {
|
|
10320
12493
|
ok: true,
|
|
10321
12494
|
result,
|
|
10322
12495
|
effects: first.effects
|
|
12496
|
+
} : {
|
|
12497
|
+
ok: true,
|
|
12498
|
+
result,
|
|
12499
|
+
effects: first.effects,
|
|
12500
|
+
reads: compared
|
|
10323
12501
|
};
|
|
10324
12502
|
}
|
|
10325
|
-
|
|
10326
|
-
//#region src/generate.ts
|
|
12503
|
+
// src/generate.ts
|
|
10327
12504
|
/**
|
|
10328
12505
|
* This function makes a source of random numbers with a seed (the algorithm mulberry32). It needs no package.
|
|
10329
12506
|
*
|
|
@@ -10725,8 +12902,7 @@ function generate(world, options) {
|
|
|
10725
12902
|
hint: void 0
|
|
10726
12903
|
});
|
|
10727
12904
|
}
|
|
10728
|
-
|
|
10729
|
-
//#region src/conform.ts
|
|
12905
|
+
// src/conform.ts
|
|
10730
12906
|
/** The order of the obligations in the list of findings. */
|
|
10731
12907
|
const ORDER = [
|
|
10732
12908
|
"C1",
|
|
@@ -10738,7 +12914,9 @@ const ORDER = [
|
|
|
10738
12914
|
"C7",
|
|
10739
12915
|
"C8",
|
|
10740
12916
|
"local",
|
|
10741
|
-
"agree"
|
|
12917
|
+
"agree",
|
|
12918
|
+
"truth",
|
|
12919
|
+
"cover"
|
|
10742
12920
|
];
|
|
10743
12921
|
/** A function in the results of the probe parts. It gives the count of its arguments. */
|
|
10744
12922
|
const PROBE_FN = (...args) => args.length;
|
|
@@ -11616,14 +13794,58 @@ function examine(env, sample, contract) {
|
|
|
11616
13794
|
if (env.runner) for (const scenario of scenariosOf(env, sample, contract, true)) checkShot(env, sample, contract, scenario, shoot(env, code, sample, scenario));
|
|
11617
13795
|
}
|
|
11618
13796
|
/**
|
|
13797
|
+
* This function checks the declared truth of a profile (D86). The function `truthy` must give `true` or `false` for
|
|
13798
|
+
* each member of `truthType`, and it must not throw. The members come from the lists `values` of the domains of the
|
|
13799
|
+
* type. A type without such a list gets its members from the results of the palette and from the samples of the
|
|
13800
|
+
* domains (D99).
|
|
13801
|
+
*/
|
|
13802
|
+
function checkTruth(env, code, probe) {
|
|
13803
|
+
const truth = declaredTruthOf(code);
|
|
13804
|
+
if (truth === void 0) return;
|
|
13805
|
+
const domains = env.machine.domains;
|
|
13806
|
+
if (typeIssues(truth.truthType, domains).length > 0) return;
|
|
13807
|
+
const samples = env.results.flatMap((result) => result.ok ? [result.value] : []);
|
|
13808
|
+
const of = (value) => `the member ${valueText(value)} of the truth type ${typeText(truth.truthType)}`;
|
|
13809
|
+
for (const value of membersOf(truth.truthType, domains, samples)) {
|
|
13810
|
+
let given;
|
|
13811
|
+
try {
|
|
13812
|
+
given = truth.truthy(value);
|
|
13813
|
+
} catch (error) {
|
|
13814
|
+
report(env, "truth", `The function truthy of the profile throws for ${of(value)}: ${describe(error)}`, () => probe(value));
|
|
13815
|
+
return;
|
|
13816
|
+
}
|
|
13817
|
+
if (typeof given === "boolean") continue;
|
|
13818
|
+
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));
|
|
13819
|
+
return;
|
|
13820
|
+
}
|
|
13821
|
+
}
|
|
13822
|
+
/**
|
|
13823
|
+
* This function checks the covers of the domains of the machine from samples (D94). Each sample of a covered domain
|
|
13824
|
+
* must be in one domain of each valid cover. The samples of a domain come from its list `values`. A domain without
|
|
13825
|
+
* that list gets its members from the palette and from the samples of the domains, as for the check `truth`.
|
|
13826
|
+
*/
|
|
13827
|
+
function checkCovers(env, probe) {
|
|
13828
|
+
const domains = env.machine.domains;
|
|
13829
|
+
if (domains === void 0) return;
|
|
13830
|
+
const palette = env.results.flatMap((result) => result.ok ? [result.value] : []);
|
|
13831
|
+
const samples = {};
|
|
13832
|
+
for (const [name, domain] of Object.entries(domains)) if (domain.covers !== void 0) samples[name] = membersOf(name, domains, palette);
|
|
13833
|
+
const found = falseCovers(domains, samples)[0];
|
|
13834
|
+
if (found === void 0) return;
|
|
13835
|
+
report(env, "cover", `The member ${valueText(found.value)} of the domain ${found.domain} is in no domain of its cover ${found.cover.join(", ")}. The check of types trusts the cover, thus it is unsound for ${found.domain}.`, () => probe(found.value));
|
|
13836
|
+
}
|
|
13837
|
+
/**
|
|
11619
13838
|
* This function examines one evaluator or runner of a machine against the obligations of host code (K92, D36). It
|
|
11620
13839
|
* starts the code directly, without the kernel, with probe parts and a probe runtime. It generates probe documents from
|
|
11621
13840
|
* the contract of each kind that names the code, and it gives the probe parts chosen and random results.
|
|
11622
13841
|
*
|
|
11623
13842
|
* The checks follow the obligations C1 to C8 of `docs/SEMANTICS.md`. Two more checks follow the premises of the
|
|
11624
13843
|
* simulation of the plan. The check `local` finds state that stays from one call to the next. The check `agree` compares
|
|
11625
|
-
* compiled code with its definition. The
|
|
11626
|
-
*
|
|
13844
|
+
* compiled code with its definition. The finding of an obligation names the first probe that breaks it. An empty list
|
|
13845
|
+
* means that each probe conforms.
|
|
13846
|
+
*
|
|
13847
|
+
* The check `truth` tests the declared truth of a formula profile (D86). The check `cover` tests the covers of the
|
|
13848
|
+
* domains of the machine from samples (D94).
|
|
11627
13849
|
*
|
|
11628
13850
|
* @param machine - The machine with the code and the world of the kinds.
|
|
11629
13851
|
* @param name - The name of the evaluator or of the runner. A runner wins over an evaluator of the same name.
|
|
@@ -11654,18 +13876,39 @@ function conforms(machine, name, options = {}) {
|
|
|
11654
13876
|
found: /* @__PURE__ */ new Map()
|
|
11655
13877
|
};
|
|
11656
13878
|
const types = options.types ?? typesNaming(machine.world, name, verb);
|
|
13879
|
+
let first;
|
|
11657
13880
|
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.`);
|
|
11658
13881
|
for (const type of types) {
|
|
11659
13882
|
const found = lookupKind(machine.world, type);
|
|
11660
13883
|
if (!found.ok) throw new Error(`The world has no kind for the type ${typeName(type)}.`);
|
|
11661
13884
|
const contract = contractOf(env, found.kind, found.path, options.caps);
|
|
11662
13885
|
const extra = own(options.values, typeName(type)) ?? own(options.values, found.path.join(".")) ?? [];
|
|
11663
|
-
for (const sample of samplesOf(type, found.path, found.kind, contract, extra))
|
|
13886
|
+
for (const sample of samplesOf(type, found.path, found.kind, contract, extra)) {
|
|
13887
|
+
first ??= sample;
|
|
13888
|
+
examine(env, sample, contract);
|
|
13889
|
+
}
|
|
11664
13890
|
}
|
|
13891
|
+
const probeWith = (value) => first === void 0 ? {
|
|
13892
|
+
type: typeName(types[0]),
|
|
13893
|
+
doc: {
|
|
13894
|
+
type: types[0],
|
|
13895
|
+
value: null
|
|
13896
|
+
},
|
|
13897
|
+
results: {},
|
|
13898
|
+
waits: [],
|
|
13899
|
+
caps: NO_CAPS,
|
|
13900
|
+
running: runner
|
|
13901
|
+
} : probeOf(first, {
|
|
13902
|
+
results: first.split.parts.map(() => ok(value)),
|
|
13903
|
+
waits: [],
|
|
13904
|
+
caps: NO_CAPS,
|
|
13905
|
+
running: runner
|
|
13906
|
+
});
|
|
13907
|
+
checkTruth(env, code, probeWith);
|
|
13908
|
+
checkCovers(env, probeWith);
|
|
11665
13909
|
return ORDER.filter((obligation) => env.found.has(obligation)).map((obligation) => env.found.get(obligation));
|
|
11666
13910
|
}
|
|
11667
|
-
|
|
11668
|
-
//#region src/same.ts
|
|
13911
|
+
// src/same.ts
|
|
11669
13912
|
/**
|
|
11670
13913
|
* This function tells if two results are the same for an early cutoff (RESULT.SAME). A schedule uses it to stop the
|
|
11671
13914
|
* work when a part that evaluates again gives the same result. A wrong `true` keeps a stale value, but a wrong `false`
|
|
@@ -11701,8 +13944,7 @@ function sameError(a, b) {
|
|
|
11701
13944
|
if (a.place === void 0 || b.place === void 0) return a.place === b.place;
|
|
11702
13945
|
return docEquals(a.place, b.place);
|
|
11703
13946
|
}
|
|
11704
|
-
|
|
11705
|
-
//#region src/reads.ts
|
|
13947
|
+
// src/reads.ts
|
|
11706
13948
|
/** One answer serves each list whose reads are all the same. */
|
|
11707
13949
|
const SAME = /* @__PURE__ */ Object.freeze({ same: true });
|
|
11708
13950
|
/** This function tells if two lists of hops are the same: the same steps, rungs and things, in order. */
|
|
@@ -11793,8 +14035,7 @@ function verifyReads(target, reads, context, options = {}) {
|
|
|
11793
14035
|
}
|
|
11794
14036
|
return SAME;
|
|
11795
14037
|
}
|
|
11796
|
-
|
|
11797
|
-
//#region src/durable.ts
|
|
14038
|
+
// src/durable.ts
|
|
11798
14039
|
/** The most values that a cache of value documents keeps, as in `planner`. */
|
|
11799
14040
|
const MAX_VALUES = 1e3;
|
|
11800
14041
|
/** No reads. One frozen list serves each result of a pure document without reads. */
|
|
@@ -11934,8 +14175,7 @@ function durable(machine, options = {}) {
|
|
|
11934
14175
|
}
|
|
11935
14176
|
};
|
|
11936
14177
|
}
|
|
11937
|
-
|
|
11938
|
-
//#region src/graph.ts
|
|
14178
|
+
// src/graph.ts
|
|
11939
14179
|
/** This function gives the indexed form of a graph, with the edges that the options keep. */
|
|
11940
14180
|
function indexed(graph, options) {
|
|
11941
14181
|
const eager = options.eager === true;
|
|
@@ -12265,8 +14505,7 @@ function deepestFirst(from, edgesOf) {
|
|
|
12265
14505
|
}
|
|
12266
14506
|
return out;
|
|
12267
14507
|
}
|
|
12268
|
-
|
|
12269
|
-
//#region src/schedule.ts
|
|
14508
|
+
// src/schedule.ts
|
|
12270
14509
|
/**
|
|
12271
14510
|
* This function gives the edges of a node from the static facts of its document (SCHED.EDGES). Each read of the root
|
|
12272
14511
|
* `world` is an edge to the node of its first key. The keys after that key are the field of the edge (SCHED.GRAIN).
|
|
@@ -12560,8 +14799,7 @@ function schedule(machine, options = {}) {
|
|
|
12560
14799
|
}
|
|
12561
14800
|
};
|
|
12562
14801
|
}
|
|
12563
|
-
|
|
12564
|
-
//#region src/meaning.ts
|
|
14802
|
+
// src/meaning.ts
|
|
12565
14803
|
/** A list of eight reference types. The generator then gives more references, thus more reads of nodes. */
|
|
12566
14804
|
function moreRefs() {
|
|
12567
14805
|
return Array.from({ length: 8 }, () => ["ref"]);
|
|
@@ -12960,8 +15198,7 @@ function agreeSchedule(engine, options = {}) {
|
|
|
12960
15198
|
counts
|
|
12961
15199
|
};
|
|
12962
15200
|
}
|
|
12963
|
-
|
|
12964
|
-
//#region src/providers.ts
|
|
15201
|
+
// src/providers.ts
|
|
12965
15202
|
/** The deepest schema that `typeFromSchema` reads. A deeper schema reads as `unknown`. */
|
|
12966
15203
|
const MAX_SCHEMA_DEPTH = 256;
|
|
12967
15204
|
/** The keywords that describe a schema and do not test a value. The reading ignores them without an issue. */
|
|
@@ -13270,7 +15507,6 @@ function samplesAt(values, closed, depth) {
|
|
|
13270
15507
|
function typeFromSamples(values, options = {}) {
|
|
13271
15508
|
return samplesAt(values, options.closed === true, 0);
|
|
13272
15509
|
}
|
|
13273
|
-
|
|
13274
|
-
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, agreeSchedule, 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, 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 };
|
|
15510
|
+
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 };
|
|
13275
15511
|
|
|
13276
15512
|
//# sourceMappingURL=index.js.map
|