@dxos/datalog 0.0.1 → 0.13.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.
Files changed (61) hide show
  1. package/LICENSE +105 -0
  2. package/README.md +58 -1
  3. package/dist/lib/Ast.mjs +72 -0
  4. package/dist/lib/Ast.mjs.map +1 -0
  5. package/dist/lib/Builtin.mjs +37 -0
  6. package/dist/lib/Builtin.mjs.map +1 -0
  7. package/dist/lib/Checker.mjs +3 -0
  8. package/dist/lib/Engine.mjs +3 -0
  9. package/dist/lib/Parser.mjs +2 -0
  10. package/dist/lib/chunk-Checker.mjs +414 -0
  11. package/dist/lib/chunk-Checker.mjs.map +1 -0
  12. package/dist/lib/chunk-Engine.mjs +488 -0
  13. package/dist/lib/chunk-Engine.mjs.map +1 -0
  14. package/dist/lib/chunk-Parser.mjs +366 -0
  15. package/dist/lib/chunk-Parser.mjs.map +1 -0
  16. package/dist/lib/chunk-rolldown-runtime.mjs +13 -0
  17. package/dist/lib/index.mjs +6 -0
  18. package/dist/types/src/Ast.d.ts +81 -0
  19. package/dist/types/src/Ast.d.ts.map +1 -0
  20. package/dist/types/src/Builtin.d.ts +35 -0
  21. package/dist/types/src/Builtin.d.ts.map +1 -0
  22. package/dist/types/src/Checker.d.ts +61 -0
  23. package/dist/types/src/Checker.d.ts.map +1 -0
  24. package/dist/types/src/Checker.test.d.ts +2 -0
  25. package/dist/types/src/Checker.test.d.ts.map +1 -0
  26. package/dist/types/src/Engine.d.ts +55 -0
  27. package/dist/types/src/Engine.d.ts.map +1 -0
  28. package/dist/types/src/Engine.test.d.ts +2 -0
  29. package/dist/types/src/Engine.test.d.ts.map +1 -0
  30. package/dist/types/src/Engine.workerd.test.d.ts +2 -0
  31. package/dist/types/src/Engine.workerd.test.d.ts.map +1 -0
  32. package/dist/types/src/Parser.d.ts +43 -0
  33. package/dist/types/src/Parser.d.ts.map +1 -0
  34. package/dist/types/src/Parser.test.d.ts +2 -0
  35. package/dist/types/src/Parser.test.d.ts.map +1 -0
  36. package/dist/types/src/index.d.ts +6 -0
  37. package/dist/types/src/index.d.ts.map +1 -0
  38. package/dist/types/src/internal/lexer.d.ts +46 -0
  39. package/dist/types/src/internal/lexer.d.ts.map +1 -0
  40. package/dist/types/src/internal/plan.d.ts +96 -0
  41. package/dist/types/src/internal/plan.d.ts.map +1 -0
  42. package/dist/types/src/internal/relation.d.ts +20 -0
  43. package/dist/types/src/internal/relation.d.ts.map +1 -0
  44. package/dist/types/src/internal/stratify.d.ts +28 -0
  45. package/dist/types/src/internal/stratify.d.ts.map +1 -0
  46. package/dist/types/tsconfig.tsbuildinfo +1 -0
  47. package/package.json +57 -5
  48. package/src/Ast.ts +136 -0
  49. package/src/Builtin.ts +73 -0
  50. package/src/Checker.test.ts +99 -0
  51. package/src/Checker.ts +156 -0
  52. package/src/Engine.test.ts +239 -0
  53. package/src/Engine.ts +614 -0
  54. package/src/Engine.workerd.test.ts +33 -0
  55. package/src/Parser.test.ts +84 -0
  56. package/src/Parser.ts +180 -0
  57. package/src/index.ts +9 -0
  58. package/src/internal/lexer.ts +154 -0
  59. package/src/internal/plan.ts +346 -0
  60. package/src/internal/relation.ts +95 -0
  61. package/src/internal/stratify.ts +114 -0
