factbag 0.0.0-alpha.2 → 0.0.0-alpha.4
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/CHANGELOG.md +47 -0
- package/README.md +4 -4
- package/dist/core/{chunk-5J3WGMN2.js → chunk-N2RRSOEN.js} +2 -1
- package/dist/core/{chunk-X3LSDJL4.js → chunk-QOMFY3XB.js} +4 -1
- package/dist/core/dsl/enumeration.d.ts +5 -1
- package/dist/core/dsl/index.d.ts +4 -4
- package/dist/core/dsl/index.js +76 -7
- package/dist/core/dsl/operators/arithmetic.d.ts +69 -2
- package/dist/core/dsl/operators/comparison.d.ts +11 -1
- package/dist/core/dsl/types.d.ts +2 -0
- package/dist/core/exceptions/programmer-errors.d.ts +4 -4
- package/dist/core/explanation/explain.d.ts +5 -1
- package/dist/core/explanation/index.js +45 -5
- package/dist/core/knowledge-base/index.js +2 -2
- package/dist/core/query/index.d.ts +1 -1
- package/dist/core/query/index.js +406 -67
- package/dist/core/solver/calendar.d.ts +4 -0
- package/dist/core/solver/candidate-search.d.ts +3 -1
- package/dist/core/solver/program-index.d.ts +17 -0
- package/dist/core/solver/query-validation/utils.d.ts +2 -0
- package/dist/core/types/atom.d.ts +7 -2
- package/dist/core/types/goal.d.ts +23 -5
- package/dist/core/types/judgment.d.ts +2 -2
- package/dist/core/types/schema.d.ts +2 -2
- package/dist/jev/chunk-QEZ4O3YS.js +203 -0
- package/dist/jev/chunk-ZAAXPTJJ.js +105 -0
- package/dist/jev/classifier/classifiable-predicates.d.ts +12 -0
- package/dist/jev/classifier/classifier.d.ts +16 -0
- package/dist/jev/classifier/extract-evidence.d.ts +17 -0
- package/dist/jev/classifier/filter-predicates.d.ts +9 -0
- package/dist/jev/classifier/filter-symbols.d.ts +14 -0
- package/dist/jev/classifier/index.d.ts +6 -0
- package/dist/jev/classifier/index.js +246 -0
- package/dist/jev/classifier/types.d.ts +13 -0
- package/dist/jev/config.d.ts +17 -0
- package/dist/jev/config.js +18 -0
- package/dist/jev/jev/model.d.ts +11 -0
- package/dist/jev/jev/prompts.d.ts +29 -0
- package/dist/jev/resolve.d.ts +5 -72
- package/dist/jev/resolve.js +24 -47
- package/dist/jev/settings.d.ts +11 -0
- package/dist/jev/types/config.d.ts +18 -0
- package/dist/jev/types/model.d.ts +58 -0
- package/dist/jev/types/run.d.ts +9 -0
- package/dist/jev/utils.d.ts +9 -0
- package/package.json +7 -3
- package/dist/jev/chunk-PISXBJOT.js +0 -206
- package/dist/jev/classifier.d.ts +0 -28
- package/dist/jev/classifier.js +0 -171
- package/dist/jev/client.d.ts +0 -62
- package/dist/jev/jobs.d.ts +0 -61
- package/dist/jev/tools.d.ts +0 -9
- package/dist/jev/types.d.ts +0 -63
- /package/dist/core/{chunk-A6ZZJWIO.js → chunk-NDUIQAH4.js} +0 -0
package/dist/core/query/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isFormula
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-NDUIQAH4.js";
|
|
4
4
|
import {
|
|
5
5
|
PrimitiveArithmeticProgrammerError,
|
|
6
6
|
UnconstructiveRecursionProgrammerError,
|
|
7
7
|
UnsafeUnboundVariableProgrammerError
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-QOMFY3XB.js";
|
|
9
9
|
import {
|
|
10
10
|
assertGroundAtom,
|
|
11
11
|
atomEquals,
|
|
@@ -13,8 +13,9 @@ import {
|
|
|
13
13
|
fieldIdentityToken,
|
|
14
14
|
isAtomForPredicate,
|
|
15
15
|
isAtomLike,
|
|
16
|
+
isEntityValue,
|
|
16
17
|
isGroundAtom
|
|
17
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-N2RRSOEN.js";
|
|
18
19
|
import {
|
|
19
20
|
AssertionError,
|
|
20
21
|
Var,
|
|
@@ -22,6 +23,39 @@ import {
|
|
|
22
23
|
isVar
|
|
23
24
|
} from "../chunk-UVOCOH2I.js";
|
|
24
25
|
|
|
26
|
+
// src/solver/calendar.ts
|
|
27
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
28
|
+
function utcDay(date) {
|
|
29
|
+
return Math.floor(date.getTime() / DAY_MS);
|
|
30
|
+
}
|
|
31
|
+
function daysBetween(later, earlier) {
|
|
32
|
+
return utcDay(later) - utcDay(earlier);
|
|
33
|
+
}
|
|
34
|
+
function yearsBetween(later, earlier) {
|
|
35
|
+
if (later.getTime() < earlier.getTime()) {
|
|
36
|
+
return -yearsBetween(earlier, later) || 0;
|
|
37
|
+
}
|
|
38
|
+
const years = later.getUTCFullYear() - earlier.getUTCFullYear();
|
|
39
|
+
const laterDay = later.getUTCMonth() * 100 + later.getUTCDate();
|
|
40
|
+
const earlierDay = earlier.getUTCMonth() * 100 + earlier.getUTCDate();
|
|
41
|
+
return laterDay < earlierDay ? years - 1 : years;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/types/goal.ts
|
|
45
|
+
var ARITHMETIC_KINDS = [
|
|
46
|
+
"add",
|
|
47
|
+
"sub",
|
|
48
|
+
"mul",
|
|
49
|
+
"div",
|
|
50
|
+
"daysBetween",
|
|
51
|
+
"yearsBetween",
|
|
52
|
+
"min",
|
|
53
|
+
"max",
|
|
54
|
+
"roundTo",
|
|
55
|
+
"floorTo",
|
|
56
|
+
"ceilTo"
|
|
57
|
+
];
|
|
58
|
+
|
|
25
59
|
// src/solver/query-validation/utils.ts
|
|
26
60
|
function ruleEdges(rule) {
|
|
27
61
|
const headPredicate = rule.head.atom.predicate;
|
|
@@ -31,6 +65,7 @@ function ruleEdges(rule) {
|
|
|
31
65
|
return edges;
|
|
32
66
|
}
|
|
33
67
|
function reachablePredicates(start, edges) {
|
|
68
|
+
const next = adjacency(edges);
|
|
34
69
|
const reachable = /* @__PURE__ */ new Set([start]);
|
|
35
70
|
const stack = [start];
|
|
36
71
|
while (stack.length > 0) {
|
|
@@ -38,16 +73,71 @@ function reachablePredicates(start, edges) {
|
|
|
38
73
|
if (current === void 0) {
|
|
39
74
|
continue;
|
|
40
75
|
}
|
|
41
|
-
for (const
|
|
42
|
-
if (
|
|
76
|
+
for (const target of next.get(current) ?? []) {
|
|
77
|
+
if (reachable.has(target)) {
|
|
43
78
|
continue;
|
|
44
79
|
}
|
|
45
|
-
reachable.add(
|
|
46
|
-
stack.push(
|
|
80
|
+
reachable.add(target);
|
|
81
|
+
stack.push(target);
|
|
47
82
|
}
|
|
48
83
|
}
|
|
49
84
|
return reachable;
|
|
50
85
|
}
|
|
86
|
+
function adjacency(edges) {
|
|
87
|
+
const next = /* @__PURE__ */ new Map();
|
|
88
|
+
for (const { from, to } of edges) {
|
|
89
|
+
let targets = next.get(from);
|
|
90
|
+
if (targets === void 0) {
|
|
91
|
+
targets = /* @__PURE__ */ new Set();
|
|
92
|
+
next.set(from, targets);
|
|
93
|
+
}
|
|
94
|
+
targets.add(to);
|
|
95
|
+
}
|
|
96
|
+
return next;
|
|
97
|
+
}
|
|
98
|
+
function stronglyConnectedComponents(edges) {
|
|
99
|
+
const next = adjacency(edges);
|
|
100
|
+
const indices = /* @__PURE__ */ new Map();
|
|
101
|
+
const onStack = /* @__PURE__ */ new Set();
|
|
102
|
+
const stack = [];
|
|
103
|
+
const components = /* @__PURE__ */ new Map();
|
|
104
|
+
function visit(predicate) {
|
|
105
|
+
const index = indices.size;
|
|
106
|
+
indices.set(predicate, index);
|
|
107
|
+
let lowLink = index;
|
|
108
|
+
stack.push(predicate);
|
|
109
|
+
onStack.add(predicate);
|
|
110
|
+
for (const target of next.get(predicate) ?? []) {
|
|
111
|
+
const targetIndex = indices.get(target);
|
|
112
|
+
if (targetIndex === void 0) {
|
|
113
|
+
const targetLowLink = visit(target);
|
|
114
|
+
lowLink = Math.min(lowLink, targetLowLink);
|
|
115
|
+
} else if (onStack.has(target)) {
|
|
116
|
+
lowLink = Math.min(lowLink, targetIndex);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (lowLink === index) {
|
|
120
|
+
const component = /* @__PURE__ */ new Set();
|
|
121
|
+
for (let member = stack.pop(); member !== void 0; member = stack.pop()) {
|
|
122
|
+
onStack.delete(member);
|
|
123
|
+
component.add(member);
|
|
124
|
+
if (member === predicate) {
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
for (const member of component) {
|
|
129
|
+
components.set(member, component);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return lowLink;
|
|
133
|
+
}
|
|
134
|
+
for (const predicate of next.keys()) {
|
|
135
|
+
if (!indices.has(predicate)) {
|
|
136
|
+
visit(predicate);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return components;
|
|
140
|
+
}
|
|
51
141
|
function isNegativeLiteral(value) {
|
|
52
142
|
return typeof value === "object" && value !== null && "sign" in value && value.sign === "-" && "atom" in value && isAtomLike(value.atom);
|
|
53
143
|
}
|
|
@@ -58,7 +148,7 @@ function isCompleteWhenOption(value) {
|
|
|
58
148
|
return typeof value === "object" && value !== null && "completeWhen" in value;
|
|
59
149
|
}
|
|
60
150
|
function isPrimitiveFormula(value) {
|
|
61
|
-
return isFormula(value) && [
|
|
151
|
+
return isFormula(value) && [...ARITHMETIC_KINDS, "eq", "neq", "lt", "lte", "gt", "gte"].includes(value.kind);
|
|
62
152
|
}
|
|
63
153
|
function bindVariables(value, bound) {
|
|
64
154
|
for (const variable of variablesIn(value)) {
|
|
@@ -177,7 +267,7 @@ function collectBodyEdgesWithKind(body, headPredicate, bound, edges, kind) {
|
|
|
177
267
|
function assertConstructiveRecursion(context) {
|
|
178
268
|
const edges = context.rules.flatMap(ruleEdges);
|
|
179
269
|
const reachable = reachablePredicates(context.atom.predicate, edges);
|
|
180
|
-
const components =
|
|
270
|
+
const components = componentsAmong(reachable, edges);
|
|
181
271
|
const needsCandidateEnumeration = variablesIn(context.atom).length > 0;
|
|
182
272
|
for (const component of components) {
|
|
183
273
|
const internalEdges = edges.filter(
|
|
@@ -200,31 +290,22 @@ function assertConstructiveRecursion(context) {
|
|
|
200
290
|
}
|
|
201
291
|
function hasNonRecursiveBaseRule(component, rules) {
|
|
202
292
|
return rules.some(
|
|
203
|
-
(rule) => component.has(rule.head.atom.predicate) &&
|
|
293
|
+
(rule) => component.has(rule.head.atom.predicate) && hasNonRecursiveAlternative(rule.body, component)
|
|
204
294
|
);
|
|
205
295
|
}
|
|
206
|
-
function
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
for (const predicate of predicates) {
|
|
210
|
-
if (!remaining.has(predicate)) {
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
const forward = reachablePredicates(predicate, edges);
|
|
214
|
-
const backward = reachablePredicates(predicate, reverseEdges(edges));
|
|
215
|
-
const component = /* @__PURE__ */ new Set();
|
|
216
|
-
for (const candidate of predicates) {
|
|
217
|
-
if (forward.has(candidate) && backward.has(candidate)) {
|
|
218
|
-
component.add(candidate);
|
|
219
|
-
remaining.delete(candidate);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
components.push(component);
|
|
296
|
+
function hasNonRecursiveAlternative(body, component) {
|
|
297
|
+
if (isFormula(body, "or")) {
|
|
298
|
+
return body.args.some((branch) => hasNonRecursiveAlternative(branch, component));
|
|
223
299
|
}
|
|
224
|
-
return
|
|
300
|
+
return !bodyReferencesPredicates(body, component);
|
|
225
301
|
}
|
|
226
|
-
function
|
|
227
|
-
|
|
302
|
+
function componentsAmong(predicates, edges) {
|
|
303
|
+
const components = /* @__PURE__ */ new Set();
|
|
304
|
+
const byPredicate = stronglyConnectedComponents(edges);
|
|
305
|
+
for (const predicate of predicates) {
|
|
306
|
+
components.add(byPredicate.get(predicate) ?? /* @__PURE__ */ new Set([predicate]));
|
|
307
|
+
}
|
|
308
|
+
return [...components];
|
|
228
309
|
}
|
|
229
310
|
function bodyReferencesPredicates(body, predicates) {
|
|
230
311
|
if (isAtomLike(body)) {
|
|
@@ -451,18 +532,17 @@ function groundAtom(atom, bindings) {
|
|
|
451
532
|
var CandidateSearch = class {
|
|
452
533
|
groups = /* @__PURE__ */ new Map();
|
|
453
534
|
tables = /* @__PURE__ */ new Map();
|
|
535
|
+
evaluatingGroups = 0;
|
|
454
536
|
constructor(rules) {
|
|
455
|
-
const
|
|
537
|
+
const components = stronglyConnectedComponents(rules.flatMap(ruleEdges));
|
|
456
538
|
for (const rule of rules) {
|
|
457
539
|
const predicate = rule.head.atom.predicate;
|
|
458
540
|
if (this.groups.has(predicate)) {
|
|
459
541
|
continue;
|
|
460
542
|
}
|
|
461
543
|
const group = { tables: /* @__PURE__ */ new Set(), evaluating: false, changed: false };
|
|
462
|
-
for (const
|
|
463
|
-
|
|
464
|
-
this.groups.set(dependency, group);
|
|
465
|
-
}
|
|
544
|
+
for (const member of components.get(predicate) ?? [predicate]) {
|
|
545
|
+
this.groups.set(member, group);
|
|
466
546
|
}
|
|
467
547
|
}
|
|
468
548
|
}
|
|
@@ -491,6 +571,7 @@ var CandidateSearch = class {
|
|
|
491
571
|
group.changed = true;
|
|
492
572
|
if (!group.evaluating) {
|
|
493
573
|
group.evaluating = true;
|
|
574
|
+
this.evaluatingGroups += 1;
|
|
494
575
|
try {
|
|
495
576
|
do {
|
|
496
577
|
group.changed = false;
|
|
@@ -500,6 +581,7 @@ var CandidateSearch = class {
|
|
|
500
581
|
} while (group.changed);
|
|
501
582
|
} finally {
|
|
502
583
|
group.evaluating = false;
|
|
584
|
+
this.evaluatingGroups -= 1;
|
|
503
585
|
}
|
|
504
586
|
}
|
|
505
587
|
return [...table.atoms.values()];
|
|
@@ -507,8 +589,17 @@ var CandidateSearch = class {
|
|
|
507
589
|
sameComponent(left, right) {
|
|
508
590
|
return left === right || this.groups.has(left) && this.groups.get(left) === this.groups.get(right);
|
|
509
591
|
}
|
|
510
|
-
|
|
511
|
-
|
|
592
|
+
/** A judgment depending on these predicates might observe a partial candidate table. */
|
|
593
|
+
evaluatingAmong(predicates) {
|
|
594
|
+
if (this.evaluatingGroups === 0) {
|
|
595
|
+
return false;
|
|
596
|
+
}
|
|
597
|
+
for (const predicate of predicates) {
|
|
598
|
+
if (this.groups.get(predicate)?.evaluating === true) {
|
|
599
|
+
return true;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
return false;
|
|
512
603
|
}
|
|
513
604
|
};
|
|
514
605
|
function expandTable(table) {
|
|
@@ -540,13 +631,88 @@ function patternKey(atom) {
|
|
|
540
631
|
);
|
|
541
632
|
}
|
|
542
633
|
|
|
634
|
+
// src/solver/program-index.ts
|
|
635
|
+
var ProgramIndex = class {
|
|
636
|
+
literals = /* @__PURE__ */ new Map();
|
|
637
|
+
rules = /* @__PURE__ */ new Map();
|
|
638
|
+
edges;
|
|
639
|
+
reachable = /* @__PURE__ */ new Map();
|
|
640
|
+
predicateIds = /* @__PURE__ */ new Map();
|
|
641
|
+
constructor(literals, rules) {
|
|
642
|
+
for (const literal of literals) {
|
|
643
|
+
const predicate = literal.atom.predicate;
|
|
644
|
+
const group = this.literals.get(predicate);
|
|
645
|
+
if (group === void 0) {
|
|
646
|
+
this.literals.set(predicate, [literal]);
|
|
647
|
+
} else {
|
|
648
|
+
group.push(literal);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
for (const rule of rules) {
|
|
652
|
+
const predicate = rule.head.atom.predicate;
|
|
653
|
+
const group = this.rules.get(predicate);
|
|
654
|
+
if (group === void 0) {
|
|
655
|
+
this.rules.set(predicate, [rule]);
|
|
656
|
+
} else {
|
|
657
|
+
group.push(rule);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
this.edges = rules.flatMap(ruleEdges);
|
|
661
|
+
}
|
|
662
|
+
literalsFor(predicate) {
|
|
663
|
+
return this.literals.get(predicate) ?? [];
|
|
664
|
+
}
|
|
665
|
+
rulesFor(predicate) {
|
|
666
|
+
return this.rules.get(predicate) ?? [];
|
|
667
|
+
}
|
|
668
|
+
/** Atom keys must distinguish declarations even when they share a display name. */
|
|
669
|
+
predicateId(predicate) {
|
|
670
|
+
let id = this.predicateIds.get(predicate);
|
|
671
|
+
if (id === void 0) {
|
|
672
|
+
id = this.predicateIds.size;
|
|
673
|
+
this.predicateIds.set(predicate, id);
|
|
674
|
+
}
|
|
675
|
+
return id;
|
|
676
|
+
}
|
|
677
|
+
reaches(predicate) {
|
|
678
|
+
let reachable = this.reachable.get(predicate);
|
|
679
|
+
if (reachable === void 0) {
|
|
680
|
+
reachable = reachablePredicates(predicate, this.edges);
|
|
681
|
+
this.reachable.set(predicate, reachable);
|
|
682
|
+
}
|
|
683
|
+
return reachable;
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
|
|
543
687
|
// src/solver/solve-all.ts
|
|
544
688
|
var UNBOUND = /* @__PURE__ */ Symbol("factbag.unbound");
|
|
689
|
+
function tabledComponents(literals, rules) {
|
|
690
|
+
const edges = rules.flatMap(ruleEdges);
|
|
691
|
+
const components = stronglyConnectedComponents(edges);
|
|
692
|
+
const selfRecursive = new Set(
|
|
693
|
+
edges.filter((edge) => edge.from === edge.to).map((edge) => edge.from)
|
|
694
|
+
);
|
|
695
|
+
const stated = new Set(literals.map((literal) => literal.atom.predicate));
|
|
696
|
+
const tabled = /* @__PURE__ */ new Map();
|
|
697
|
+
for (const component of new Set(components.values())) {
|
|
698
|
+
const members = [...component];
|
|
699
|
+
if (members.some((member) => member.world !== "closed" || stated.has(member)) || !(component.size > 1 || members.some((member) => selfRecursive.has(member)))) {
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
for (const member of members) {
|
|
703
|
+
tabled.set(member, component);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
return tabled;
|
|
707
|
+
}
|
|
545
708
|
function solveQueryAll(literals, rules, atom) {
|
|
546
709
|
validateQuery(literals, rules, atom);
|
|
547
|
-
const result = solveQueryAllInternal(literals, rules, atom, /* @__PURE__ */ new
|
|
710
|
+
const result = solveQueryAllInternal(literals, rules, atom, /* @__PURE__ */ new Map(), {
|
|
548
711
|
candidates: new CandidateSearch(rules),
|
|
549
|
-
judgments: /* @__PURE__ */ new Map()
|
|
712
|
+
judgments: /* @__PURE__ */ new Map(),
|
|
713
|
+
tabled: tabledComponents(literals, rules),
|
|
714
|
+
tables: /* @__PURE__ */ new Map(),
|
|
715
|
+
index: new ProgramIndex(literals, rules)
|
|
550
716
|
});
|
|
551
717
|
const stack = [result];
|
|
552
718
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -584,7 +750,11 @@ function solveQueryAllInternal(literals, rules, atom, active, search) {
|
|
|
584
750
|
);
|
|
585
751
|
}
|
|
586
752
|
function solveGroundAtom(literals, rules, atom, active, search) {
|
|
587
|
-
if (search.
|
|
753
|
+
if (search.tabled.has(atom.predicate)) {
|
|
754
|
+
return solveTabledAtom(literals, rules, atom, active, search);
|
|
755
|
+
}
|
|
756
|
+
const dependencies = search.index.reaches(atom.predicate);
|
|
757
|
+
if (search.candidates.evaluatingAmong(dependencies)) {
|
|
588
758
|
return evaluateGroundAtom(literals, rules, atom, active, search);
|
|
589
759
|
}
|
|
590
760
|
let judgments = search.judgments.get(atom.predicate);
|
|
@@ -592,7 +762,8 @@ function solveGroundAtom(literals, rules, atom, active, search) {
|
|
|
592
762
|
judgments = /* @__PURE__ */ new Map();
|
|
593
763
|
search.judgments.set(atom.predicate, judgments);
|
|
594
764
|
}
|
|
595
|
-
const
|
|
765
|
+
const relevantActive = [...active].filter(([, predicate]) => dependencies.has(predicate)).map(([key2]) => key2);
|
|
766
|
+
const key = JSON.stringify([groundAtomKey(atom, search), relevantActive.sort()]);
|
|
596
767
|
const cached = judgments.get(key);
|
|
597
768
|
if (cached !== void 0 && judgmentMatchesAtom(cached, atom)) {
|
|
598
769
|
return cached;
|
|
@@ -604,12 +775,107 @@ function solveGroundAtom(literals, rules, atom, active, search) {
|
|
|
604
775
|
function judgmentMatchesAtom(judgment, atom) {
|
|
605
776
|
return atomEquals(judgment.goal.atom, atom);
|
|
606
777
|
}
|
|
778
|
+
function solveTabledAtom(literals, rules, atom, active, search) {
|
|
779
|
+
const table = tableFor(search, atom.predicate);
|
|
780
|
+
const key = groundAtomKey(atom, search);
|
|
781
|
+
const enter = () => {
|
|
782
|
+
const absent = {
|
|
783
|
+
value: "false",
|
|
784
|
+
goal: { kind: "atom", atom },
|
|
785
|
+
bindings: [],
|
|
786
|
+
negative: [{ source: "closed-world-absence" }]
|
|
787
|
+
};
|
|
788
|
+
table.judgments.set(key, absent);
|
|
789
|
+
table.atoms.set(key, atom);
|
|
790
|
+
table.queue.push(key);
|
|
791
|
+
table.queued.add(key);
|
|
792
|
+
return absent;
|
|
793
|
+
};
|
|
794
|
+
if (table.running) {
|
|
795
|
+
const judgment = table.judgments.get(key) ?? enter();
|
|
796
|
+
if (table.evaluating !== void 0) {
|
|
797
|
+
let dependents = table.dependents.get(key);
|
|
798
|
+
if (dependents === void 0) {
|
|
799
|
+
dependents = /* @__PURE__ */ new Set();
|
|
800
|
+
table.dependents.set(key, dependents);
|
|
801
|
+
}
|
|
802
|
+
dependents.add(table.evaluating);
|
|
803
|
+
}
|
|
804
|
+
assert(judgmentMatchesAtom(judgment, atom), "Tabled judgment must match its atom");
|
|
805
|
+
return judgment;
|
|
806
|
+
}
|
|
807
|
+
const settled = table.judgments.get(key);
|
|
808
|
+
if (settled !== void 0) {
|
|
809
|
+
assert(judgmentMatchesAtom(settled, atom), "Tabled judgment must match its atom");
|
|
810
|
+
return settled;
|
|
811
|
+
}
|
|
812
|
+
enter();
|
|
813
|
+
table.running = true;
|
|
814
|
+
try {
|
|
815
|
+
for (let offset = 0; offset < table.queue.length; offset += 1) {
|
|
816
|
+
const next = table.queue[offset];
|
|
817
|
+
assert(next !== void 0, "Queued atom must have a key");
|
|
818
|
+
table.queued.delete(next);
|
|
819
|
+
const current = table.judgments.get(next);
|
|
820
|
+
const subject = table.atoms.get(next);
|
|
821
|
+
assert(current !== void 0 && subject !== void 0, "Queued atom must be in the table");
|
|
822
|
+
table.evaluating = next;
|
|
823
|
+
const judged = evaluateGroundAtom(literals, rules, subject, active, search);
|
|
824
|
+
table.evaluating = void 0;
|
|
825
|
+
table.judgments.set(next, judged);
|
|
826
|
+
if (judged.value === current.value) {
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
829
|
+
assert(
|
|
830
|
+
maxGoalValue(current.value, judged.value) === judged.value,
|
|
831
|
+
"Tabled recursion must be monotone"
|
|
832
|
+
);
|
|
833
|
+
const changes = (table.changes.get(next) ?? 0) + 1;
|
|
834
|
+
assert(changes <= 3, "Tabled recursion did not converge");
|
|
835
|
+
table.changes.set(next, changes);
|
|
836
|
+
for (const dependent of table.dependents.get(next) ?? []) {
|
|
837
|
+
if (!table.queued.has(dependent)) {
|
|
838
|
+
table.queue.push(dependent);
|
|
839
|
+
table.queued.add(dependent);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
table.queue.length = 0;
|
|
844
|
+
} finally {
|
|
845
|
+
table.running = false;
|
|
846
|
+
table.evaluating = void 0;
|
|
847
|
+
}
|
|
848
|
+
const result = table.judgments.get(key);
|
|
849
|
+
assert(result !== void 0 && judgmentMatchesAtom(result, atom), "Run must judge its seed");
|
|
850
|
+
return result;
|
|
851
|
+
}
|
|
852
|
+
function tableFor(search, predicate) {
|
|
853
|
+
let table = search.tables.get(predicate);
|
|
854
|
+
if (table === void 0) {
|
|
855
|
+
const component = search.tabled.get(predicate);
|
|
856
|
+
assert(component !== void 0, "Only a tabled predicate has a table");
|
|
857
|
+
table = {
|
|
858
|
+
judgments: /* @__PURE__ */ new Map(),
|
|
859
|
+
atoms: /* @__PURE__ */ new Map(),
|
|
860
|
+
dependents: /* @__PURE__ */ new Map(),
|
|
861
|
+
changes: /* @__PURE__ */ new Map(),
|
|
862
|
+
queue: [],
|
|
863
|
+
queued: /* @__PURE__ */ new Set(),
|
|
864
|
+
running: false,
|
|
865
|
+
evaluating: void 0
|
|
866
|
+
};
|
|
867
|
+
for (const member of component) {
|
|
868
|
+
search.tables.set(member, table);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
return table;
|
|
872
|
+
}
|
|
607
873
|
function evaluateGroundAtom(literals, rules, atom, active, search) {
|
|
608
874
|
const goal = { kind: "atom", atom };
|
|
609
875
|
const bindings = [];
|
|
610
|
-
const atomKey = groundAtomKey(atom);
|
|
611
|
-
const positiveLiterals = matchingLiterals(
|
|
612
|
-
const negativeLiterals = matchingLiterals(
|
|
876
|
+
const atomKey = groundAtomKey(atom, search);
|
|
877
|
+
const positiveLiterals = matchingLiterals(search.index.literalsFor(atom.predicate), atom, "+");
|
|
878
|
+
const negativeLiterals = matchingLiterals(search.index.literalsFor(atom.predicate), atom, "-");
|
|
613
879
|
const values = [];
|
|
614
880
|
const positive = [];
|
|
615
881
|
const negative = [];
|
|
@@ -622,9 +888,9 @@ function evaluateGroundAtom(literals, rules, atom, active, search) {
|
|
|
622
888
|
values.push("false");
|
|
623
889
|
}
|
|
624
890
|
if (!active.has(atomKey)) {
|
|
625
|
-
const nextActive = new
|
|
626
|
-
nextActive.
|
|
627
|
-
for (const rule of
|
|
891
|
+
const nextActive = new Map(active);
|
|
892
|
+
nextActive.set(atomKey, atom.predicate);
|
|
893
|
+
for (const rule of search.index.rulesFor(atom.predicate)) {
|
|
628
894
|
const match = matchRuleForAtom(rule, atom);
|
|
629
895
|
if (match === void 0) {
|
|
630
896
|
continue;
|
|
@@ -760,7 +1026,10 @@ function evaluateRuleBodyMany(literals, rules, body, bindings, active, context,
|
|
|
760
1026
|
];
|
|
761
1027
|
}
|
|
762
1028
|
const atom = groundAtom(bodyAtom, bindings);
|
|
763
|
-
assert(
|
|
1029
|
+
assert(
|
|
1030
|
+
!active.has(groundAtomKey(atom, context.search)),
|
|
1031
|
+
"Validated query must not recurse through negation"
|
|
1032
|
+
);
|
|
764
1033
|
const child = solveGroundAtom(literals, rules, atom, active, context.search);
|
|
765
1034
|
const goal = { kind: "not", goal: { kind: "atom", atom } };
|
|
766
1035
|
const value = atom.predicate.world === "closed" ? negateAsFailureGoalValue(child.value) : negateGoalValue(child.value);
|
|
@@ -1070,8 +1339,15 @@ function evaluateAtomBody(literals, rules, atom, bindings, active, context, path
|
|
|
1070
1339
|
function evaluateComparisonBody(kind, args, bindings, context, pathCanHaveUnavailableBindings) {
|
|
1071
1340
|
const left = evaluateExpression(args[0], bindings, context);
|
|
1072
1341
|
const right = evaluateExpression(args[1], left.bindings, context);
|
|
1073
|
-
|
|
1074
|
-
assert(
|
|
1342
|
+
const allowsEntities = kind === "eq" || kind === "neq";
|
|
1343
|
+
assert(
|
|
1344
|
+
isScalarGoalExpression(left.expression) || allowsEntities && isEntityValue(left.expression),
|
|
1345
|
+
"Left comparison operand must be scalar, or entity for equality"
|
|
1346
|
+
);
|
|
1347
|
+
assert(
|
|
1348
|
+
isScalarGoalExpression(right.expression) || allowsEntities && isEntityValue(right.expression),
|
|
1349
|
+
"Right comparison operand must be scalar, or entity for equality"
|
|
1350
|
+
);
|
|
1075
1351
|
const nextBindings = right.bindings;
|
|
1076
1352
|
const judgmentBindings = bindingsArray(nextBindings);
|
|
1077
1353
|
const goal = {
|
|
@@ -1645,7 +1921,7 @@ function primitiveRangeForExact(value) {
|
|
|
1645
1921
|
return { range: { lower: value, upper: value } };
|
|
1646
1922
|
}
|
|
1647
1923
|
function isArithmeticFormula(value) {
|
|
1648
|
-
return isFormula(value) &&
|
|
1924
|
+
return isFormula(value) && ARITHMETIC_KINDS.some((kind) => kind === value.kind) && value.args.length === 2;
|
|
1649
1925
|
}
|
|
1650
1926
|
function isComparisonFormula(value) {
|
|
1651
1927
|
return isFormula(value) && ["eq", "neq", "lt", "lte", "gt", "gte"].includes(value.kind) && value.args.length === 2;
|
|
@@ -1663,18 +1939,17 @@ function calculateValue(kind, left, right, goal) {
|
|
|
1663
1939
|
if (left === UNBOUND || right === UNBOUND) {
|
|
1664
1940
|
return UNBOUND;
|
|
1665
1941
|
}
|
|
1666
|
-
if (left instanceof Date &&
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
case "div":
|
|
1673
|
-
break;
|
|
1674
|
-
default:
|
|
1675
|
-
return assertNever(kind, "Unhandled arithmetic kind");
|
|
1942
|
+
if (left instanceof Date && right instanceof Date) {
|
|
1943
|
+
if (kind === "daysBetween") {
|
|
1944
|
+
return daysBetween(left, right);
|
|
1945
|
+
}
|
|
1946
|
+
if (kind === "yearsBetween") {
|
|
1947
|
+
return yearsBetween(left, right);
|
|
1676
1948
|
}
|
|
1677
1949
|
}
|
|
1950
|
+
if (left instanceof Date && typeof right === "number" && kind === "sub") {
|
|
1951
|
+
return new Date(left.getTime() - right);
|
|
1952
|
+
}
|
|
1678
1953
|
if (typeof left === "number" && typeof right === "number") {
|
|
1679
1954
|
switch (kind) {
|
|
1680
1955
|
case "add":
|
|
@@ -1685,12 +1960,67 @@ function calculateValue(kind, left, right, goal) {
|
|
|
1685
1960
|
return left * right;
|
|
1686
1961
|
case "div":
|
|
1687
1962
|
return left / right;
|
|
1963
|
+
case "daysBetween":
|
|
1964
|
+
case "yearsBetween":
|
|
1965
|
+
break;
|
|
1966
|
+
case "min":
|
|
1967
|
+
return Math.min(left, right);
|
|
1968
|
+
case "max":
|
|
1969
|
+
return Math.max(left, right);
|
|
1970
|
+
case "roundTo":
|
|
1971
|
+
case "floorTo":
|
|
1972
|
+
case "ceilTo":
|
|
1973
|
+
if (!Number.isInteger(right)) {
|
|
1974
|
+
throw new PrimitiveArithmeticProgrammerError(kind, [left, right], goal);
|
|
1975
|
+
}
|
|
1976
|
+
return roundAtPlaces(left, right, kind);
|
|
1688
1977
|
default:
|
|
1689
1978
|
return assertNever(kind, "Unhandled arithmetic kind");
|
|
1690
1979
|
}
|
|
1691
1980
|
}
|
|
1692
1981
|
throw new PrimitiveArithmeticProgrammerError(kind, [left, right], goal);
|
|
1693
1982
|
}
|
|
1983
|
+
function shiftDecimal(value, places) {
|
|
1984
|
+
if (value === 0 || !Number.isFinite(value)) {
|
|
1985
|
+
return value;
|
|
1986
|
+
}
|
|
1987
|
+
const [coefficient, exponent = "0"] = String(value).split("e");
|
|
1988
|
+
const shiftedExponent = Number(exponent) + places;
|
|
1989
|
+
if (shiftedExponent > 308) {
|
|
1990
|
+
return Math.sign(value) * Infinity;
|
|
1991
|
+
}
|
|
1992
|
+
if (shiftedExponent < -324) {
|
|
1993
|
+
return Math.sign(value) * 0;
|
|
1994
|
+
}
|
|
1995
|
+
return Number(`${coefficient}e${shiftedExponent}`);
|
|
1996
|
+
}
|
|
1997
|
+
function roundAtPlaces(value, places, kind) {
|
|
1998
|
+
if (value === 0 || !Number.isFinite(value)) {
|
|
1999
|
+
return value;
|
|
2000
|
+
}
|
|
2001
|
+
let shifted = shiftDecimal(value, places);
|
|
2002
|
+
if (!Number.isFinite(shifted)) {
|
|
2003
|
+
return value;
|
|
2004
|
+
}
|
|
2005
|
+
if (shifted === 0) {
|
|
2006
|
+
shifted = Math.sign(value) * Number.MIN_VALUE;
|
|
2007
|
+
}
|
|
2008
|
+
let rounded;
|
|
2009
|
+
switch (kind) {
|
|
2010
|
+
case "roundTo":
|
|
2011
|
+
rounded = Math.sign(shifted) * Math.round(Math.abs(shifted));
|
|
2012
|
+
break;
|
|
2013
|
+
case "floorTo":
|
|
2014
|
+
rounded = Math.floor(shifted);
|
|
2015
|
+
break;
|
|
2016
|
+
case "ceilTo":
|
|
2017
|
+
rounded = Math.ceil(shifted);
|
|
2018
|
+
break;
|
|
2019
|
+
default:
|
|
2020
|
+
return assertNever(kind, "Unhandled rounding kind");
|
|
2021
|
+
}
|
|
2022
|
+
return shiftDecimal(rounded, -places);
|
|
2023
|
+
}
|
|
1694
2024
|
function compareValues(kind, left, right) {
|
|
1695
2025
|
assert(left !== UNBOUND && right !== UNBOUND, "Comparison operands must be bound");
|
|
1696
2026
|
if (isNumericRangeValue(left) && typeof right === "number") {
|
|
@@ -1703,9 +2033,9 @@ function compareValues(kind, left, right) {
|
|
|
1703
2033
|
const rightComparable = right instanceof Date ? right.getTime() : right;
|
|
1704
2034
|
switch (kind) {
|
|
1705
2035
|
case "eq":
|
|
1706
|
-
return leftComparable
|
|
2036
|
+
return sameValue(leftComparable, rightComparable) ? "true" : "false";
|
|
1707
2037
|
case "neq":
|
|
1708
|
-
return leftComparable
|
|
2038
|
+
return sameValue(leftComparable, rightComparable) ? "false" : "true";
|
|
1709
2039
|
case "lt":
|
|
1710
2040
|
case "lte":
|
|
1711
2041
|
case "gt":
|
|
@@ -1715,6 +2045,15 @@ function compareValues(kind, left, right) {
|
|
|
1715
2045
|
return assertNever(kind, "Unhandled comparison kind");
|
|
1716
2046
|
}
|
|
1717
2047
|
}
|
|
2048
|
+
function sameValue(left, right) {
|
|
2049
|
+
if (left === right) {
|
|
2050
|
+
return true;
|
|
2051
|
+
}
|
|
2052
|
+
if (isEntityValue(left) && isEntityValue(right)) {
|
|
2053
|
+
return JSON.stringify(fieldIdentityToken(left)) === JSON.stringify(fieldIdentityToken(right));
|
|
2054
|
+
}
|
|
2055
|
+
return false;
|
|
2056
|
+
}
|
|
1718
2057
|
function compareOrderedValues(kind, left, right) {
|
|
1719
2058
|
assert(typeof left === "number" && typeof right === "number", "Values must be comparable");
|
|
1720
2059
|
switch (kind) {
|
|
@@ -1918,14 +2257,14 @@ function uniqueBindings(bindings) {
|
|
|
1918
2257
|
function canGroundAtom(atom, bindings) {
|
|
1919
2258
|
return Object.values(atom.fields).every((value) => !isVar(value) || bindings.has(value));
|
|
1920
2259
|
}
|
|
1921
|
-
function groundAtomKey(atom) {
|
|
2260
|
+
function groundAtomKey(atom, search) {
|
|
1922
2261
|
const fields = atom.predicate.paramNames.map((name) => fieldIdentityToken(atom.fields[name]));
|
|
1923
|
-
return `${atom.predicate
|
|
2262
|
+
return `${search.index.predicateId(atom.predicate)}:${JSON.stringify(fields)}`;
|
|
1924
2263
|
}
|
|
1925
2264
|
function candidateGroundAtoms(literals, rules, atom, active, search, pathCanHaveUnavailableBindings = false) {
|
|
1926
2265
|
return search.candidates.candidates(
|
|
1927
2266
|
atom,
|
|
1928
|
-
matchingGroundAtoms(
|
|
2267
|
+
matchingGroundAtoms(search.index.literalsFor(atom.predicate), atom),
|
|
1929
2268
|
() => deriveCandidateAtoms(literals, rules, atom, active, search, pathCanHaveUnavailableBindings)
|
|
1930
2269
|
).filter(
|
|
1931
2270
|
(candidate) => isAtomForPredicate(candidate, atom.predicate)
|
|
@@ -1933,7 +2272,7 @@ function candidateGroundAtoms(literals, rules, atom, active, search, pathCanHave
|
|
|
1933
2272
|
}
|
|
1934
2273
|
function deriveCandidateAtoms(literals, rules, atom, active, search, pathCanHaveUnavailableBindings = false) {
|
|
1935
2274
|
const atoms = [];
|
|
1936
|
-
for (const rule of
|
|
2275
|
+
for (const rule of search.index.rulesFor(atom.predicate)) {
|
|
1937
2276
|
const ruleHead = atomForPredicate(rule.head.atom, atom.predicate);
|
|
1938
2277
|
if (ruleHead === void 0) {
|
|
1939
2278
|
continue;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Signed UTC calendar days from `earlier` to `later`, ignoring time of day. */
|
|
2
|
+
export declare function daysBetween(later: Date, earlier: Date): number;
|
|
3
|
+
/** Whole UTC anniversary years, with February 29 anniversaries on March 1 in common years. */
|
|
4
|
+
export declare function yearsBetween(later: Date, earlier: Date): number;
|