@bjornpagen/bumbledb 0.15.0 → 0.17.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.
- package/COOKBOOK.md +33 -49
- package/README.md +3 -3
- package/dist/capacity.d.ts +24 -136
- package/dist/capacity.d.ts.map +1 -1
- package/dist/capacity.js +18 -40
- package/dist/capacity.js.map +1 -1
- package/dist/closed.d.ts +0 -156
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +0 -104
- package/dist/closed.js.map +1 -1
- package/dist/db.d.ts +7 -223
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +147 -396
- package/dist/db.js.map +1 -1
- package/dist/face.d.ts +0 -133
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js +0 -33
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +1 -145
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +2 -91
- package/dist/fields.js.map +1 -1
- package/dist/index.d.ts +11 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -13
- package/dist/index.js.map +1 -1
- package/dist/law.d.ts +111 -93
- package/dist/law.d.ts.map +1 -1
- package/dist/law.js +23 -27
- package/dist/law.js.map +1 -1
- package/dist/lower.d.ts +9 -35
- package/dist/lower.d.ts.map +1 -1
- package/dist/lower.js +8 -53
- package/dist/lower.js.map +1 -1
- package/dist/marshal.d.ts +0 -65
- package/dist/marshal.d.ts.map +1 -1
- package/dist/marshal.js +0 -72
- package/dist/marshal.js.map +1 -1
- package/dist/native.d.ts +25 -290
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +6 -66
- package/dist/native.js.map +1 -1
- package/dist/query/atom.d.ts +10 -276
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js +1 -96
- package/dist/query/atom.js.map +1 -1
- package/dist/query/find.d.ts +10 -76
- package/dist/query/find.d.ts.map +1 -1
- package/dist/query/find.js +0 -30
- package/dist/query/find.js.map +1 -1
- package/dist/query/lower.d.ts +64 -146
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +19 -256
- package/dist/query/lower.js.map +1 -1
- package/dist/query/parse-ir.d.ts +0 -7
- package/dist/query/parse-ir.d.ts.map +1 -1
- package/dist/query/parse-ir.js +1 -13
- package/dist/query/parse-ir.js.map +1 -1
- package/dist/query/run.d.ts +0 -36
- package/dist/query/run.d.ts.map +1 -1
- package/dist/query/run.js +0 -44
- package/dist/query/run.js.map +1 -1
- package/dist/query/scope.d.ts +24 -180
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +2 -66
- package/dist/query/scope.js.map +1 -1
- package/dist/relation.d.ts +2 -50
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +2 -37
- package/dist/relation.js.map +1 -1
- package/dist/schema.d.ts +13 -63
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +118 -92
- package/dist/schema.js.map +1 -1
- package/dist/spec.d.ts +1 -140
- package/dist/spec.d.ts.map +1 -1
- package/dist/spec.js +1 -68
- package/dist/spec.js.map +1 -1
- package/dist/statements.d.ts +6 -137
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +16 -119
- package/dist/statements.js.map +1 -1
- package/package.json +2 -2
- package/src/capacity.ts +26 -140
- package/src/closed.ts +5 -206
- package/src/db.ts +203 -692
- package/src/face.ts +0 -142
- package/src/fields.ts +4 -172
- package/src/index.ts +9 -15
- package/src/law.ts +201 -129
- package/src/lower.ts +8 -53
- package/src/marshal.ts +1 -85
- package/src/native.ts +47 -323
- package/src/query/atom.ts +26 -313
- package/src/query/find.ts +24 -110
- package/src/query/lower.ts +132 -377
- package/src/query/parse-ir.ts +1 -14
- package/src/query/run.ts +0 -45
- package/src/query/scope.ts +25 -186
- package/src/relation.ts +2 -66
- package/src/schema.ts +143 -122
- package/src/spec.ts +1 -160
- package/src/statements.ts +22 -174
package/dist/query/lower.js
CHANGED
|
@@ -1,47 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `query()` and the IR lowering, REFERENCE-IDENTITY edition. A query is
|
|
3
|
-
* built kysely-shaped — variables minted by {@link v} outside the rule and
|
|
4
|
-
* reused by REFERENCE to join:
|
|
5
|
-
*
|
|
6
|
-
* query(S).rule((r) => {
|
|
7
|
-
* const acct = v(Account)
|
|
8
|
-
* const h = v(Holder)
|
|
9
|
-
* return r
|
|
10
|
-
* .match(Account, { id: acct.id, holder: acct.holder })
|
|
11
|
-
* .match(Holder, { id: acct.holder })
|
|
12
|
-
* .where(r.eq(acct.holder, r.param("root")))
|
|
13
|
-
* .find({ account: acct.id, holder: acct.holder })
|
|
14
|
-
* })
|
|
15
|
-
*
|
|
16
|
-
* — and is an INERT value: `Query<Rels, Row, Params>` with `Row` inferred
|
|
17
|
-
* from each rule's `.find` RECORD (its keys ARE the answer columns) and
|
|
18
|
-
* `Params` inferred to be EXACTLY the params the rules use (params are typed
|
|
19
|
-
* BY USE; a param no rule uses never registers). Variable IDENTITY is the
|
|
20
|
-
* object reference: reusing one value across binding positions IS the join,
|
|
21
|
-
* lowering is a pure function of the query value down to the
|
|
22
|
-
* bridge's `QueryIr` (`bumbledb/crates/bumbledb/src/ir.rs`): relations by
|
|
23
|
-
* declaration ordinal, variables by dense per-rule first-occurrence ids
|
|
24
|
-
* (keyed on the object REFERENCE — the discipline is unchanged, only the map
|
|
25
|
-
* key moved from name to reference), params by first-use order. Lowering is
|
|
26
|
-
* STABLE — the same query value lowers to deeply-equal IR every time, and
|
|
27
|
-
* two identically-written queries (fresh mints each) lower identically.
|
|
28
|
-
* Construction validates negation safety and boundness (typed by the var's
|
|
29
|
-
* label — object identity is invisible to the type tier, so these are
|
|
30
|
-
* construction-time walls); everything else (types, aggregate
|
|
31
|
-
* rosters, rule caps) is the ENGINE's judge, surfacing at prepare.
|
|
32
|
-
*/
|
|
33
1
|
import * as errors from "@superbuilders/errors";
|
|
34
2
|
import { sealedFieldsOf } from "#closed.ts";
|
|
35
3
|
import { assertDeclarationOrderKey, isIntervalValue, literalShapeError, rosterOf } from "#fields.ts";
|
|
36
4
|
import { allen, and, eq, ge, gt, le, lt, ne, not, or, pointIn } from "#query/atom.ts";
|
|
37
5
|
import { count, max, min, pack, sum } from "#query/find.ts";
|
|
38
6
|
import { parseQueryIr } from "#query/parse-ir.ts";
|
|
39
|
-
import { fieldJoins, inferred, isTerm,
|
|
40
|
-
/** The frozen constructor vocabulary every rule builder spreads. */
|
|
7
|
+
import { fieldJoins, inferred, isTerm, makeParam, makeSetParam, renderFieldKind, term } from "#query/scope.ts";
|
|
41
8
|
const termOps = Object.freeze({
|
|
42
9
|
param: makeParam,
|
|
43
10
|
inSet: makeSetParam,
|
|
44
|
-
duration: makeDuration,
|
|
45
11
|
eq,
|
|
46
12
|
ne,
|
|
47
13
|
lt,
|
|
@@ -59,7 +25,6 @@ const termOps = Object.freeze({
|
|
|
59
25
|
max,
|
|
60
26
|
pack
|
|
61
27
|
});
|
|
62
|
-
/** The empty rule state. */
|
|
63
28
|
const EMPTY_RULE = Object.freeze({
|
|
64
29
|
items: Object.freeze([]),
|
|
65
30
|
bound: new Set(),
|
|
@@ -80,11 +45,6 @@ function mintSlotOf(context, ref) {
|
|
|
80
45
|
}
|
|
81
46
|
return { field: ref.field, class: context.classes[ref.owner.name]?.[ref.column] };
|
|
82
47
|
}
|
|
83
|
-
/**
|
|
84
|
-
* Judges one membership ARRAY at a binding position — legal exactly at a
|
|
85
|
-
* CLOSED-reference field, holding ≥ 2 DISTINCT handle names. The returned
|
|
86
|
-
* name is CONTENT-ADDRESSED (vocabulary + the member SET).
|
|
87
|
-
*/
|
|
88
48
|
function membershipSet(context, field, value) {
|
|
89
49
|
const roster = rosterOf(field);
|
|
90
50
|
if (roster === undefined) {
|
|
@@ -110,14 +70,6 @@ function membershipSet(context, field, value) {
|
|
|
110
70
|
const key = [...members].sort();
|
|
111
71
|
return { name: `∈ ${roster.name} ${JSON.stringify(key)}`, members: Object.freeze(members) };
|
|
112
72
|
}
|
|
113
|
-
/**
|
|
114
|
-
* Resolves a bindings record against an atom owner's matchable fields, in
|
|
115
|
-
* the record's written order: terms classify by their runtime tag,
|
|
116
|
-
* everything else is a bare literal. Every VARIABLE binding judges
|
|
117
|
-
* `fieldJoins(mintSlot, positionSlot)` and throws on a class-unequal reuse
|
|
118
|
-
* (the runtime twin of `CheckBindings`); the bound refs are collected for
|
|
119
|
-
* the rule's boundness set.
|
|
120
|
-
*/
|
|
121
73
|
function resolveBindings(context, label, relation, bindings) {
|
|
122
74
|
const entries = [];
|
|
123
75
|
const vars = [];
|
|
@@ -171,8 +123,6 @@ function resolveBindings(context, label, relation, bindings) {
|
|
|
171
123
|
}));
|
|
172
124
|
break;
|
|
173
125
|
}
|
|
174
|
-
case "duration":
|
|
175
|
-
throw errors.new(`${label}.${fieldName}: the measure is not a field-typed value — it lives in comparisons and find entries`);
|
|
176
126
|
}
|
|
177
127
|
}
|
|
178
128
|
else if (Array.isArray(value)) {
|
|
@@ -193,7 +143,6 @@ function resolveBindings(context, label, relation, bindings) {
|
|
|
193
143
|
}
|
|
194
144
|
return { atom: Object.freeze({ relation, bindings: Object.freeze(entries) }), vars, uses };
|
|
195
145
|
}
|
|
196
|
-
/** Extends a rule state with one positive atom; the bound variable references accumulate into the boundness set. */
|
|
197
146
|
function advanceMatch(context, state, relation, bindings) {
|
|
198
147
|
const resolved = resolveBindings(context, `relation ${relation.name}`, relation, bindings);
|
|
199
148
|
const bound = new Set(state.bound);
|
|
@@ -206,7 +155,6 @@ function advanceMatch(context, state, relation, bindings) {
|
|
|
206
155
|
paramUses: Object.freeze([...state.paramUses, ...resolved.uses])
|
|
207
156
|
});
|
|
208
157
|
}
|
|
209
|
-
/** Resolves one comparison side to its runtime term (variables and the measure ride by reference). */
|
|
210
158
|
function cmpTermDataOf(value) {
|
|
211
159
|
if (isTerm(value)) {
|
|
212
160
|
switch (value[term]) {
|
|
@@ -216,31 +164,15 @@ function cmpTermDataOf(value) {
|
|
|
216
164
|
return Object.freeze({ kind: "param", name: value.name });
|
|
217
165
|
case "setParam":
|
|
218
166
|
return Object.freeze({ kind: "setParam", name: value.name });
|
|
219
|
-
case "duration":
|
|
220
|
-
return Object.freeze({ kind: "measure", ref: value.over });
|
|
221
167
|
}
|
|
222
168
|
}
|
|
223
169
|
return Object.freeze({ kind: "literal", value });
|
|
224
170
|
}
|
|
225
|
-
/**
|
|
226
|
-
* One comparison side's contribution to the param census: a param/set side
|
|
227
|
-
* anchors to its SIBLING — a variable's field descriptor or the measure; an
|
|
228
|
-
* unanchorable use records with no anchor.
|
|
229
|
-
*/
|
|
230
171
|
function sideUses(op, side, sibling, uses) {
|
|
231
172
|
if (side.kind !== "param" && side.kind !== "setParam") {
|
|
232
173
|
return;
|
|
233
174
|
}
|
|
234
|
-
|
|
235
|
-
if (sibling.kind === "var") {
|
|
236
|
-
anchor = sibling.ref.field;
|
|
237
|
-
}
|
|
238
|
-
else if (sibling.kind === "measure") {
|
|
239
|
-
anchor = "measure";
|
|
240
|
-
}
|
|
241
|
-
else {
|
|
242
|
-
anchor = undefined;
|
|
243
|
-
}
|
|
175
|
+
const anchor = sibling.kind === "var" ? sibling.ref.field : undefined;
|
|
244
176
|
uses.push(Object.freeze({
|
|
245
177
|
name: side.name,
|
|
246
178
|
shape: side.kind === "param" ? "value" : "set",
|
|
@@ -249,7 +181,6 @@ function sideUses(op, side, sibling, uses) {
|
|
|
249
181
|
members: undefined
|
|
250
182
|
}));
|
|
251
183
|
}
|
|
252
|
-
/** Lowers one condition VALUE to its runtime data, recording param uses. */
|
|
253
184
|
function condDataOf(cond, uses) {
|
|
254
185
|
if (cond.cond === "cmp") {
|
|
255
186
|
const lhs = cmpTermDataOf(cond.lhs);
|
|
@@ -278,7 +209,6 @@ function condDataOf(cond, uses) {
|
|
|
278
209
|
}
|
|
279
210
|
throw errors.new("a negated atom is not a condition-tree node — pass not(...) to where() directly, never inside and()/or()");
|
|
280
211
|
}
|
|
281
|
-
/** Extends a rule state with one `.where` item (a condition or a negated atom). */
|
|
282
212
|
function advanceWhere(context, state, cond) {
|
|
283
213
|
if (typeof cond !== "object" || cond === null || !("cond" in cond)) {
|
|
284
214
|
throw errors.new("where() takes a comparison, an and()/or() tree, or a negated atom");
|
|
@@ -309,15 +239,6 @@ function advanceWhere(context, state, cond) {
|
|
|
309
239
|
paramUses: Object.freeze([...state.paramUses, ...uses])
|
|
310
240
|
});
|
|
311
241
|
}
|
|
312
|
-
/**
|
|
313
|
-
* Extends a rule state with one interior atom (a named record over head keys;
|
|
314
|
-
* vars validated at completion). A POSITIVE interior atom is a positive
|
|
315
|
-
* occurrence exactly as the engine represents it (`check_atoms` walks Interior
|
|
316
|
-
* and Edb in one loop), so its variables GROUND: they enter the rule's
|
|
317
|
-
* boundness set, may ride the head, and satisfy negation safety — the
|
|
318
|
-
* interior-only identity projection of a finished table is spellable with no
|
|
319
|
-
* re-grounding join. A NEGATED one binds nothing, only rejects.
|
|
320
|
-
*/
|
|
321
242
|
function advanceInterior(state, target, bindings, kind) {
|
|
322
243
|
const resolved = [];
|
|
323
244
|
for (const [key, value] of Object.entries(bindings)) {
|
|
@@ -341,7 +262,6 @@ function advanceInterior(state, target, bindings, kind) {
|
|
|
341
262
|
paramUses: state.paramUses
|
|
342
263
|
});
|
|
343
264
|
}
|
|
344
|
-
/** Narrows a find entry to an aggregate value. */
|
|
345
265
|
function isAggregateEntry(value) {
|
|
346
266
|
return typeof value === "object" && value !== null && "agg" in value;
|
|
347
267
|
}
|
|
@@ -352,7 +272,6 @@ function asVarTerm(context, value) {
|
|
|
352
272
|
}
|
|
353
273
|
throw errors.new(`${context}: expected a variable`);
|
|
354
274
|
}
|
|
355
|
-
/** Classifies one aggregate find entry into its runtime data (variables ride by reference). */
|
|
356
275
|
function aggDataOf(name, entry) {
|
|
357
276
|
if (entry.agg === "count") {
|
|
358
277
|
return Object.freeze({ op: "count" });
|
|
@@ -365,10 +284,7 @@ function aggDataOf(name, entry) {
|
|
|
365
284
|
if (isTerm(over) && over[term] === "var") {
|
|
366
285
|
return Object.freeze({ op: "fold", fold: entry.agg, over });
|
|
367
286
|
}
|
|
368
|
-
|
|
369
|
-
return Object.freeze({ op: "fold", fold: entry.agg, over: Object.freeze({ duration: over.over }) });
|
|
370
|
-
}
|
|
371
|
-
throw errors.new(`find ${name} (${entry.agg}): takes a variable or r.duration(v)`);
|
|
287
|
+
throw errors.new(`find ${name} (${entry.agg}): takes a variable`);
|
|
372
288
|
}
|
|
373
289
|
case "pack":
|
|
374
290
|
return Object.freeze({ op: "pack", over: asVarTerm(`find ${name} (pack)`, over) });
|
|
@@ -376,11 +292,6 @@ function aggDataOf(name, entry) {
|
|
|
376
292
|
throw errors.new(`find ${name}: unknown aggregate ${entry.agg}`);
|
|
377
293
|
}
|
|
378
294
|
}
|
|
379
|
-
/**
|
|
380
|
-
* Classifies one find entry into its named answer column (the KEY names the
|
|
381
|
-
* column, `count` included). The `slot`/`closed` slices are resolved LATER,
|
|
382
|
-
* at rule completion, where boundness and the mint slots are in hand.
|
|
383
|
-
*/
|
|
384
295
|
function findColumnOf(name, entry) {
|
|
385
296
|
if (isTerm(entry)) {
|
|
386
297
|
if (entry[term] === "var") {
|
|
@@ -391,15 +302,7 @@ function findColumnOf(name, entry) {
|
|
|
391
302
|
slot: undefined
|
|
392
303
|
});
|
|
393
304
|
}
|
|
394
|
-
|
|
395
|
-
return Object.freeze({
|
|
396
|
-
name,
|
|
397
|
-
entry: Object.freeze({ kind: "measure", over: entry.over }),
|
|
398
|
-
closed: undefined,
|
|
399
|
-
slot: undefined
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
throw errors.new(`find ${name}: a ${entry[term]} is not projectable — find takes variables, r.duration(v), or aggregates`);
|
|
305
|
+
throw errors.new(`find ${name}: a ${entry[term]} is not projectable — find takes variables or aggregates`);
|
|
403
306
|
}
|
|
404
307
|
if (isAggregateEntry(entry)) {
|
|
405
308
|
return Object.freeze({
|
|
@@ -409,43 +312,39 @@ function findColumnOf(name, entry) {
|
|
|
409
312
|
slot: undefined
|
|
410
313
|
});
|
|
411
314
|
}
|
|
412
|
-
throw errors.new(`find ${name}: not a find entry — find takes variables
|
|
315
|
+
throw errors.new(`find ${name}: not a find entry — find takes variables or aggregates`);
|
|
413
316
|
}
|
|
414
317
|
/**
|
|
415
|
-
* The orderable ban's pointed refusal
|
|
318
|
+
* The orderable ban's pointed refusal
|
|
416
319
|
* § orderability): a closed reference is equality-and-membership only.
|
|
417
320
|
*/
|
|
418
321
|
function closedOrderError(context, position, vocabulary) {
|
|
419
|
-
return errors.new(`${context}: ${position} is a ${vocabulary} reference — declaration order is an accident, not semantics: vocabularies do not order (
|
|
322
|
+
return errors.new(`${context}: ${position} is a ${vocabulary} reference — declaration order is an accident, not semantics: vocabularies do not order (equality, membership, and counting remain)`);
|
|
420
323
|
}
|
|
421
|
-
/** The comparison ops under the orderable ban (order roster + point membership). */
|
|
422
324
|
function isOrderOp(op) {
|
|
423
325
|
return op === "lt" || op === "le" || op === "gt" || op === "ge" || op === "pointIn";
|
|
424
326
|
}
|
|
425
|
-
/** Requires a variable to be bound by a relation atom of the rule (the boundness wall — invisible to the type tier). */
|
|
426
327
|
function assertBound(where, bound, ref) {
|
|
427
328
|
if (!bound.has(ref)) {
|
|
428
329
|
throw errors.new(`${where}: the variable ${ref.label} is not bound by a relation atom of the rule`);
|
|
429
330
|
}
|
|
430
331
|
}
|
|
431
|
-
/** Requires a variable to be interval-typed (the measure's and pack's domain), off its own descriptor. */
|
|
432
332
|
function assertInterval(where, ref) {
|
|
433
333
|
if (ref.field.kind !== "interval") {
|
|
434
334
|
throw errors.new(`${where}: ${ref.label} is not interval-typed — the measure is defined over interval-typed variables only`);
|
|
435
335
|
}
|
|
436
336
|
}
|
|
437
|
-
/** Requires a variable's own field to be non-closed (the orderable ban's runtime twin). */
|
|
438
337
|
function assertNotClosed(where, position, ref) {
|
|
439
338
|
const roster = rosterOf(ref.field);
|
|
440
339
|
if (roster !== undefined) {
|
|
441
340
|
throw closedOrderError(where, `${position} ${ref.label}`, roster.name);
|
|
442
341
|
}
|
|
443
342
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
343
|
+
function assertNumeric(where, position, ref) {
|
|
344
|
+
if (ref.field.kind !== "u64" && ref.field.kind !== "i64") {
|
|
345
|
+
throw errors.new(`${where}: ${position} ${ref.label} is ${ref.field.kind}, not numeric — a fold reads u64/i64 only`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
449
348
|
function findColumnSlotOf(context, column) {
|
|
450
349
|
const entry = column.entry;
|
|
451
350
|
if (entry.kind === "var") {
|
|
@@ -453,7 +352,6 @@ function findColumnSlotOf(context, column) {
|
|
|
453
352
|
}
|
|
454
353
|
return undefined;
|
|
455
354
|
}
|
|
456
|
-
/** Validates one find column's variable references (boundness + the orderable/interval walls, off the var's own field). */
|
|
457
355
|
function validateColumn(context, bound, column) {
|
|
458
356
|
const where = `${contextLabel(context)} find ${column.name}`;
|
|
459
357
|
const entry = column.entry;
|
|
@@ -461,23 +359,14 @@ function validateColumn(context, bound, column) {
|
|
|
461
359
|
assertBound(where, bound, entry.over);
|
|
462
360
|
return;
|
|
463
361
|
}
|
|
464
|
-
if (entry.kind === "measure") {
|
|
465
|
-
assertBound(where, bound, entry.over);
|
|
466
|
-
assertInterval(where, entry.over);
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
362
|
const agg = entry.agg;
|
|
470
363
|
switch (agg.op) {
|
|
471
364
|
case "count":
|
|
472
365
|
return;
|
|
473
366
|
case "fold": {
|
|
474
|
-
if ("duration" in agg.over) {
|
|
475
|
-
assertBound(where, bound, agg.over.duration);
|
|
476
|
-
assertInterval(where, agg.over.duration);
|
|
477
|
-
return;
|
|
478
|
-
}
|
|
479
367
|
assertBound(where, bound, agg.over);
|
|
480
368
|
assertNotClosed(where, `the ${agg.fold} input`, agg.over);
|
|
369
|
+
assertNumeric(where, `the ${agg.fold} input`, agg.over);
|
|
481
370
|
return;
|
|
482
371
|
}
|
|
483
372
|
case "pack":
|
|
@@ -486,11 +375,6 @@ function validateColumn(context, bound, column) {
|
|
|
486
375
|
return;
|
|
487
376
|
}
|
|
488
377
|
}
|
|
489
|
-
/**
|
|
490
|
-
* Validates one condition's variable references against the rule's bound
|
|
491
|
-
* set — and, for `eq`/`ne` over two variables, holds the class wall through
|
|
492
|
-
* the mint slots (the unification IS a join; bare pairs only with bare).
|
|
493
|
-
*/
|
|
494
378
|
function validateCond(context, bound, cond) {
|
|
495
379
|
const label = contextLabel(context);
|
|
496
380
|
if (cond.kind === "cmp") {
|
|
@@ -502,10 +386,6 @@ function validateCond(context, bound, cond) {
|
|
|
502
386
|
throw closedOrderError(label, `the ${cond.op.kind} side ${side.ref.label}`, roster.name);
|
|
503
387
|
}
|
|
504
388
|
}
|
|
505
|
-
if (side.kind === "measure") {
|
|
506
|
-
assertBound(label, bound, side.ref);
|
|
507
|
-
assertInterval(label, side.ref);
|
|
508
|
-
}
|
|
509
389
|
}
|
|
510
390
|
if ((cond.op.kind === "eq" || cond.op.kind === "ne") && cond.lhs.kind === "var" && cond.rhs.kind === "var") {
|
|
511
391
|
assertBound(label, bound, cond.lhs.ref);
|
|
@@ -522,16 +402,6 @@ function validateCond(context, bound, cond) {
|
|
|
522
402
|
validateCond(context, bound, child);
|
|
523
403
|
}
|
|
524
404
|
}
|
|
525
|
-
/**
|
|
526
|
-
* Validates one interior item: every head column of the table is bound exactly
|
|
527
|
-
* once (a missing or extra key is a pointed error) and each variable joins
|
|
528
|
-
* its head column's classed slot. A POSITIVE interior atom GROUNDS its
|
|
529
|
-
* variables (a positive occurrence, exactly the engine's representation),
|
|
530
|
-
* so no boundness precondition exists; a NEGATED one binds nothing — its
|
|
531
|
-
* variables must be positively bound elsewhere in the rule, the same
|
|
532
|
-
* safety rule as EDB negation. When the table's own first rule is in flight
|
|
533
|
-
* (`finds` empty), the completing rule's OWN find columns ARE the head.
|
|
534
|
-
*/
|
|
535
405
|
function validateInterior(context, bound, item, columns) {
|
|
536
406
|
const label = contextLabel(context);
|
|
537
407
|
const headColumns = item.target.finds.length > 0 ? item.target.finds : columns;
|
|
@@ -567,12 +437,6 @@ function validateInterior(context, bound, item, columns) {
|
|
|
567
437
|
}
|
|
568
438
|
}
|
|
569
439
|
}
|
|
570
|
-
/**
|
|
571
|
-
* Completes one rule: enriches the find columns (declaration-order-safe
|
|
572
|
-
* keys, boundness validated, each column's classed slot and closed slice
|
|
573
|
-
* resolved), then walks the body walls — negated-atom boundness safety, interior
|
|
574
|
-
* head pairing, and condition validation.
|
|
575
|
-
*/
|
|
576
440
|
function completeRule(context, state, rawColumns) {
|
|
577
441
|
const label = contextLabel(context);
|
|
578
442
|
if (rawColumns.length === 0) {
|
|
@@ -601,11 +465,9 @@ function completeRule(context, state, rawColumns) {
|
|
|
601
465
|
}
|
|
602
466
|
return Object.freeze({ items: state.items, finds: Object.freeze(columns), paramUses: state.paramUses });
|
|
603
467
|
}
|
|
604
|
-
/** Builds one typed rule value over completed rule data. */
|
|
605
468
|
function makeRuleValue(rule) {
|
|
606
469
|
return Object.freeze({ rule });
|
|
607
470
|
}
|
|
608
|
-
/** The diagnostic label of a chain context. */
|
|
609
471
|
function contextLabel(context) {
|
|
610
472
|
switch (context.kind) {
|
|
611
473
|
case "query":
|
|
@@ -618,11 +480,9 @@ function contextLabel(context) {
|
|
|
618
480
|
return `rec ${context.self.name} rec`;
|
|
619
481
|
}
|
|
620
482
|
}
|
|
621
|
-
/** RecHandle is name-only staging; RecHead/RecData carry the sealed finds. */
|
|
622
483
|
function isRecHead(rec) {
|
|
623
484
|
return Array.isArray(rec.finds);
|
|
624
485
|
}
|
|
625
|
-
/** Resolves a derived-table name against the context's visible tables. */
|
|
626
486
|
function lookupDerived(context, name) {
|
|
627
487
|
const interior = context.interiors.find(function byName(candidate) {
|
|
628
488
|
return candidate.name === name;
|
|
@@ -648,22 +508,15 @@ function lookupDerived(context, name) {
|
|
|
648
508
|
}
|
|
649
509
|
throw errors.new(`${contextLabel(context)}: no derived table named ${name} is in scope`);
|
|
650
510
|
}
|
|
651
|
-
/** Validates and records one interior atom per the context's cut. */
|
|
652
511
|
function interiorAdvance(context, state, name, bindings) {
|
|
653
512
|
return advanceInterior(state, lookupDerived(context, name), bindings, "interior");
|
|
654
513
|
}
|
|
655
|
-
/**
|
|
656
|
-
* Validates and records one NEGATED finished-table atom — main and interior
|
|
657
|
-
* rules: a finished set is a set. Rec bodies refuse every negation
|
|
658
|
-
* (`NegationInRec` — self is the wall; EDB / earlier-interior is this-cut).
|
|
659
|
-
*/
|
|
660
514
|
function notInteriorAdvance(context, state, name, bindings) {
|
|
661
515
|
if (context.kind === "rec-base" || context.kind === "rec-arm") {
|
|
662
516
|
throw errors.new(`rec ${context.self.name}: a rec rule negates no table — self-negation is negation through the cycle (a finished set is what keeps the operator monotone), and a finished table's fold belongs in the main rules`);
|
|
663
517
|
}
|
|
664
518
|
return advanceInterior(state, lookupDerived(context, name), bindings, "negatedInterior");
|
|
665
519
|
}
|
|
666
|
-
/** Classifies one find record per the context (interior and rec heads project bound variables only). */
|
|
667
520
|
function findColumns(context, entries) {
|
|
668
521
|
const columns = [];
|
|
669
522
|
const derivedHead = context.kind !== "query";
|
|
@@ -679,7 +532,6 @@ function findColumns(context, entries) {
|
|
|
679
532
|
}
|
|
680
533
|
return columns;
|
|
681
534
|
}
|
|
682
|
-
/** Builds one runtime chain (immutably — every step is a fresh chain over fresh state). */
|
|
683
535
|
function makeRawChain(context, state) {
|
|
684
536
|
const chain = {
|
|
685
537
|
match(relation, bindings) {
|
|
@@ -698,7 +550,6 @@ function makeRawChain(context, state) {
|
|
|
698
550
|
Object.freeze(chain);
|
|
699
551
|
return chain;
|
|
700
552
|
}
|
|
701
|
-
/** Builds one runtime rule-builder over a context. */
|
|
702
553
|
function makeRawScope(context) {
|
|
703
554
|
const scope = {
|
|
704
555
|
...termOps,
|
|
@@ -724,7 +575,6 @@ function makeRawScope(context) {
|
|
|
724
575
|
function isTypedScope(scope) {
|
|
725
576
|
return typeof scope.match === "function";
|
|
726
577
|
}
|
|
727
|
-
/** Builds one query-rule builder (the typed face of the raw builder). */
|
|
728
578
|
function makeQueryRuleScope(theory, env) {
|
|
729
579
|
const raw = makeRawScope({ kind: "query", classes: theory.classes, theory, ...env });
|
|
730
580
|
if (!isTypedScope(raw)) {
|
|
@@ -732,7 +582,6 @@ function makeQueryRuleScope(theory, env) {
|
|
|
732
582
|
}
|
|
733
583
|
return raw;
|
|
734
584
|
}
|
|
735
|
-
/** Builds one interior-rule builder. */
|
|
736
585
|
function makeInteriorRuleScope(theory, env, self) {
|
|
737
586
|
const raw = makeRawScope({ kind: "interior", self, classes: theory.classes, theory, ...env });
|
|
738
587
|
if (!isTypedScope(raw)) {
|
|
@@ -761,14 +610,12 @@ function makeRecRuleScope(theory, env, self, kind) {
|
|
|
761
610
|
}
|
|
762
611
|
return raw;
|
|
763
612
|
}
|
|
764
|
-
/** Renders one head column's closed slice for the rule-alignment check's diagnostics. */
|
|
765
613
|
function renderClosedSlice(closed) {
|
|
766
614
|
return closed === undefined ? "a bare value" : `a ${closed.name} reference`;
|
|
767
615
|
}
|
|
768
|
-
/** Renders one head column's signature for the rule-alignment check. */
|
|
769
616
|
function headSignature(column) {
|
|
770
617
|
const entry = column.entry;
|
|
771
|
-
if (entry.kind === "var"
|
|
618
|
+
if (entry.kind === "var") {
|
|
772
619
|
return `${column.name}:var`;
|
|
773
620
|
}
|
|
774
621
|
const agg = entry.agg;
|
|
@@ -777,11 +624,6 @@ function headSignature(column) {
|
|
|
777
624
|
}
|
|
778
625
|
return `${column.name}:${agg.op}`;
|
|
779
626
|
}
|
|
780
|
-
/** The roster a param anchor carries: present exactly on a closed-reference field anchor. */
|
|
781
|
-
function anchorRosterOf(anchor) {
|
|
782
|
-
return anchor === "measure" ? undefined : rosterOf(anchor);
|
|
783
|
-
}
|
|
784
|
-
/** Renders one param anchor's closedness for the registry's coherence diagnostics. */
|
|
785
627
|
function renderParamAnchor(roster) {
|
|
786
628
|
return roster === undefined ? "a non-closed position" : `a ${roster.name} reference`;
|
|
787
629
|
}
|
|
@@ -820,8 +662,8 @@ function paramRegistryOf(interiors, rec, rules) {
|
|
|
820
662
|
throw errors.new(`query param ${use.name} is used both as a ${existing.shape} param and a ${use.shape} param — one name, one shape`);
|
|
821
663
|
}
|
|
822
664
|
if (existing.anchor !== undefined && use.anchor !== undefined) {
|
|
823
|
-
const registered =
|
|
824
|
-
const anchored =
|
|
665
|
+
const registered = rosterOf(existing.anchor);
|
|
666
|
+
const anchored = rosterOf(use.anchor);
|
|
825
667
|
if (registered !== anchored) {
|
|
826
668
|
throw errors.new(`query param ${use.name} is anchored at ${renderParamAnchor(registered)} and at ${renderParamAnchor(anchored)} — a closed-anchored param translates handle names through ONE roster (one name, one domain); name the params differently`);
|
|
827
669
|
}
|
|
@@ -853,14 +695,6 @@ function paramRegistryOf(interiors, rec, rules) {
|
|
|
853
695
|
if (entry === undefined) {
|
|
854
696
|
throw errors.new(`query param ${name} lost its registry entry`);
|
|
855
697
|
}
|
|
856
|
-
/**
|
|
857
|
-
* A membership array's handle names are program constants, so the
|
|
858
|
-
* entry stores the resolved IMAGE: each name rides the one
|
|
859
|
-
* roster-verification point (`taggedHandleId`, through
|
|
860
|
-
* `taggedCmpLiteral`) exactly once, HERE — an out-of-roster name
|
|
861
|
-
* fails at build, and every execute returns this frozen value by
|
|
862
|
-
* reference.
|
|
863
|
-
*/
|
|
864
698
|
let membership;
|
|
865
699
|
if (entry.members !== undefined) {
|
|
866
700
|
const anchor = entry.anchor;
|
|
@@ -877,7 +711,6 @@ function paramRegistryOf(interiors, rec, rules) {
|
|
|
877
711
|
return Object.freeze({ name, shape: entry.shape, anchor: entry.anchor, op: entry.op, membership });
|
|
878
712
|
}));
|
|
879
713
|
}
|
|
880
|
-
/** Asserts every rule in a list derives the same head (name, aggregate shape, closed slice, class). */
|
|
881
714
|
function assertAlignedHeads(label, rules) {
|
|
882
715
|
const first = rules[0];
|
|
883
716
|
if (first === undefined) {
|
|
@@ -906,13 +739,6 @@ function assertAlignedHeads(label, rules) {
|
|
|
906
739
|
function afterMainError(what) {
|
|
907
740
|
return errors.new(`query: ${what} after a main rule is unwritable — declaration order is interiors, then rec, then main`);
|
|
908
741
|
}
|
|
909
|
-
/**
|
|
910
|
-
* Assembles the runtime query value over completed rules: every rule must
|
|
911
|
-
* derive the SAME head (name and aggregate shape, position for position —
|
|
912
|
-
* the decode labels and the engine's alignment rule agree), and the param
|
|
913
|
-
* registry folds in query-walk order. CQ lowering does not mention rec;
|
|
914
|
-
* Reach carries RecData by value.
|
|
915
|
-
*/
|
|
916
742
|
function makeRawQuery(theory, interiors, rec, rules) {
|
|
917
743
|
assertAlignedHeads("a query", rules);
|
|
918
744
|
const first = rules[0];
|
|
@@ -959,7 +785,6 @@ function makeRawQuery(theory, interiors, rec, rules) {
|
|
|
959
785
|
function makeQuery(theory, interiors, rec, rules) {
|
|
960
786
|
return makeRawQuery(theory, interiors, rec, rules);
|
|
961
787
|
}
|
|
962
|
-
/** Collects one Interior from its builders. */
|
|
963
788
|
function collectInterior(theory, env, name, builds) {
|
|
964
789
|
if (builds.length === 0) {
|
|
965
790
|
throw errors.new(`query: interior ${name} needs at least one rule`);
|
|
@@ -974,7 +799,6 @@ function collectInterior(theory, env, name, builds) {
|
|
|
974
799
|
}
|
|
975
800
|
return Object.freeze({ name, finds: first.finds, rules: Object.freeze(rules) });
|
|
976
801
|
}
|
|
977
|
-
/** Collects the Rec from tagged base/rec builder arrays. */
|
|
978
802
|
function collectRec(theory, interiors, name, baseBuilds, recBuilds) {
|
|
979
803
|
if (baseBuilds.length === 0) {
|
|
980
804
|
throw errors.new(`query: rec ${name} has no base arms`);
|
|
@@ -1017,7 +841,6 @@ function collectRec(theory, interiors, name, baseBuilds, recBuilds) {
|
|
|
1017
841
|
});
|
|
1018
842
|
return recData;
|
|
1019
843
|
}
|
|
1020
|
-
/** Builds the CQ query start (interiors, then reach or the first main rule). */
|
|
1021
844
|
function makeQueryStart(theory, interiors) {
|
|
1022
845
|
const env = { interiors };
|
|
1023
846
|
const start = {
|
|
@@ -1053,7 +876,6 @@ function makeQueryStart(theory, interiors) {
|
|
|
1053
876
|
Object.freeze(start);
|
|
1054
877
|
return start;
|
|
1055
878
|
}
|
|
1056
|
-
/** Builds the Reach query start (rec sealed; only the first main rule remains). */
|
|
1057
879
|
function makeQueryReachStart(theory, interiors, rec) {
|
|
1058
880
|
const env = { interiors, rec };
|
|
1059
881
|
const start = {
|
|
@@ -1065,21 +887,9 @@ function makeQueryReachStart(theory, interiors, rec) {
|
|
|
1065
887
|
Object.freeze(start);
|
|
1066
888
|
return start;
|
|
1067
889
|
}
|
|
1068
|
-
/**
|
|
1069
|
-
* Opens a query over a schema: `query(S).rule(r => ...)`, optionally with
|
|
1070
|
-
* `interior` / `reach` first. Each `.rule` adds one conjunctive rule;
|
|
1071
|
-
* multiple rules are the set union. The schema's law-computed class map and
|
|
1072
|
-
* theory value ride into every rule builder — the join walls compare
|
|
1073
|
-
* against the mint slots off it.
|
|
1074
|
-
*/
|
|
1075
890
|
function query(theory) {
|
|
1076
891
|
return makeQueryStart(theory, []);
|
|
1077
892
|
}
|
|
1078
|
-
/**
|
|
1079
|
-
* Tags one closed-reference literal: the handle NAME, verified against the
|
|
1080
|
-
* roster and translated to its declaration-order row id, tagged u64. THE
|
|
1081
|
-
* single roster-verification point of the query surface.
|
|
1082
|
-
*/
|
|
1083
893
|
function taggedHandleId(context, closed, value) {
|
|
1084
894
|
if (typeof value !== "string") {
|
|
1085
895
|
throw literalShapeError(context, `a ${closed.name} handle name (string)`, value);
|
|
@@ -1090,11 +900,6 @@ function taggedHandleId(context, closed, value) {
|
|
|
1090
900
|
}
|
|
1091
901
|
return { kind: "u64", value: BigInt(id) };
|
|
1092
902
|
}
|
|
1093
|
-
/**
|
|
1094
|
-
* Tags one literal in an interval element domain: a bigint tags as the
|
|
1095
|
-
* element (the membership typing rule's point side), an interval-shaped
|
|
1096
|
-
* value as the interval (value equality).
|
|
1097
|
-
*/
|
|
1098
903
|
function taggedAtElementDomain(context, element, value) {
|
|
1099
904
|
if (typeof value === "bigint") {
|
|
1100
905
|
if (element === "u64") {
|
|
@@ -1110,10 +915,6 @@ function taggedAtElementDomain(context, element, value) {
|
|
|
1110
915
|
}
|
|
1111
916
|
throw literalShapeError(context, "bigint (point) or { start, end } (interval)", value);
|
|
1112
917
|
}
|
|
1113
|
-
/**
|
|
1114
|
-
* Tags one host literal at a FIELD position (atom bindings): the field's
|
|
1115
|
-
* structural kind directs the tag, never a guess.
|
|
1116
|
-
*/
|
|
1117
918
|
function taggedLiteral(context, field, value) {
|
|
1118
919
|
const roster = rosterOf(field);
|
|
1119
920
|
if (roster !== undefined) {
|
|
@@ -1167,12 +968,6 @@ function taggedLiteral(context, field, value) {
|
|
|
1167
968
|
* interval shape against a scalar sibling stays refused.
|
|
1168
969
|
*/
|
|
1169
970
|
function taggedCmpLiteral(context, sibling, value, op) {
|
|
1170
|
-
if (sibling === "measure") {
|
|
1171
|
-
if (typeof value !== "bigint") {
|
|
1172
|
-
throw literalShapeError(context, "bigint (the measure is u64)", value);
|
|
1173
|
-
}
|
|
1174
|
-
return { kind: "u64", value };
|
|
1175
|
-
}
|
|
1176
971
|
if (rosterOf(sibling) === undefined && sibling.kind === "interval") {
|
|
1177
972
|
return taggedAtElementDomain(context, sibling.element, value);
|
|
1178
973
|
}
|
|
@@ -1184,7 +979,6 @@ function taggedCmpLiteral(context, sibling, value, op) {
|
|
|
1184
979
|
}
|
|
1185
980
|
return taggedLiteral(context, sibling, value);
|
|
1186
981
|
}
|
|
1187
|
-
/** Creates one rule-scoped variable numberer. */
|
|
1188
982
|
function freshVarIds() {
|
|
1189
983
|
const assigned = new Map();
|
|
1190
984
|
return {
|
|
@@ -1199,7 +993,6 @@ function freshVarIds() {
|
|
|
1199
993
|
}
|
|
1200
994
|
};
|
|
1201
995
|
}
|
|
1202
|
-
/** Resolves a param name to its dense positional id. */
|
|
1203
996
|
function paramIdOf(ctx, name) {
|
|
1204
997
|
const id = ctx.paramIds.get(name);
|
|
1205
998
|
if (id === undefined) {
|
|
@@ -1207,10 +1000,6 @@ function paramIdOf(ctx, name) {
|
|
|
1207
1000
|
}
|
|
1208
1001
|
return id;
|
|
1209
1002
|
}
|
|
1210
|
-
/**
|
|
1211
|
-
* Lowers one EDB atom (either polarity). A CLOSED owner lowers through the
|
|
1212
|
-
* same edb source, with field ordinals over the SEALED shape.
|
|
1213
|
-
*/
|
|
1214
1003
|
function lowerAtom(ctx, atom, ids) {
|
|
1215
1004
|
const member = ctx.theory.relations[atom.relation.name];
|
|
1216
1005
|
if (member !== atom.relation) {
|
|
@@ -1232,7 +1021,6 @@ function lowerAtom(ctx, atom, ids) {
|
|
|
1232
1021
|
});
|
|
1233
1022
|
return { source: { kind: "edb", relation: relationId }, bindings };
|
|
1234
1023
|
}
|
|
1235
|
-
/** Lowers one binding term. A membership ARRAY lowers to the existing param-set term over its content-addressed entry. */
|
|
1236
1024
|
function lowerBindingTerm(ctx, context, binding, ids) {
|
|
1237
1025
|
const bound = binding.term;
|
|
1238
1026
|
switch (bound.kind) {
|
|
@@ -1273,7 +1061,6 @@ function lowerInteriorAtom(ctx, target, bindings, ids) {
|
|
|
1273
1061
|
});
|
|
1274
1062
|
return { source: { kind: "interior", interior }, bindings: irBindings };
|
|
1275
1063
|
}
|
|
1276
|
-
/** Lowers one comparison side; literals tag by the sibling's anchor (op-aware at `pointIn`). */
|
|
1277
1064
|
function lowerCmpTerm(ctx, side, sibling, ids, op) {
|
|
1278
1065
|
switch (side.kind) {
|
|
1279
1066
|
case "var":
|
|
@@ -1282,31 +1069,24 @@ function lowerCmpTerm(ctx, side, sibling, ids, op) {
|
|
|
1282
1069
|
return { kind: "param", param: paramIdOf(ctx, side.name) };
|
|
1283
1070
|
case "setParam":
|
|
1284
1071
|
return { kind: "paramSet", param: paramIdOf(ctx, side.name) };
|
|
1285
|
-
case "measure":
|
|
1286
|
-
return { kind: "measure", var: ids.of(side.ref) };
|
|
1287
1072
|
case "literal": {
|
|
1288
1073
|
const anchor = cmpAnchorOf(ctx, sibling);
|
|
1289
1074
|
if (anchor === undefined) {
|
|
1290
|
-
throw errors.new("query lowering: a comparison literal needs a bound-variable
|
|
1075
|
+
throw errors.new("query lowering: a comparison literal needs a bound-variable or anchored-param sibling to type it");
|
|
1291
1076
|
}
|
|
1292
1077
|
return { kind: "literal", value: taggedCmpLiteral("comparison literal", anchor, side.value, op) };
|
|
1293
1078
|
}
|
|
1294
1079
|
}
|
|
1295
1080
|
}
|
|
1296
|
-
/** Resolves the anchor a comparison literal tags by: the sibling variable's field, the measure, or an anchored param. */
|
|
1297
1081
|
function cmpAnchorOf(ctx, sibling) {
|
|
1298
1082
|
if (sibling.kind === "var") {
|
|
1299
1083
|
return sibling.ref.field;
|
|
1300
1084
|
}
|
|
1301
|
-
if (sibling.kind === "measure") {
|
|
1302
|
-
return "measure";
|
|
1303
|
-
}
|
|
1304
1085
|
if (sibling.kind === "param" || sibling.kind === "setParam") {
|
|
1305
1086
|
return ctx.params.get(sibling.name)?.anchor;
|
|
1306
1087
|
}
|
|
1307
1088
|
return undefined;
|
|
1308
1089
|
}
|
|
1309
|
-
/** Lowers one comparison. */
|
|
1310
1090
|
function lowerComparison(ctx, cmp, ids) {
|
|
1311
1091
|
if (cmp.op.kind === "allen") {
|
|
1312
1092
|
return {
|
|
@@ -1321,7 +1101,6 @@ function lowerComparison(ctx, cmp, ids) {
|
|
|
1321
1101
|
rhs: lowerCmpTerm(ctx, cmp.rhs, cmp.lhs, ids, cmp.op.kind)
|
|
1322
1102
|
};
|
|
1323
1103
|
}
|
|
1324
|
-
/** Lowers one condition node (comparison leaf or and/or tree). */
|
|
1325
1104
|
function lowerCondition(ctx, cond, ids) {
|
|
1326
1105
|
if (cond.kind === "cmp") {
|
|
1327
1106
|
return { kind: "leaf", cmp: lowerComparison(ctx, cond, ids) };
|
|
@@ -1333,29 +1112,20 @@ function lowerCondition(ctx, cond, ids) {
|
|
|
1333
1112
|
})
|
|
1334
1113
|
};
|
|
1335
1114
|
}
|
|
1336
|
-
/** Lowers one find entry to its per-rule find term. */
|
|
1337
1115
|
function lowerFind(entry, ids) {
|
|
1338
1116
|
if (entry.kind === "var") {
|
|
1339
1117
|
return { kind: "var", var: ids.of(entry.over) };
|
|
1340
1118
|
}
|
|
1341
|
-
if (entry.kind === "measure") {
|
|
1342
|
-
return { kind: "measure", var: ids.of(entry.over) };
|
|
1343
|
-
}
|
|
1344
1119
|
const agg = entry.agg;
|
|
1345
1120
|
switch (agg.op) {
|
|
1346
1121
|
case "count":
|
|
1347
1122
|
return { kind: "count" };
|
|
1348
|
-
case "fold":
|
|
1349
|
-
if ("duration" in agg.over) {
|
|
1350
|
-
return { kind: "aggregateMeasure", op: { kind: agg.fold }, over: ids.of(agg.over.duration) };
|
|
1351
|
-
}
|
|
1123
|
+
case "fold":
|
|
1352
1124
|
return { kind: "aggregate", op: { kind: agg.fold }, over: ids.of(agg.over) };
|
|
1353
|
-
}
|
|
1354
1125
|
case "pack":
|
|
1355
1126
|
return { kind: "pack", over: ids.of(agg.over) };
|
|
1356
1127
|
}
|
|
1357
1128
|
}
|
|
1358
|
-
/** One aggregate's var-free head-op kind (`AggOp::head_op`). */
|
|
1359
1129
|
function headOpOf(agg) {
|
|
1360
1130
|
switch (agg.op) {
|
|
1361
1131
|
case "count":
|
|
@@ -1366,15 +1136,13 @@ function headOpOf(agg) {
|
|
|
1366
1136
|
return "pack";
|
|
1367
1137
|
}
|
|
1368
1138
|
}
|
|
1369
|
-
/** One find entry's var-free head shape. */
|
|
1370
1139
|
function headTermOf(column) {
|
|
1371
1140
|
const entry = column.entry;
|
|
1372
|
-
if (entry.kind === "var"
|
|
1141
|
+
if (entry.kind === "var") {
|
|
1373
1142
|
return { kind: "var" };
|
|
1374
1143
|
}
|
|
1375
1144
|
return { kind: "aggregate", op: headOpOf(entry.agg) };
|
|
1376
1145
|
}
|
|
1377
|
-
/** Lowers one rule: body walked in written order (var ids by first occurrence), finds last. */
|
|
1378
1146
|
function lowerRule(ctx, rule) {
|
|
1379
1147
|
const ids = freshVarIds();
|
|
1380
1148
|
const atoms = [];
|
|
@@ -1413,11 +1181,6 @@ function lowerRule(ctx, rule) {
|
|
|
1413
1181
|
conditions
|
|
1414
1182
|
};
|
|
1415
1183
|
}
|
|
1416
|
-
/**
|
|
1417
|
-
* Lowers a query value to the bridge's `QueryIr` — pure and stable:
|
|
1418
|
-
* interiors in declaration order, then CQ or Reach, then main. Every
|
|
1419
|
-
* registered param must carry a field anchor by now.
|
|
1420
|
-
*/
|
|
1421
1184
|
function lowerQuery(q) {
|
|
1422
1185
|
const theory = q.schema;
|
|
1423
1186
|
const relationIds = new Map();
|