@@ -0,0 +1,414 @@
1
+ import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
2
+ import { formatRule, variablesOf } from "./Ast.mjs";
3
+ import { acceptsBinding } from "./Builtin.mjs";
4
+ import { BaseError } from "@dxos/errors";
5
+ //#region src/internal/plan.ts
6
+ /** Assigns a slot per named variable of a rule. */
7
+ var Slots = class {
8
+ #slots = /* @__PURE__ */ new Map();
9
+ get size() {
10
+ return this.#slots.size;
11
+ }
12
+ slot(name) {
13
+ let slot = this.#slots.get(name);
14
+ if (slot === void 0) {
15
+ slot = this.#slots.size;
16
+ this.#slots.set(name, slot);
17
+ }
18
+ return slot;
19
+ }
20
+ ref(term) {
21
+ if (term.type === "constant") return {
22
+ kind: "const",
23
+ value: term.value
24
+ };
25
+ return term.anonymous ? { kind: "wild" } : {
26
+ kind: "slot",
27
+ slot: this.slot(term.name)
28
+ };
29
+ }
30
+ };
31
+ var isBound = (term, bound) => term.type === "constant" || !term.anonymous && bound.has(term.name);
32
+ var unboundNames = (terms, bound) => terms.flatMap((term) => term.type === "variable" && !term.anonymous && !bound.has(term.name) ? [term.name] : []);
33
+ var variableNames = (literal) => {
34
+ switch (literal.type) {
35
+ case "atom": return literal.atom.terms.flatMap((term) => term.type === "variable" && !term.anonymous ? [term.name] : []);
36
+ case "comparison": return [literal.left, literal.right].flatMap((term) => term.type === "variable" && !term.anonymous ? [term.name] : []);
37
+ case "aggregate": return [...[literal.result, ...literal.target ? [literal.target] : []].flatMap((term) => term.type === "variable" && !term.anonymous ? [term.name] : []), ...literal.body.flatMap(variableNames)];
38
+ }
39
+ };
40
+ /**
41
+ * Orders a rule body so every literal runs once its inputs are bound: filters as early as
42
+ * possible, then the relation scan with the most bound columns, then generating built-ins.
43
+ * @param outer Variables used outside the body (the head, or the enclosing rule of an aggregate).
44
+ * @param first Literal to scan first (the semi-naive delta).
45
+ */
46
+ var planBody = (options) => {
47
+ const { body, builtins, slots, outer, first } = options;
48
+ const bound = new Set(options.bound ?? []);
49
+ const steps = [];
50
+ const remaining = new Set(body.keys());
51
+ const unresolved = [];
52
+ const scan = (index, literal) => {
53
+ const boundColumns = [];
54
+ const binds = [];
55
+ literal.atom.terms.forEach((term, column) => {
56
+ if (isBound(term, bound)) boundColumns.push({
57
+ column,
58
+ ref: slots.ref(term)
59
+ });
60
+ else if (term.type === "variable" && !term.anonymous) binds.push({
61
+ column,
62
+ slot: slots.slot(term.name)
63
+ });
64
+ });
65
+ for (const name of unboundNames(literal.atom.terms, bound)) bound.add(name);
66
+ steps.push({
67
+ kind: "scan",
68
+ literal: index,
69
+ relation: literal.atom.predicate,
70
+ bound: boundColumns,
71
+ binds
72
+ });
73
+ };
74
+ /** Variables any other literal or the outer scope uses, which an aggregate must correlate on. */
75
+ const correlated = (index, literal) => {
76
+ const elsewhere = /* @__PURE__ */ new Set([...outer, ...body.flatMap((other, at) => at === index ? [] : variableNames(other))]);
77
+ return [...new Set(literal.body.flatMap(variableNames))].filter((name) => elsewhere.has(name));
78
+ };
79
+ /** Schedules `index` if it is a filter (or, with `generate`, a generator) whose inputs are bound. */
80
+ const trySchedule = (index, generate) => {
81
+ const literal = body[index];
82
+ switch (literal.type) {
83
+ case "atom": {
84
+ const builtin = builtins.get(literal.atom.predicate);
85
+ if (!builtin) {
86
+ if (!literal.negated) return false;
87
+ if (unboundNames(literal.atom.terms, bound).length > 0) return false;
88
+ steps.push({
89
+ kind: "negation",
90
+ relation: literal.atom.predicate,
91
+ bound: literal.atom.terms.flatMap((term, column) => term.type === "variable" && term.anonymous ? [] : [{
92
+ column,
93
+ ref: slots.ref(term)
94
+ }])
95
+ });
96
+ return true;
97
+ }
98
+ const boundArgs = literal.atom.terms.map((term) => isBound(term, bound));
99
+ if (!boundArgs.every(Boolean) && (!generate || literal.negated || !acceptsBinding(builtin, boundArgs))) return false;
100
+ steps.push({
101
+ kind: "builtin",
102
+ builtin,
103
+ args: literal.atom.terms.map((term) => slots.ref(term)),
104
+ free: boundArgs.map((value) => !value),
105
+ negated: literal.negated
106
+ });
107
+ for (const name of unboundNames(literal.atom.terms, bound)) bound.add(name);
108
+ return true;
109
+ }
110
+ case "comparison": {
111
+ const left = isBound(literal.left, bound);
112
+ const right = isBound(literal.right, bound);
113
+ if (left && right) {
114
+ steps.push({
115
+ kind: "compare",
116
+ operator: literal.operator,
117
+ left: slots.ref(literal.left),
118
+ right: slots.ref(literal.right)
119
+ });
120
+ return true;
121
+ }
122
+ if (!generate || literal.operator !== "=" || left === right) return false;
123
+ const [target, source] = left ? [literal.right, literal.left] : [literal.left, literal.right];
124
+ if (target.type !== "variable" || target.anonymous) return false;
125
+ steps.push({
126
+ kind: "compare",
127
+ operator: "=",
128
+ left: slots.ref(literal.left),
129
+ right: slots.ref(literal.right),
130
+ assign: {
131
+ slot: slots.slot(target.name),
132
+ from: slots.ref(source)
133
+ }
134
+ });
135
+ bound.add(target.name);
136
+ return true;
137
+ }
138
+ case "aggregate": {
139
+ const correlation = correlated(index, literal);
140
+ if (correlation.some((name) => !bound.has(name))) return false;
141
+ const resultBound = isBound(literal.result, bound);
142
+ if (!resultBound && !generate) return false;
143
+ const inner = planBody({
144
+ body: literal.body,
145
+ builtins,
146
+ slots,
147
+ outer: new Set(correlation),
148
+ bound: new Set(correlation)
149
+ });
150
+ if (inner.unresolved.length > 0) {
151
+ unresolved.push(...inner.unresolved.map(({ reason }) => ({
152
+ literal: index,
153
+ reason
154
+ })));
155
+ return true;
156
+ }
157
+ if (literal.target && !isBound(literal.target, inner.bound)) {
158
+ unresolved.push({
159
+ literal: index,
160
+ reason: `aggregate target is not bound by the aggregate body`
161
+ });
162
+ return true;
163
+ }
164
+ const local = [...inner.bound].filter((name) => !correlation.includes(name)).map((name) => slots.slot(name));
165
+ steps.push({
166
+ kind: "aggregate",
167
+ function: literal.function,
168
+ steps: inner.steps,
169
+ local,
170
+ target: literal.target ? slots.ref(literal.target) : void 0,
171
+ result: slots.ref(literal.result),
172
+ assignResult: !resultBound
173
+ });
174
+ if (literal.result.type === "variable" && !literal.result.anonymous) bound.add(literal.result.name);
175
+ return true;
176
+ }
177
+ }
178
+ };
179
+ if (first !== void 0) {
180
+ const literal = body[first];
181
+ if (literal.type === "atom" && !literal.negated && !builtins.has(literal.atom.predicate)) {
182
+ remaining.delete(first);
183
+ scan(first, literal);
184
+ }
185
+ }
186
+ while (remaining.size > 0) {
187
+ let progressed = false;
188
+ for (const index of remaining) if (trySchedule(index, false)) {
189
+ remaining.delete(index);
190
+ progressed = true;
191
+ }
192
+ if (progressed) continue;
193
+ let best;
194
+ let bestBound = -1;
195
+ for (const index of remaining) {
196
+ const literal = body[index];
197
+ if (literal.type === "atom" && !literal.negated && !builtins.has(literal.atom.predicate)) {
198
+ const boundCount = literal.atom.terms.filter((term) => isBound(term, bound)).length;
199
+ if (boundCount > bestBound) {
200
+ best = index;
201
+ bestBound = boundCount;
202
+ }
203
+ }
204
+ }
205
+ if (best !== void 0) {
206
+ const literal = body[best];
207
+ if (literal.type === "atom") {
208
+ remaining.delete(best);
209
+ scan(best, literal);
210
+ }
211
+ continue;
212
+ }
213
+ for (const index of remaining) if (trySchedule(index, true)) {
214
+ remaining.delete(index);
215
+ progressed = true;
216
+ break;
217
+ }
218
+ if (!progressed) break;
219
+ }
220
+ for (const index of remaining) unresolved.push({
221
+ literal: index,
222
+ reason: describeUnresolved(body[index], bound, builtins)
223
+ });
224
+ return {
225
+ steps,
226
+ unresolved,
227
+ bound
228
+ };
229
+ };
230
+ var describeUnresolved = (literal, bound, builtins) => {
231
+ switch (literal.type) {
232
+ case "atom": {
233
+ const names = unboundNames(literal.atom.terms, bound).join(", ");
234
+ if (builtins.has(literal.atom.predicate)) return literal.negated ? `negated built-in ${literal.atom.predicate} needs every argument bound (${names} unbound)` : `no binding mode of built-in ${literal.atom.predicate}/${literal.atom.terms.length} accepts ${names} unbound`;
235
+ return `variable ${names} under negation is not bound by a positive literal`;
236
+ }
237
+ case "comparison": return `variable ${unboundNames([literal.left, literal.right], bound).join(", ")} in comparison is not bound`;
238
+ case "aggregate": return `aggregate depends on unbound variables`;
239
+ }
240
+ };
241
+ //#endregion
242
+ //#region src/internal/stratify.ts
243
+ /** Relations a literal reads; negation and aggregation read them non-monotonically. */
244
+ var dependencies = (literal, builtins) => {
245
+ switch (literal.type) {
246
+ case "atom": return builtins.has(literal.atom.predicate) ? [] : [{
247
+ predicate: literal.atom.predicate,
248
+ negative: literal.negated
249
+ }];
250
+ case "comparison": return [];
251
+ case "aggregate": return literal.body.flatMap((inner) => dependencies(inner, builtins).map(({ predicate }) => ({
252
+ predicate,
253
+ negative: true
254
+ })));
255
+ }
256
+ };
257
+ /**
258
+ * Splits rules into strata: one per strongly connected component of the predicate graph, in
259
+ * dependency order; fails when negation or aggregation occurs inside a component.
260
+ */
261
+ var stratify = (rules, builtins) => {
262
+ const defined = new Set(rules.filter((rule) => rule.body.length > 0).map((rule) => rule.head.predicate));
263
+ const edges = /* @__PURE__ */ new Map();
264
+ for (const predicate of defined) edges.set(predicate, []);
265
+ for (const rule of rules) for (const literal of rule.body) for (const dependency of dependencies(literal, builtins)) if (defined.has(dependency.predicate)) edges.get(rule.head.predicate)?.push(dependency);
266
+ const components = [];
267
+ const index = /* @__PURE__ */ new Map();
268
+ const lowLink = /* @__PURE__ */ new Map();
269
+ const stack = [];
270
+ const onStack = /* @__PURE__ */ new Set();
271
+ const visit = (predicate) => {
272
+ index.set(predicate, index.size);
273
+ lowLink.set(predicate, index.get(predicate) ?? 0);
274
+ stack.push(predicate);
275
+ onStack.add(predicate);
276
+ for (const { predicate: next } of edges.get(predicate) ?? []) if (!index.has(next)) {
277
+ visit(next);
278
+ lowLink.set(predicate, Math.min(lowLink.get(predicate) ?? 0, lowLink.get(next) ?? 0));
279
+ } else if (onStack.has(next)) lowLink.set(predicate, Math.min(lowLink.get(predicate) ?? 0, index.get(next) ?? 0));
280
+ if (lowLink.get(predicate) === index.get(predicate)) {
281
+ const component = [];
282
+ let member;
283
+ do {
284
+ member = stack.pop();
285
+ if (member !== void 0) {
286
+ onStack.delete(member);
287
+ component.push(member);
288
+ }
289
+ } while (member !== void 0 && member !== predicate);
290
+ components.push(component);
291
+ }
292
+ };
293
+ for (const predicate of defined) if (!index.has(predicate)) visit(predicate);
294
+ const cycles = [];
295
+ const strata = components.map((component) => {
296
+ const members = new Set(component);
297
+ const internal = component.flatMap((predicate) => (edges.get(predicate) ?? []).filter((dependency) => members.has(dependency.predicate)));
298
+ if (internal.some((dependency) => dependency.negative)) cycles.push(component.sort());
299
+ return {
300
+ predicates: members,
301
+ rules: rules.flatMap((rule, ruleIndex) => rule.body.length > 0 && members.has(rule.head.predicate) ? [ruleIndex] : []),
302
+ recursive: internal.length > 0
303
+ };
304
+ });
305
+ return cycles.length > 0 ? {
306
+ ok: false,
307
+ cycles
308
+ } : {
309
+ ok: true,
310
+ strata
311
+ };
312
+ };
313
+ //#endregion
314
+ //#region src/Checker.ts
315
+ var Checker_exports = /* @__PURE__ */ __exportAll({
316
+ CheckError: () => CheckError,
317
+ check: () => check,
318
+ formatDiagnostic: () => formatDiagnostic
319
+ });
320
+ /** Thrown when a program with diagnostics is evaluated. */
321
+ var CheckError = class extends BaseError.extend("CheckError", "Invalid program") {
322
+ diagnostics;
323
+ constructor(diagnostics) {
324
+ super({ message: `Invalid program:\n${diagnostics.map(formatDiagnostic).join("\n")}` });
325
+ this.diagnostics = diagnostics;
326
+ }
327
+ };
328
+ /** Formats a diagnostic as `line:column code: message`. */
329
+ var formatDiagnostic = (diagnostic) => {
330
+ return `${diagnostic.position ? `${diagnostic.position.line}:${diagnostic.position.column} ` : ""}${diagnostic.code}: ${diagnostic.message}`;
331
+ };
332
+ /**
333
+ * Checks arity consistency, built-in heads, undefined predicates, range restriction (safety) and
334
+ * stratification; an empty result means the program can be evaluated.
335
+ */
336
+ var check = (program, options = {}) => {
337
+ const builtins = options.builtins ?? /* @__PURE__ */ new Map();
338
+ const diagnostics = [];
339
+ const arities = /* @__PURE__ */ new Map();
340
+ for (const builtin of builtins.values()) arities.set(builtin.name, builtin.arity);
341
+ for (const [name, arity] of Object.entries(options.relations ?? {})) arities.set(name, arity);
342
+ const defined = new Set(program.rules.map((rule) => rule.head.predicate));
343
+ const visitAtom = (atom, rule, position) => {
344
+ const expected = arities.get(atom.predicate);
345
+ if (expected === void 0) arities.set(atom.predicate, atom.terms.length);
346
+ else if (expected !== atom.terms.length) diagnostics.push({
347
+ code: "arity",
348
+ message: `${atom.predicate}/${atom.terms.length} used where ${atom.predicate}/${expected} is expected`,
349
+ rule,
350
+ position
351
+ });
352
+ };
353
+ const visitLiteral = (literal, rule, fallback) => {
354
+ const position = literal.position ?? fallback;
355
+ if (literal.type === "atom") {
356
+ visitAtom(literal.atom, rule, position);
357
+ const predicate = literal.atom.predicate;
358
+ if (options.relations && !defined.has(predicate) && !builtins.has(predicate) && !(predicate in options.relations)) diagnostics.push({
359
+ code: "undefined",
360
+ message: `${predicate}/${literal.atom.terms.length} is not a relation, rule or built-in`,
361
+ rule,
362
+ position
363
+ });
364
+ } else if (literal.type === "aggregate") literal.body.forEach((inner) => visitLiteral(inner, rule, position));
365
+ };
366
+ program.rules.forEach((rule, index) => {
367
+ if (builtins.has(rule.head.predicate)) diagnostics.push({
368
+ code: "builtin-head",
369
+ message: `rule defines built-in ${rule.head.predicate}`,
370
+ rule: index,
371
+ position: rule.position
372
+ });
373
+ visitAtom(rule.head, index, rule.position);
374
+ rule.body.forEach((literal) => visitLiteral(literal, index, rule.position));
375
+ const head = new Set(variablesOf(rule.head.terms));
376
+ const plan = planBody({
377
+ body: rule.body,
378
+ builtins,
379
+ slots: new Slots(),
380
+ outer: head
381
+ });
382
+ for (const { literal, reason } of plan.unresolved) diagnostics.push({
383
+ code: "unsafe",
384
+ message: `${reason} in: ${formatRule(rule)}`,
385
+ rule: index,
386
+ position: rule.body[literal].position ?? rule.position
387
+ });
388
+ const unboundHead = [...head].filter((name) => !plan.bound.has(name));
389
+ if (unboundHead.length > 0) diagnostics.push({
390
+ code: "unsafe",
391
+ message: `head variable ${unboundHead.join(", ")} is not bound by the body in: ${formatRule(rule)}`,
392
+ rule: index,
393
+ position: rule.position
394
+ });
395
+ if (rule.head.terms.some((term) => term.type === "variable" && term.anonymous)) diagnostics.push({
396
+ code: "unsafe",
397
+ message: `anonymous variable in head of: ${formatRule(rule)}`,
398
+ rule: index,
399
+ position: rule.position
400
+ });
401
+ });
402
+ const stratification = stratify(program.rules, builtins);
403
+ if (!stratification.ok) for (const cycle of stratification.cycles) diagnostics.push({
404
+ code: "unstratifiable",
405
+ message: `negation or aggregation through recursion among: ${cycle.join(", ")}`,
406
+ rule: program.rules.findIndex((rule) => cycle.includes(rule.head.predicate)),
407
+ position: program.rules.find((rule) => cycle.includes(rule.head.predicate))?.position
408
+ });
409
+ return diagnostics;
410
+ };
411
+ //#endregion
412
+ export { dependencies as a, planBody as c, formatDiagnostic as i, Checker_exports as n, stratify as o, check as r, Slots as s, CheckError as t };
413
+
414
+ //# sourceMappingURL=chunk-Checker.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-Checker.mjs","names":[],"sources":["../../src/internal/plan.ts","../../src/internal/stratify.ts","../../src/Checker.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport type * as Ast from '../Ast.ts';\nimport * as Builtin from '../Builtin.ts';\n\n/** A term resolved against a rule's variable slots; `wild` is an anonymous variable. */\nexport type Ref =\n | { readonly kind: 'const'; readonly value: Ast.Value }\n | { readonly kind: 'slot'; readonly slot: number }\n | { readonly kind: 'wild' };\n\nexport type ScanStep = {\n readonly kind: 'scan';\n readonly literal: number;\n readonly relation: string;\n /** Columns whose value is known before the scan, with the value source. */\n readonly bound: ReadonlyArray<{ readonly column: number; readonly ref: Ref }>;\n /** Columns that bind a slot, or check it when the variable repeats within the atom. */\n readonly binds: ReadonlyArray<{ readonly column: number; readonly slot: number }>;\n};\n\nexport type NegationStep = {\n readonly kind: 'negation';\n readonly relation: string;\n readonly bound: ReadonlyArray<{ readonly column: number; readonly ref: Ref }>;\n};\n\nexport type CompareStep = {\n readonly kind: 'compare';\n readonly operator: Ast.ComparisonOperator;\n readonly left: Ref;\n readonly right: Ref;\n /** Set for `X = term` with `X` unbound: assigns instead of comparing. */\n readonly assign?: { readonly slot: number; readonly from: Ref };\n};\n\nexport type BuiltinStep = {\n readonly kind: 'builtin';\n readonly builtin: Builtin.Builtin;\n readonly args: ReadonlyArray<Ref>;\n /** Arguments unbound at call time, passed as `undefined` and bound from the results. */\n readonly free: ReadonlyArray<boolean>;\n readonly negated: boolean;\n};\n\nexport type AggregateStep = {\n readonly kind: 'aggregate';\n readonly function: Ast.AggregateFunction;\n readonly steps: ReadonlyArray<Step>;\n /** Slots local to the aggregate body; distinct assignments of these are aggregated. */\n readonly local: ReadonlyArray<number>;\n readonly target?: Ref;\n readonly result: Ref;\n readonly assignResult: boolean;\n};\n\nexport type Step = ScanStep | NegationStep | CompareStep | BuiltinStep | AggregateStep;\n\nexport type Unresolved = { readonly literal: number; readonly reason: string };\n\nexport type Plan = {\n readonly steps: ReadonlyArray<Step>;\n readonly unresolved: ReadonlyArray<Unresolved>;\n readonly bound: ReadonlySet<string>;\n};\n\n/** Assigns a slot per named variable of a rule. */\nexport class Slots {\n readonly #slots = new Map<string, number>();\n\n get size(): number {\n return this.#slots.size;\n }\n\n slot(name: string): number {\n let slot = this.#slots.get(name);\n if (slot === undefined) {\n slot = this.#slots.size;\n this.#slots.set(name, slot);\n }\n return slot;\n }\n\n ref(term: Ast.Term): Ref {\n if (term.type === 'constant') {\n return { kind: 'const', value: term.value };\n }\n return term.anonymous ? { kind: 'wild' } : { kind: 'slot', slot: this.slot(term.name) };\n }\n}\n\nconst isBound = (term: Ast.Term, bound: ReadonlySet<string>) =>\n term.type === 'constant' || (!term.anonymous && bound.has(term.name));\n\nconst unboundNames = (terms: ReadonlyArray<Ast.Term>, bound: ReadonlySet<string>) =>\n terms.flatMap((term) => (term.type === 'variable' && !term.anonymous && !bound.has(term.name) ? [term.name] : []));\n\nconst variableNames = (literal: Ast.Literal): string[] => {\n switch (literal.type) {\n case 'atom':\n return literal.atom.terms.flatMap((term) => (term.type === 'variable' && !term.anonymous ? [term.name] : []));\n case 'comparison':\n return [literal.left, literal.right].flatMap((term) =>\n term.type === 'variable' && !term.anonymous ? [term.name] : [],\n );\n case 'aggregate':\n return [\n ...[literal.result, ...(literal.target ? [literal.target] : [])].flatMap((term) =>\n term.type === 'variable' && !term.anonymous ? [term.name] : [],\n ),\n ...literal.body.flatMap(variableNames),\n ];\n }\n};\n\n/**\n * Orders a rule body so every literal runs once its inputs are bound: filters as early as\n * possible, then the relation scan with the most bound columns, then generating built-ins.\n * @param outer Variables used outside the body (the head, or the enclosing rule of an aggregate).\n * @param first Literal to scan first (the semi-naive delta).\n */\nexport const planBody = (options: {\n body: ReadonlyArray<Ast.Literal>;\n builtins: Builtin.Registry;\n slots: Slots;\n outer: ReadonlySet<string>;\n bound?: ReadonlySet<string>;\n first?: number;\n}): Plan => {\n const { body, builtins, slots, outer, first } = options;\n const bound = new Set(options.bound ?? []);\n const steps: Step[] = [];\n const remaining = new Set(body.keys());\n const unresolved: Unresolved[] = [];\n\n const scan = (index: number, literal: Ast.AtomLiteral) => {\n const boundColumns: { column: number; ref: Ref }[] = [];\n const binds: { column: number; slot: number }[] = [];\n literal.atom.terms.forEach((term, column) => {\n if (isBound(term, bound)) {\n boundColumns.push({ column, ref: slots.ref(term) });\n } else if (term.type === 'variable' && !term.anonymous) {\n binds.push({ column, slot: slots.slot(term.name) });\n }\n });\n for (const name of unboundNames(literal.atom.terms, bound)) {\n bound.add(name);\n }\n steps.push({ kind: 'scan', literal: index, relation: literal.atom.predicate, bound: boundColumns, binds });\n };\n\n /** Variables any other literal or the outer scope uses, which an aggregate must correlate on. */\n const correlated = (index: number, literal: Ast.AggregateLiteral): string[] => {\n const elsewhere = new Set([...outer, ...body.flatMap((other, at) => (at === index ? [] : variableNames(other)))]);\n const inner = new Set(literal.body.flatMap(variableNames));\n return [...inner].filter((name) => elsewhere.has(name));\n };\n\n /** Schedules `index` if it is a filter (or, with `generate`, a generator) whose inputs are bound. */\n const trySchedule = (index: number, generate: boolean): boolean => {\n const literal = body[index];\n switch (literal.type) {\n case 'atom': {\n const builtin = builtins.get(literal.atom.predicate);\n if (!builtin) {\n if (!literal.negated) {\n return false;\n }\n if (unboundNames(literal.atom.terms, bound).length > 0) {\n return false;\n }\n steps.push({\n kind: 'negation',\n relation: literal.atom.predicate,\n bound: literal.atom.terms.flatMap((term, column) =>\n term.type === 'variable' && term.anonymous ? [] : [{ column, ref: slots.ref(term) }],\n ),\n });\n return true;\n }\n const boundArgs = literal.atom.terms.map((term) => isBound(term, bound));\n const allBound = boundArgs.every(Boolean);\n if (!allBound && (!generate || literal.negated || !Builtin.acceptsBinding(builtin, boundArgs))) {\n return false;\n }\n steps.push({\n kind: 'builtin',\n builtin,\n args: literal.atom.terms.map((term) => slots.ref(term)),\n free: boundArgs.map((value) => !value),\n negated: literal.negated,\n });\n for (const name of unboundNames(literal.atom.terms, bound)) {\n bound.add(name);\n }\n return true;\n }\n case 'comparison': {\n const left = isBound(literal.left, bound);\n const right = isBound(literal.right, bound);\n if (left && right) {\n steps.push({\n kind: 'compare',\n operator: literal.operator,\n left: slots.ref(literal.left),\n right: slots.ref(literal.right),\n });\n return true;\n }\n if (!generate || literal.operator !== '=' || left === right) {\n return false;\n }\n const [target, source] = left ? [literal.right, literal.left] : [literal.left, literal.right];\n if (target.type !== 'variable' || target.anonymous) {\n return false;\n }\n steps.push({\n kind: 'compare',\n operator: '=',\n left: slots.ref(literal.left),\n right: slots.ref(literal.right),\n assign: { slot: slots.slot(target.name), from: slots.ref(source) },\n });\n bound.add(target.name);\n return true;\n }\n case 'aggregate': {\n const correlation = correlated(index, literal);\n if (correlation.some((name) => !bound.has(name))) {\n return false;\n }\n const resultBound = isBound(literal.result, bound);\n if (!resultBound && !generate) {\n return false;\n }\n const inner = planBody({\n body: literal.body,\n builtins,\n slots,\n outer: new Set(correlation),\n bound: new Set(correlation),\n });\n if (inner.unresolved.length > 0) {\n unresolved.push(...inner.unresolved.map(({ reason }) => ({ literal: index, reason })));\n return true;\n }\n if (literal.target && !isBound(literal.target, inner.bound)) {\n unresolved.push({ literal: index, reason: `aggregate target is not bound by the aggregate body` });\n return true;\n }\n const local = [...inner.bound].filter((name) => !correlation.includes(name)).map((name) => slots.slot(name));\n steps.push({\n kind: 'aggregate',\n function: literal.function,\n steps: inner.steps,\n local,\n target: literal.target ? slots.ref(literal.target) : undefined,\n result: slots.ref(literal.result),\n assignResult: !resultBound,\n });\n if (literal.result.type === 'variable' && !literal.result.anonymous) {\n bound.add(literal.result.name);\n }\n return true;\n }\n }\n };\n\n if (first !== undefined) {\n const literal = body[first];\n if (literal.type === 'atom' && !literal.negated && !builtins.has(literal.atom.predicate)) {\n remaining.delete(first);\n scan(first, literal);\n }\n }\n\n while (remaining.size > 0) {\n let progressed = false;\n for (const index of remaining) {\n if (trySchedule(index, false)) {\n remaining.delete(index);\n progressed = true;\n }\n }\n if (progressed) {\n continue;\n }\n\n let best: number | undefined;\n let bestBound = -1;\n for (const index of remaining) {\n const literal = body[index];\n if (literal.type === 'atom' && !literal.negated && !builtins.has(literal.atom.predicate)) {\n const boundCount = literal.atom.terms.filter((term) => isBound(term, bound)).length;\n if (boundCount > bestBound) {\n best = index;\n bestBound = boundCount;\n }\n }\n }\n if (best !== undefined) {\n const literal = body[best];\n if (literal.type === 'atom') {\n remaining.delete(best);\n scan(best, literal);\n }\n continue;\n }\n\n for (const index of remaining) {\n if (trySchedule(index, true)) {\n remaining.delete(index);\n progressed = true;\n break;\n }\n }\n if (!progressed) {\n break;\n }\n }\n\n for (const index of remaining) {\n unresolved.push({ literal: index, reason: describeUnresolved(body[index], bound, builtins) });\n }\n return { steps, unresolved, bound };\n};\n\nconst describeUnresolved = (literal: Ast.Literal, bound: ReadonlySet<string>, builtins: Builtin.Registry): string => {\n switch (literal.type) {\n case 'atom': {\n const names = unboundNames(literal.atom.terms, bound).join(', ');\n if (builtins.has(literal.atom.predicate)) {\n return literal.negated\n ? `negated built-in ${literal.atom.predicate} needs every argument bound (${names} unbound)`\n : `no binding mode of built-in ${literal.atom.predicate}/${literal.atom.terms.length} accepts ${names} unbound`;\n }\n return `variable ${names} under negation is not bound by a positive literal`;\n }\n case 'comparison':\n return `variable ${unboundNames([literal.left, literal.right], bound).join(', ')} in comparison is not bound`;\n case 'aggregate':\n return `aggregate depends on unbound variables`;\n }\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport type * as Ast from '../Ast.ts';\nimport type * as Builtin from '../Builtin.ts';\n\nexport type Dependency = { readonly predicate: string; readonly negative: boolean };\n\nexport type Stratum = {\n /** Predicates defined by the stratum's rules (one strongly connected component). */\n readonly predicates: ReadonlySet<string>;\n /** Indexes into the program's rules. */\n readonly rules: ReadonlyArray<number>;\n readonly recursive: boolean;\n};\n\nexport type Stratification =\n | { readonly ok: true; readonly strata: ReadonlyArray<Stratum> }\n | { readonly ok: false; readonly cycles: ReadonlyArray<ReadonlyArray<string>> };\n\n/** Relations a literal reads; negation and aggregation read them non-monotonically. */\nexport const dependencies = (literal: Ast.Literal, builtins: Builtin.Registry): Dependency[] => {\n switch (literal.type) {\n case 'atom':\n return builtins.has(literal.atom.predicate)\n ? []\n : [{ predicate: literal.atom.predicate, negative: literal.negated }];\n case 'comparison':\n return [];\n case 'aggregate':\n return literal.body.flatMap((inner) =>\n dependencies(inner, builtins).map(({ predicate }) => ({ predicate, negative: true })),\n );\n }\n};\n\n/**\n * Splits rules into strata: one per strongly connected component of the predicate graph, in\n * dependency order; fails when negation or aggregation occurs inside a component.\n */\nexport const stratify = (rules: ReadonlyArray<Ast.Rule>, builtins: Builtin.Registry): Stratification => {\n const defined = new Set(rules.filter((rule) => rule.body.length > 0).map((rule) => rule.head.predicate));\n const edges = new Map<string, Dependency[]>();\n for (const predicate of defined) {\n edges.set(predicate, []);\n }\n for (const rule of rules) {\n for (const literal of rule.body) {\n for (const dependency of dependencies(literal, builtins)) {\n if (defined.has(dependency.predicate)) {\n edges.get(rule.head.predicate)?.push(dependency);\n }\n }\n }\n }\n\n // Tarjan's algorithm emits each component after every component it depends on.\n const components: string[][] = [];\n const index = new Map<string, number>();\n const lowLink = new Map<string, number>();\n const stack: string[] = [];\n const onStack = new Set<string>();\n const visit = (predicate: string) => {\n index.set(predicate, index.size);\n lowLink.set(predicate, index.get(predicate) ?? 0);\n stack.push(predicate);\n onStack.add(predicate);\n for (const { predicate: next } of edges.get(predicate) ?? []) {\n if (!index.has(next)) {\n visit(next);\n lowLink.set(predicate, Math.min(lowLink.get(predicate) ?? 0, lowLink.get(next) ?? 0));\n } else if (onStack.has(next)) {\n lowLink.set(predicate, Math.min(lowLink.get(predicate) ?? 0, index.get(next) ?? 0));\n }\n }\n if (lowLink.get(predicate) === index.get(predicate)) {\n const component: string[] = [];\n let member: string | undefined;\n do {\n member = stack.pop();\n if (member !== undefined) {\n onStack.delete(member);\n component.push(member);\n }\n } while (member !== undefined && member !== predicate);\n components.push(component);\n }\n };\n for (const predicate of defined) {\n if (!index.has(predicate)) {\n visit(predicate);\n }\n }\n\n const cycles: string[][] = [];\n const strata: Stratum[] = components.map((component) => {\n const members = new Set(component);\n const internal = component.flatMap((predicate) =>\n (edges.get(predicate) ?? []).filter((dependency) => members.has(dependency.predicate)),\n );\n if (internal.some((dependency) => dependency.negative)) {\n cycles.push(component.sort());\n }\n return {\n predicates: members,\n rules: rules.flatMap((rule, ruleIndex) =>\n rule.body.length > 0 && members.has(rule.head.predicate) ? [ruleIndex] : [],\n ),\n recursive: internal.length > 0,\n };\n });\n return cycles.length > 0 ? { ok: false, cycles } : { ok: true, strata };\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nimport { BaseError } from '@dxos/errors';\n\nimport * as Ast from './Ast.ts';\nimport type * as Builtin from './Builtin.ts';\nimport { Slots, planBody } from './internal/plan.ts';\nimport { stratify } from './internal/stratify.ts';\n\nexport type DiagnosticCode = 'arity' | 'builtin-head' | 'undefined' | 'unsafe' | 'unstratifiable';\n\n/** A static error in a program. */\nexport type Diagnostic = {\n readonly code: DiagnosticCode;\n readonly message: string;\n /** Index of the offending rule. */\n readonly rule?: number;\n readonly position?: Ast.Position;\n};\n\nexport type Options = {\n readonly builtins?: Builtin.Registry;\n /**\n * Base relations and their arities. When given, a body predicate that is neither defined by a\n * rule, declared here nor a built-in is reported as `undefined`.\n */\n readonly relations?: Readonly<Record<string, number>>;\n};\n\n/** Thrown when a program with diagnostics is evaluated. */\nexport class CheckError extends BaseError.extend('CheckError', 'Invalid program') {\n // A typed field rather than `context`, which would repeat every diagnostic in the message as JSON.\n readonly diagnostics: ReadonlyArray<Diagnostic>;\n\n constructor(diagnostics: ReadonlyArray<Diagnostic>) {\n super({ message: `Invalid program:\\n${diagnostics.map(formatDiagnostic).join('\\n')}` });\n this.diagnostics = diagnostics;\n }\n}\n\n/** Formats a diagnostic as `line:column code: message`. */\nexport const formatDiagnostic = (diagnostic: Diagnostic): string => {\n const at = diagnostic.position ? `${diagnostic.position.line}:${diagnostic.position.column} ` : '';\n return `${at}${diagnostic.code}: ${diagnostic.message}`;\n};\n\n/**\n * Checks arity consistency, built-in heads, undefined predicates, range restriction (safety) and\n * stratification; an empty result means the program can be evaluated.\n */\nexport const check = (program: Ast.Program, options: Options = {}): Diagnostic[] => {\n const builtins = options.builtins ?? new Map();\n const diagnostics: Diagnostic[] = [];\n const arities = new Map<string, number>();\n for (const builtin of builtins.values()) {\n arities.set(builtin.name, builtin.arity);\n }\n for (const [name, arity] of Object.entries(options.relations ?? {})) {\n arities.set(name, arity);\n }\n const defined = new Set(program.rules.map((rule) => rule.head.predicate));\n\n const visitAtom = (atom: Ast.Atom, rule: number, position: Ast.Position | undefined) => {\n const expected = arities.get(atom.predicate);\n if (expected === undefined) {\n arities.set(atom.predicate, atom.terms.length);\n } else if (expected !== atom.terms.length) {\n diagnostics.push({\n code: 'arity',\n message: `${atom.predicate}/${atom.terms.length} used where ${atom.predicate}/${expected} is expected`,\n rule,\n position,\n });\n }\n };\n const visitLiteral = (literal: Ast.Literal, rule: number, fallback: Ast.Position | undefined) => {\n const position = literal.position ?? fallback;\n if (literal.type === 'atom') {\n visitAtom(literal.atom, rule, position);\n const predicate = literal.atom.predicate;\n if (\n options.relations &&\n !defined.has(predicate) &&\n !builtins.has(predicate) &&\n !(predicate in options.relations)\n ) {\n diagnostics.push({\n code: 'undefined',\n message: `${predicate}/${literal.atom.terms.length} is not a relation, rule or built-in`,\n rule,\n position,\n });\n }\n } else if (literal.type === 'aggregate') {\n literal.body.forEach((inner) => visitLiteral(inner, rule, position));\n }\n };\n\n program.rules.forEach((rule, index) => {\n if (builtins.has(rule.head.predicate)) {\n diagnostics.push({\n code: 'builtin-head',\n message: `rule defines built-in ${rule.head.predicate}`,\n rule: index,\n position: rule.position,\n });\n }\n visitAtom(rule.head, index, rule.position);\n rule.body.forEach((literal) => visitLiteral(literal, index, rule.position));\n\n const head = new Set(Ast.variablesOf(rule.head.terms));\n const plan = planBody({ body: rule.body, builtins, slots: new Slots(), outer: head });\n for (const { literal, reason } of plan.unresolved) {\n diagnostics.push({\n code: 'unsafe',\n message: `${reason} in: ${Ast.formatRule(rule)}`,\n rule: index,\n position: rule.body[literal].position ?? rule.position,\n });\n }\n const unboundHead = [...head].filter((name) => !plan.bound.has(name));\n if (unboundHead.length > 0) {\n diagnostics.push({\n code: 'unsafe',\n message: `head variable ${unboundHead.join(', ')} is not bound by the body in: ${Ast.formatRule(rule)}`,\n rule: index,\n position: rule.position,\n });\n }\n if (rule.head.terms.some((term) => term.type === 'variable' && term.anonymous)) {\n diagnostics.push({\n code: 'unsafe',\n message: `anonymous variable in head of: ${Ast.formatRule(rule)}`,\n rule: index,\n position: rule.position,\n });\n }\n });\n\n const stratification = stratify(program.rules, builtins);\n if (!stratification.ok) {\n for (const cycle of stratification.cycles) {\n diagnostics.push({\n code: 'unstratifiable',\n message: `negation or aggregation through recursion among: ${cycle.join(', ')}`,\n rule: program.rules.findIndex((rule) => cycle.includes(rule.head.predicate)),\n position: program.rules.find((rule) => cycle.includes(rule.head.predicate))?.position,\n });\n }\n }\n return diagnostics;\n};\n"],"mappings":"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"}