eyeprolog 1.5.4 → 1.5.5
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/package.json +1 -1
- package/src/datalog-common.js +56 -0
- package/src/datalog.js +10 -65
- package/src/wfs.js +11 -47
package/package.json
CHANGED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Shared plumbing for the finite positive-Datalog and WFS evaluators.
|
|
2
|
+
//
|
|
3
|
+
// Keep evaluator-specific relation storage and fixpoint algorithms in their
|
|
4
|
+
// own modules; this file only contains representation/traversal helpers whose
|
|
5
|
+
// semantics are common to both execution paths.
|
|
6
|
+
|
|
7
|
+
import { ATOM, COMPOUND, VAR } from './term.js';
|
|
8
|
+
|
|
9
|
+
export const EMPTY_ARRAY = Object.freeze([]);
|
|
10
|
+
|
|
11
|
+
export function predicateKey(module, name, arity) {
|
|
12
|
+
return `${module ?? 'user'}:${name}/${arity}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function directLiteral(goal, module) {
|
|
16
|
+
if (goal?.type !== COMPOUND && goal?.type !== ATOM) return null;
|
|
17
|
+
return {
|
|
18
|
+
key: predicateKey(goal.module ?? module, goal.name, goal.arity),
|
|
19
|
+
name: goal.name,
|
|
20
|
+
arity: goal.arity,
|
|
21
|
+
module: goal.module ?? module,
|
|
22
|
+
args: goal.args ?? EMPTY_ARRAY,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function dependencyCone(program, rootGroup, literalForGoal = directLiteral) {
|
|
27
|
+
const groups = [];
|
|
28
|
+
const seen = new Set();
|
|
29
|
+
const stack = [rootGroup];
|
|
30
|
+
while (stack.length > 0) {
|
|
31
|
+
const group = stack.pop();
|
|
32
|
+
const key = predicateKey(group.module, group.name, group.arity);
|
|
33
|
+
if (seen.has(key)) continue;
|
|
34
|
+
seen.add(key);
|
|
35
|
+
groups.push(group);
|
|
36
|
+
for (const clause of group.clauses) {
|
|
37
|
+
for (const goal of clause.body) {
|
|
38
|
+
const literal = literalForGoal(goal, group.module);
|
|
39
|
+
if (!literal) continue;
|
|
40
|
+
const target = program.findGroup(literal.name, literal.arity, literal.module);
|
|
41
|
+
if (target) stack.push(target);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return groups;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function resolvePatternTerm(term, bindings) {
|
|
49
|
+
if (term.type === VAR) return bindings.get(term.name) ?? null;
|
|
50
|
+
return term;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function estimateLiteral(literal, relation, bindings) {
|
|
54
|
+
const candidates = relation.candidateIndexes(literal.args, bindings);
|
|
55
|
+
return candidates == null ? relation.rows.length : candidates.length;
|
|
56
|
+
}
|
package/src/datalog.js
CHANGED
|
@@ -5,14 +5,16 @@
|
|
|
5
5
|
// newly-added row fires only the rule occurrences that mention its predicate.
|
|
6
6
|
// This avoids replaying the whole recursive program on every table round.
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { VAR } from './term.js';
|
|
9
9
|
import { numberValueKey } from './number-value.js';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
import {
|
|
11
|
+
EMPTY_ARRAY,
|
|
12
|
+
dependencyCone,
|
|
13
|
+
directLiteral,
|
|
14
|
+
estimateLiteral,
|
|
15
|
+
predicateKey,
|
|
16
|
+
resolvePatternTerm,
|
|
17
|
+
} from './datalog-common.js';
|
|
16
18
|
|
|
17
19
|
const scalarKeyCache = new WeakMap();
|
|
18
20
|
|
|
@@ -30,11 +32,6 @@ function sameScalar(left, right) {
|
|
|
30
32
|
return scalarKey(left) === scalarKey(right);
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
function tupleKey(tuple) {
|
|
34
|
-
if (tuple.length === 1) return scalarKey(tuple[0]);
|
|
35
|
-
return tuple.map(scalarKey).join('\u0001');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
35
|
export class DatalogRelation {
|
|
39
36
|
constructor(arity) {
|
|
40
37
|
this.arity = arity;
|
|
@@ -117,50 +114,16 @@ export class DatalogRelation {
|
|
|
117
114
|
}
|
|
118
115
|
}
|
|
119
116
|
|
|
120
|
-
function directLiteral(goal, module) {
|
|
121
|
-
if (goal?.type !== COMPOUND && goal?.type !== ATOM) return null;
|
|
122
|
-
return {
|
|
123
|
-
key: predicateKey(goal.module ?? module, goal.name, goal.arity),
|
|
124
|
-
name: goal.name,
|
|
125
|
-
arity: goal.arity,
|
|
126
|
-
module: goal.module ?? module,
|
|
127
|
-
args: goal.args ?? EMPTY_ARRAY,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function dependencyCone(program, rootGroup) {
|
|
132
|
-
const groups = [];
|
|
133
|
-
const seen = new Set();
|
|
134
|
-
const stack = [rootGroup];
|
|
135
|
-
while (stack.length > 0) {
|
|
136
|
-
const group = stack.pop();
|
|
137
|
-
const key = predicateKey(group.module, group.name, group.arity);
|
|
138
|
-
if (seen.has(key)) continue;
|
|
139
|
-
seen.add(key);
|
|
140
|
-
groups.push(group);
|
|
141
|
-
for (const clause of group.clauses) {
|
|
142
|
-
for (const goal of clause.body) {
|
|
143
|
-
const literal = directLiteral(goal, group.module);
|
|
144
|
-
if (!literal) continue;
|
|
145
|
-
const target = program.findGroup(literal.name, literal.arity, literal.module);
|
|
146
|
-
if (target) stack.push(target);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
return groups;
|
|
151
|
-
}
|
|
152
117
|
|
|
153
118
|
function compileProgram(program, rootGroup) {
|
|
154
119
|
const groups = dependencyCone(program, rootGroup);
|
|
155
120
|
const relations = new Map();
|
|
156
|
-
const groupByKey = new Map();
|
|
157
121
|
const rules = [];
|
|
158
122
|
const triggers = new Map();
|
|
159
123
|
|
|
160
124
|
for (const group of groups) {
|
|
161
125
|
const key = predicateKey(group.module, group.name, group.arity);
|
|
162
126
|
relations.set(key, new DatalogRelation(group.arity));
|
|
163
|
-
groupByKey.set(key, group);
|
|
164
127
|
}
|
|
165
128
|
|
|
166
129
|
for (const group of groups) {
|
|
@@ -181,13 +144,9 @@ function compileProgram(program, rootGroup) {
|
|
|
181
144
|
}
|
|
182
145
|
}
|
|
183
146
|
|
|
184
|
-
return { groups,
|
|
147
|
+
return { groups, relations, rules, triggers };
|
|
185
148
|
}
|
|
186
149
|
|
|
187
|
-
function resolvePatternTerm(term, bindings) {
|
|
188
|
-
if (term.type === VAR) return bindings.get(term.name) ?? null;
|
|
189
|
-
return term;
|
|
190
|
-
}
|
|
191
150
|
|
|
192
151
|
function matchTupleMutable(args, tuple, bindings) {
|
|
193
152
|
const added = [];
|
|
@@ -218,10 +177,6 @@ function undoBindings(bindings, added) {
|
|
|
218
177
|
for (let i = added.length - 1; i >= 0; i--) bindings.delete(added[i]);
|
|
219
178
|
}
|
|
220
179
|
|
|
221
|
-
function estimateLiteral(literal, relation, bindings) {
|
|
222
|
-
const candidates = relation.candidateIndexes(literal.args, bindings);
|
|
223
|
-
return candidates == null ? relation.rows.length : candidates.length;
|
|
224
|
-
}
|
|
225
180
|
|
|
226
181
|
function forEachBinding(body, relations, callback, bindings, remaining) {
|
|
227
182
|
if (remaining.length === 0) {
|
|
@@ -271,16 +226,6 @@ function forEachBinding(body, relations, callback, bindings, remaining) {
|
|
|
271
226
|
}
|
|
272
227
|
}
|
|
273
228
|
|
|
274
|
-
function instantiateTuple(args, bindings) {
|
|
275
|
-
const tuple = new Array(args.length);
|
|
276
|
-
for (let i = 0; i < args.length; i++) {
|
|
277
|
-
const arg = args[i];
|
|
278
|
-
const value = arg.type === VAR ? bindings.get(arg.name) : arg;
|
|
279
|
-
if (value == null || value.type === VAR) return null;
|
|
280
|
-
tuple[i] = value;
|
|
281
|
-
}
|
|
282
|
-
return tuple;
|
|
283
|
-
}
|
|
284
229
|
|
|
285
230
|
export function evaluatePositiveDatalog(program, rootGroup) {
|
|
286
231
|
const compiled = compileProgram(program, rootGroup);
|
package/src/wfs.js
CHANGED
|
@@ -12,10 +12,14 @@
|
|
|
12
12
|
|
|
13
13
|
import { ATOM, COMPOUND, VAR } from './term.js';
|
|
14
14
|
import { numberValueKey } from './number-value.js';
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
import {
|
|
16
|
+
EMPTY_ARRAY,
|
|
17
|
+
dependencyCone,
|
|
18
|
+
directLiteral,
|
|
19
|
+
estimateLiteral,
|
|
20
|
+
predicateKey,
|
|
21
|
+
resolvePatternTerm,
|
|
22
|
+
} from './datalog-common.js';
|
|
19
23
|
|
|
20
24
|
function scalarKey(term) {
|
|
21
25
|
if (term.type === 'number') return `number\u0000${numberValueKey(term.name)}`;
|
|
@@ -73,8 +77,6 @@ class Relation {
|
|
|
73
77
|
}
|
|
74
78
|
}
|
|
75
79
|
|
|
76
|
-
const EMPTY_ARRAY = Object.freeze([]);
|
|
77
|
-
|
|
78
80
|
function emptyRelations(groups) {
|
|
79
81
|
const relations = new Map();
|
|
80
82
|
for (const group of groups) {
|
|
@@ -93,48 +95,18 @@ function copyBaseRelations(base, groups) {
|
|
|
93
95
|
return relations;
|
|
94
96
|
}
|
|
95
97
|
|
|
96
|
-
function directLiteral(goal, module) {
|
|
97
|
-
if (goal?.type !== COMPOUND && goal?.type !== ATOM) return null;
|
|
98
|
-
return {
|
|
99
|
-
key: predicateKey(goal.module ?? module, goal.name, goal.arity),
|
|
100
|
-
name: goal.name,
|
|
101
|
-
arity: goal.arity,
|
|
102
|
-
module: goal.module ?? module,
|
|
103
|
-
args: goal.args ?? EMPTY_ARRAY,
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
98
|
|
|
107
99
|
function negativeLiteral(goal, module) {
|
|
108
100
|
if (goal?.type !== COMPOUND || goal.name !== 'tnot' || goal.arity !== 1) return null;
|
|
109
101
|
return directLiteral(goal.args[0], module);
|
|
110
102
|
}
|
|
111
103
|
|
|
112
|
-
function
|
|
113
|
-
|
|
114
|
-
const seen = new Set();
|
|
115
|
-
const stack = [rootGroup];
|
|
116
|
-
while (stack.length > 0) {
|
|
117
|
-
const group = stack.pop();
|
|
118
|
-
const key = predicateKey(group.module, group.name, group.arity);
|
|
119
|
-
if (seen.has(key)) continue;
|
|
120
|
-
seen.add(key);
|
|
121
|
-
groups.push(group);
|
|
122
|
-
|
|
123
|
-
for (const clause of group.clauses) {
|
|
124
|
-
for (const goal of clause.body) {
|
|
125
|
-
const neg = negativeLiteral(goal, group.module);
|
|
126
|
-
const literal = neg ?? directLiteral(goal, group.module);
|
|
127
|
-
if (!literal) continue;
|
|
128
|
-
const target = program.findGroup(literal.name, literal.arity, literal.module);
|
|
129
|
-
if (target) stack.push(target);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return groups;
|
|
104
|
+
function dependencyLiteral(goal, module) {
|
|
105
|
+
return negativeLiteral(goal, module) ?? directLiteral(goal, module);
|
|
134
106
|
}
|
|
135
107
|
|
|
136
108
|
function compileProgram(program, rootGroup) {
|
|
137
|
-
const groups = dependencyCone(program, rootGroup);
|
|
109
|
+
const groups = dependencyCone(program, rootGroup, dependencyLiteral);
|
|
138
110
|
const base = emptyRelations(groups);
|
|
139
111
|
const rules = [];
|
|
140
112
|
|
|
@@ -175,10 +147,6 @@ function compileProgram(program, rootGroup) {
|
|
|
175
147
|
return { groups, base, rules };
|
|
176
148
|
}
|
|
177
149
|
|
|
178
|
-
function resolvePatternTerm(term, bindings) {
|
|
179
|
-
if (term.type === VAR) return bindings.get(term.name) ?? null;
|
|
180
|
-
return term;
|
|
181
|
-
}
|
|
182
150
|
|
|
183
151
|
function matchTuple(args, tuple, bindings) {
|
|
184
152
|
let next = null;
|
|
@@ -199,10 +167,6 @@ function matchTuple(args, tuple, bindings) {
|
|
|
199
167
|
return next ?? bindings;
|
|
200
168
|
}
|
|
201
169
|
|
|
202
|
-
function estimateLiteral(literal, relation, bindings) {
|
|
203
|
-
const candidates = relation.candidateIndexes(literal.args, bindings);
|
|
204
|
-
return candidates == null ? relation.rows.length : candidates.length;
|
|
205
|
-
}
|
|
206
170
|
|
|
207
171
|
function forEachPositiveBinding(positives, relations, callback, bindings = new Map(), remaining = null) {
|
|
208
172
|
if (remaining == null) remaining = positives.map((_, index) => index);
|