@bjornpagen/bumbledb 0.20.2 → 1.0.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 +283 -1611
- package/README.md +156 -118
- package/dist/capacity.d.ts +16 -12
- package/dist/capacity.d.ts.map +1 -1
- package/dist/capacity.js +16 -48
- package/dist/capacity.js.map +1 -1
- package/dist/changes.d.ts +54 -0
- package/dist/changes.d.ts.map +1 -0
- package/dist/changes.js +245 -0
- package/dist/changes.js.map +1 -0
- package/dist/close.d.ts +24 -0
- package/dist/close.d.ts.map +1 -0
- package/dist/close.js +34 -0
- package/dist/close.js.map +1 -0
- package/dist/closed.d.ts +1 -3
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +24 -12
- package/dist/closed.js.map +1 -1
- package/dist/codec.d.ts +56 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +289 -0
- package/dist/codec.js.map +1 -0
- package/dist/compile.d.ts +68 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +75 -0
- package/dist/compile.js.map +1 -0
- package/dist/db-native.d.ts +170 -0
- package/dist/db-native.d.ts.map +1 -0
- package/dist/db-native.js +5 -0
- package/dist/db-native.js.map +1 -0
- package/dist/db.d.ts +101 -233
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +232 -1084
- package/dist/db.js.map +1 -1
- package/dist/errors.d.ts +57 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +32 -0
- package/dist/errors.js.map +1 -0
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js +2 -2
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +65 -24
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +94 -24
- package/dist/fields.js.map +1 -1
- package/dist/index.d.ts +37 -32
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -21
- package/dist/index.js.map +1 -1
- package/dist/internal/log.d.ts +24 -0
- package/dist/internal/log.d.ts.map +1 -0
- package/dist/internal/log.js +10 -0
- package/dist/internal/log.js.map +1 -0
- package/dist/law.d.ts +8 -13
- package/dist/law.d.ts.map +1 -1
- package/dist/law.js +6 -59
- package/dist/law.js.map +1 -1
- package/dist/lower.d.ts +0 -13
- package/dist/lower.d.ts.map +1 -1
- package/dist/lower.js +9 -17
- package/dist/lower.js.map +1 -1
- package/dist/migration.d.ts +33 -0
- package/dist/migration.d.ts.map +1 -0
- package/dist/migration.js +48 -0
- package/dist/migration.js.map +1 -0
- package/dist/native.d.ts +256 -301
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +65 -108
- package/dist/native.js.map +1 -1
- package/dist/query/atom.d.ts +24 -13
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js +7 -3
- package/dist/query/atom.js.map +1 -1
- package/dist/query/compute.d.ts +68 -0
- package/dist/query/compute.d.ts.map +1 -0
- package/dist/query/compute.js +141 -0
- package/dist/query/compute.js.map +1 -0
- package/dist/query/find.d.ts +15 -8
- package/dist/query/find.d.ts.map +1 -1
- package/dist/query/find.js +6 -2
- package/dist/query/find.js.map +1 -1
- package/dist/query/lower.d.ts +23 -2
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +433 -80
- package/dist/query/lower.js.map +1 -1
- package/dist/query/parse-ir.d.ts.map +1 -1
- package/dist/query/parse-ir.js +17 -8
- package/dist/query/parse-ir.js.map +1 -1
- package/dist/query/run.d.ts +12 -2
- package/dist/query/run.d.ts.map +1 -1
- package/dist/query/run.js +26 -9
- package/dist/query/run.js.map +1 -1
- package/dist/query/scope.d.ts +47 -3
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +83 -19
- package/dist/query/scope.js.map +1 -1
- package/dist/relation.d.ts +3 -8
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +16 -10
- package/dist/relation.js.map +1 -1
- package/dist/result.d.ts +47 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +50 -0
- package/dist/result.js.map +1 -0
- package/dist/rows.d.ts +69 -0
- package/dist/rows.d.ts.map +1 -0
- package/dist/rows.js +328 -0
- package/dist/rows.js.map +1 -0
- package/dist/runtime-codes.d.ts +2 -0
- package/dist/runtime-codes.d.ts.map +1 -0
- package/dist/runtime-codes.js +19 -0
- package/dist/runtime-codes.js.map +1 -0
- package/dist/runtime-errors.d.ts +106 -0
- package/dist/runtime-errors.d.ts.map +1 -0
- package/dist/runtime-errors.js +62 -0
- package/dist/runtime-errors.js.map +1 -0
- package/dist/runtime-native.d.ts +171 -0
- package/dist/runtime-native.d.ts.map +1 -0
- package/dist/runtime-native.js +5 -0
- package/dist/runtime-native.js.map +1 -0
- package/dist/runtime.d.ts +73 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +293 -0
- package/dist/runtime.js.map +1 -0
- package/dist/scalar.d.ts +165 -0
- package/dist/scalar.d.ts.map +1 -0
- package/dist/scalar.js +344 -0
- package/dist/scalar.js.map +1 -0
- package/dist/schema.d.ts +1 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +34 -37
- package/dist/schema.js.map +1 -1
- package/dist/shape.d.ts +28 -0
- package/dist/shape.d.ts.map +1 -0
- package/dist/shape.js +2 -0
- package/dist/shape.js.map +1 -0
- package/dist/spec.d.ts +27 -3
- package/dist/spec.d.ts.map +1 -1
- package/dist/spec.js +22 -1
- package/dist/spec.js.map +1 -1
- package/dist/statements.d.ts +16 -3
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +54 -28
- package/dist/statements.js.map +1 -1
- package/dist/uuid.d.ts +23 -0
- package/dist/uuid.d.ts.map +1 -0
- package/dist/uuid.js +54 -0
- package/dist/uuid.js.map +1 -0
- package/pack-provenance.json +7 -0
- package/package.json +12 -16
- package/src/capacity.ts +34 -54
- package/src/changes.ts +361 -0
- package/src/close.ts +53 -0
- package/src/closed.ts +25 -19
- package/src/codec.ts +379 -0
- package/src/compile.ts +149 -0
- package/src/db-native.ts +247 -0
- package/src/db.ts +461 -1632
- package/src/errors.ts +50 -0
- package/src/face.ts +2 -2
- package/src/fields.ts +182 -55
- package/src/index.ts +62 -126
- package/src/internal/log.ts +51 -0
- package/src/law.ts +13 -28
- package/src/lower.ts +9 -4
- package/src/migration.ts +69 -0
- package/src/native.ts +326 -487
- package/src/query/atom.ts +61 -25
- package/src/query/compute.ts +237 -0
- package/src/query/find.ts +31 -12
- package/src/query/lower.ts +531 -143
- package/src/query/parse-ir.ts +18 -13
- package/src/query/run.ts +32 -16
- package/src/query/scope.ts +140 -8
- package/src/relation.ts +17 -21
- package/src/result.ts +130 -0
- package/src/rows.ts +404 -0
- package/src/runtime-codes.ts +18 -0
- package/src/runtime-errors.ts +69 -0
- package/src/runtime-native.ts +242 -0
- package/src/runtime.ts +440 -0
- package/src/scalar.ts +560 -0
- package/src/schema.ts +35 -37
- package/src/shape.ts +31 -0
- package/src/spec.ts +33 -3
- package/src/statements.ts +75 -68
- package/src/uuid.ts +69 -0
- package/dist/marshal.d.ts +0 -44
- package/dist/marshal.d.ts.map +0 -1
- package/dist/marshal.js +0 -165
- package/dist/marshal.js.map +0 -1
- package/src/marshal.ts +0 -200
package/src/scalar.ts
ADDED
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE leaf-scoped scalar AST (C1/C8): one tagged literal grammar and
|
|
3
|
+
* operator roster, parameterized by {@link ScalarLeafScope}. TypeScript never
|
|
4
|
+
* evaluates — the native compiler binds source fields and the engine
|
|
5
|
+
* `ScalarEvaluator` executes. Construction is constant work against cached
|
|
6
|
+
* depth/kind summaries; it does not re-walk the tree.
|
|
7
|
+
*
|
|
8
|
+
* Query-var leaves carry a known schema kind. Source-field leaves stay
|
|
9
|
+
* `unresolved` until native snapshot binding. A result kind is recorded only
|
|
10
|
+
* when it is derivable from known operands; otherwise it stays unresolved.
|
|
11
|
+
* Known incompatible kinds refuse here. Unresolved trees are inert metadata,
|
|
12
|
+
* not typechecked programs.
|
|
13
|
+
*
|
|
14
|
+
* Wire spellings (the grammar arms, not a second flattened interpretation):
|
|
15
|
+
* migration plan JSON — field/literal/operator nodes; one-arm literals (`{ u64: n }`)
|
|
16
|
+
* query IR — `{ kind: "literal", value: ValueSpec }` via `literalWireOf`
|
|
17
|
+
*/
|
|
18
|
+
import { AuthoringError } from "#errors.ts"
|
|
19
|
+
import type { ValueSpec } from "#spec.ts"
|
|
20
|
+
|
|
21
|
+
/** F0: leaf scope for the shared AST (C1). Query vars are typed; source fields stay unresolved. */
|
|
22
|
+
export type ScalarLeafScope = "query-var" | "source-field"
|
|
23
|
+
|
|
24
|
+
/** The engine's scalar result vocabulary — distinct at the type level. */
|
|
25
|
+
type ScalarKind = "u64" | "i64" | "f64" | "bool"
|
|
26
|
+
|
|
27
|
+
/** Cached result kind: known only when derivable; otherwise honestly unresolved. */
|
|
28
|
+
type ScalarResultKind = ScalarKind | "unresolved"
|
|
29
|
+
|
|
30
|
+
/** Host value for a derived scalar kind. */
|
|
31
|
+
type ScalarValue<K extends ScalarKind> = K extends "f64" ? number : K extends "bool" ? boolean : bigint
|
|
32
|
+
|
|
33
|
+
type NumericCast = "toF64" | "toF64Exact" | "toI64Exact" | "toU64Exact"
|
|
34
|
+
|
|
35
|
+
type NumericKind = "u64" | "i64" | "f64"
|
|
36
|
+
|
|
37
|
+
type NumericOrUnresolved = NumericKind | "unresolved"
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Combine known numeric kinds, or stay unresolved when a source-field leaf
|
|
41
|
+
* still needs native binding. Distinct known kinds collapse to `never`
|
|
42
|
+
* (static refusal) and throw at the constructor.
|
|
43
|
+
*/
|
|
44
|
+
type CombineNumeric<L, R> = L extends "unresolved"
|
|
45
|
+
? R extends "bool"
|
|
46
|
+
? never
|
|
47
|
+
: "unresolved"
|
|
48
|
+
: R extends "unresolved"
|
|
49
|
+
? L extends "bool"
|
|
50
|
+
? never
|
|
51
|
+
: "unresolved"
|
|
52
|
+
: L extends R
|
|
53
|
+
? L
|
|
54
|
+
: never
|
|
55
|
+
|
|
56
|
+
/** The tagged literal subset shared by every leaf scope. */
|
|
57
|
+
type ScalarLiteral =
|
|
58
|
+
| { readonly bool: boolean }
|
|
59
|
+
| { readonly u64: bigint }
|
|
60
|
+
| { readonly i64: bigint }
|
|
61
|
+
| { readonly f64: number }
|
|
62
|
+
|
|
63
|
+
type ScalarOpKind = "literal" | "negate" | "isNaN" | "isFinite" | "add" | "subtract" | "multiply" | "divide" | "cast"
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Structural query-variable leaf. The query layer supplies a bound variable
|
|
67
|
+
* whose field kind is already a schema kind; this module does not import
|
|
68
|
+
* query types (scope → atom → compute → scalar would cycle).
|
|
69
|
+
*/
|
|
70
|
+
interface ScalarQueryVar {
|
|
71
|
+
readonly label: string
|
|
72
|
+
readonly field: { readonly kind: string }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
type QueryVarLeaf = { readonly kind: "var"; readonly ref: ScalarQueryVar }
|
|
76
|
+
|
|
77
|
+
type SourceFieldLeaf = { readonly kind: "field"; readonly name: string }
|
|
78
|
+
|
|
79
|
+
type ScalarLeaf<S extends ScalarLeafScope> = S extends "query-var" ? QueryVarLeaf : SourceFieldLeaf
|
|
80
|
+
|
|
81
|
+
type ScalarNodeBody<S extends ScalarLeafScope> =
|
|
82
|
+
| ScalarLeaf<S>
|
|
83
|
+
| { readonly kind: "literal"; readonly value: ScalarLiteral }
|
|
84
|
+
| { readonly kind: "negate"; readonly expr: ScalarNode<S> }
|
|
85
|
+
| { readonly kind: "isNaN"; readonly expr: ScalarNode<S> }
|
|
86
|
+
| { readonly kind: "isFinite"; readonly expr: ScalarNode<S> }
|
|
87
|
+
| { readonly kind: "add"; readonly left: ScalarNode<S>; readonly right: ScalarNode<S> }
|
|
88
|
+
| { readonly kind: "subtract"; readonly left: ScalarNode<S>; readonly right: ScalarNode<S> }
|
|
89
|
+
| { readonly kind: "multiply"; readonly left: ScalarNode<S>; readonly right: ScalarNode<S> }
|
|
90
|
+
| { readonly kind: "divide"; readonly left: ScalarNode<S>; readonly right: ScalarNode<S> }
|
|
91
|
+
| { readonly kind: "cast"; readonly cast: NumericCast; readonly expr: ScalarNode<S> }
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* One authored node: roster arms plus cached `scope` / `result` / `depth`.
|
|
95
|
+
* Summaries are construction metadata; lowering and L17 serialization read
|
|
96
|
+
* the grammar arms and ignore the summaries.
|
|
97
|
+
*/
|
|
98
|
+
type ScalarNode<
|
|
99
|
+
S extends ScalarLeafScope = ScalarLeafScope,
|
|
100
|
+
K extends ScalarResultKind = ScalarResultKind
|
|
101
|
+
> = ScalarNodeBody<S> & {
|
|
102
|
+
readonly scope: S
|
|
103
|
+
readonly result: K
|
|
104
|
+
readonly depth: number
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Migration authoring expression (source-field scope). `K` is the cached
|
|
109
|
+
* result kind, not a host value type: I64 and U64 remain distinct even
|
|
110
|
+
* though both use bigint in JavaScript.
|
|
111
|
+
*/
|
|
112
|
+
type ScalarExpr<K extends ScalarResultKind = ScalarResultKind> = ScalarNode<"source-field", K>
|
|
113
|
+
|
|
114
|
+
/** A migration field read: unresolved until native snapshot binding. */
|
|
115
|
+
type ScalarFieldRef = ScalarNode<"source-field", "unresolved"> & SourceFieldLeaf
|
|
116
|
+
|
|
117
|
+
const MAX_SCALAR_DEPTH = 128
|
|
118
|
+
const U64_MAX = (1n << 64n) - 1n
|
|
119
|
+
const I64_MIN = -(1n << 63n)
|
|
120
|
+
const I64_MAX = (1n << 63n) - 1n
|
|
121
|
+
|
|
122
|
+
/** Constructor admissions only — not descendant visits. D27 construction-work pin. */
|
|
123
|
+
let authoringWork = 0
|
|
124
|
+
|
|
125
|
+
function scalarAuthoringWork(): number {
|
|
126
|
+
return authoringWork
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function admit<
|
|
130
|
+
const Node extends { readonly scope: ScalarLeafScope; readonly result: ScalarResultKind; readonly depth: number }
|
|
131
|
+
>(where: string, node: Node): Readonly<Node> {
|
|
132
|
+
authoringWork += 1
|
|
133
|
+
assertDepth(where, node.depth)
|
|
134
|
+
return Object.freeze(node)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function checkU64(value: bigint, where: string): void {
|
|
138
|
+
if (typeof value !== "bigint" || value < 0n || value > U64_MAX) {
|
|
139
|
+
throw new AuthoringError({ message: `${where}: a u64 literal is a bigint in 0..=2^64-1` })
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function checkI64(value: bigint, where: string): void {
|
|
144
|
+
if (typeof value !== "bigint" || value < I64_MIN || value > I64_MAX) {
|
|
145
|
+
throw new AuthoringError({ message: `${where}: an i64 literal is a bigint in -2^63..=2^63-1` })
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function checkF64(value: number, where: string): void {
|
|
150
|
+
if (typeof value !== "number") {
|
|
151
|
+
throw new AuthoringError({ message: `${where}: an f64 literal is a number` })
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function checkBool(value: boolean, where: string): void {
|
|
156
|
+
if (typeof value !== "boolean") {
|
|
157
|
+
throw new AuthoringError({ message: `${where}: a bool literal is a boolean` })
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function literalValue(value: ScalarLiteral): ScalarLiteral {
|
|
162
|
+
return Object.freeze(value)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Converts a shared one-arm literal to the query wire `ValueSpec` spelling. */
|
|
166
|
+
function literalWireOf(value: ScalarLiteral): ValueSpec {
|
|
167
|
+
if ("bool" in value) {
|
|
168
|
+
return { kind: "bool", value: value.bool }
|
|
169
|
+
}
|
|
170
|
+
if ("u64" in value) {
|
|
171
|
+
return { kind: "u64", value: value.u64 }
|
|
172
|
+
}
|
|
173
|
+
if ("i64" in value) {
|
|
174
|
+
return { kind: "i64", value: value.i64 }
|
|
175
|
+
}
|
|
176
|
+
return { kind: "f64", value: value.f64 }
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function literalKindOf(value: ScalarLiteral): ScalarKind {
|
|
180
|
+
if ("bool" in value) {
|
|
181
|
+
return "bool"
|
|
182
|
+
}
|
|
183
|
+
if ("u64" in value) {
|
|
184
|
+
return "u64"
|
|
185
|
+
}
|
|
186
|
+
if ("i64" in value) {
|
|
187
|
+
return "i64"
|
|
188
|
+
}
|
|
189
|
+
return "f64"
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function assertDepth(where: string, depth: number): void {
|
|
193
|
+
if (depth > MAX_SCALAR_DEPTH) {
|
|
194
|
+
throw new AuthoringError({
|
|
195
|
+
message: `${where}: the expression is deeper than ${MAX_SCALAR_DEPTH} nodes (the engine's scalar depth bound)`
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function assertNumeric(where: string, kind: ScalarKind): void {
|
|
201
|
+
if (kind === "bool") {
|
|
202
|
+
throw new AuthoringError({ message: `${where}: the operand is bool, not numeric (u64/i64/f64)` })
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function assertSameKind(where: string, left: ScalarKind, right: ScalarKind): ScalarKind {
|
|
207
|
+
if (left !== right) {
|
|
208
|
+
throw new AuthoringError({
|
|
209
|
+
message: `${where}: operand kinds differ (${left} vs ${right}) — the engine has no mixed promotion; cast explicitly (Scalar.toF64/ toF64Exact/ toI64Exact/ toU64Exact)`
|
|
210
|
+
})
|
|
211
|
+
}
|
|
212
|
+
return left
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function assertScope<S extends ScalarLeafScope>(where: string, left: ScalarNode<S>, right: ScalarNode<S>): S {
|
|
216
|
+
if (left.scope !== right.scope) {
|
|
217
|
+
throw new AuthoringError({
|
|
218
|
+
message: `${where}: leaf scopes differ (${left.scope} vs ${right.scope}) — query-var and source-field trees do not mix`
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
return left.scope
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function combineNumeric(where: string, left: ScalarResultKind, right: ScalarResultKind): ScalarResultKind {
|
|
225
|
+
if (left === "unresolved" || right === "unresolved") {
|
|
226
|
+
if (left !== "unresolved") {
|
|
227
|
+
assertNumeric(where, left)
|
|
228
|
+
}
|
|
229
|
+
if (right !== "unresolved") {
|
|
230
|
+
assertNumeric(where, right)
|
|
231
|
+
}
|
|
232
|
+
return "unresolved"
|
|
233
|
+
}
|
|
234
|
+
assertNumeric(where, left)
|
|
235
|
+
assertNumeric(where, right)
|
|
236
|
+
return assertSameKind(where, left, right)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function negateKind(where: string, inner: ScalarResultKind): ScalarResultKind {
|
|
240
|
+
if (inner === "unresolved") {
|
|
241
|
+
return "unresolved"
|
|
242
|
+
}
|
|
243
|
+
if (inner !== "i64" && inner !== "f64") {
|
|
244
|
+
throw new AuthoringError({
|
|
245
|
+
message: `${where}: the operand is ${inner} — negation is defined over i64 and f64 only`
|
|
246
|
+
})
|
|
247
|
+
}
|
|
248
|
+
return inner
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function assertCastOperand(where: string, inner: ScalarResultKind): void {
|
|
252
|
+
if (inner === "unresolved") {
|
|
253
|
+
return
|
|
254
|
+
}
|
|
255
|
+
assertNumeric(where, inner)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function assertFloatOperand(where: string, inner: ScalarResultKind): void {
|
|
259
|
+
if (inner === "unresolved") {
|
|
260
|
+
return
|
|
261
|
+
}
|
|
262
|
+
if (inner !== "f64") {
|
|
263
|
+
throw new AuthoringError({ message: `${where}: the operand is ${inner} — the float predicates read f64` })
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function isScalarNode(value: unknown): value is ScalarNode {
|
|
268
|
+
return (
|
|
269
|
+
typeof value === "object" &&
|
|
270
|
+
value !== null &&
|
|
271
|
+
"kind" in value &&
|
|
272
|
+
"scope" in value &&
|
|
273
|
+
"result" in value &&
|
|
274
|
+
"depth" in value
|
|
275
|
+
)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function isUnresolvedScalar(node: ScalarNode): boolean {
|
|
279
|
+
return node.result === "unresolved"
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** Source-field leaf — unresolved until L14 binds it against the verified snapshot. */
|
|
283
|
+
function sourceField(name: string): ScalarFieldRef {
|
|
284
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
285
|
+
throw new AuthoringError({ message: "scalar field(...) names a nonempty source field" })
|
|
286
|
+
}
|
|
287
|
+
return admit("Scalar.field", {
|
|
288
|
+
kind: "field",
|
|
289
|
+
name,
|
|
290
|
+
scope: "source-field",
|
|
291
|
+
result: "unresolved",
|
|
292
|
+
depth: 1
|
|
293
|
+
})
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** Query-var leaf — kind is the variable's schema kind, already known. */
|
|
297
|
+
function queryVarLeaf<K extends ScalarKind>(ref: ScalarQueryVar, kind: K): ScalarNode<"query-var", K> {
|
|
298
|
+
return admit("Scalar.queryVar", {
|
|
299
|
+
kind: "var",
|
|
300
|
+
ref,
|
|
301
|
+
scope: "query-var",
|
|
302
|
+
result: kind,
|
|
303
|
+
depth: 1
|
|
304
|
+
})
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function scalarLiteral<S extends ScalarLeafScope>(scope: S, value: ScalarLiteral): ScalarNode<S, ScalarKind> {
|
|
308
|
+
const kind = literalKindOf(value)
|
|
309
|
+
return admit("Scalar.literal", {
|
|
310
|
+
kind: "literal",
|
|
311
|
+
value: literalValue(value),
|
|
312
|
+
scope,
|
|
313
|
+
result: kind,
|
|
314
|
+
depth: 1
|
|
315
|
+
}) as ScalarNode<S, ScalarKind>
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
type BinaryOp = "add" | "subtract" | "multiply" | "divide"
|
|
319
|
+
|
|
320
|
+
function scalarBinary<S extends ScalarLeafScope>(
|
|
321
|
+
where: string,
|
|
322
|
+
op: BinaryOp,
|
|
323
|
+
left: ScalarNode<S>,
|
|
324
|
+
right: ScalarNode<S>
|
|
325
|
+
): ScalarNode<S, ScalarResultKind> {
|
|
326
|
+
const scope = assertScope(where, left, right)
|
|
327
|
+
return admit(where, {
|
|
328
|
+
kind: op,
|
|
329
|
+
left,
|
|
330
|
+
right,
|
|
331
|
+
scope,
|
|
332
|
+
result: combineNumeric(where, left.result, right.result),
|
|
333
|
+
depth: Math.max(left.depth, right.depth) + 1
|
|
334
|
+
})
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function scalarNegate<S extends ScalarLeafScope>(where: string, expr: ScalarNode<S>): ScalarNode<S, ScalarResultKind> {
|
|
338
|
+
return admit(where, {
|
|
339
|
+
kind: "negate",
|
|
340
|
+
expr,
|
|
341
|
+
scope: expr.scope,
|
|
342
|
+
result: negateKind(where, expr.result),
|
|
343
|
+
depth: expr.depth + 1
|
|
344
|
+
})
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function scalarCast<S extends ScalarLeafScope>(
|
|
348
|
+
where: string,
|
|
349
|
+
cast: NumericCast,
|
|
350
|
+
result: ScalarKind,
|
|
351
|
+
expr: ScalarNode<S>
|
|
352
|
+
): ScalarNode<S, ScalarKind> {
|
|
353
|
+
assertCastOperand(where, expr.result)
|
|
354
|
+
return admit(where, {
|
|
355
|
+
kind: "cast",
|
|
356
|
+
cast,
|
|
357
|
+
expr,
|
|
358
|
+
scope: expr.scope,
|
|
359
|
+
result,
|
|
360
|
+
depth: expr.depth + 1
|
|
361
|
+
})
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function scalarFloatPredicate<S extends ScalarLeafScope>(
|
|
365
|
+
where: string,
|
|
366
|
+
op: "isNaN" | "isFinite",
|
|
367
|
+
expr: ScalarNode<S>
|
|
368
|
+
): ScalarNode<S, "bool"> {
|
|
369
|
+
assertFloatOperand(where, expr.result)
|
|
370
|
+
return admit(where, {
|
|
371
|
+
kind: op,
|
|
372
|
+
expr,
|
|
373
|
+
scope: expr.scope,
|
|
374
|
+
result: "bool",
|
|
375
|
+
depth: expr.depth + 1
|
|
376
|
+
})
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/** Query-var leaves collected once at find-binding time — not during construction. */
|
|
380
|
+
function queryVarsOf(node: ScalarNode<"query-var">): readonly ScalarQueryVar[] {
|
|
381
|
+
const vars: ScalarQueryVar[] = []
|
|
382
|
+
const pending: ScalarNode<"query-var">[] = [node]
|
|
383
|
+
while (pending.length > 0) {
|
|
384
|
+
const next = pending.pop()
|
|
385
|
+
if (next === undefined) {
|
|
386
|
+
break
|
|
387
|
+
}
|
|
388
|
+
switch (next.kind) {
|
|
389
|
+
case "var":
|
|
390
|
+
vars.push(next.ref)
|
|
391
|
+
break
|
|
392
|
+
case "literal":
|
|
393
|
+
break
|
|
394
|
+
case "negate":
|
|
395
|
+
case "isNaN":
|
|
396
|
+
case "isFinite":
|
|
397
|
+
case "cast":
|
|
398
|
+
pending.push(next.expr)
|
|
399
|
+
break
|
|
400
|
+
case "add":
|
|
401
|
+
case "subtract":
|
|
402
|
+
case "multiply":
|
|
403
|
+
case "divide":
|
|
404
|
+
pending.push(next.left, next.right)
|
|
405
|
+
break
|
|
406
|
+
default:
|
|
407
|
+
throw new AuthoringError({
|
|
408
|
+
message: "queryVarsOf: a query-var tree cannot carry a source-field leaf"
|
|
409
|
+
})
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return vars
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function field(name: string): ScalarFieldRef {
|
|
416
|
+
return sourceField(name)
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function literal(value: ScalarLiteral): ScalarExpr<ScalarKind> {
|
|
420
|
+
return scalarLiteral("source-field", value)
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function u64(value: bigint): ScalarExpr<"u64"> {
|
|
424
|
+
checkU64(value, "Scalar.u64")
|
|
425
|
+
return scalarLiteral("source-field", { u64: value }) as ScalarExpr<"u64">
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function i64(value: bigint): ScalarExpr<"i64"> {
|
|
429
|
+
checkI64(value, "Scalar.i64")
|
|
430
|
+
return scalarLiteral("source-field", { i64: value }) as ScalarExpr<"i64">
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function f64(value: number): ScalarExpr<"f64"> {
|
|
434
|
+
checkF64(value, "Scalar.f64")
|
|
435
|
+
return scalarLiteral("source-field", { f64: value }) as ScalarExpr<"f64">
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function bool(value: boolean): ScalarExpr<"bool"> {
|
|
439
|
+
checkBool(value, "Scalar.bool")
|
|
440
|
+
return scalarLiteral("source-field", { bool: value }) as ScalarExpr<"bool">
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function negate<K extends "i64" | "f64" | "unresolved">(expr: ScalarExpr<K>): ScalarExpr<K> {
|
|
444
|
+
return scalarNegate("Scalar.negate", expr) as ScalarExpr<K>
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function add<L extends NumericOrUnresolved, R extends CombineNumeric<L, R> extends never ? never : NumericOrUnresolved>(
|
|
448
|
+
left: ScalarExpr<L>,
|
|
449
|
+
right: ScalarExpr<R>
|
|
450
|
+
): ScalarExpr<CombineNumeric<L, R>> {
|
|
451
|
+
return scalarBinary("Scalar.add", "add", left, right) as ScalarExpr<CombineNumeric<L, R>>
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function subtract<
|
|
455
|
+
L extends NumericOrUnresolved,
|
|
456
|
+
R extends CombineNumeric<L, R> extends never ? never : NumericOrUnresolved
|
|
457
|
+
>(left: ScalarExpr<L>, right: ScalarExpr<R>): ScalarExpr<CombineNumeric<L, R>> {
|
|
458
|
+
return scalarBinary("Scalar.subtract", "subtract", left, right) as ScalarExpr<CombineNumeric<L, R>>
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function multiply<
|
|
462
|
+
L extends NumericOrUnresolved,
|
|
463
|
+
R extends CombineNumeric<L, R> extends never ? never : NumericOrUnresolved
|
|
464
|
+
>(left: ScalarExpr<L>, right: ScalarExpr<R>): ScalarExpr<CombineNumeric<L, R>> {
|
|
465
|
+
return scalarBinary("Scalar.multiply", "multiply", left, right) as ScalarExpr<CombineNumeric<L, R>>
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function divide<
|
|
469
|
+
L extends NumericOrUnresolved,
|
|
470
|
+
R extends CombineNumeric<L, R> extends never ? never : NumericOrUnresolved
|
|
471
|
+
>(left: ScalarExpr<L>, right: ScalarExpr<R>): ScalarExpr<CombineNumeric<L, R>> {
|
|
472
|
+
return scalarBinary("Scalar.divide", "divide", left, right) as ScalarExpr<CombineNumeric<L, R>>
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function toF64(expr: ScalarExpr<NumericOrUnresolved>): ScalarExpr<"f64"> {
|
|
476
|
+
return scalarCast("Scalar.toF64", "toF64", "f64", expr) as ScalarExpr<"f64">
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function toF64Exact(expr: ScalarExpr<NumericOrUnresolved>): ScalarExpr<"f64"> {
|
|
480
|
+
return scalarCast("Scalar.toF64Exact", "toF64Exact", "f64", expr) as ScalarExpr<"f64">
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function toI64Exact(expr: ScalarExpr<NumericOrUnresolved>): ScalarExpr<"i64"> {
|
|
484
|
+
return scalarCast("Scalar.toI64Exact", "toI64Exact", "i64", expr) as ScalarExpr<"i64">
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function toU64Exact(expr: ScalarExpr<NumericOrUnresolved>): ScalarExpr<"u64"> {
|
|
488
|
+
return scalarCast("Scalar.toU64Exact", "toU64Exact", "u64", expr) as ScalarExpr<"u64">
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function isNaNExpr(expr: ScalarExpr<"f64" | "unresolved">): ScalarExpr<"bool"> {
|
|
492
|
+
return scalarFloatPredicate("Scalar.isNaN", "isNaN", expr)
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function isFiniteExpr(expr: ScalarExpr<"f64" | "unresolved">): ScalarExpr<"bool"> {
|
|
496
|
+
return scalarFloatPredicate("Scalar.isFinite", "isFinite", expr)
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/** The authoring namespace: `Scalar.field("units")`, `Scalar.add(...)`. */
|
|
500
|
+
const Scalar = Object.freeze({
|
|
501
|
+
field,
|
|
502
|
+
literal,
|
|
503
|
+
u64,
|
|
504
|
+
i64,
|
|
505
|
+
f64,
|
|
506
|
+
bool,
|
|
507
|
+
negate,
|
|
508
|
+
add,
|
|
509
|
+
subtract,
|
|
510
|
+
multiply,
|
|
511
|
+
divide,
|
|
512
|
+
toF64,
|
|
513
|
+
toF64Exact,
|
|
514
|
+
toI64Exact,
|
|
515
|
+
toU64Exact,
|
|
516
|
+
isNaN: isNaNExpr,
|
|
517
|
+
isFinite: isFiniteExpr
|
|
518
|
+
})
|
|
519
|
+
|
|
520
|
+
export type {
|
|
521
|
+
CombineNumeric,
|
|
522
|
+
NumericCast,
|
|
523
|
+
NumericKind,
|
|
524
|
+
NumericOrUnresolved,
|
|
525
|
+
QueryVarLeaf,
|
|
526
|
+
ScalarExpr,
|
|
527
|
+
ScalarFieldRef,
|
|
528
|
+
ScalarKind,
|
|
529
|
+
ScalarLiteral,
|
|
530
|
+
ScalarNode,
|
|
531
|
+
ScalarOpKind,
|
|
532
|
+
ScalarQueryVar,
|
|
533
|
+
ScalarResultKind,
|
|
534
|
+
ScalarValue,
|
|
535
|
+
SourceFieldLeaf
|
|
536
|
+
}
|
|
537
|
+
export {
|
|
538
|
+
assertDepth,
|
|
539
|
+
assertNumeric,
|
|
540
|
+
assertSameKind,
|
|
541
|
+
checkBool,
|
|
542
|
+
checkF64,
|
|
543
|
+
checkI64,
|
|
544
|
+
checkU64,
|
|
545
|
+
isScalarNode,
|
|
546
|
+
isUnresolvedScalar,
|
|
547
|
+
literalKindOf,
|
|
548
|
+
literalValue,
|
|
549
|
+
literalWireOf,
|
|
550
|
+
MAX_SCALAR_DEPTH,
|
|
551
|
+
queryVarLeaf,
|
|
552
|
+
queryVarsOf,
|
|
553
|
+
Scalar,
|
|
554
|
+
scalarAuthoringWork,
|
|
555
|
+
scalarBinary,
|
|
556
|
+
scalarCast,
|
|
557
|
+
scalarFloatPredicate,
|
|
558
|
+
scalarLiteral,
|
|
559
|
+
scalarNegate
|
|
560
|
+
}
|