@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/src/query/lower.ts
CHANGED
|
@@ -1,36 +1,3 @@
|
|
|
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
|
-
|
|
34
1
|
import * as errors from "@superbuilders/errors"
|
|
35
2
|
import { sealedFieldsOf } from "#closed.ts"
|
|
36
3
|
import type { AnyField, ClosedRoster } from "#fields.ts"
|
|
@@ -80,97 +47,60 @@ import { allen, and, eq, ge, gt, le, lt, ne, not, or, pointIn } from "#query/ato
|
|
|
80
47
|
import type { CheckFind, CheckRecFind, FindShape, HeadRecordOf, RowOfFind } from "#query/find.ts"
|
|
81
48
|
import { count, max, min, pack, sum } from "#query/find.ts"
|
|
82
49
|
import { parseQueryIr } from "#query/parse-ir.ts"
|
|
83
|
-
import type {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
50
|
+
import type {
|
|
51
|
+
AnyVar,
|
|
52
|
+
ClassedField,
|
|
53
|
+
ExactVars,
|
|
54
|
+
Flatten,
|
|
55
|
+
InferredOf,
|
|
56
|
+
ParamEntry,
|
|
57
|
+
ParamsRecord,
|
|
58
|
+
ShapeOf,
|
|
59
|
+
VarsOf
|
|
93
60
|
} from "#query/scope.ts"
|
|
61
|
+
import { fieldJoins, inferred, isTerm, makeParam, makeSetParam, renderFieldKind, term } from "#query/scope.ts"
|
|
94
62
|
import type { AnySchema, Schema, SchemaRelations } from "#schema.ts"
|
|
95
63
|
|
|
96
|
-
/**
|
|
97
|
-
* The matchable members of a schema's record — ordinary relations AND
|
|
98
|
-
* closed vocabularies (ψ query atoms; the ENGINE decides folding vs virtual
|
|
99
|
-
* image, the SDK lowers pass-through).
|
|
100
|
-
*/
|
|
101
64
|
type QueryRelation<Rels extends SchemaRelations> = Extract<Rels[keyof Rels], MatchOwner>
|
|
102
65
|
|
|
103
|
-
/** Reads an inferred-params carrier off a rec reference or rule value. */
|
|
104
66
|
type ParamsOf<T> = InferredOf<T> extends { readonly params: infer P extends ParamsRecord } ? P : Record<never, never>
|
|
105
67
|
|
|
106
|
-
/** Reads an inferred-row carrier off a rule value or query. */
|
|
107
68
|
type RowOf<T> = InferredOf<T> extends { readonly row: infer R } ? R : never
|
|
108
69
|
|
|
109
|
-
/**
|
|
110
|
-
* A derived table's HEAD signature as classed slots, keyed by column
|
|
111
|
-
* name; `undefined` on values that carry no head.
|
|
112
|
-
*/
|
|
113
70
|
type HeadShape = Readonly<Record<string, ClassedField>> | undefined
|
|
114
71
|
|
|
115
|
-
/**
|
|
116
|
-
* One finished rule as a plain value: the runtime data plus the inferred
|
|
117
|
-
* row/params carrier (and, for an interior or rec rule, the head
|
|
118
|
-
* record of classed slots an `.interior(name)` join pairs against).
|
|
119
|
-
* `.rule(...)` consumes it.
|
|
120
|
-
*/
|
|
121
72
|
interface RuleValue<Row, P extends ParamsRecord, Head extends HeadShape = undefined> {
|
|
122
73
|
readonly rule: RuleData
|
|
123
74
|
readonly [inferred]?: { readonly row: Row; readonly params: P; readonly head: Head }
|
|
124
75
|
}
|
|
125
76
|
|
|
126
|
-
/** Any finished rule value. */
|
|
127
77
|
type AnyRuleValue = RuleValue<unknown, ParamsRecord, HeadShape>
|
|
128
78
|
|
|
129
|
-
/** Reads an inferred-head carrier off a rule value or rec reference. */
|
|
130
79
|
type HeadOf<T> =
|
|
131
80
|
InferredOf<T> extends { readonly head: infer H extends Readonly<Record<string, ClassedField>> } ? H : undefined
|
|
132
81
|
|
|
133
|
-
/** One `.interior(name)` position's judgment: a variable. */
|
|
134
82
|
type InteriorBindingOk<V> = V extends AnyVar ? true : false
|
|
135
83
|
|
|
136
|
-
/**
|
|
137
|
-
* The validated `.interior(name)` bindings record: every entry must be a
|
|
138
|
-
* variable. Arity and class against the named table's head are
|
|
139
|
-
* construction-time (the name is a string, so the head is not a type-level
|
|
140
|
-
* fact).
|
|
141
|
-
*/
|
|
142
84
|
type CheckInteriorBindings<B> = {
|
|
143
85
|
readonly [K in keyof B]: InteriorBindingOk<B[K]> extends true ? B[K] : never
|
|
144
86
|
}
|
|
145
87
|
|
|
146
|
-
/** One interior-rule builder function. */
|
|
147
88
|
type InteriorBuild<Rels extends SchemaRelations, Classes extends SchemaClasses = SchemaClasses> = (
|
|
148
89
|
r: InteriorRuleScope<Rels, Classes>
|
|
149
90
|
) => AnyRuleValue
|
|
150
91
|
|
|
151
|
-
/** One rec-arm builder function. */
|
|
152
92
|
type RecBuild<Rels extends SchemaRelations, Classes extends SchemaClasses = SchemaClasses> = (
|
|
153
93
|
r: RecRuleScope<Rels, Classes>
|
|
154
94
|
) => AnyRuleValue
|
|
155
95
|
|
|
156
|
-
/** A build function's rule value. */
|
|
157
96
|
type BuiltRule<F> = F extends (r: never) => infer RV ? RV : never
|
|
158
97
|
|
|
159
|
-
/** The intersected params record of a tuple of rule builds. */
|
|
160
98
|
type BuildsParams<Builds extends readonly ((r: never) => AnyRuleValue)[]> = ShapeOf<ParamsOf<BuiltRule<Builds[number]>>>
|
|
161
99
|
|
|
162
|
-
/**
|
|
163
|
-
* The term/predicate/aggregate constructor vocabulary every rule builder
|
|
164
|
-
* carries — pure value builders. Variables are minted by the free {@link v},
|
|
165
|
-
* outside the rule, and reused by reference; `r` no longer mints them.
|
|
166
|
-
*/
|
|
167
100
|
interface TermOps {
|
|
168
|
-
/** Names one scalar parameter: typed by its use; the key of the execute params object. */
|
|
169
101
|
readonly param: typeof makeParam
|
|
170
|
-
|
|
102
|
+
|
|
171
103
|
readonly inSet: typeof makeSetParam
|
|
172
|
-
/** The measure of an interval-typed variable: `|[s, e)| = e − s`, u64. */
|
|
173
|
-
readonly duration: typeof makeDuration
|
|
174
104
|
readonly eq: typeof eq
|
|
175
105
|
readonly ne: typeof ne
|
|
176
106
|
readonly lt: typeof lt
|
|
@@ -189,52 +119,85 @@ interface TermOps {
|
|
|
189
119
|
readonly pack: typeof pack
|
|
190
120
|
}
|
|
191
121
|
|
|
192
|
-
/** The rule builder a `query(S).rule(...)` callback receives (`Classes` — the join judge's authority). */
|
|
193
122
|
interface QueryRuleScope<Rels extends SchemaRelations, Classes extends SchemaClasses = SchemaClasses> extends TermOps {
|
|
194
|
-
/**
|
|
123
|
+
/**
|
|
124
|
+
* The FULL binding: every column of R bound to its own v(R) mint — the
|
|
125
|
+
* identity atom, stated as a signature so it holds for GENERIC R too
|
|
126
|
+
* (VarsOf unifies with itself by identity; the general form's deferred
|
|
127
|
+
* conditionals cannot). The mint invariant — a variable's mint slot IS its
|
|
128
|
+
* position slot, same owner, same column — discharges the join judgment by
|
|
129
|
+
* construction (proposals/one-representation/50-generic-binding.md, "The
|
|
130
|
+
* ruling"); an all-var record contributes no params, so the chain starts
|
|
131
|
+
* paramless. {@link ExactVars} maps a foreign key to `never`, so an
|
|
132
|
+
* aliased extra-key record falls to the general form's judgment.
|
|
133
|
+
*/
|
|
134
|
+
match<R extends QueryRelation<Rels>, B extends VarsOf<R>>(
|
|
135
|
+
relation: R,
|
|
136
|
+
bindings: B & ExactVars<R, B>
|
|
137
|
+
): QueryRuleChain<Rels, Record<never, never>, Classes>
|
|
138
|
+
|
|
195
139
|
match<R extends QueryRelation<Rels>, const B extends MatchShape<MatchFields<R>>>(
|
|
196
140
|
relation: R,
|
|
197
141
|
bindings: B & CheckBindings<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
|
|
198
142
|
): QueryRuleChain<Rels, BindParamsShape<MatchFields<R>, B>, Classes>
|
|
199
|
-
|
|
200
|
-
* A rule may START with a finished table: an interior atom is a positive
|
|
201
|
-
* occurrence exactly as the engine represents it, so its variables ground
|
|
202
|
-
* — the identity projection `(c) | reach(c);` is spellable with no
|
|
203
|
-
* re-grounding join over a domain relation.
|
|
204
|
-
*/
|
|
143
|
+
|
|
205
144
|
interior<const B extends Readonly<Record<string, AnyVar>>>(
|
|
206
145
|
name: string,
|
|
207
146
|
bindings: B & CheckInteriorBindings<B>
|
|
208
147
|
): QueryRuleChain<Rels, Record<never, never>, Classes>
|
|
209
148
|
}
|
|
210
149
|
|
|
211
|
-
/** The chain of a plain query rule: more atoms, residual predicates, then the head. */
|
|
212
150
|
interface QueryRuleChain<
|
|
213
151
|
Rels extends SchemaRelations,
|
|
214
152
|
P extends ParamsRecord,
|
|
215
153
|
Classes extends SchemaClasses = SchemaClasses
|
|
216
154
|
> {
|
|
217
|
-
/**
|
|
155
|
+
/**
|
|
156
|
+
* The FULL binding: every column of R bound to its own v(R) mint — the
|
|
157
|
+
* identity atom, generic R included. The mint invariant (a variable's mint
|
|
158
|
+
* slot IS its position slot) discharges the join judgment by construction
|
|
159
|
+
* (proposals/one-representation/50-generic-binding.md, "The ruling"); an
|
|
160
|
+
* all-var record contributes no params — P rides through unchanged.
|
|
161
|
+
* {@link ExactVars} maps a foreign key to `never`, so an aliased
|
|
162
|
+
* extra-key record falls to the general form's judgment.
|
|
163
|
+
*/
|
|
164
|
+
match<R extends QueryRelation<Rels>, B extends VarsOf<R>>(
|
|
165
|
+
relation: R,
|
|
166
|
+
bindings: B & ExactVars<R, B>
|
|
167
|
+
): QueryRuleChain<Rels, P, Classes>
|
|
168
|
+
|
|
218
169
|
match<R extends QueryRelation<Rels>, const B extends MatchShape<MatchFields<R>>>(
|
|
219
170
|
relation: R,
|
|
220
171
|
bindings: B & CheckBindings<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
|
|
221
172
|
): QueryRuleChain<Rels, Flatten<P & BindParamsShape<MatchFields<R>, B>>, Classes>
|
|
222
|
-
|
|
173
|
+
|
|
223
174
|
where<const C extends AnyCond>(
|
|
224
175
|
cond: CheckCond<Classes, C> & C
|
|
225
176
|
): QueryRuleChain<Rels, Flatten<P & CondParamsShape<C>>, Classes>
|
|
226
|
-
|
|
177
|
+
|
|
227
178
|
interior<const B extends Readonly<Record<string, AnyVar>>>(
|
|
228
179
|
name: string,
|
|
229
180
|
bindings: B & CheckInteriorBindings<B>
|
|
230
181
|
): QueryRuleChain<Rels, P, Classes>
|
|
231
|
-
|
|
182
|
+
|
|
232
183
|
find<const F extends FindShape>(entries: F & CheckFind<F>): RuleValue<RowOfFind<F>, P>
|
|
233
184
|
}
|
|
234
185
|
|
|
235
|
-
/** The rule builder an `interior("mid", ...)` callback receives. */
|
|
236
186
|
interface InteriorRuleScope<Rels extends SchemaRelations, Classes extends SchemaClasses = SchemaClasses>
|
|
237
187
|
extends TermOps {
|
|
188
|
+
/**
|
|
189
|
+
* The FULL binding: every column of R bound to its own v(R) mint — the
|
|
190
|
+
* identity atom, generic R included. The mint invariant (a variable's mint
|
|
191
|
+
* slot IS its position slot) discharges the join judgment by construction
|
|
192
|
+
* (proposals/one-representation/50-generic-binding.md, "The ruling"); an
|
|
193
|
+
* all-var record contributes no params, so the chain starts paramless.
|
|
194
|
+
* {@link ExactVars} maps a foreign key to `never`, so an aliased
|
|
195
|
+
* extra-key record falls to the general form's judgment.
|
|
196
|
+
*/
|
|
197
|
+
match<R extends QueryRelation<Rels>, B extends VarsOf<R>>(
|
|
198
|
+
relation: R,
|
|
199
|
+
bindings: B & ExactVars<R, B>
|
|
200
|
+
): InteriorRuleChain<Rels, Record<never, never>, Classes>
|
|
238
201
|
match<R extends QueryRelation<Rels>, const B extends MatchShape<MatchFields<R>>>(
|
|
239
202
|
relation: R,
|
|
240
203
|
bindings: B & CheckBindings<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
|
|
@@ -245,12 +208,24 @@ interface InteriorRuleScope<Rels extends SchemaRelations, Classes extends Schema
|
|
|
245
208
|
): InteriorRuleChain<Rels, Record<never, never>, Classes>
|
|
246
209
|
}
|
|
247
210
|
|
|
248
|
-
/** The chain of an interior rule: bound-variable heads only. */
|
|
249
211
|
interface InteriorRuleChain<
|
|
250
212
|
Rels extends SchemaRelations,
|
|
251
213
|
P extends ParamsRecord,
|
|
252
214
|
Classes extends SchemaClasses = SchemaClasses
|
|
253
215
|
> {
|
|
216
|
+
/**
|
|
217
|
+
* The FULL binding: every column of R bound to its own v(R) mint — the
|
|
218
|
+
* identity atom, generic R included. The mint invariant (a variable's mint
|
|
219
|
+
* slot IS its position slot) discharges the join judgment by construction
|
|
220
|
+
* (proposals/one-representation/50-generic-binding.md, "The ruling"); an
|
|
221
|
+
* all-var record contributes no params — P rides through unchanged.
|
|
222
|
+
* {@link ExactVars} maps a foreign key to `never`, so an aliased
|
|
223
|
+
* extra-key record falls to the general form's judgment.
|
|
224
|
+
*/
|
|
225
|
+
match<R extends QueryRelation<Rels>, B extends VarsOf<R>>(
|
|
226
|
+
relation: R,
|
|
227
|
+
bindings: B & ExactVars<R, B>
|
|
228
|
+
): InteriorRuleChain<Rels, P, Classes>
|
|
254
229
|
match<R extends QueryRelation<Rels>, const B extends MatchShape<MatchFields<R>>>(
|
|
255
230
|
relation: R,
|
|
256
231
|
bindings: B & CheckBindings<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
|
|
@@ -265,8 +240,20 @@ interface InteriorRuleChain<
|
|
|
265
240
|
find<const F extends FindShape>(entries: F & CheckRecFind<F>): RuleValue<RowOfFind<F>, P, HeadRecordOf<Classes, F>>
|
|
266
241
|
}
|
|
267
242
|
|
|
268
|
-
/** The rule builder a `.reach("reach", { base, rec })` arm receives. */
|
|
269
243
|
interface RecRuleScope<Rels extends SchemaRelations, Classes extends SchemaClasses = SchemaClasses> extends TermOps {
|
|
244
|
+
/**
|
|
245
|
+
* The FULL binding: every column of R bound to its own v(R) mint — the
|
|
246
|
+
* identity atom, generic R included. The mint invariant (a variable's mint
|
|
247
|
+
* slot IS its position slot) discharges the join judgment by construction
|
|
248
|
+
* (proposals/one-representation/50-generic-binding.md, "The ruling"); an
|
|
249
|
+
* all-var record contributes no params, so the chain starts paramless.
|
|
250
|
+
* {@link ExactVars} maps a foreign key to `never`, so an aliased
|
|
251
|
+
* extra-key record falls to the general form's judgment.
|
|
252
|
+
*/
|
|
253
|
+
match<R extends QueryRelation<Rels>, B extends VarsOf<R>>(
|
|
254
|
+
relation: R,
|
|
255
|
+
bindings: B & ExactVars<R, B>
|
|
256
|
+
): RecRuleChain<Rels, Record<never, never>, Classes>
|
|
270
257
|
match<R extends QueryRelation<Rels>, const B extends MatchShape<MatchFields<R>>>(
|
|
271
258
|
relation: R,
|
|
272
259
|
bindings: B & CheckBindings<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
|
|
@@ -277,17 +264,24 @@ interface RecRuleScope<Rels extends SchemaRelations, Classes extends SchemaClass
|
|
|
277
264
|
): RecRuleChain<Rels, Record<never, never>, Classes>
|
|
278
265
|
}
|
|
279
266
|
|
|
280
|
-
/**
|
|
281
|
-
* The chain of a rec arm. `.interior("reach", …)` is the self-atom
|
|
282
|
-
* on rec arms (and a prior interior on either list). `find` takes bound
|
|
283
|
-
* variables only — aggregates and the measure are unrepresentable in a
|
|
284
|
-
* rec head.
|
|
285
|
-
*/
|
|
286
267
|
interface RecRuleChain<
|
|
287
268
|
Rels extends SchemaRelations,
|
|
288
269
|
P extends ParamsRecord,
|
|
289
270
|
Classes extends SchemaClasses = SchemaClasses
|
|
290
271
|
> {
|
|
272
|
+
/**
|
|
273
|
+
* The FULL binding: every column of R bound to its own v(R) mint — the
|
|
274
|
+
* identity atom, generic R included. The mint invariant (a variable's mint
|
|
275
|
+
* slot IS its position slot) discharges the join judgment by construction
|
|
276
|
+
* (proposals/one-representation/50-generic-binding.md, "The ruling"); an
|
|
277
|
+
* all-var record contributes no params — P rides through unchanged.
|
|
278
|
+
* {@link ExactVars} maps a foreign key to `never`, so an aliased
|
|
279
|
+
* extra-key record falls to the general form's judgment.
|
|
280
|
+
*/
|
|
281
|
+
match<R extends QueryRelation<Rels>, B extends VarsOf<R>>(
|
|
282
|
+
relation: R,
|
|
283
|
+
bindings: B & ExactVars<R, B>
|
|
284
|
+
): RecRuleChain<Rels, P, Classes>
|
|
291
285
|
match<R extends QueryRelation<Rels>, const B extends MatchShape<MatchFields<R>>>(
|
|
292
286
|
relation: R,
|
|
293
287
|
bindings: B & CheckBindings<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
|
|
@@ -302,38 +296,32 @@ interface RecRuleChain<
|
|
|
302
296
|
find<const F extends FindShape>(entries: F & CheckRecFind<F>): RuleValue<RowOfFind<F>, P, HeadRecordOf<Classes, F>>
|
|
303
297
|
}
|
|
304
298
|
|
|
305
|
-
/** A query's runtime description — everything lowering, the wire marshal, and answer decode read. */
|
|
306
299
|
type QueryData =
|
|
307
300
|
| {
|
|
308
301
|
readonly kind: "cq"
|
|
309
|
-
|
|
302
|
+
|
|
310
303
|
readonly interiors: readonly InteriorData[]
|
|
311
|
-
|
|
304
|
+
|
|
312
305
|
readonly rules: readonly RuleData[]
|
|
313
|
-
|
|
306
|
+
|
|
314
307
|
readonly finds: readonly FindColumn[]
|
|
315
|
-
|
|
308
|
+
|
|
316
309
|
readonly params: readonly ParamEntry[]
|
|
317
310
|
}
|
|
318
311
|
| {
|
|
319
312
|
readonly kind: "reach"
|
|
320
|
-
|
|
313
|
+
|
|
321
314
|
readonly interiors: readonly InteriorData[]
|
|
322
|
-
|
|
315
|
+
|
|
323
316
|
readonly rec: RecData
|
|
324
|
-
|
|
317
|
+
|
|
325
318
|
readonly rules: readonly RuleData[]
|
|
326
|
-
|
|
319
|
+
|
|
327
320
|
readonly finds: readonly FindColumn[]
|
|
328
|
-
|
|
321
|
+
|
|
329
322
|
readonly params: readonly ParamEntry[]
|
|
330
323
|
}
|
|
331
324
|
|
|
332
|
-
/**
|
|
333
|
-
* An inert query value. `Row` is the inferred answer-row object type;
|
|
334
|
-
* `Params` the inferred execute-params object type — exactly the params the
|
|
335
|
-
* rules use. Prepare with `db.prepare(q)`.
|
|
336
|
-
*/
|
|
337
325
|
interface Query<
|
|
338
326
|
Rels extends SchemaRelations,
|
|
339
327
|
Row,
|
|
@@ -342,34 +330,26 @@ interface Query<
|
|
|
342
330
|
> {
|
|
343
331
|
readonly schema: Schema<Rels, Classes>
|
|
344
332
|
readonly data: QueryData
|
|
345
|
-
|
|
333
|
+
|
|
346
334
|
rule<RV extends AnyRuleValue>(
|
|
347
335
|
build: (r: QueryRuleScope<Rels, Classes>) => RV
|
|
348
336
|
): Query<Rels, Row | RowOf<RV>, Flatten<Params & ParamsOf<RV>>, Classes>
|
|
349
|
-
|
|
337
|
+
|
|
350
338
|
interior(name: string, ...builds: never[]): never
|
|
351
|
-
|
|
339
|
+
|
|
352
340
|
reach(name: string, arms: never): never
|
|
353
341
|
readonly [inferred]?: { readonly row: Row; readonly params: Params }
|
|
354
342
|
}
|
|
355
343
|
|
|
356
|
-
/** Any query value as lowering and the runtime consume it. */
|
|
357
344
|
interface AnyQuery {
|
|
358
345
|
readonly schema: AnySchema
|
|
359
346
|
readonly data: QueryData
|
|
360
347
|
}
|
|
361
348
|
|
|
362
|
-
/** Extracts a query value's inferred answer-row type. */
|
|
363
349
|
type QueryRow<Q extends AnyQuery> = RowOf<Q>
|
|
364
350
|
|
|
365
|
-
/** Extracts a query value's inferred execute-params type. */
|
|
366
351
|
type QueryParams<Q extends AnyQuery> = ParamsOf<Q>
|
|
367
352
|
|
|
368
|
-
/**
|
|
369
|
-
* The entry value of `query(S)`: interiors, then reach (moves to
|
|
370
|
-
* {@link QueryReachStart}), then the first `.rule` mints the query.
|
|
371
|
-
* `interior` / `reach` exist only on this CQ start.
|
|
372
|
-
*/
|
|
373
353
|
type QueryStart<
|
|
374
354
|
Rels extends SchemaRelations,
|
|
375
355
|
Classes extends SchemaClasses = SchemaClasses,
|
|
@@ -388,7 +368,6 @@ type QueryStart<
|
|
|
388
368
|
): QueryReachStart<Rels, Classes, Flatten<P & BuildsParams<Base> & BuildsParams<Step>>>
|
|
389
369
|
}
|
|
390
370
|
|
|
391
|
-
/** After `.reach()`: interior/reach are unrepresentable; only `.rule` remains. */
|
|
392
371
|
type QueryReachStart<
|
|
393
372
|
Rels extends SchemaRelations,
|
|
394
373
|
Classes extends SchemaClasses = SchemaClasses,
|
|
@@ -399,11 +378,9 @@ type QueryReachStart<
|
|
|
399
378
|
): Query<Rels, RowOf<RV>, Flatten<P & ParamsOf<RV>>, Classes>
|
|
400
379
|
}
|
|
401
380
|
|
|
402
|
-
/** The frozen constructor vocabulary every rule builder spreads. */
|
|
403
381
|
const termOps: TermOps = Object.freeze({
|
|
404
382
|
param: makeParam,
|
|
405
383
|
inSet: makeSetParam,
|
|
406
|
-
duration: makeDuration,
|
|
407
384
|
eq,
|
|
408
385
|
ne,
|
|
409
386
|
lt,
|
|
@@ -422,21 +399,18 @@ const termOps: TermOps = Object.freeze({
|
|
|
422
399
|
pack
|
|
423
400
|
})
|
|
424
401
|
|
|
425
|
-
/** One rule under construction: immutable — every chain step is a fresh state. Boundness rides the `bound` set of var references. */
|
|
426
402
|
interface RuleBuildState {
|
|
427
403
|
readonly items: readonly RuleItem[]
|
|
428
404
|
readonly bound: ReadonlySet<AnyVar>
|
|
429
405
|
readonly paramUses: readonly ParamUse[]
|
|
430
406
|
}
|
|
431
407
|
|
|
432
|
-
/** The empty rule state. */
|
|
433
408
|
const EMPTY_RULE: RuleBuildState = Object.freeze({
|
|
434
409
|
items: Object.freeze([]),
|
|
435
410
|
bound: new Set<AnyVar>(),
|
|
436
411
|
paramUses: Object.freeze([])
|
|
437
412
|
})
|
|
438
413
|
|
|
439
|
-
/** One resolved bindings record: the atom entries, the variable references it binds, and the params it uses. */
|
|
440
414
|
interface ResolvedBindings {
|
|
441
415
|
readonly atom: AtomData
|
|
442
416
|
readonly vars: readonly AnyVar[]
|
|
@@ -461,11 +435,6 @@ function mintSlotOf(context: ChainContext, ref: AnyVar): ClassedField {
|
|
|
461
435
|
return { field: ref.field, class: context.classes[ref.owner.name]?.[ref.column] }
|
|
462
436
|
}
|
|
463
437
|
|
|
464
|
-
/**
|
|
465
|
-
* Judges one membership ARRAY at a binding position — legal exactly at a
|
|
466
|
-
* CLOSED-reference field, holding ≥ 2 DISTINCT handle names. The returned
|
|
467
|
-
* name is CONTENT-ADDRESSED (vocabulary + the member SET).
|
|
468
|
-
*/
|
|
469
438
|
function membershipSet(
|
|
470
439
|
context: string,
|
|
471
440
|
field: AnyField,
|
|
@@ -502,14 +471,6 @@ function membershipSet(
|
|
|
502
471
|
return { name: `∈ ${roster.name} ${JSON.stringify(key)}`, members: Object.freeze(members) }
|
|
503
472
|
}
|
|
504
473
|
|
|
505
|
-
/**
|
|
506
|
-
* Resolves a bindings record against an atom owner's matchable fields, in
|
|
507
|
-
* the record's written order: terms classify by their runtime tag,
|
|
508
|
-
* everything else is a bare literal. Every VARIABLE binding judges
|
|
509
|
-
* `fieldJoins(mintSlot, positionSlot)` and throws on a class-unequal reuse
|
|
510
|
-
* (the runtime twin of `CheckBindings`); the bound refs are collected for
|
|
511
|
-
* the rule's boundness set.
|
|
512
|
-
*/
|
|
513
474
|
function resolveBindings(
|
|
514
475
|
context: ChainContext,
|
|
515
476
|
label: string,
|
|
@@ -574,10 +535,6 @@ function resolveBindings(
|
|
|
574
535
|
)
|
|
575
536
|
break
|
|
576
537
|
}
|
|
577
|
-
case "duration":
|
|
578
|
-
throw errors.new(
|
|
579
|
-
`${label}.${fieldName}: the measure is not a field-typed value — it lives in comparisons and find entries`
|
|
580
|
-
)
|
|
581
538
|
}
|
|
582
539
|
} else if (Array.isArray(value)) {
|
|
583
540
|
const set = membershipSet(`${label}.${fieldName}`, declared.field, value)
|
|
@@ -599,7 +556,6 @@ function resolveBindings(
|
|
|
599
556
|
return { atom: Object.freeze({ relation, bindings: Object.freeze(entries) }), vars, uses }
|
|
600
557
|
}
|
|
601
558
|
|
|
602
|
-
/** Extends a rule state with one positive atom; the bound variable references accumulate into the boundness set. */
|
|
603
559
|
function advanceMatch(
|
|
604
560
|
context: ChainContext,
|
|
605
561
|
state: RuleBuildState,
|
|
@@ -618,7 +574,6 @@ function advanceMatch(
|
|
|
618
574
|
})
|
|
619
575
|
}
|
|
620
576
|
|
|
621
|
-
/** Resolves one comparison side to its runtime term (variables and the measure ride by reference). */
|
|
622
577
|
function cmpTermDataOf(value: unknown): CmpTermData {
|
|
623
578
|
if (isTerm(value)) {
|
|
624
579
|
switch (value[term]) {
|
|
@@ -628,30 +583,16 @@ function cmpTermDataOf(value: unknown): CmpTermData {
|
|
|
628
583
|
return Object.freeze({ kind: "param" as const, name: value.name })
|
|
629
584
|
case "setParam":
|
|
630
585
|
return Object.freeze({ kind: "setParam" as const, name: value.name })
|
|
631
|
-
case "duration":
|
|
632
|
-
return Object.freeze({ kind: "measure" as const, ref: value.over })
|
|
633
586
|
}
|
|
634
587
|
}
|
|
635
588
|
return Object.freeze({ kind: "literal" as const, value })
|
|
636
589
|
}
|
|
637
590
|
|
|
638
|
-
/**
|
|
639
|
-
* One comparison side's contribution to the param census: a param/set side
|
|
640
|
-
* anchors to its SIBLING — a variable's field descriptor or the measure; an
|
|
641
|
-
* unanchorable use records with no anchor.
|
|
642
|
-
*/
|
|
643
591
|
function sideUses(op: CmpKind, side: CmpTermData, sibling: CmpTermData, uses: ParamUse[]): void {
|
|
644
592
|
if (side.kind !== "param" && side.kind !== "setParam") {
|
|
645
593
|
return
|
|
646
594
|
}
|
|
647
|
-
|
|
648
|
-
if (sibling.kind === "var") {
|
|
649
|
-
anchor = sibling.ref.field
|
|
650
|
-
} else if (sibling.kind === "measure") {
|
|
651
|
-
anchor = "measure"
|
|
652
|
-
} else {
|
|
653
|
-
anchor = undefined
|
|
654
|
-
}
|
|
595
|
+
const anchor = sibling.kind === "var" ? sibling.ref.field : undefined
|
|
655
596
|
uses.push(
|
|
656
597
|
Object.freeze({
|
|
657
598
|
name: side.name,
|
|
@@ -663,7 +604,6 @@ function sideUses(op: CmpKind, side: CmpTermData, sibling: CmpTermData, uses: Pa
|
|
|
663
604
|
)
|
|
664
605
|
}
|
|
665
606
|
|
|
666
|
-
/** Lowers one condition VALUE to its runtime data, recording param uses. */
|
|
667
607
|
function condDataOf(cond: AnyCond, uses: ParamUse[]): CondData {
|
|
668
608
|
if (cond.cond === "cmp") {
|
|
669
609
|
const lhs = cmpTermDataOf(cond.lhs)
|
|
@@ -695,7 +635,6 @@ function condDataOf(cond: AnyCond, uses: ParamUse[]): CondData {
|
|
|
695
635
|
)
|
|
696
636
|
}
|
|
697
637
|
|
|
698
|
-
/** Extends a rule state with one `.where` item (a condition or a negated atom). */
|
|
699
638
|
function advanceWhere(context: ChainContext, state: RuleBuildState, cond: AnyCond): RuleBuildState {
|
|
700
639
|
if (typeof cond !== "object" || cond === null || !("cond" in cond)) {
|
|
701
640
|
throw errors.new("where() takes a comparison, an and()/or() tree, or a negated atom")
|
|
@@ -731,15 +670,6 @@ function advanceWhere(context: ChainContext, state: RuleBuildState, cond: AnyCon
|
|
|
731
670
|
})
|
|
732
671
|
}
|
|
733
672
|
|
|
734
|
-
/**
|
|
735
|
-
* Extends a rule state with one interior atom (a named record over head keys;
|
|
736
|
-
* vars validated at completion). A POSITIVE interior atom is a positive
|
|
737
|
-
* occurrence exactly as the engine represents it (`check_atoms` walks Interior
|
|
738
|
-
* and Edb in one loop), so its variables GROUND: they enter the rule's
|
|
739
|
-
* boundness set, may ride the head, and satisfy negation safety — the
|
|
740
|
-
* interior-only identity projection of a finished table is spellable with no
|
|
741
|
-
* re-grounding join. A NEGATED one binds nothing, only rejects.
|
|
742
|
-
*/
|
|
743
673
|
function advanceInterior(
|
|
744
674
|
state: RuleBuildState,
|
|
745
675
|
target: DerivedTable,
|
|
@@ -771,7 +701,6 @@ function advanceInterior(
|
|
|
771
701
|
})
|
|
772
702
|
}
|
|
773
703
|
|
|
774
|
-
/** Narrows a find entry to an aggregate value. */
|
|
775
704
|
function isAggregateEntry(value: unknown): value is { readonly agg: string; readonly over?: unknown } {
|
|
776
705
|
return typeof value === "object" && value !== null && "agg" in value
|
|
777
706
|
}
|
|
@@ -784,7 +713,6 @@ function asVarTerm(context: string, value: unknown): AnyVar {
|
|
|
784
713
|
throw errors.new(`${context}: expected a variable`)
|
|
785
714
|
}
|
|
786
715
|
|
|
787
|
-
/** Classifies one aggregate find entry into its runtime data (variables ride by reference). */
|
|
788
716
|
function aggDataOf(name: string, entry: { readonly agg: string; readonly over?: unknown }): AggData {
|
|
789
717
|
if (entry.agg === "count") {
|
|
790
718
|
return Object.freeze({ op: "count" as const })
|
|
@@ -797,10 +725,7 @@ function aggDataOf(name: string, entry: { readonly agg: string; readonly over?:
|
|
|
797
725
|
if (isTerm(over) && over[term] === "var") {
|
|
798
726
|
return Object.freeze({ op: "fold" as const, fold: entry.agg, over })
|
|
799
727
|
}
|
|
800
|
-
|
|
801
|
-
return Object.freeze({ op: "fold" as const, fold: entry.agg, over: Object.freeze({ duration: over.over }) })
|
|
802
|
-
}
|
|
803
|
-
throw errors.new(`find ${name} (${entry.agg}): takes a variable or r.duration(v)`)
|
|
728
|
+
throw errors.new(`find ${name} (${entry.agg}): takes a variable`)
|
|
804
729
|
}
|
|
805
730
|
case "pack":
|
|
806
731
|
return Object.freeze({ op: "pack" as const, over: asVarTerm(`find ${name} (pack)`, over) })
|
|
@@ -809,11 +734,6 @@ function aggDataOf(name: string, entry: { readonly agg: string; readonly over?:
|
|
|
809
734
|
}
|
|
810
735
|
}
|
|
811
736
|
|
|
812
|
-
/**
|
|
813
|
-
* Classifies one find entry into its named answer column (the KEY names the
|
|
814
|
-
* column, `count` included). The `slot`/`closed` slices are resolved LATER,
|
|
815
|
-
* at rule completion, where boundness and the mint slots are in hand.
|
|
816
|
-
*/
|
|
817
737
|
function findColumnOf(name: string, entry: unknown): FindColumn {
|
|
818
738
|
if (isTerm(entry)) {
|
|
819
739
|
if (entry[term] === "var") {
|
|
@@ -824,17 +744,7 @@ function findColumnOf(name: string, entry: unknown): FindColumn {
|
|
|
824
744
|
slot: undefined
|
|
825
745
|
})
|
|
826
746
|
}
|
|
827
|
-
|
|
828
|
-
return Object.freeze({
|
|
829
|
-
name,
|
|
830
|
-
entry: Object.freeze({ kind: "measure" as const, over: entry.over }),
|
|
831
|
-
closed: undefined,
|
|
832
|
-
slot: undefined
|
|
833
|
-
})
|
|
834
|
-
}
|
|
835
|
-
throw errors.new(
|
|
836
|
-
`find ${name}: a ${entry[term]} is not projectable — find takes variables, r.duration(v), or aggregates`
|
|
837
|
-
)
|
|
747
|
+
throw errors.new(`find ${name}: a ${entry[term]} is not projectable — find takes variables or aggregates`)
|
|
838
748
|
}
|
|
839
749
|
if (isAggregateEntry(entry)) {
|
|
840
750
|
return Object.freeze({
|
|
@@ -844,32 +754,29 @@ function findColumnOf(name: string, entry: unknown): FindColumn {
|
|
|
844
754
|
slot: undefined
|
|
845
755
|
})
|
|
846
756
|
}
|
|
847
|
-
throw errors.new(`find ${name}: not a find entry — find takes variables
|
|
757
|
+
throw errors.new(`find ${name}: not a find entry — find takes variables or aggregates`)
|
|
848
758
|
}
|
|
849
759
|
|
|
850
760
|
/**
|
|
851
|
-
* The orderable ban's pointed refusal
|
|
761
|
+
* The orderable ban's pointed refusal
|
|
852
762
|
* § orderability): a closed reference is equality-and-membership only.
|
|
853
763
|
*/
|
|
854
764
|
function closedOrderError(context: string, position: string, vocabulary: string): Error {
|
|
855
765
|
return errors.new(
|
|
856
|
-
`${context}: ${position} is a ${vocabulary} reference — declaration order is an accident, not semantics: vocabularies do not order (
|
|
766
|
+
`${context}: ${position} is a ${vocabulary} reference — declaration order is an accident, not semantics: vocabularies do not order (equality, membership, and counting remain)`
|
|
857
767
|
)
|
|
858
768
|
}
|
|
859
769
|
|
|
860
|
-
/** The comparison ops under the orderable ban (order roster + point membership). */
|
|
861
770
|
function isOrderOp(op: CmpKind | "binding"): op is "lt" | "le" | "gt" | "ge" | "pointIn" {
|
|
862
771
|
return op === "lt" || op === "le" || op === "gt" || op === "ge" || op === "pointIn"
|
|
863
772
|
}
|
|
864
773
|
|
|
865
|
-
/** Requires a variable to be bound by a relation atom of the rule (the boundness wall — invisible to the type tier). */
|
|
866
774
|
function assertBound(where: string, bound: ReadonlySet<AnyVar>, ref: AnyVar): void {
|
|
867
775
|
if (!bound.has(ref)) {
|
|
868
776
|
throw errors.new(`${where}: the variable ${ref.label} is not bound by a relation atom of the rule`)
|
|
869
777
|
}
|
|
870
778
|
}
|
|
871
779
|
|
|
872
|
-
/** Requires a variable to be interval-typed (the measure's and pack's domain), off its own descriptor. */
|
|
873
780
|
function assertInterval(where: string, ref: AnyVar): void {
|
|
874
781
|
if (ref.field.kind !== "interval") {
|
|
875
782
|
throw errors.new(
|
|
@@ -878,7 +785,6 @@ function assertInterval(where: string, ref: AnyVar): void {
|
|
|
878
785
|
}
|
|
879
786
|
}
|
|
880
787
|
|
|
881
|
-
/** Requires a variable's own field to be non-closed (the orderable ban's runtime twin). */
|
|
882
788
|
function assertNotClosed(where: string, position: string, ref: AnyVar): void {
|
|
883
789
|
const roster = rosterOf(ref.field)
|
|
884
790
|
if (roster !== undefined) {
|
|
@@ -886,11 +792,12 @@ function assertNotClosed(where: string, position: string, ref: AnyVar): void {
|
|
|
886
792
|
}
|
|
887
793
|
}
|
|
888
794
|
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
795
|
+
function assertNumeric(where: string, position: string, ref: AnyVar): void {
|
|
796
|
+
if (ref.field.kind !== "u64" && ref.field.kind !== "i64") {
|
|
797
|
+
throw errors.new(`${where}: ${position} ${ref.label} is ${ref.field.kind}, not numeric — a fold reads u64/i64 only`)
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
894
801
|
function findColumnSlotOf(context: ChainContext, column: FindColumn): ClassedField | undefined {
|
|
895
802
|
const entry = column.entry
|
|
896
803
|
if (entry.kind === "var") {
|
|
@@ -899,7 +806,6 @@ function findColumnSlotOf(context: ChainContext, column: FindColumn): ClassedFie
|
|
|
899
806
|
return undefined
|
|
900
807
|
}
|
|
901
808
|
|
|
902
|
-
/** Validates one find column's variable references (boundness + the orderable/interval walls, off the var's own field). */
|
|
903
809
|
function validateColumn(context: ChainContext, bound: ReadonlySet<AnyVar>, column: FindColumn): void {
|
|
904
810
|
const where = `${contextLabel(context)} find ${column.name}`
|
|
905
811
|
const entry = column.entry
|
|
@@ -907,23 +813,14 @@ function validateColumn(context: ChainContext, bound: ReadonlySet<AnyVar>, colum
|
|
|
907
813
|
assertBound(where, bound, entry.over)
|
|
908
814
|
return
|
|
909
815
|
}
|
|
910
|
-
if (entry.kind === "measure") {
|
|
911
|
-
assertBound(where, bound, entry.over)
|
|
912
|
-
assertInterval(where, entry.over)
|
|
913
|
-
return
|
|
914
|
-
}
|
|
915
816
|
const agg = entry.agg
|
|
916
817
|
switch (agg.op) {
|
|
917
818
|
case "count":
|
|
918
819
|
return
|
|
919
820
|
case "fold": {
|
|
920
|
-
if ("duration" in agg.over) {
|
|
921
|
-
assertBound(where, bound, agg.over.duration)
|
|
922
|
-
assertInterval(where, agg.over.duration)
|
|
923
|
-
return
|
|
924
|
-
}
|
|
925
821
|
assertBound(where, bound, agg.over)
|
|
926
822
|
assertNotClosed(where, `the ${agg.fold} input`, agg.over)
|
|
823
|
+
assertNumeric(where, `the ${agg.fold} input`, agg.over)
|
|
927
824
|
return
|
|
928
825
|
}
|
|
929
826
|
case "pack":
|
|
@@ -933,11 +830,6 @@ function validateColumn(context: ChainContext, bound: ReadonlySet<AnyVar>, colum
|
|
|
933
830
|
}
|
|
934
831
|
}
|
|
935
832
|
|
|
936
|
-
/**
|
|
937
|
-
* Validates one condition's variable references against the rule's bound
|
|
938
|
-
* set — and, for `eq`/`ne` over two variables, holds the class wall through
|
|
939
|
-
* the mint slots (the unification IS a join; bare pairs only with bare).
|
|
940
|
-
*/
|
|
941
833
|
function validateCond(context: ChainContext, bound: ReadonlySet<AnyVar>, cond: CondData): void {
|
|
942
834
|
const label = contextLabel(context)
|
|
943
835
|
if (cond.kind === "cmp") {
|
|
@@ -949,10 +841,6 @@ function validateCond(context: ChainContext, bound: ReadonlySet<AnyVar>, cond: C
|
|
|
949
841
|
throw closedOrderError(label, `the ${cond.op.kind} side ${side.ref.label}`, roster.name)
|
|
950
842
|
}
|
|
951
843
|
}
|
|
952
|
-
if (side.kind === "measure") {
|
|
953
|
-
assertBound(label, bound, side.ref)
|
|
954
|
-
assertInterval(label, side.ref)
|
|
955
|
-
}
|
|
956
844
|
}
|
|
957
845
|
if ((cond.op.kind === "eq" || cond.op.kind === "ne") && cond.lhs.kind === "var" && cond.rhs.kind === "var") {
|
|
958
846
|
assertBound(label, bound, cond.lhs.ref)
|
|
@@ -972,16 +860,6 @@ function validateCond(context: ChainContext, bound: ReadonlySet<AnyVar>, cond: C
|
|
|
972
860
|
}
|
|
973
861
|
}
|
|
974
862
|
|
|
975
|
-
/**
|
|
976
|
-
* Validates one interior item: every head column of the table is bound exactly
|
|
977
|
-
* once (a missing or extra key is a pointed error) and each variable joins
|
|
978
|
-
* its head column's classed slot. A POSITIVE interior atom GROUNDS its
|
|
979
|
-
* variables (a positive occurrence, exactly the engine's representation),
|
|
980
|
-
* so no boundness precondition exists; a NEGATED one binds nothing — its
|
|
981
|
-
* variables must be positively bound elsewhere in the rule, the same
|
|
982
|
-
* safety rule as EDB negation. When the table's own first rule is in flight
|
|
983
|
-
* (`finds` empty), the completing rule's OWN find columns ARE the head.
|
|
984
|
-
*/
|
|
985
863
|
function validateInterior(
|
|
986
864
|
context: ChainContext,
|
|
987
865
|
bound: ReadonlySet<AnyVar>,
|
|
@@ -1035,12 +913,6 @@ function validateInterior(
|
|
|
1035
913
|
}
|
|
1036
914
|
}
|
|
1037
915
|
|
|
1038
|
-
/**
|
|
1039
|
-
* Completes one rule: enriches the find columns (declaration-order-safe
|
|
1040
|
-
* keys, boundness validated, each column's classed slot and closed slice
|
|
1041
|
-
* resolved), then walks the body walls — negated-atom boundness safety, interior
|
|
1042
|
-
* head pairing, and condition validation.
|
|
1043
|
-
*/
|
|
1044
916
|
function completeRule(context: ChainContext, state: RuleBuildState, rawColumns: readonly FindColumn[]): RuleData {
|
|
1045
917
|
const label = contextLabel(context)
|
|
1046
918
|
if (rawColumns.length === 0) {
|
|
@@ -1072,15 +944,10 @@ function completeRule(context: ChainContext, state: RuleBuildState, rawColumns:
|
|
|
1072
944
|
return Object.freeze({ items: state.items, finds: Object.freeze(columns), paramUses: state.paramUses })
|
|
1073
945
|
}
|
|
1074
946
|
|
|
1075
|
-
/** Builds one typed rule value over completed rule data. */
|
|
1076
947
|
function makeRuleValue<Row, P extends ParamsRecord>(rule: RuleData): RuleValue<Row, P> {
|
|
1077
948
|
return Object.freeze({ rule })
|
|
1078
949
|
}
|
|
1079
950
|
|
|
1080
|
-
/**
|
|
1081
|
-
* The one runtime chain every context shares — non-generic on purpose. The
|
|
1082
|
-
* typed chain interfaces apply at the scope factories' boundaries.
|
|
1083
|
-
*/
|
|
1084
951
|
interface RawChain {
|
|
1085
952
|
match(relation: MatchOwner, bindings: Readonly<Record<string, unknown>>): RawChain
|
|
1086
953
|
where(cond: AnyCond): RawChain
|
|
@@ -1088,18 +955,15 @@ interface RawChain {
|
|
|
1088
955
|
find(entries: Readonly<Record<string, unknown>>): RuleValue<never, never>
|
|
1089
956
|
}
|
|
1090
957
|
|
|
1091
|
-
/** The runtime rule-builder shape beneath every typed scope. */
|
|
1092
958
|
interface RawScope extends TermOps {
|
|
1093
959
|
match(relation: MatchOwner, bindings: Readonly<Record<string, unknown>>): RawChain
|
|
1094
960
|
interior(name: string, bindings: Readonly<Record<string, unknown>>): RawChain
|
|
1095
961
|
}
|
|
1096
962
|
|
|
1097
|
-
/** The declared derived tables a chain may name. */
|
|
1098
963
|
type DerivedEnv =
|
|
1099
964
|
| { readonly interiors: readonly InteriorData[] }
|
|
1100
965
|
| { readonly interiors: readonly InteriorData[]; readonly rec: RecHandle | RecHead | RecData }
|
|
1101
966
|
|
|
1102
|
-
/** Which rule family a chain builds — plus the schema's runtime class map and theory value (the join judge's authority). */
|
|
1103
967
|
type ChainContext = { readonly classes: SchemaClasses; readonly theory: AnySchema } & DerivedEnv &
|
|
1104
968
|
(
|
|
1105
969
|
| { readonly kind: "query" }
|
|
@@ -1108,7 +972,6 @@ type ChainContext = { readonly classes: SchemaClasses; readonly theory: AnySchem
|
|
|
1108
972
|
| { readonly kind: "rec-arm"; readonly self: RecHead }
|
|
1109
973
|
)
|
|
1110
974
|
|
|
1111
|
-
/** The diagnostic label of a chain context. */
|
|
1112
975
|
function contextLabel(context: ChainContext): string {
|
|
1113
976
|
switch (context.kind) {
|
|
1114
977
|
case "query":
|
|
@@ -1122,12 +985,10 @@ function contextLabel(context: ChainContext): string {
|
|
|
1122
985
|
}
|
|
1123
986
|
}
|
|
1124
987
|
|
|
1125
|
-
/** RecHandle is name-only staging; RecHead/RecData carry the sealed finds. */
|
|
1126
988
|
function isRecHead(rec: RecHandle | RecHead | RecData): rec is RecHead {
|
|
1127
989
|
return Array.isArray((rec as RecHead).finds)
|
|
1128
990
|
}
|
|
1129
991
|
|
|
1130
|
-
/** Resolves a derived-table name against the context's visible tables. */
|
|
1131
992
|
function lookupDerived(context: ChainContext, name: string): DerivedTable {
|
|
1132
993
|
const interior = context.interiors.find(function byName(candidate) {
|
|
1133
994
|
return candidate.name === name
|
|
@@ -1156,7 +1017,6 @@ function lookupDerived(context: ChainContext, name: string): DerivedTable {
|
|
|
1156
1017
|
throw errors.new(`${contextLabel(context)}: no derived table named ${name} is in scope`)
|
|
1157
1018
|
}
|
|
1158
1019
|
|
|
1159
|
-
/** Validates and records one interior atom per the context's cut. */
|
|
1160
1020
|
function interiorAdvance(
|
|
1161
1021
|
context: ChainContext,
|
|
1162
1022
|
state: RuleBuildState,
|
|
@@ -1166,11 +1026,6 @@ function interiorAdvance(
|
|
|
1166
1026
|
return advanceInterior(state, lookupDerived(context, name), bindings, "interior")
|
|
1167
1027
|
}
|
|
1168
1028
|
|
|
1169
|
-
/**
|
|
1170
|
-
* Validates and records one NEGATED finished-table atom — main and interior
|
|
1171
|
-
* rules: a finished set is a set. Rec bodies refuse every negation
|
|
1172
|
-
* (`NegationInRec` — self is the wall; EDB / earlier-interior is this-cut).
|
|
1173
|
-
*/
|
|
1174
1029
|
function notInteriorAdvance(
|
|
1175
1030
|
context: ChainContext,
|
|
1176
1031
|
state: RuleBuildState,
|
|
@@ -1185,7 +1040,6 @@ function notInteriorAdvance(
|
|
|
1185
1040
|
return advanceInterior(state, lookupDerived(context, name), bindings, "negatedInterior")
|
|
1186
1041
|
}
|
|
1187
1042
|
|
|
1188
|
-
/** Classifies one find record per the context (interior and rec heads project bound variables only). */
|
|
1189
1043
|
function findColumns(context: ChainContext, entries: Readonly<Record<string, unknown>>): FindColumn[] {
|
|
1190
1044
|
const columns: FindColumn[] = []
|
|
1191
1045
|
const derivedHead = context.kind !== "query"
|
|
@@ -1204,7 +1058,6 @@ function findColumns(context: ChainContext, entries: Readonly<Record<string, unk
|
|
|
1204
1058
|
return columns
|
|
1205
1059
|
}
|
|
1206
1060
|
|
|
1207
|
-
/** Builds one runtime chain (immutably — every step is a fresh chain over fresh state). */
|
|
1208
1061
|
function makeRawChain(context: ChainContext, state: RuleBuildState): RawChain {
|
|
1209
1062
|
const chain: RawChain = {
|
|
1210
1063
|
match(relation, bindings) {
|
|
@@ -1224,7 +1077,6 @@ function makeRawChain(context: ChainContext, state: RuleBuildState): RawChain {
|
|
|
1224
1077
|
return chain
|
|
1225
1078
|
}
|
|
1226
1079
|
|
|
1227
|
-
/** Builds one runtime rule-builder over a context. */
|
|
1228
1080
|
function makeRawScope(context: ChainContext): RawScope {
|
|
1229
1081
|
const scope: RawScope = {
|
|
1230
1082
|
...termOps,
|
|
@@ -1252,7 +1104,6 @@ function isTypedScope<S>(scope: RawScope): scope is RawScope & S {
|
|
|
1252
1104
|
return typeof scope.match === "function"
|
|
1253
1105
|
}
|
|
1254
1106
|
|
|
1255
|
-
/** Builds one query-rule builder (the typed face of the raw builder). */
|
|
1256
1107
|
function makeQueryRuleScope<Rels extends SchemaRelations, Classes extends SchemaClasses>(
|
|
1257
1108
|
theory: AnySchema,
|
|
1258
1109
|
env: DerivedEnv
|
|
@@ -1264,7 +1115,6 @@ function makeQueryRuleScope<Rels extends SchemaRelations, Classes extends Schema
|
|
|
1264
1115
|
return raw
|
|
1265
1116
|
}
|
|
1266
1117
|
|
|
1267
|
-
/** Builds one interior-rule builder. */
|
|
1268
1118
|
function makeInteriorRuleScope<Rels extends SchemaRelations, Classes extends SchemaClasses>(
|
|
1269
1119
|
theory: AnySchema,
|
|
1270
1120
|
env: DerivedEnv,
|
|
@@ -1277,7 +1127,6 @@ function makeInteriorRuleScope<Rels extends SchemaRelations, Classes extends Sch
|
|
|
1277
1127
|
return raw
|
|
1278
1128
|
}
|
|
1279
1129
|
|
|
1280
|
-
/** Builds one rec-arm builder. */
|
|
1281
1130
|
function makeRecRuleScope<Rels extends SchemaRelations, Classes extends SchemaClasses>(
|
|
1282
1131
|
theory: AnySchema,
|
|
1283
1132
|
env: DerivedEnv,
|
|
@@ -1318,15 +1167,13 @@ function makeRecRuleScope<Rels extends SchemaRelations, Classes extends SchemaCl
|
|
|
1318
1167
|
return raw
|
|
1319
1168
|
}
|
|
1320
1169
|
|
|
1321
|
-
/** Renders one head column's closed slice for the rule-alignment check's diagnostics. */
|
|
1322
1170
|
function renderClosedSlice(closed: ClosedRoster | undefined): string {
|
|
1323
1171
|
return closed === undefined ? "a bare value" : `a ${closed.name} reference`
|
|
1324
1172
|
}
|
|
1325
1173
|
|
|
1326
|
-
/** Renders one head column's signature for the rule-alignment check. */
|
|
1327
1174
|
function headSignature(column: FindColumn): string {
|
|
1328
1175
|
const entry = column.entry
|
|
1329
|
-
if (entry.kind === "var"
|
|
1176
|
+
if (entry.kind === "var") {
|
|
1330
1177
|
return `${column.name}:var`
|
|
1331
1178
|
}
|
|
1332
1179
|
const agg = entry.agg
|
|
@@ -1336,12 +1183,6 @@ function headSignature(column: FindColumn): string {
|
|
|
1336
1183
|
return `${column.name}:${agg.op}`
|
|
1337
1184
|
}
|
|
1338
1185
|
|
|
1339
|
-
/** The roster a param anchor carries: present exactly on a closed-reference field anchor. */
|
|
1340
|
-
function anchorRosterOf(anchor: AnyField | "measure" | undefined): ClosedRoster | undefined {
|
|
1341
|
-
return anchor === "measure" ? undefined : rosterOf(anchor)
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
/** Renders one param anchor's closedness for the registry's coherence diagnostics. */
|
|
1345
1186
|
function renderParamAnchor(roster: ClosedRoster | undefined): string {
|
|
1346
1187
|
return roster === undefined ? "a non-closed position" : `a ${roster.name} reference`
|
|
1347
1188
|
}
|
|
@@ -1397,8 +1238,8 @@ function paramRegistryOf(
|
|
|
1397
1238
|
)
|
|
1398
1239
|
}
|
|
1399
1240
|
if (existing.anchor !== undefined && use.anchor !== undefined) {
|
|
1400
|
-
const registered =
|
|
1401
|
-
const anchored =
|
|
1241
|
+
const registered = rosterOf(existing.anchor)
|
|
1242
|
+
const anchored = rosterOf(use.anchor)
|
|
1402
1243
|
if (registered !== anchored) {
|
|
1403
1244
|
throw errors.new(
|
|
1404
1245
|
`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`
|
|
@@ -1433,14 +1274,7 @@ function paramRegistryOf(
|
|
|
1433
1274
|
if (entry === undefined) {
|
|
1434
1275
|
throw errors.new(`query param ${name} lost its registry entry`)
|
|
1435
1276
|
}
|
|
1436
|
-
|
|
1437
|
-
* A membership array's handle names are program constants, so the
|
|
1438
|
-
* entry stores the resolved IMAGE: each name rides the one
|
|
1439
|
-
* roster-verification point (`taggedHandleId`, through
|
|
1440
|
-
* `taggedCmpLiteral`) exactly once, HERE — an out-of-roster name
|
|
1441
|
-
* fails at build, and every execute returns this frozen value by
|
|
1442
|
-
* reference.
|
|
1443
|
-
*/
|
|
1277
|
+
|
|
1444
1278
|
let membership: QueryParam | undefined
|
|
1445
1279
|
if (entry.members !== undefined) {
|
|
1446
1280
|
const anchor = entry.anchor
|
|
@@ -1461,7 +1295,6 @@ function paramRegistryOf(
|
|
|
1461
1295
|
)
|
|
1462
1296
|
}
|
|
1463
1297
|
|
|
1464
|
-
/** The runtime query shape beneath the typed `Query` face. */
|
|
1465
1298
|
interface RawQuery {
|
|
1466
1299
|
readonly schema: AnySchema
|
|
1467
1300
|
readonly data: QueryData
|
|
@@ -1470,7 +1303,6 @@ interface RawQuery {
|
|
|
1470
1303
|
reach(name: string, arms: never): never
|
|
1471
1304
|
}
|
|
1472
1305
|
|
|
1473
|
-
/** Asserts every rule in a list derives the same head (name, aggregate shape, closed slice, class). */
|
|
1474
1306
|
function assertAlignedHeads(label: string, rules: readonly RuleData[]): void {
|
|
1475
1307
|
const first = rules[0]
|
|
1476
1308
|
if (first === undefined) {
|
|
@@ -1509,13 +1341,6 @@ function afterMainError(what: string): Error {
|
|
|
1509
1341
|
)
|
|
1510
1342
|
}
|
|
1511
1343
|
|
|
1512
|
-
/**
|
|
1513
|
-
* Assembles the runtime query value over completed rules: every rule must
|
|
1514
|
-
* derive the SAME head (name and aggregate shape, position for position —
|
|
1515
|
-
* the decode labels and the engine's alignment rule agree), and the param
|
|
1516
|
-
* registry folds in query-walk order. CQ lowering does not mention rec;
|
|
1517
|
-
* Reach carries RecData by value.
|
|
1518
|
-
*/
|
|
1519
1344
|
function makeRawQuery(
|
|
1520
1345
|
theory: AnySchema,
|
|
1521
1346
|
interiors: readonly InteriorData[],
|
|
@@ -1575,7 +1400,6 @@ function makeQuery<Rels extends SchemaRelations, Row, P extends ParamsRecord, Cl
|
|
|
1575
1400
|
return makeRawQuery(theory, interiors, rec, rules) as unknown as Query<Rels, Row, P, Classes>
|
|
1576
1401
|
}
|
|
1577
1402
|
|
|
1578
|
-
/** Collects one Interior from its builders. */
|
|
1579
1403
|
function collectInterior<Rels extends SchemaRelations, Classes extends SchemaClasses>(
|
|
1580
1404
|
theory: Schema<Rels, Classes>,
|
|
1581
1405
|
env: DerivedEnv,
|
|
@@ -1596,7 +1420,6 @@ function collectInterior<Rels extends SchemaRelations, Classes extends SchemaCla
|
|
|
1596
1420
|
return Object.freeze({ name, finds: first.finds, rules: Object.freeze(rules) })
|
|
1597
1421
|
}
|
|
1598
1422
|
|
|
1599
|
-
/** Collects the Rec from tagged base/rec builder arrays. */
|
|
1600
1423
|
function collectRec<Rels extends SchemaRelations, Classes extends SchemaClasses>(
|
|
1601
1424
|
theory: Schema<Rels, Classes>,
|
|
1602
1425
|
interiors: readonly InteriorData[],
|
|
@@ -1646,7 +1469,6 @@ function collectRec<Rels extends SchemaRelations, Classes extends SchemaClasses>
|
|
|
1646
1469
|
return recData
|
|
1647
1470
|
}
|
|
1648
1471
|
|
|
1649
|
-
/** Builds the CQ query start (interiors, then reach or the first main rule). */
|
|
1650
1472
|
function makeQueryStart<Rels extends SchemaRelations, Classes extends SchemaClasses, P extends ParamsRecord>(
|
|
1651
1473
|
theory: Schema<Rels, Classes>,
|
|
1652
1474
|
interiors: readonly InteriorData[]
|
|
@@ -1702,7 +1524,6 @@ function makeQueryStart<Rels extends SchemaRelations, Classes extends SchemaClas
|
|
|
1702
1524
|
return start as unknown as QueryStart<Rels, Classes, P>
|
|
1703
1525
|
}
|
|
1704
1526
|
|
|
1705
|
-
/** Builds the Reach query start (rec sealed; only the first main rule remains). */
|
|
1706
1527
|
function makeQueryReachStart<Rels extends SchemaRelations, Classes extends SchemaClasses, P extends ParamsRecord>(
|
|
1707
1528
|
theory: Schema<Rels, Classes>,
|
|
1708
1529
|
interiors: readonly InteriorData[],
|
|
@@ -1721,24 +1542,12 @@ function makeQueryReachStart<Rels extends SchemaRelations, Classes extends Schem
|
|
|
1721
1542
|
return start as unknown as QueryReachStart<Rels, Classes, P>
|
|
1722
1543
|
}
|
|
1723
1544
|
|
|
1724
|
-
/**
|
|
1725
|
-
* Opens a query over a schema: `query(S).rule(r => ...)`, optionally with
|
|
1726
|
-
* `interior` / `reach` first. Each `.rule` adds one conjunctive rule;
|
|
1727
|
-
* multiple rules are the set union. The schema's law-computed class map and
|
|
1728
|
-
* theory value ride into every rule builder — the join walls compare
|
|
1729
|
-
* against the mint slots off it.
|
|
1730
|
-
*/
|
|
1731
1545
|
function query<Rels extends SchemaRelations, Classes extends SchemaClasses>(
|
|
1732
1546
|
theory: Schema<Rels, Classes>
|
|
1733
1547
|
): QueryStart<Rels, Classes> {
|
|
1734
1548
|
return makeQueryStart<Rels, Classes, Record<never, never>>(theory, [])
|
|
1735
1549
|
}
|
|
1736
1550
|
|
|
1737
|
-
/**
|
|
1738
|
-
* Tags one closed-reference literal: the handle NAME, verified against the
|
|
1739
|
-
* roster and translated to its declaration-order row id, tagged u64. THE
|
|
1740
|
-
* single roster-verification point of the query surface.
|
|
1741
|
-
*/
|
|
1742
1551
|
function taggedHandleId(
|
|
1743
1552
|
context: string,
|
|
1744
1553
|
closed: { readonly name: string; readonly handles: readonly string[] },
|
|
@@ -1756,11 +1565,6 @@ function taggedHandleId(
|
|
|
1756
1565
|
return { kind: "u64", value: BigInt(id) }
|
|
1757
1566
|
}
|
|
1758
1567
|
|
|
1759
|
-
/**
|
|
1760
|
-
* Tags one literal in an interval element domain: a bigint tags as the
|
|
1761
|
-
* element (the membership typing rule's point side), an interval-shaped
|
|
1762
|
-
* value as the interval (value equality).
|
|
1763
|
-
*/
|
|
1764
1568
|
function taggedAtElementDomain(context: string, element: "u64" | "i64", value: unknown): TaggedValue {
|
|
1765
1569
|
if (typeof value === "bigint") {
|
|
1766
1570
|
if (element === "u64") {
|
|
@@ -1777,10 +1581,6 @@ function taggedAtElementDomain(context: string, element: "u64" | "i64", value: u
|
|
|
1777
1581
|
throw literalShapeError(context, "bigint (point) or { start, end } (interval)", value)
|
|
1778
1582
|
}
|
|
1779
1583
|
|
|
1780
|
-
/**
|
|
1781
|
-
* Tags one host literal at a FIELD position (atom bindings): the field's
|
|
1782
|
-
* structural kind directs the tag, never a guess.
|
|
1783
|
-
*/
|
|
1784
1584
|
function taggedLiteral(context: string, field: AnyField, value: unknown): TaggedValue {
|
|
1785
1585
|
const roster = rosterOf(field)
|
|
1786
1586
|
if (roster !== undefined) {
|
|
@@ -1834,18 +1634,7 @@ function taggedLiteral(context: string, field: AnyField, value: unknown): Tagged
|
|
|
1834
1634
|
* interval operand of `pointIn(t, span(...))`; under every other operator an
|
|
1835
1635
|
* interval shape against a scalar sibling stays refused.
|
|
1836
1636
|
*/
|
|
1837
|
-
function taggedCmpLiteral(
|
|
1838
|
-
context: string,
|
|
1839
|
-
sibling: AnyField | "measure",
|
|
1840
|
-
value: unknown,
|
|
1841
|
-
op: CmpKind | "binding"
|
|
1842
|
-
): TaggedValue {
|
|
1843
|
-
if (sibling === "measure") {
|
|
1844
|
-
if (typeof value !== "bigint") {
|
|
1845
|
-
throw literalShapeError(context, "bigint (the measure is u64)", value)
|
|
1846
|
-
}
|
|
1847
|
-
return { kind: "u64", value }
|
|
1848
|
-
}
|
|
1637
|
+
function taggedCmpLiteral(context: string, sibling: AnyField, value: unknown, op: CmpKind | "binding"): TaggedValue {
|
|
1849
1638
|
if (rosterOf(sibling) === undefined && sibling.kind === "interval") {
|
|
1850
1639
|
return taggedAtElementDomain(context, sibling.element, value)
|
|
1851
1640
|
}
|
|
@@ -1860,7 +1649,6 @@ function taggedCmpLiteral(
|
|
|
1860
1649
|
return taggedLiteral(context, sibling, value)
|
|
1861
1650
|
}
|
|
1862
1651
|
|
|
1863
|
-
/** The shared lowering context of one `lowerQuery` run. */
|
|
1864
1652
|
interface LowerContext {
|
|
1865
1653
|
readonly theory: AnySchema
|
|
1866
1654
|
readonly relationIds: ReadonlyMap<string, number>
|
|
@@ -1869,12 +1657,10 @@ interface LowerContext {
|
|
|
1869
1657
|
readonly params: ReadonlyMap<string, ParamEntry>
|
|
1870
1658
|
}
|
|
1871
1659
|
|
|
1872
|
-
/** One rule's dense variable numbering: first occurrence in written order, keyed on the object REFERENCE. */
|
|
1873
1660
|
interface VarIds {
|
|
1874
1661
|
of(ref: AnyVar): number
|
|
1875
1662
|
}
|
|
1876
1663
|
|
|
1877
|
-
/** Creates one rule-scoped variable numberer. */
|
|
1878
1664
|
function freshVarIds(): VarIds {
|
|
1879
1665
|
const assigned = new Map<AnyVar, number>()
|
|
1880
1666
|
return {
|
|
@@ -1890,7 +1676,6 @@ function freshVarIds(): VarIds {
|
|
|
1890
1676
|
}
|
|
1891
1677
|
}
|
|
1892
1678
|
|
|
1893
|
-
/** Resolves a param name to its dense positional id. */
|
|
1894
1679
|
function paramIdOf(ctx: LowerContext, name: string): number {
|
|
1895
1680
|
const id = ctx.paramIds.get(name)
|
|
1896
1681
|
if (id === undefined) {
|
|
@@ -1899,10 +1684,6 @@ function paramIdOf(ctx: LowerContext, name: string): number {
|
|
|
1899
1684
|
return id
|
|
1900
1685
|
}
|
|
1901
1686
|
|
|
1902
|
-
/**
|
|
1903
|
-
* Lowers one EDB atom (either polarity). A CLOSED owner lowers through the
|
|
1904
|
-
* same edb source, with field ordinals over the SEALED shape.
|
|
1905
|
-
*/
|
|
1906
1687
|
function lowerAtom(ctx: LowerContext, atom: AtomData, ids: VarIds): AtomIr {
|
|
1907
1688
|
const member = ctx.theory.relations[atom.relation.name]
|
|
1908
1689
|
if (member !== atom.relation) {
|
|
@@ -1927,7 +1708,6 @@ function lowerAtom(ctx: LowerContext, atom: AtomData, ids: VarIds): AtomIr {
|
|
|
1927
1708
|
return { source: { kind: "edb", relation: relationId }, bindings }
|
|
1928
1709
|
}
|
|
1929
1710
|
|
|
1930
|
-
/** Lowers one binding term. A membership ARRAY lowers to the existing param-set term over its content-addressed entry. */
|
|
1931
1711
|
function lowerBindingTerm(ctx: LowerContext, context: string, binding: BindingEntry, ids: VarIds): TermIr {
|
|
1932
1712
|
const bound = binding.term
|
|
1933
1713
|
switch (bound.kind) {
|
|
@@ -1975,7 +1755,6 @@ function lowerInteriorAtom(
|
|
|
1975
1755
|
return { source: { kind: "interior", interior }, bindings: irBindings }
|
|
1976
1756
|
}
|
|
1977
1757
|
|
|
1978
|
-
/** Lowers one comparison side; literals tag by the sibling's anchor (op-aware at `pointIn`). */
|
|
1979
1758
|
function lowerCmpTerm(ctx: LowerContext, side: CmpTermData, sibling: CmpTermData, ids: VarIds, op: CmpKind): TermIr {
|
|
1980
1759
|
switch (side.kind) {
|
|
1981
1760
|
case "var":
|
|
@@ -1984,13 +1763,11 @@ function lowerCmpTerm(ctx: LowerContext, side: CmpTermData, sibling: CmpTermData
|
|
|
1984
1763
|
return { kind: "param", param: paramIdOf(ctx, side.name) }
|
|
1985
1764
|
case "setParam":
|
|
1986
1765
|
return { kind: "paramSet", param: paramIdOf(ctx, side.name) }
|
|
1987
|
-
case "measure":
|
|
1988
|
-
return { kind: "measure", var: ids.of(side.ref) }
|
|
1989
1766
|
case "literal": {
|
|
1990
1767
|
const anchor = cmpAnchorOf(ctx, sibling)
|
|
1991
1768
|
if (anchor === undefined) {
|
|
1992
1769
|
throw errors.new(
|
|
1993
|
-
"query lowering: a comparison literal needs a bound-variable
|
|
1770
|
+
"query lowering: a comparison literal needs a bound-variable or anchored-param sibling to type it"
|
|
1994
1771
|
)
|
|
1995
1772
|
}
|
|
1996
1773
|
return { kind: "literal", value: taggedCmpLiteral("comparison literal", anchor, side.value, op) }
|
|
@@ -1998,21 +1775,16 @@ function lowerCmpTerm(ctx: LowerContext, side: CmpTermData, sibling: CmpTermData
|
|
|
1998
1775
|
}
|
|
1999
1776
|
}
|
|
2000
1777
|
|
|
2001
|
-
|
|
2002
|
-
function cmpAnchorOf(ctx: LowerContext, sibling: CmpTermData): AnyField | "measure" | undefined {
|
|
1778
|
+
function cmpAnchorOf(ctx: LowerContext, sibling: CmpTermData): AnyField | undefined {
|
|
2003
1779
|
if (sibling.kind === "var") {
|
|
2004
1780
|
return sibling.ref.field
|
|
2005
1781
|
}
|
|
2006
|
-
if (sibling.kind === "measure") {
|
|
2007
|
-
return "measure"
|
|
2008
|
-
}
|
|
2009
1782
|
if (sibling.kind === "param" || sibling.kind === "setParam") {
|
|
2010
1783
|
return ctx.params.get(sibling.name)?.anchor
|
|
2011
1784
|
}
|
|
2012
1785
|
return undefined
|
|
2013
1786
|
}
|
|
2014
1787
|
|
|
2015
|
-
/** Lowers one comparison. */
|
|
2016
1788
|
function lowerComparison(ctx: LowerContext, cmp: CmpData, ids: VarIds): ComparisonIr {
|
|
2017
1789
|
if (cmp.op.kind === "allen") {
|
|
2018
1790
|
return {
|
|
@@ -2028,7 +1800,6 @@ function lowerComparison(ctx: LowerContext, cmp: CmpData, ids: VarIds): Comparis
|
|
|
2028
1800
|
}
|
|
2029
1801
|
}
|
|
2030
1802
|
|
|
2031
|
-
/** Lowers one condition node (comparison leaf or and/or tree). */
|
|
2032
1803
|
function lowerCondition(ctx: LowerContext, cond: CondData, ids: VarIds): ConditionTreeIr {
|
|
2033
1804
|
if (cond.kind === "cmp") {
|
|
2034
1805
|
return { kind: "leaf", cmp: lowerComparison(ctx, cond, ids) }
|
|
@@ -2041,30 +1812,21 @@ function lowerCondition(ctx: LowerContext, cond: CondData, ids: VarIds): Conditi
|
|
|
2041
1812
|
}
|
|
2042
1813
|
}
|
|
2043
1814
|
|
|
2044
|
-
/** Lowers one find entry to its per-rule find term. */
|
|
2045
1815
|
function lowerFind(entry: FindEntryData, ids: VarIds): FindTermIr {
|
|
2046
1816
|
if (entry.kind === "var") {
|
|
2047
1817
|
return { kind: "var", var: ids.of(entry.over) }
|
|
2048
1818
|
}
|
|
2049
|
-
if (entry.kind === "measure") {
|
|
2050
|
-
return { kind: "measure", var: ids.of(entry.over) }
|
|
2051
|
-
}
|
|
2052
1819
|
const agg = entry.agg
|
|
2053
1820
|
switch (agg.op) {
|
|
2054
1821
|
case "count":
|
|
2055
1822
|
return { kind: "count" }
|
|
2056
|
-
case "fold":
|
|
2057
|
-
if ("duration" in agg.over) {
|
|
2058
|
-
return { kind: "aggregateMeasure", op: { kind: agg.fold }, over: ids.of(agg.over.duration) }
|
|
2059
|
-
}
|
|
1823
|
+
case "fold":
|
|
2060
1824
|
return { kind: "aggregate", op: { kind: agg.fold }, over: ids.of(agg.over) }
|
|
2061
|
-
}
|
|
2062
1825
|
case "pack":
|
|
2063
1826
|
return { kind: "pack", over: ids.of(agg.over) }
|
|
2064
1827
|
}
|
|
2065
1828
|
}
|
|
2066
1829
|
|
|
2067
|
-
/** One aggregate's var-free head-op kind (`AggOp::head_op`). */
|
|
2068
1830
|
function headOpOf(agg: AggData): HeadOpIr {
|
|
2069
1831
|
switch (agg.op) {
|
|
2070
1832
|
case "count":
|
|
@@ -2076,16 +1838,14 @@ function headOpOf(agg: AggData): HeadOpIr {
|
|
|
2076
1838
|
}
|
|
2077
1839
|
}
|
|
2078
1840
|
|
|
2079
|
-
/** One find entry's var-free head shape. */
|
|
2080
1841
|
function headTermOf(column: FindColumn): HeadTermIr {
|
|
2081
1842
|
const entry = column.entry
|
|
2082
|
-
if (entry.kind === "var"
|
|
1843
|
+
if (entry.kind === "var") {
|
|
2083
1844
|
return { kind: "var" }
|
|
2084
1845
|
}
|
|
2085
1846
|
return { kind: "aggregate", op: headOpOf(entry.agg) }
|
|
2086
1847
|
}
|
|
2087
1848
|
|
|
2088
|
-
/** Lowers one rule: body walked in written order (var ids by first occurrence), finds last. */
|
|
2089
1849
|
function lowerRule(ctx: LowerContext, rule: RuleData): RuleIr {
|
|
2090
1850
|
const ids = freshVarIds()
|
|
2091
1851
|
const atoms: AtomIr[] = []
|
|
@@ -2125,11 +1885,6 @@ function lowerRule(ctx: LowerContext, rule: RuleData): RuleIr {
|
|
|
2125
1885
|
}
|
|
2126
1886
|
}
|
|
2127
1887
|
|
|
2128
|
-
/**
|
|
2129
|
-
* Lowers a query value to the bridge's `QueryIr` — pure and stable:
|
|
2130
|
-
* interiors in declaration order, then CQ or Reach, then main. Every
|
|
2131
|
-
* registered param must carry a field anchor by now.
|
|
2132
|
-
*/
|
|
2133
1888
|
function lowerQuery(q: AnyQuery): ParsedQuery {
|
|
2134
1889
|
const theory = q.schema
|
|
2135
1890
|
const relationIds = new Map<string, number>()
|