@bjornpagen/bumbledb 1.2.0 → 1.3.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 +178 -0
- package/README.md +2 -2
- package/dist/alternatives.d.ts +25 -0
- package/dist/alternatives.d.ts.map +1 -0
- package/dist/alternatives.js +59 -0
- package/dist/alternatives.js.map +1 -0
- package/dist/capacity.d.ts +7 -4
- package/dist/capacity.d.ts.map +1 -1
- package/dist/capacity.js +3 -1
- package/dist/capacity.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/law.d.ts +3 -1
- package/dist/law.d.ts.map +1 -1
- package/dist/law.js.map +1 -1
- package/dist/native.d.ts +14 -0
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js.map +1 -1
- package/dist/query/atom.d.ts +2 -1
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js.map +1 -1
- package/dist/query/compute.d.ts +16 -1
- package/dist/query/compute.d.ts.map +1 -1
- package/dist/query/compute.js +15 -2
- package/dist/query/compute.js.map +1 -1
- package/dist/query/description.d.ts +10 -2
- package/dist/query/description.d.ts.map +1 -1
- package/dist/query/description.js +10 -1
- package/dist/query/description.js.map +1 -1
- package/dist/query/find.d.ts +32 -10
- package/dist/query/find.d.ts.map +1 -1
- package/dist/query/find.js +1 -1
- package/dist/query/find.js.map +1 -1
- package/dist/query/lower.d.ts +18 -6
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +43 -30
- package/dist/query/lower.js.map +1 -1
- package/dist/query/parse-ir.d.ts.map +1 -1
- package/dist/query/parse-ir.js +24 -1
- package/dist/query/parse-ir.js.map +1 -1
- package/dist/query/scope.d.ts +21 -12
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js.map +1 -1
- package/dist/query/segments.d.ts +20 -0
- package/dist/query/segments.d.ts.map +1 -0
- package/dist/query/segments.js +35 -0
- package/dist/query/segments.js.map +1 -0
- package/dist/scalar.d.ts +30 -8
- package/dist/scalar.d.ts.map +1 -1
- package/dist/scalar.js +80 -2
- package/dist/scalar.js.map +1 -1
- package/dist/shape.d.ts +6 -3
- package/dist/shape.d.ts.map +1 -1
- package/pack-provenance.json +3 -3
- package/package.json +4 -4
- package/src/alternatives.ts +95 -0
- package/src/capacity.ts +7 -4
- package/src/index.ts +14 -2
- package/src/law.ts +8 -5
- package/src/native.ts +14 -0
- package/src/query/atom.ts +2 -0
- package/src/query/compute.ts +41 -2
- package/src/query/description.ts +34 -4
- package/src/query/find.ts +47 -23
- package/src/query/lower.ts +73 -43
- package/src/query/parse-ir.ts +23 -1
- package/src/query/scope.ts +32 -15
- package/src/query/segments.ts +61 -0
- package/src/scalar.ts +140 -7
- package/src/shape.ts +9 -3
package/src/query/scope.ts
CHANGED
|
@@ -122,9 +122,23 @@ function isImportedSource(value: unknown): value is ImportedSource {
|
|
|
122
122
|
|
|
123
123
|
type RowOfImport<Q> = InferredOf<Q> extends { readonly row: infer R } ? R : never
|
|
124
124
|
|
|
125
|
+
type HeadOfImport<Q> = InferredOf<Q> extends { readonly head: infer H } ? H : never
|
|
126
|
+
|
|
127
|
+
/** Named imported slot so downstream declarations need not spell its private marker. */
|
|
128
|
+
interface ImportedFieldVar<S extends ClassedField, K extends string> extends Var<S["field"], string, K> {
|
|
129
|
+
readonly [inferred]?: { readonly slot: S }
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Retain the same descriptor and carrier class as the runtime import facade.
|
|
133
|
+
// A host bigint alone cannot distinguish i64 from u64 or a closed identifier.
|
|
134
|
+
type ImportedVar<Q, K extends string> =
|
|
135
|
+
HeadOfImport<Q> extends Readonly<Record<K, infer S extends ClassedField>>
|
|
136
|
+
? ImportedFieldVar<S, K>
|
|
137
|
+
: Var<AnyField, string, K>
|
|
138
|
+
|
|
125
139
|
type ImportVars<Q> = [RowOfImport<Q>] extends [never]
|
|
126
140
|
? Readonly<Record<string, Var<AnyField, string, string>>>
|
|
127
|
-
: { readonly [K in keyof RowOfImport<Q> & string]:
|
|
141
|
+
: { readonly [K in keyof RowOfImport<Q> & string]: ImportedVar<Q, K> }
|
|
128
142
|
|
|
129
143
|
interface ImportFacade {
|
|
130
144
|
/** The pseudo-owner every import var carries (structurally an owner). */
|
|
@@ -241,10 +255,11 @@ type MintClassOf<Classes extends SchemaClasses, V> =
|
|
|
241
255
|
? ClassLookup<ClassRecordOf<Classes, RN>, K>
|
|
242
256
|
: never
|
|
243
257
|
|
|
244
|
-
type MintSlotOf<Classes extends SchemaClasses, V extends AnyVar> =
|
|
245
|
-
readonly field: V["field"]
|
|
246
|
-
readonly
|
|
247
|
-
|
|
258
|
+
type MintSlotOf<Classes extends SchemaClasses, V extends AnyVar> = [InferredOf<V>] extends [never]
|
|
259
|
+
? { readonly field: V["field"]; readonly class: MintClassOf<Classes, V> }
|
|
260
|
+
: InferredOf<V> extends { readonly slot: infer S extends ClassedField }
|
|
261
|
+
? S
|
|
262
|
+
: { readonly field: V["field"]; readonly class: MintClassOf<Classes, V> }
|
|
248
263
|
|
|
249
264
|
type ParamsRecord = Readonly<Record<string, unknown>>
|
|
250
265
|
|
|
@@ -257,15 +272,8 @@ type UnionToIntersection<U> = (U extends unknown ? (member: U) => void : never)
|
|
|
257
272
|
type ShapeOf<U> = [U] extends [never] ? Record<never, never> : Flatten<UnionToIntersection<U>>
|
|
258
273
|
|
|
259
274
|
/**
|
|
260
|
-
* A slot
|
|
261
|
-
*
|
|
262
|
-
* face pairing wall) plus the slot's law class.
|
|
263
|
-
*/
|
|
264
|
-
type SlotSignature<S extends ClassedField> = readonly [SignatureOf<S["field"]>, S["class"]]
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* A widened slot — a variable minted from an imported query template (its
|
|
268
|
-
* facade columns carry `AnyField`) or fully generic code — cannot be judged
|
|
275
|
+
* A widened slot — an explicitly erased query template or fully generic
|
|
276
|
+
* code with no retained descriptor — cannot be judged
|
|
269
277
|
* at the type tier. The degradation law applies: best effort degrades to
|
|
270
278
|
* SILENT, never to a wrong refusal; the runtime join judgment (real
|
|
271
279
|
* descriptors off the imported head's slots) stays authoritative.
|
|
@@ -273,7 +281,15 @@ type SlotSignature<S extends ClassedField> = readonly [SignatureOf<S["field"]>,
|
|
|
273
281
|
type WidenedSlot<S extends ClassedField> = [AnyField] extends [S["field"]] ? true : false
|
|
274
282
|
|
|
275
283
|
type JoinOk<A extends ClassedField, B extends ClassedField> =
|
|
276
|
-
WidenedSlot<A> extends true
|
|
284
|
+
WidenedSlot<A> extends true
|
|
285
|
+
? true
|
|
286
|
+
: WidenedSlot<B> extends true
|
|
287
|
+
? true
|
|
288
|
+
: Same<SignatureOf<A["field"]>, SignatureOf<B["field"]>> extends true
|
|
289
|
+
? string extends A["class"] | B["class"]
|
|
290
|
+
? true
|
|
291
|
+
: Same<A["class"], B["class"]>
|
|
292
|
+
: false
|
|
277
293
|
|
|
278
294
|
type U64Wire<F extends AnyField> = F extends { readonly kind: "u64" }
|
|
279
295
|
? F extends { readonly closed: unknown }
|
|
@@ -390,6 +406,7 @@ export type {
|
|
|
390
406
|
ClassedField,
|
|
391
407
|
ExactVars,
|
|
392
408
|
Flatten,
|
|
409
|
+
ImportedFieldVar,
|
|
393
410
|
ImportedSource,
|
|
394
411
|
ImportVars,
|
|
395
412
|
InferredOf,
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { AuthoringError } from "#errors.ts"
|
|
2
|
+
import type { IntervalElementKind, IntervalField } from "#fields.ts"
|
|
3
|
+
import { type AnyVar, isTerm, term } from "#query/scope.ts"
|
|
4
|
+
import { recordValue } from "#values.ts"
|
|
5
|
+
|
|
6
|
+
type IntervalVar<E extends IntervalElementKind = IntervalElementKind> = AnyVar & { readonly field: IntervalField<E> }
|
|
7
|
+
type SegmentOp = "intersection" | "difference"
|
|
8
|
+
|
|
9
|
+
/** A relational find output, using the native binary segment operator directly. */
|
|
10
|
+
interface Segments<E extends IntervalElementKind = IntervalElementKind> {
|
|
11
|
+
readonly kind: "segments"
|
|
12
|
+
readonly op: SegmentOp
|
|
13
|
+
readonly left: IntervalVar<E>
|
|
14
|
+
readonly right: IntervalVar<E>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function segments<E extends IntervalElementKind>(
|
|
18
|
+
op: SegmentOp,
|
|
19
|
+
left: IntervalVar<E>,
|
|
20
|
+
right: IntervalVar<NoInfer<E>>
|
|
21
|
+
): Segments<E> {
|
|
22
|
+
if (
|
|
23
|
+
!isTerm(left) ||
|
|
24
|
+
left[term] !== "var" ||
|
|
25
|
+
!isTerm(right) ||
|
|
26
|
+
right[term] !== "var" ||
|
|
27
|
+
left.field.kind !== "interval" ||
|
|
28
|
+
right.field.kind !== "interval" ||
|
|
29
|
+
left.field.element !== right.field.element
|
|
30
|
+
)
|
|
31
|
+
throw new AuthoringError({ message: `${op}: expected interval variables of the same element kind` })
|
|
32
|
+
return Object.freeze({ kind: "segments", op, left, right })
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function intersection<E extends IntervalElementKind>(
|
|
36
|
+
left: IntervalVar<E>,
|
|
37
|
+
right: IntervalVar<NoInfer<E>>
|
|
38
|
+
): Segments<E> {
|
|
39
|
+
return segments("intersection", left, right)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function difference<E extends IntervalElementKind>(left: IntervalVar<E>, right: IntervalVar<NoInfer<E>>): Segments<E> {
|
|
43
|
+
return segments("difference", left, right)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function isSegments(input: unknown): input is Segments {
|
|
47
|
+
if (typeof input !== "object" || input === null || !("kind" in input) || input.kind !== "segments") return false
|
|
48
|
+
recordValue("segments", input, ["kind", "op", "left", "right"])
|
|
49
|
+
const value = input as Segments
|
|
50
|
+
if (value.op !== "intersection" && value.op !== "difference")
|
|
51
|
+
throw new AuthoringError({ message: "unknown segment operator" })
|
|
52
|
+
segments(value.op, value.left, value.right)
|
|
53
|
+
return true
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function segmentField(value: Segments): IntervalField<IntervalElementKind, undefined> {
|
|
57
|
+
return Object.freeze({ kind: "interval", element: value.left.field.element, width: undefined })
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type { IntervalVar, SegmentOp, Segments }
|
|
61
|
+
export { difference, intersection, isSegments, segmentField }
|
package/src/scalar.ts
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* query IR — `{ kind: "literal", value: ValueSpec }` via `literalWireOf`
|
|
17
17
|
*/
|
|
18
18
|
import { AuthoringError } from "#errors.ts"
|
|
19
|
+
import type { ScalarExprIr } from "#native.ts"
|
|
19
20
|
import type { ValueSpec } from "#spec.ts"
|
|
20
21
|
|
|
21
22
|
/** F0: leaf scope for the shared AST (C1). Query vars are typed; source fields stay unresolved. */
|
|
@@ -25,7 +26,10 @@ export type ScalarLeafScope = "query-var" | "source-field"
|
|
|
25
26
|
type ScalarKind = "u64" | "i64" | "f64" | "bool"
|
|
26
27
|
|
|
27
28
|
/** Cached result kind: known only when derivable; otherwise honestly unresolved. */
|
|
28
|
-
type
|
|
29
|
+
type IntervalKind = "intervalU64" | "intervalI64" | "intervalF64"
|
|
30
|
+
type ScalarInputKind = ScalarKind | IntervalKind
|
|
31
|
+
type ScalarResultKind = ScalarInputKind | "unresolved"
|
|
32
|
+
type Rounding = "towardZero" | "nearestTiesAwayFromZero" | "nearestTiesToEven"
|
|
29
33
|
|
|
30
34
|
/** Host value for a derived scalar kind. */
|
|
31
35
|
type ScalarValue<K extends ScalarKind> = K extends "f64" ? number : K extends "bool" ? boolean : bigint
|
|
@@ -60,7 +64,18 @@ type ScalarLiteral =
|
|
|
60
64
|
| { readonly i64: bigint }
|
|
61
65
|
| { readonly f64: number }
|
|
62
66
|
|
|
63
|
-
type ScalarOpKind =
|
|
67
|
+
type ScalarOpKind =
|
|
68
|
+
| "literal"
|
|
69
|
+
| "negate"
|
|
70
|
+
| "isNaN"
|
|
71
|
+
| "isFinite"
|
|
72
|
+
| "add"
|
|
73
|
+
| "subtract"
|
|
74
|
+
| "multiply"
|
|
75
|
+
| "divide"
|
|
76
|
+
| "cast"
|
|
77
|
+
| "mulDiv"
|
|
78
|
+
| "measure"
|
|
64
79
|
|
|
65
80
|
/**
|
|
66
81
|
* Structural query-variable leaf. The query layer supplies a bound variable
|
|
@@ -79,6 +94,14 @@ type SourceFieldLeaf = { readonly kind: "field"; readonly name: string }
|
|
|
79
94
|
type ScalarLeaf<S extends ScalarLeafScope> = S extends "query-var" ? QueryVarLeaf : SourceFieldLeaf
|
|
80
95
|
|
|
81
96
|
type ScalarNodeBody<S extends ScalarLeafScope> =
|
|
97
|
+
| { readonly kind: "measure"; readonly expr: ScalarNode<S> }
|
|
98
|
+
| {
|
|
99
|
+
readonly kind: "mulDiv"
|
|
100
|
+
readonly a: ScalarNode<S>
|
|
101
|
+
readonly b: ScalarNode<S>
|
|
102
|
+
readonly divisor: ScalarNode<S>
|
|
103
|
+
readonly rounding: Rounding
|
|
104
|
+
}
|
|
82
105
|
| ScalarLeaf<S>
|
|
83
106
|
| { readonly kind: "literal"; readonly value: ScalarLiteral }
|
|
84
107
|
| { readonly kind: "negate"; readonly expr: ScalarNode<S> }
|
|
@@ -197,13 +220,13 @@ function assertDepth(where: string, depth: number): void {
|
|
|
197
220
|
}
|
|
198
221
|
}
|
|
199
222
|
|
|
200
|
-
function assertNumeric(where: string, kind:
|
|
201
|
-
if (kind
|
|
223
|
+
function assertNumeric(where: string, kind: ScalarInputKind): asserts kind is NumericKind {
|
|
224
|
+
if (kind !== "u64" && kind !== "i64" && kind !== "f64") {
|
|
202
225
|
throw new AuthoringError({ message: `${where}: the operand is bool, not numeric (u64/i64/f64)` })
|
|
203
226
|
}
|
|
204
227
|
}
|
|
205
228
|
|
|
206
|
-
function assertSameKind(where: string, left:
|
|
229
|
+
function assertSameKind(where: string, left: ScalarInputKind, right: ScalarInputKind): ScalarInputKind {
|
|
207
230
|
if (left !== right) {
|
|
208
231
|
throw new AuthoringError({
|
|
209
232
|
message: `${where}: operand kinds differ (${left} vs ${right}) — the engine has no mixed promotion; cast explicitly (Scalar.toF64/ toF64Exact/ toI64Exact/ toU64Exact)`
|
|
@@ -294,7 +317,7 @@ function sourceField(name: string): ScalarFieldRef {
|
|
|
294
317
|
}
|
|
295
318
|
|
|
296
319
|
/** Query-var leaf — kind is the variable's schema kind, already known. */
|
|
297
|
-
function queryVarLeaf<K extends
|
|
320
|
+
function queryVarLeaf<K extends ScalarInputKind>(ref: ScalarQueryVar, kind: K): ScalarNode<"query-var", K> {
|
|
298
321
|
return admit("Scalar.queryVar", {
|
|
299
322
|
kind: "var",
|
|
300
323
|
ref,
|
|
@@ -334,6 +357,56 @@ function scalarBinary<S extends ScalarLeafScope>(
|
|
|
334
357
|
})
|
|
335
358
|
}
|
|
336
359
|
|
|
360
|
+
function roundingMode(value: unknown): Rounding {
|
|
361
|
+
if (value !== "towardZero" && value !== "nearestTiesAwayFromZero" && value !== "nearestTiesToEven")
|
|
362
|
+
throw new AuthoringError({ message: "unknown integer rounding mode" })
|
|
363
|
+
return value
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function scalarMulDiv<S extends ScalarLeafScope>(
|
|
367
|
+
where: string,
|
|
368
|
+
a: ScalarNode<S>,
|
|
369
|
+
b: ScalarNode<S>,
|
|
370
|
+
divisor: ScalarNode<S>,
|
|
371
|
+
rounding: Rounding
|
|
372
|
+
): ScalarNode<S> {
|
|
373
|
+
const scope = assertScope(where, a, b)
|
|
374
|
+
assertScope(where, a, divisor)
|
|
375
|
+
let known: "i64" | "u64" | undefined
|
|
376
|
+
for (const operand of [a, b, divisor]) {
|
|
377
|
+
if (operand.result === "unresolved") continue
|
|
378
|
+
if ((operand.result !== "i64" && operand.result !== "u64") || (known !== undefined && known !== operand.result))
|
|
379
|
+
throw new AuthoringError({ message: `${where}: operands must have one matching integer kind` })
|
|
380
|
+
known = operand.result
|
|
381
|
+
}
|
|
382
|
+
return admit(where, {
|
|
383
|
+
kind: "mulDiv",
|
|
384
|
+
a,
|
|
385
|
+
b,
|
|
386
|
+
divisor,
|
|
387
|
+
rounding: roundingMode(rounding),
|
|
388
|
+
scope,
|
|
389
|
+
result:
|
|
390
|
+
a.result === "unresolved" || b.result === "unresolved" || divisor.result === "unresolved"
|
|
391
|
+
? "unresolved"
|
|
392
|
+
: a.result,
|
|
393
|
+
depth: Math.max(a.depth, b.depth, divisor.depth) + 1
|
|
394
|
+
})
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function scalarMeasure<S extends ScalarLeafScope>(where: string, expr: ScalarNode<S>): ScalarNode<S> {
|
|
398
|
+
const kind = expr.result
|
|
399
|
+
if (kind !== "unresolved" && kind !== "intervalU64" && kind !== "intervalI64" && kind !== "intervalF64")
|
|
400
|
+
throw new AuthoringError({ message: `${where}: measure requires an interval` })
|
|
401
|
+
return admit(where, {
|
|
402
|
+
kind: "measure",
|
|
403
|
+
expr,
|
|
404
|
+
scope: expr.scope,
|
|
405
|
+
result: ({ unresolved: "unresolved", intervalF64: "f64", intervalI64: "u64", intervalU64: "u64" } as const)[kind],
|
|
406
|
+
depth: expr.depth + 1
|
|
407
|
+
})
|
|
408
|
+
}
|
|
409
|
+
|
|
337
410
|
function scalarNegate<S extends ScalarLeafScope>(where: string, expr: ScalarNode<S>): ScalarNode<S, ScalarResultKind> {
|
|
338
411
|
return admit(where, {
|
|
339
412
|
kind: "negate",
|
|
@@ -391,12 +464,16 @@ function queryVarsOf(node: ScalarNode<"query-var">): readonly ScalarQueryVar[] {
|
|
|
391
464
|
break
|
|
392
465
|
case "literal":
|
|
393
466
|
break
|
|
467
|
+
case "measure":
|
|
394
468
|
case "negate":
|
|
395
469
|
case "isNaN":
|
|
396
470
|
case "isFinite":
|
|
397
471
|
case "cast":
|
|
398
472
|
pending.push(next.expr)
|
|
399
473
|
break
|
|
474
|
+
case "mulDiv":
|
|
475
|
+
pending.push(next.a, next.b, next.divisor)
|
|
476
|
+
break
|
|
400
477
|
case "add":
|
|
401
478
|
case "subtract":
|
|
402
479
|
case "multiply":
|
|
@@ -472,6 +549,27 @@ function divide<
|
|
|
472
549
|
return scalarBinary("Scalar.divide", "divide", left, right) as ScalarExpr<CombineNumeric<L, R>>
|
|
473
550
|
}
|
|
474
551
|
|
|
552
|
+
function mulDiv<
|
|
553
|
+
L extends "i64" | "u64" | "unresolved",
|
|
554
|
+
R extends CombineNumeric<L, R> extends never ? never : "i64" | "u64" | "unresolved",
|
|
555
|
+
D extends CombineNumeric<L, D> extends never
|
|
556
|
+
? never
|
|
557
|
+
: CombineNumeric<R, D> extends never
|
|
558
|
+
? never
|
|
559
|
+
: "i64" | "u64" | "unresolved"
|
|
560
|
+
>(
|
|
561
|
+
a: ScalarExpr<L>,
|
|
562
|
+
b: ScalarExpr<R>,
|
|
563
|
+
divisor: ScalarExpr<D>,
|
|
564
|
+
rounding: Rounding
|
|
565
|
+
): ScalarExpr<CombineNumeric<CombineNumeric<L, R>, D>> {
|
|
566
|
+
return scalarMulDiv("Scalar.mulDiv", a, b, divisor, rounding) as ScalarExpr<CombineNumeric<CombineNumeric<L, R>, D>>
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function measure(expr: ScalarFieldRef): ScalarExpr<"unresolved"> {
|
|
570
|
+
return scalarMeasure("Scalar.measure", expr) as ScalarExpr<"unresolved">
|
|
571
|
+
}
|
|
572
|
+
|
|
475
573
|
function toF64(expr: ScalarExpr<NumericOrUnresolved>): ScalarExpr<"f64"> {
|
|
476
574
|
return scalarCast("Scalar.toF64", "toF64", "f64", expr) as ScalarExpr<"f64">
|
|
477
575
|
}
|
|
@@ -496,6 +594,32 @@ function isFiniteExpr(expr: ScalarExpr<"f64" | "unresolved">): ScalarExpr<"bool"
|
|
|
496
594
|
return scalarFloatPredicate("Scalar.isFinite", "isFinite", expr)
|
|
497
595
|
}
|
|
498
596
|
|
|
597
|
+
/** One lowering walk for query variables and row-field bindings. */
|
|
598
|
+
function scalarWire(node: ScalarNode, leaf: (node: QueryVarLeaf | SourceFieldLeaf) => number): ScalarExprIr {
|
|
599
|
+
const walk = (expr: ScalarNode): ScalarExprIr => scalarWire(expr, leaf)
|
|
600
|
+
switch (node.kind) {
|
|
601
|
+
case "var":
|
|
602
|
+
case "field":
|
|
603
|
+
return { kind: "var", var: leaf(node) }
|
|
604
|
+
case "literal":
|
|
605
|
+
return { kind: "literal", value: literalWireOf(node.value) }
|
|
606
|
+
case "measure":
|
|
607
|
+
case "negate":
|
|
608
|
+
case "isNaN":
|
|
609
|
+
case "isFinite":
|
|
610
|
+
return { kind: node.kind, expr: walk(node.expr) }
|
|
611
|
+
case "cast":
|
|
612
|
+
return { kind: node.kind, cast: node.cast, expr: walk(node.expr) }
|
|
613
|
+
case "mulDiv":
|
|
614
|
+
return { kind: node.kind, a: walk(node.a), b: walk(node.b), divisor: walk(node.divisor), rounding: node.rounding }
|
|
615
|
+
case "add":
|
|
616
|
+
case "subtract":
|
|
617
|
+
case "multiply":
|
|
618
|
+
case "divide":
|
|
619
|
+
return { kind: node.kind, left: walk(node.left), right: walk(node.right) }
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
499
623
|
/** The authoring namespace: `Scalar.field("units")`, `Scalar.add(...)`. */
|
|
500
624
|
const Scalar = Object.freeze({
|
|
501
625
|
field,
|
|
@@ -509,6 +633,8 @@ const Scalar = Object.freeze({
|
|
|
509
633
|
subtract,
|
|
510
634
|
multiply,
|
|
511
635
|
divide,
|
|
636
|
+
mulDiv,
|
|
637
|
+
measure,
|
|
512
638
|
toF64,
|
|
513
639
|
toF64Exact,
|
|
514
640
|
toI64Exact,
|
|
@@ -519,12 +645,15 @@ const Scalar = Object.freeze({
|
|
|
519
645
|
|
|
520
646
|
export type {
|
|
521
647
|
CombineNumeric,
|
|
648
|
+
IntervalKind,
|
|
522
649
|
NumericCast,
|
|
523
650
|
NumericKind,
|
|
524
651
|
NumericOrUnresolved,
|
|
525
652
|
QueryVarLeaf,
|
|
653
|
+
Rounding,
|
|
526
654
|
ScalarExpr,
|
|
527
655
|
ScalarFieldRef,
|
|
656
|
+
ScalarInputKind,
|
|
528
657
|
ScalarKind,
|
|
529
658
|
ScalarLiteral,
|
|
530
659
|
ScalarNode,
|
|
@@ -550,11 +679,15 @@ export {
|
|
|
550
679
|
MAX_SCALAR_DEPTH,
|
|
551
680
|
queryVarLeaf,
|
|
552
681
|
queryVarsOf,
|
|
682
|
+
roundingMode,
|
|
553
683
|
Scalar,
|
|
554
684
|
scalarAuthoringWork,
|
|
555
685
|
scalarBinary,
|
|
556
686
|
scalarCast,
|
|
557
687
|
scalarFloatPredicate,
|
|
558
688
|
scalarLiteral,
|
|
559
|
-
|
|
689
|
+
scalarMeasure,
|
|
690
|
+
scalarMulDiv,
|
|
691
|
+
scalarNegate,
|
|
692
|
+
scalarWire
|
|
560
693
|
}
|
package/src/shape.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Query } from "#query/lower.ts"
|
|
2
|
-
import type { ParamsRecord } from "#query/scope.ts"
|
|
2
|
+
import type { inferred, ParamsRecord } from "#query/scope.ts"
|
|
3
3
|
/**
|
|
4
4
|
* Shared derived types: `S` is a
|
|
5
5
|
* declared core schema value's type, `Rel<S>` its ordinary (writable)
|
|
@@ -24,7 +24,13 @@ type Key<K extends KeyStatement<AnyRelation, readonly string[]>> = Pick<
|
|
|
24
24
|
Extract<K["projection"][number], keyof Fact<K["owner"]>>
|
|
25
25
|
>
|
|
26
26
|
|
|
27
|
-
/**
|
|
28
|
-
|
|
27
|
+
/** The immutable executable view of a query. Readers consume its schema/data
|
|
28
|
+
* and inferred result/parameters, not the author's rule-building methods.
|
|
29
|
+
* Runtime schema identity remains authoritative for dynamically built schemas.
|
|
30
|
+
*/
|
|
31
|
+
type QueryTemplate<S extends AnySchema, P extends ParamsRecord, A> = Pick<
|
|
32
|
+
Query<S["relations"], A, P>,
|
|
33
|
+
"schema" | "data" | typeof inferred
|
|
34
|
+
>
|
|
29
35
|
|
|
30
36
|
export type { Key, QueryTemplate, Rel }
|