factbag 0.0.0-alpha.0 → 0.0.0-alpha.1
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 +7 -0
- package/README.md +2 -2
- package/dist/core/query/index.js +170 -54
- package/dist/core/solver/query-validation/utils.d.ts +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# factbag
|
|
2
2
|
|
|
3
|
+
## 0.0.0-alpha.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- ebc4a8a: A `letIn` or aggregate whose variable is already bound now constrains that variable instead of rebinding it. Before, a rule such as `tax(P, R) iff letIn(R, ...)` concluded `tax(alice, 99)` as true when its body computed 98, and an `or` branch evaluated under another branch's bindings reported those bindings as true. Proofs for such queries now carry a temporary variable and an `eq` comparison.
|
|
8
|
+
- eca65c2: An `or` is now judged per binding environment, so a branch that fails without binding a variable can no longer borrow the truth of a sibling branch whose bindings were discarded. Before, `and(or(p(X), r()), s(X))` could be judged true when `p(x1)` and `s(x3)` held but nothing satisfied both: the false `r()` branch produced an environment without `X`, the `or` was judged true there via `p(x1)`, and `s(X)` then ranged over every candidate. The recursion validator's edge collector also now carries variables bound in every `or` branch forward, matching the variable-use checker, so a comparison after such an `or` is no longer reported as unconstructive recursion.
|
|
9
|
+
|
|
3
10
|
## 0.0.0-alpha.0
|
|
4
11
|
|
|
5
12
|
Initial experimental distribution of core, corpus, guards, and Jev functionality.
|
package/README.md
CHANGED
|
@@ -7,8 +7,8 @@ sources and rules behind a conclusion.
|
|
|
7
7
|
Factbag includes a document corpus, permission controls, and an integration with
|
|
8
8
|
TypeSafe AI’s Jev model.
|
|
9
9
|
|
|
10
|
-
Read [Introducing Factbag](https://
|
|
11
|
-
why to use it, or [Jev and the Art of Truth Maintenance](https://
|
|
10
|
+
Read [Introducing Factbag](https://daniel.du-pless.is/introducing-factbag) for how and
|
|
11
|
+
why to use it, or [Jev and the Art of Truth Maintenance](https://daniel.du-pless.is/jev-and-the-art-of-truth-maintenance)
|
|
12
12
|
for the thinking behind it.
|
|
13
13
|
|
|
14
14
|
## Install
|
package/dist/core/query/index.js
CHANGED
|
@@ -79,6 +79,15 @@ function uniqueVariables(variables) {
|
|
|
79
79
|
}
|
|
80
80
|
return unique;
|
|
81
81
|
}
|
|
82
|
+
function bindingsAfterOr(branchBindings, bound) {
|
|
83
|
+
const next = new Set(bound);
|
|
84
|
+
for (const candidate of uniqueVariables(branchBindings.flatMap((branch) => [...branch]))) {
|
|
85
|
+
if (branchBindings.every((branch) => branch.has(candidate))) {
|
|
86
|
+
next.add(candidate);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return next;
|
|
90
|
+
}
|
|
82
91
|
function variablesIn(value) {
|
|
83
92
|
if (isVar(value)) {
|
|
84
93
|
return [value];
|
|
@@ -95,7 +104,7 @@ function collectBodyEdges(body, headPredicate, bound, edges) {
|
|
|
95
104
|
if (isAtomLike(body)) {
|
|
96
105
|
edges.push({ from: headPredicate, to: body.predicate, kind: "positive" });
|
|
97
106
|
bindVariables(body, bound);
|
|
98
|
-
return;
|
|
107
|
+
return bound;
|
|
99
108
|
}
|
|
100
109
|
if (isNegativeLiteral(body)) {
|
|
101
110
|
edges.push({
|
|
@@ -103,20 +112,26 @@ function collectBodyEdges(body, headPredicate, bound, edges) {
|
|
|
103
112
|
to: body.atom.predicate,
|
|
104
113
|
kind: body.atom.predicate.world === "closed" ? "closed-world-not" : "explicit-not"
|
|
105
114
|
});
|
|
106
|
-
|
|
107
|
-
return;
|
|
115
|
+
return bound;
|
|
108
116
|
}
|
|
109
117
|
if (isFormula(body, "and")) {
|
|
118
|
+
let nextBound = bound;
|
|
110
119
|
for (const arg of body.args) {
|
|
111
|
-
collectBodyEdges(arg, headPredicate,
|
|
120
|
+
nextBound = collectBodyEdges(arg, headPredicate, nextBound, edges);
|
|
112
121
|
}
|
|
113
|
-
return;
|
|
122
|
+
return nextBound;
|
|
123
|
+
}
|
|
124
|
+
if (isFormula(body, "or")) {
|
|
125
|
+
const branchBindings = body.args.map(
|
|
126
|
+
(arg) => collectBodyEdges(arg, headPredicate, new Set(bound), edges)
|
|
127
|
+
);
|
|
128
|
+
return bindingsAfterOr(branchBindings, bound);
|
|
114
129
|
}
|
|
115
|
-
if (isFormula(body, "
|
|
130
|
+
if (isFormula(body, "exists")) {
|
|
116
131
|
for (const arg of body.args) {
|
|
117
132
|
collectBodyEdges(arg, headPredicate, new Set(bound), edges);
|
|
118
133
|
}
|
|
119
|
-
return;
|
|
134
|
+
return bound;
|
|
120
135
|
}
|
|
121
136
|
if (isFormula(body, "letIn")) {
|
|
122
137
|
const [variable, value, nextBody] = body.args;
|
|
@@ -128,12 +143,12 @@ function collectBodyEdges(body, headPredicate, bound, edges) {
|
|
|
128
143
|
if (isVar(variable)) {
|
|
129
144
|
bound.add(variable);
|
|
130
145
|
}
|
|
131
|
-
collectBodyEdges(nextBody, headPredicate, bound, edges);
|
|
132
|
-
return;
|
|
146
|
+
return collectBodyEdges(nextBody, headPredicate, bound, edges);
|
|
133
147
|
}
|
|
134
148
|
if (isPrimitiveFormula(body) && hasUnboundVariable(body, bound)) {
|
|
135
149
|
edges.push({ from: headPredicate, to: headPredicate, kind: "primitive-ungrounded" });
|
|
136
150
|
}
|
|
151
|
+
return bound;
|
|
137
152
|
}
|
|
138
153
|
function collectAggregateEdges(aggregate, headPredicate, bound, edges) {
|
|
139
154
|
for (const arg of aggregate.args.slice(1)) {
|
|
@@ -252,7 +267,10 @@ function assertSafeBody(body, bound) {
|
|
|
252
267
|
return nextBound;
|
|
253
268
|
}
|
|
254
269
|
if (isFormula(body, "or")) {
|
|
255
|
-
return
|
|
270
|
+
return bindingsAfterOr(
|
|
271
|
+
body.args.map((arg) => assertSafeBody(arg, new Set(bound))),
|
|
272
|
+
bound
|
|
273
|
+
);
|
|
256
274
|
}
|
|
257
275
|
if (isFormula(body, "exists")) {
|
|
258
276
|
const nextBody = body.args.at(-1);
|
|
@@ -288,16 +306,6 @@ function assertSafeAggregate(aggregate, bound) {
|
|
|
288
306
|
assertSafeBody(options.completeWhen, new Set(bound));
|
|
289
307
|
}
|
|
290
308
|
}
|
|
291
|
-
function bindingsAvailableAfterOr(args, bound) {
|
|
292
|
-
const branchBindings = args.map((arg) => assertSafeBody(arg, new Set(bound)));
|
|
293
|
-
const next = new Set(bound);
|
|
294
|
-
for (const candidate of uniqueVariables(branchBindings.flatMap((branch) => [...branch]))) {
|
|
295
|
-
if (branchBindings.length > 0 && branchBindings.every((branch) => branch.has(candidate))) {
|
|
296
|
-
next.add(candidate);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
return next;
|
|
300
|
-
}
|
|
301
309
|
function assertVariablesBound(variables, bound) {
|
|
302
310
|
const unbound = uniqueVariables(variables).filter((variable) => !bound.has(variable));
|
|
303
311
|
if (unbound.length > 0) {
|
|
@@ -727,7 +735,15 @@ function assertNever(value, message) {
|
|
|
727
735
|
}
|
|
728
736
|
function evaluateRuleBodyMany(literals, rules, body, bindings, active, context, pathCanHaveUnavailableBindings = false) {
|
|
729
737
|
if (isAtomLike(body)) {
|
|
730
|
-
return evaluateAtomBody(
|
|
738
|
+
return evaluateAtomBody(
|
|
739
|
+
literals,
|
|
740
|
+
rules,
|
|
741
|
+
erasedAtom(body),
|
|
742
|
+
bindings,
|
|
743
|
+
active,
|
|
744
|
+
context,
|
|
745
|
+
pathCanHaveUnavailableBindings
|
|
746
|
+
);
|
|
731
747
|
}
|
|
732
748
|
if (isLiteralLike(body) && body.sign === "-") {
|
|
733
749
|
const bodyAtom = erasedAtom(body.atom);
|
|
@@ -907,9 +923,13 @@ function evaluateOrBody(literals, rules, args, bindings, active, context, pathCa
|
|
|
907
923
|
)
|
|
908
924
|
);
|
|
909
925
|
const environments = candidateBindings.length === 0 ? [bindings] : candidateBindings;
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
926
|
+
const queue = [...environments];
|
|
927
|
+
const seen = new Set(queue.map(bindingsKey));
|
|
928
|
+
const results = [];
|
|
929
|
+
for (let next = queue.shift(); next !== void 0; next = queue.shift()) {
|
|
930
|
+
const environment = next;
|
|
931
|
+
const judgments = args.flatMap((arg) => {
|
|
932
|
+
const branch = evaluateRuleBodyMany(
|
|
913
933
|
literals,
|
|
914
934
|
rules,
|
|
915
935
|
arg,
|
|
@@ -918,15 +938,27 @@ function evaluateOrBody(literals, rules, args, bindings, active, context, pathCa
|
|
|
918
938
|
context,
|
|
919
939
|
pathCanHaveUnavailableBindings
|
|
920
940
|
);
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
941
|
+
for (const result2 of branch) {
|
|
942
|
+
if (bindsBeyond(result2.bindings, environment)) {
|
|
943
|
+
const key = bindingsKey(result2.bindings);
|
|
944
|
+
if (!seen.has(key)) {
|
|
945
|
+
seen.add(key);
|
|
946
|
+
queue.push(result2.bindings);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
const result = bestResult(
|
|
951
|
+
branch.filter((candidate) => !bindsBeyond(candidate.bindings, environment))
|
|
952
|
+
);
|
|
953
|
+
return result === void 0 ? [] : [result.judgment];
|
|
925
954
|
});
|
|
955
|
+
if (judgments.length === 0) {
|
|
956
|
+
continue;
|
|
957
|
+
}
|
|
926
958
|
const judgmentBindings = bindingsArray(environment);
|
|
927
959
|
const reboundJudgments = judgments.map((judgment) => withBindings(judgment, judgmentBindings));
|
|
928
960
|
const goal = { kind: "or", goals: reboundJudgments.map((judgment) => judgment.goal) };
|
|
929
|
-
|
|
961
|
+
results.push({
|
|
930
962
|
judgment: compoundJudgment(
|
|
931
963
|
goal,
|
|
932
964
|
maxGoalValue(...reboundJudgments.map((judgment) => judgment.value)),
|
|
@@ -934,8 +966,9 @@ function evaluateOrBody(literals, rules, args, bindings, active, context, pathCa
|
|
|
934
966
|
reboundJudgments
|
|
935
967
|
),
|
|
936
968
|
bindings: environment
|
|
937
|
-
};
|
|
938
|
-
}
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
return results;
|
|
939
972
|
}
|
|
940
973
|
function evaluateAndBody(literals, rules, args, bindings, active, context, pathCanHaveUnavailableBindings) {
|
|
941
974
|
let partials = [{ bindings, judgments: [] }];
|
|
@@ -976,7 +1009,7 @@ function evaluateAndBody(literals, rules, args, bindings, active, context, pathC
|
|
|
976
1009
|
};
|
|
977
1010
|
});
|
|
978
1011
|
}
|
|
979
|
-
function evaluateAtomBody(literals, rules, atom, bindings, active, context) {
|
|
1012
|
+
function evaluateAtomBody(literals, rules, atom, bindings, active, context, pathCanHaveUnavailableBindings = false) {
|
|
980
1013
|
const evaluateGrounded = (environment) => {
|
|
981
1014
|
const grounded = groundAtom(atom, environment);
|
|
982
1015
|
const judgmentBindings = bindingsArray(environment);
|
|
@@ -998,7 +1031,14 @@ function evaluateAtomBody(literals, rules, atom, bindings, active, context) {
|
|
|
998
1031
|
])
|
|
999
1032
|
)
|
|
1000
1033
|
};
|
|
1001
|
-
const candidateBindings = candidateGroundAtoms(
|
|
1034
|
+
const candidateBindings = candidateGroundAtoms(
|
|
1035
|
+
literals,
|
|
1036
|
+
rules,
|
|
1037
|
+
pattern,
|
|
1038
|
+
active,
|
|
1039
|
+
context.search,
|
|
1040
|
+
pathCanHaveUnavailableBindings
|
|
1041
|
+
).map((candidate) => extendBindings(atom, candidate, bindings)).filter((candidate) => candidate !== void 0);
|
|
1002
1042
|
if (candidateBindings.length === 0) {
|
|
1003
1043
|
if (atom.predicate.world === "closed") {
|
|
1004
1044
|
assert(!canGroundAtom(atom, bindings), "Ground closed-world absence must use ground solving");
|
|
@@ -1181,7 +1221,7 @@ function evaluateExpression(expression, bindings, context) {
|
|
|
1181
1221
|
const right = evaluateExpression(expression.args[1], left.bindings, context);
|
|
1182
1222
|
assert(isScalarGoalExpression(left.expression), "Left arithmetic operand must be scalar");
|
|
1183
1223
|
assert(isScalarGoalExpression(right.expression), "Right arithmetic operand must be scalar");
|
|
1184
|
-
const variable = new Var(`$
|
|
1224
|
+
const variable = new Var(`${TEMPORARY_PREFIX}${context.nextTemp++}`);
|
|
1185
1225
|
const goal = {
|
|
1186
1226
|
kind: expression.kind,
|
|
1187
1227
|
bind: variable,
|
|
@@ -1220,6 +1260,10 @@ function evaluateExpression(expression, bindings, context) {
|
|
|
1220
1260
|
};
|
|
1221
1261
|
}
|
|
1222
1262
|
function evaluateExpressionInto(variable, expression, bindings, context) {
|
|
1263
|
+
const existing = valueForVariable(bindings, variable);
|
|
1264
|
+
if (existing !== UNBOUND) {
|
|
1265
|
+
return constrainBoundVariable(variable, existing, expression, bindings, context);
|
|
1266
|
+
}
|
|
1223
1267
|
if (isArithmeticFormula(expression)) {
|
|
1224
1268
|
const left = evaluateExpression(expression.args[0], bindings, context);
|
|
1225
1269
|
const right = evaluateExpression(expression.args[1], left.bindings, context);
|
|
@@ -1256,9 +1300,35 @@ function evaluateExpressionInto(variable, expression, bindings, context) {
|
|
|
1256
1300
|
}
|
|
1257
1301
|
const value = isVar(expression) ? valueForVariable(bindings, expression) : definedValue(expression, "Expression values must be defined");
|
|
1258
1302
|
const nextBindings = new Map(bindings);
|
|
1259
|
-
|
|
1303
|
+
if (value !== UNBOUND) {
|
|
1304
|
+
nextBindings.set(variable, value);
|
|
1305
|
+
}
|
|
1260
1306
|
return { value, expression: variable, bindings: nextBindings, judgments: [] };
|
|
1261
1307
|
}
|
|
1308
|
+
function constrainBoundVariable(variable, existing, expression, bindings, context) {
|
|
1309
|
+
const computed = evaluateExpression(expression, bindings, context);
|
|
1310
|
+
assert(
|
|
1311
|
+
isScalarGoalExpression(computed.expression),
|
|
1312
|
+
"A let binding of a bound variable must compare scalar values"
|
|
1313
|
+
);
|
|
1314
|
+
const goal = { kind: "eq", left: variable, right: computed.expression };
|
|
1315
|
+
const value = computed.value === UNBOUND ? "unknown" : compareValues("eq", existing, computed.value);
|
|
1316
|
+
const comparison = primitiveComparisonJudgment(
|
|
1317
|
+
goal,
|
|
1318
|
+
value,
|
|
1319
|
+
bindingsArray(computed.bindings),
|
|
1320
|
+
existing,
|
|
1321
|
+
variable,
|
|
1322
|
+
computed.value,
|
|
1323
|
+
computed.expression
|
|
1324
|
+
);
|
|
1325
|
+
return {
|
|
1326
|
+
value: existing,
|
|
1327
|
+
expression: variable,
|
|
1328
|
+
bindings: computed.bindings,
|
|
1329
|
+
judgments: [...computed.judgments, comparison]
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1262
1332
|
function evaluateAggregateInto(literals, rules, variable, aggregate, bindings, active, context) {
|
|
1263
1333
|
const [valueExpression, whereBody, options] = aggregate.args;
|
|
1264
1334
|
const completeWhen = options?.completeWhen;
|
|
@@ -1282,11 +1352,11 @@ function evaluateAggregateInto(literals, rules, variable, aggregate, bindings, a
|
|
|
1282
1352
|
assert(completeness !== void 0, "Aggregate completeness evaluation produced no best result");
|
|
1283
1353
|
if (completeness.judgment.value !== "true") {
|
|
1284
1354
|
const judgmentBindings = bindingsArray(bindings);
|
|
1285
|
-
let
|
|
1355
|
+
let goal2;
|
|
1286
1356
|
switch (aggregate.kind) {
|
|
1287
1357
|
case "sum":
|
|
1288
1358
|
assert(isNumericGoalExpression(valueExpression), "Sum value must be numeric");
|
|
1289
|
-
|
|
1359
|
+
goal2 = {
|
|
1290
1360
|
kind: "sum",
|
|
1291
1361
|
bind: variable,
|
|
1292
1362
|
value: valueExpression,
|
|
@@ -1295,7 +1365,7 @@ function evaluateAggregateInto(literals, rules, variable, aggregate, bindings, a
|
|
|
1295
1365
|
};
|
|
1296
1366
|
break;
|
|
1297
1367
|
case "count":
|
|
1298
|
-
|
|
1368
|
+
goal2 = {
|
|
1299
1369
|
kind: "count",
|
|
1300
1370
|
bind: variable,
|
|
1301
1371
|
where: whereGoal,
|
|
@@ -1308,12 +1378,14 @@ function evaluateAggregateInto(literals, rules, variable, aggregate, bindings, a
|
|
|
1308
1378
|
return {
|
|
1309
1379
|
kind: "incomplete",
|
|
1310
1380
|
result: {
|
|
1311
|
-
judgment: { value: "unknown", goal, bindings: judgmentBindings },
|
|
1381
|
+
judgment: { value: "unknown", goal: goal2, bindings: judgmentBindings },
|
|
1312
1382
|
bindings
|
|
1313
1383
|
}
|
|
1314
1384
|
};
|
|
1315
1385
|
}
|
|
1316
1386
|
}
|
|
1387
|
+
const existing = valueForVariable(bindings, variable);
|
|
1388
|
+
const target = existing === UNBOUND ? variable : new Var(`$tmp${context.nextTemp++}`);
|
|
1317
1389
|
const nextBindings = new Map(bindings);
|
|
1318
1390
|
const values = rows.map((row) => resolveAggregateValue(valueExpression, row.bindings));
|
|
1319
1391
|
let value;
|
|
@@ -1322,29 +1394,29 @@ function evaluateAggregateInto(literals, rules, variable, aggregate, bindings, a
|
|
|
1322
1394
|
case "sum":
|
|
1323
1395
|
assert(isNumericGoalExpression(valueExpression), "Sum value must be numeric");
|
|
1324
1396
|
value = sumValues(values);
|
|
1325
|
-
nextBindings.set(
|
|
1397
|
+
nextBindings.set(target, value);
|
|
1326
1398
|
judgment = primitiveSumJudgment(
|
|
1327
1399
|
{
|
|
1328
1400
|
kind: "sum",
|
|
1329
|
-
bind:
|
|
1401
|
+
bind: target,
|
|
1330
1402
|
value: valueExpression,
|
|
1331
1403
|
where: whereGoal
|
|
1332
1404
|
},
|
|
1333
1405
|
bindingsArray(nextBindings),
|
|
1334
1406
|
values,
|
|
1335
|
-
|
|
1407
|
+
target,
|
|
1336
1408
|
value
|
|
1337
1409
|
);
|
|
1338
1410
|
break;
|
|
1339
1411
|
case "count": {
|
|
1340
1412
|
const range = countRange(possibleRows, isOpenAggregate(whereBody));
|
|
1341
1413
|
value = exactCountValue(range);
|
|
1342
|
-
nextBindings.set(
|
|
1414
|
+
nextBindings.set(target, value);
|
|
1343
1415
|
judgment = primitiveCountJudgment(
|
|
1344
|
-
{ kind: "count", bind:
|
|
1416
|
+
{ kind: "count", bind: target, where: whereGoal },
|
|
1345
1417
|
bindingsArray(nextBindings),
|
|
1346
1418
|
range,
|
|
1347
|
-
|
|
1419
|
+
target,
|
|
1348
1420
|
value
|
|
1349
1421
|
);
|
|
1350
1422
|
break;
|
|
@@ -1352,9 +1424,30 @@ function evaluateAggregateInto(literals, rules, variable, aggregate, bindings, a
|
|
|
1352
1424
|
default:
|
|
1353
1425
|
return assertNever(aggregate.kind, "Unhandled aggregate kind");
|
|
1354
1426
|
}
|
|
1427
|
+
if (existing === UNBOUND) {
|
|
1428
|
+
return {
|
|
1429
|
+
kind: "complete",
|
|
1430
|
+
expression: { value, expression: variable, bindings: nextBindings, judgments: [judgment] }
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
const goal = { kind: "eq", left: variable, right: target };
|
|
1434
|
+
const comparison = primitiveComparisonJudgment(
|
|
1435
|
+
goal,
|
|
1436
|
+
compareValues("eq", existing, value),
|
|
1437
|
+
bindingsArray(nextBindings),
|
|
1438
|
+
existing,
|
|
1439
|
+
variable,
|
|
1440
|
+
value,
|
|
1441
|
+
target
|
|
1442
|
+
);
|
|
1355
1443
|
return {
|
|
1356
1444
|
kind: "complete",
|
|
1357
|
-
expression: {
|
|
1445
|
+
expression: {
|
|
1446
|
+
value: existing,
|
|
1447
|
+
expression: variable,
|
|
1448
|
+
bindings: nextBindings,
|
|
1449
|
+
judgments: [judgment, comparison]
|
|
1450
|
+
}
|
|
1358
1451
|
};
|
|
1359
1452
|
}
|
|
1360
1453
|
function resolveAggregateValue(expression, bindings) {
|
|
@@ -1810,13 +1903,28 @@ function ruleContribution(kind, value) {
|
|
|
1810
1903
|
function bindingsArray(bindings) {
|
|
1811
1904
|
return [...bindings.entries()];
|
|
1812
1905
|
}
|
|
1906
|
+
var TEMPORARY_PREFIX = "$tmp";
|
|
1907
|
+
function isTemporary(variable) {
|
|
1908
|
+
return variable.name.startsWith(TEMPORARY_PREFIX);
|
|
1909
|
+
}
|
|
1910
|
+
function bindsBeyond(result, environment) {
|
|
1911
|
+
for (const variable of result.keys()) {
|
|
1912
|
+
if (!isTemporary(variable) && !environment.has(variable)) {
|
|
1913
|
+
return true;
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
return false;
|
|
1917
|
+
}
|
|
1918
|
+
function bindingsKey(binding) {
|
|
1919
|
+
return JSON.stringify(
|
|
1920
|
+
bindingsArray(binding).filter(([variable]) => !isTemporary(variable)).map(([variable, value]) => [variable.name, fieldIdentityToken(value)])
|
|
1921
|
+
);
|
|
1922
|
+
}
|
|
1813
1923
|
function uniqueBindings(bindings) {
|
|
1814
1924
|
const seen = /* @__PURE__ */ new Set();
|
|
1815
1925
|
const unique = [];
|
|
1816
1926
|
for (const binding of bindings) {
|
|
1817
|
-
const key =
|
|
1818
|
-
bindingsArray(binding).map(([variable, value]) => [variable.name, fieldIdentityToken(value)])
|
|
1819
|
-
);
|
|
1927
|
+
const key = bindingsKey(binding);
|
|
1820
1928
|
if (!seen.has(key)) {
|
|
1821
1929
|
seen.add(key);
|
|
1822
1930
|
unique.push(binding);
|
|
@@ -1831,16 +1939,16 @@ function groundAtomKey(atom) {
|
|
|
1831
1939
|
const fields = atom.predicate.paramNames.map((name) => fieldIdentityToken(atom.fields[name]));
|
|
1832
1940
|
return `${atom.predicate.name}:${JSON.stringify(fields)}`;
|
|
1833
1941
|
}
|
|
1834
|
-
function candidateGroundAtoms(literals, rules, atom, active, search) {
|
|
1942
|
+
function candidateGroundAtoms(literals, rules, atom, active, search, pathCanHaveUnavailableBindings = false) {
|
|
1835
1943
|
return search.candidates.candidates(
|
|
1836
1944
|
atom,
|
|
1837
1945
|
matchingGroundAtoms(literals, atom),
|
|
1838
|
-
() => deriveCandidateAtoms(literals, rules, atom, active, search)
|
|
1946
|
+
() => deriveCandidateAtoms(literals, rules, atom, active, search, pathCanHaveUnavailableBindings)
|
|
1839
1947
|
).filter(
|
|
1840
1948
|
(candidate) => isAtomForPredicate(candidate, atom.predicate)
|
|
1841
1949
|
);
|
|
1842
1950
|
}
|
|
1843
|
-
function deriveCandidateAtoms(literals, rules, atom, active, search) {
|
|
1951
|
+
function deriveCandidateAtoms(literals, rules, atom, active, search, pathCanHaveUnavailableBindings = false) {
|
|
1844
1952
|
const atoms = [];
|
|
1845
1953
|
for (const rule of rules) {
|
|
1846
1954
|
const ruleHead = atomForPredicate(rule.head.atom, atom.predicate);
|
|
@@ -1852,9 +1960,17 @@ function deriveCandidateAtoms(literals, rules, atom, active, search) {
|
|
|
1852
1960
|
continue;
|
|
1853
1961
|
}
|
|
1854
1962
|
const context = { nextTemp: 0, search, candidatePredicate: atom.predicate };
|
|
1855
|
-
const bodies = evaluateRuleBodyMany(
|
|
1963
|
+
const bodies = evaluateRuleBodyMany(
|
|
1964
|
+
literals,
|
|
1965
|
+
rules,
|
|
1966
|
+
rule.body,
|
|
1967
|
+
bindings,
|
|
1968
|
+
active,
|
|
1969
|
+
context,
|
|
1970
|
+
pathCanHaveUnavailableBindings
|
|
1971
|
+
);
|
|
1856
1972
|
for (const body of bodies) {
|
|
1857
|
-
if (!canGroundAtom(ruleHead, body.bindings)) {
|
|
1973
|
+
if (body.judgment.value === "false" || !canGroundAtom(ruleHead, body.bindings)) {
|
|
1858
1974
|
continue;
|
|
1859
1975
|
}
|
|
1860
1976
|
const candidate = groundAtom(ruleHead, body.bindings);
|
|
@@ -23,5 +23,7 @@ export declare function isCompleteWhenOption(value: unknown): value is Readonly<
|
|
|
23
23
|
export declare function isPrimitiveFormula(value: unknown): boolean;
|
|
24
24
|
export declare function bindVariables(value: unknown, bound: Set<Var>): void;
|
|
25
25
|
export declare function uniqueVariables(variables: readonly Var[]): Var[];
|
|
26
|
+
/** Bindings available after an `or`: those held before it plus those every branch makes. */
|
|
27
|
+
export declare function bindingsAfterOr(branchBindings: readonly ReadonlySet<Var>[], bound: ReadonlySet<Var>): Set<Var>;
|
|
26
28
|
export declare function variablesIn(value: unknown): Var[];
|
|
27
29
|
export {};
|