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 CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.5.4",
6
+ "version": "1.5.5",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
@@ -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 { ATOM, COMPOUND, VAR } from './term.js';
8
+ import { VAR } from './term.js';
9
9
  import { numberValueKey } from './number-value.js';
10
-
11
- const EMPTY_ARRAY = Object.freeze([]);
12
-
13
- function predicateKey(module, name, arity) {
14
- return `${module ?? 'user'}:${name}/${arity}`;
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, groupByKey, relations, rules, triggers };
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
- function predicateKey(module, name, arity) {
17
- return `${module ?? 'user'}:${name}/${arity}`;
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 dependencyCone(program, rootGroup) {
113
- const groups = [];
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);