@bjornpagen/bumbledb 1.1.0 → 1.2.1
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 +114 -11
- package/README.md +52 -9
- package/dist/capacity.d.ts +22 -59
- package/dist/capacity.d.ts.map +1 -1
- package/dist/capacity.js +66 -13
- package/dist/capacity.js.map +1 -1
- package/dist/changes.d.ts +26 -3
- package/dist/changes.d.ts.map +1 -1
- package/dist/changes.js +89 -44
- package/dist/changes.js.map +1 -1
- package/dist/closed.d.ts +23 -51
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +110 -168
- package/dist/closed.js.map +1 -1
- package/dist/codec.d.ts +4 -9
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +108 -183
- package/dist/codec.js.map +1 -1
- package/dist/compile.d.ts +16 -16
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +36 -15
- package/dist/compile.js.map +1 -1
- package/dist/db-native.d.ts +39 -0
- package/dist/db-native.d.ts.map +1 -1
- package/dist/db-native.js +8 -0
- package/dist/db-native.js.map +1 -1
- package/dist/db.d.ts +28 -8
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +91 -38
- package/dist/db.js.map +1 -1
- package/dist/errors.d.ts +11 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +13 -1
- package/dist/errors.js.map +1 -1
- package/dist/face.d.ts +19 -25
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js +30 -24
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +13 -26
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +89 -116
- package/dist/fields.js.map +1 -1
- package/dist/immutable.d.ts +10 -0
- package/dist/immutable.d.ts.map +1 -0
- package/dist/immutable.js +75 -0
- package/dist/immutable.js.map +1 -0
- package/dist/index.d.ts +16 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/internal/log.d.ts +2 -0
- package/dist/internal/log.d.ts.map +1 -1
- package/dist/internal/log.js +2 -0
- package/dist/internal/log.js.map +1 -1
- package/dist/law.d.ts +13 -11
- package/dist/law.d.ts.map +1 -1
- package/dist/law.js +1 -1
- package/dist/law.js.map +1 -1
- package/dist/lower.d.ts.map +1 -1
- package/dist/lower.js +11 -6
- package/dist/lower.js.map +1 -1
- package/dist/native.d.ts +1 -1
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js.map +1 -1
- package/dist/query/atom.d.ts +1 -1
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js.map +1 -1
- package/dist/query/description.d.ts +33 -0
- package/dist/query/description.d.ts.map +1 -0
- package/dist/query/description.js +302 -0
- package/dist/query/description.js.map +1 -0
- package/dist/query/find.d.ts +21 -6
- package/dist/query/find.d.ts.map +1 -1
- package/dist/query/find.js.map +1 -1
- package/dist/query/lower.d.ts +75 -10
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +132 -163
- package/dist/query/lower.js.map +1 -1
- package/dist/query/parse-ir.d.ts +7 -2
- package/dist/query/parse-ir.d.ts.map +1 -1
- package/dist/query/parse-ir.js +240 -53
- package/dist/query/parse-ir.js.map +1 -1
- package/dist/query/run.d.ts.map +1 -1
- package/dist/query/run.js +8 -10
- package/dist/query/run.js.map +1 -1
- package/dist/query/scope.d.ts +24 -16
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +5 -10
- package/dist/query/scope.js.map +1 -1
- package/dist/relation.d.ts +22 -48
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +30 -82
- package/dist/relation.js.map +1 -1
- package/dist/rows.d.ts +7 -4
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +44 -198
- package/dist/rows.js.map +1 -1
- package/dist/runtime-errors.d.ts +26 -2
- package/dist/runtime-errors.d.ts.map +1 -1
- package/dist/runtime-errors.js +43 -3
- package/dist/runtime-errors.js.map +1 -1
- package/dist/schema.d.ts +9 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +85 -24
- package/dist/schema.js.map +1 -1
- package/dist/selection.d.ts +27 -0
- package/dist/selection.d.ts.map +1 -0
- package/dist/selection.js +104 -0
- package/dist/selection.js.map +1 -0
- package/dist/shape.d.ts +12 -12
- package/dist/shape.d.ts.map +1 -1
- package/dist/statements.d.ts +17 -52
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +92 -111
- package/dist/statements.js.map +1 -1
- package/dist/values.d.ts +25 -0
- package/dist/values.d.ts.map +1 -0
- package/dist/values.js +203 -0
- package/dist/values.js.map +1 -0
- package/pack-provenance.json +3 -3
- package/package.json +4 -4
- package/src/capacity.ts +93 -80
- package/src/changes.ts +183 -54
- package/src/closed.ts +161 -314
- package/src/codec.ts +116 -190
- package/src/compile.ts +56 -32
- package/src/db-native.ts +42 -0
- package/src/db.ts +134 -52
- package/src/errors.ts +16 -1
- package/src/face.ts +44 -74
- package/src/fields.ts +100 -146
- package/src/immutable.ts +76 -0
- package/src/index.ts +36 -29
- package/src/internal/log.ts +2 -0
- package/src/law.ts +21 -18
- package/src/lower.ts +13 -10
- package/src/native.ts +2 -0
- package/src/query/atom.ts +1 -0
- package/src/query/description.ts +394 -0
- package/src/query/find.ts +20 -7
- package/src/query/lower.ts +177 -187
- package/src/query/parse-ir.ts +247 -54
- package/src/query/run.ts +15 -9
- package/src/query/scope.ts +42 -33
- package/src/relation.ts +58 -142
- package/src/rows.ts +62 -205
- package/src/runtime-errors.ts +47 -3
- package/src/schema.ts +99 -29
- package/src/selection.ts +124 -0
- package/src/shape.ts +18 -12
- package/src/statements.ts +138 -189
- package/src/values.ts +223 -0
package/src/query/lower.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
import { sealedFieldsOf } from "#closed.ts"
|
|
1
|
+
import { membersAgree, sealedFieldsOf } from "#closed.ts"
|
|
2
2
|
import { AuthoringError, SdkInvariantError } from "#errors.ts"
|
|
3
3
|
import type { AnyClosedRoster, AnyField } from "#fields.ts"
|
|
4
4
|
import {
|
|
5
5
|
assertDeclarationOrderKey,
|
|
6
6
|
f64 as f64Field,
|
|
7
|
+
i64 as i64Field,
|
|
7
8
|
isFloatIntervalValue,
|
|
8
9
|
isIntervalValue,
|
|
9
10
|
literalShapeError,
|
|
10
11
|
rosterOf,
|
|
12
|
+
rostersAgree,
|
|
11
13
|
u64 as u64Field
|
|
12
14
|
} from "#fields.ts"
|
|
15
|
+
import { snapshotData } from "#immutable.ts"
|
|
13
16
|
import type { ClassRecordOf, SchemaClasses } from "#law.ts"
|
|
14
17
|
import type {
|
|
15
18
|
AtomIr,
|
|
@@ -86,7 +89,8 @@ import {
|
|
|
86
89
|
} from "#query/scope.ts"
|
|
87
90
|
import { literalWireOf } from "#scalar.ts"
|
|
88
91
|
import type { AnySchema, Schema, SchemaRelations } from "#schema.ts"
|
|
89
|
-
import {
|
|
92
|
+
import { schemaDescriptor, schemasAgree } from "#schema.ts"
|
|
93
|
+
import { arrayValue, recordValue, taggedValueOf } from "#values.ts"
|
|
90
94
|
|
|
91
95
|
type QueryRelation<Rels extends SchemaRelations> = Extract<Rels[keyof Rels], MatchOwner>
|
|
92
96
|
|
|
@@ -96,6 +100,8 @@ type RowOf<T> = InferredOf<T> extends { readonly row: infer R } ? R : never
|
|
|
96
100
|
|
|
97
101
|
type HeadShape = Readonly<Record<string, ClassedField>> | undefined
|
|
98
102
|
|
|
103
|
+
type HeadOf<T> = InferredOf<T> extends { readonly head: infer H extends HeadShape } ? H : HeadShape
|
|
104
|
+
|
|
99
105
|
interface RuleValue<Row, P extends ParamsRecord, Head extends HeadShape = undefined> {
|
|
100
106
|
readonly rule: RuleData
|
|
101
107
|
readonly [inferred]?: { readonly row: Row; readonly params: P; readonly head: Head }
|
|
@@ -221,7 +227,12 @@ interface QueryRuleChain<
|
|
|
221
227
|
bindings: B & CheckInteriorBindings<B>
|
|
222
228
|
): QueryRuleChain<Rels, P, Classes>
|
|
223
229
|
|
|
224
|
-
|
|
230
|
+
/** A record of variables is already a valid projection, including v(rel)
|
|
231
|
+
* for a generic relation. Aggregate judgments remain on the general form. */
|
|
232
|
+
find<const F extends Readonly<Record<string, AnyVar>>>(
|
|
233
|
+
entries: F
|
|
234
|
+
): RuleValue<RowOfFind<F>, P, HeadRecordOf<Classes, F>>
|
|
235
|
+
find<const F extends FindShape>(entries: F & CheckFind<F>): RuleValue<RowOfFind<F>, P, HeadRecordOf<Classes, F>>
|
|
225
236
|
}
|
|
226
237
|
|
|
227
238
|
interface InteriorRuleScope<Rels extends SchemaRelations, Classes extends SchemaClasses = SchemaClasses>
|
|
@@ -288,6 +299,9 @@ interface InteriorRuleChain<
|
|
|
288
299
|
* Nonrecursive derived stages emit aggregate/computed outputs too
|
|
289
300
|
* (C05): only the RECURSIVE head stays projection-only.
|
|
290
301
|
*/
|
|
302
|
+
find<const F extends Readonly<Record<string, AnyVar>>>(
|
|
303
|
+
entries: F
|
|
304
|
+
): RuleValue<RowOfFind<F>, P, HeadRecordOf<Classes, F>>
|
|
291
305
|
find<const F extends FindShape>(entries: F & CheckFind<F>): RuleValue<RowOfFind<F>, P, HeadRecordOf<Classes, F>>
|
|
292
306
|
}
|
|
293
307
|
|
|
@@ -351,6 +365,9 @@ interface RecRuleChain<
|
|
|
351
365
|
name: string,
|
|
352
366
|
bindings: B & CheckInteriorBindings<B>
|
|
353
367
|
): RecRuleChain<Rels, P, Classes>
|
|
368
|
+
find<const F extends Readonly<Record<string, AnyVar>>>(
|
|
369
|
+
entries: F
|
|
370
|
+
): RuleValue<RowOfFind<F>, P, HeadRecordOf<Classes, F>>
|
|
354
371
|
find<const F extends FindShape>(entries: F & CheckRecFind<F>): RuleValue<RowOfFind<F>, P, HeadRecordOf<Classes, F>>
|
|
355
372
|
}
|
|
356
373
|
|
|
@@ -384,22 +401,23 @@ interface Query<
|
|
|
384
401
|
Rels extends SchemaRelations,
|
|
385
402
|
Row,
|
|
386
403
|
Params extends ParamsRecord,
|
|
387
|
-
Classes extends SchemaClasses = SchemaClasses
|
|
404
|
+
Classes extends SchemaClasses = SchemaClasses,
|
|
405
|
+
Head extends HeadShape = HeadShape
|
|
388
406
|
> {
|
|
389
407
|
readonly schema: Schema<Rels, Classes>
|
|
390
408
|
readonly data: QueryData
|
|
391
409
|
|
|
392
410
|
rule<RV extends AnyRuleValue>(
|
|
393
411
|
build: (r: QueryRuleScope<Rels, Classes>) => RV
|
|
394
|
-
): Query<Rels, Row | RowOf<RV>, Flatten<Params & ParamsOf<RV>>, Classes
|
|
412
|
+
): Query<Rels, Row | RowOf<RV>, Flatten<Params & ParamsOf<RV>>, Classes, Head | HeadOf<RV>>
|
|
395
413
|
|
|
396
414
|
/** A diagnostic name for composition/tracing — never a schema relation. */
|
|
397
|
-
named(label: string): Query<Rels, Row, Params, Classes>
|
|
415
|
+
named(label: string): Query<Rels, Row, Params, Classes, Head>
|
|
398
416
|
|
|
399
417
|
interior(name: string, ...builds: never[]): never
|
|
400
418
|
|
|
401
419
|
reach(name: string, arms: never): never
|
|
402
|
-
readonly [inferred]?: { readonly row: Row; readonly params: Params }
|
|
420
|
+
readonly [inferred]?: { readonly row: Row; readonly params: Params; readonly head: Head }
|
|
403
421
|
}
|
|
404
422
|
|
|
405
423
|
interface AnyQuery {
|
|
@@ -418,7 +436,7 @@ type QueryStart<
|
|
|
418
436
|
> = {
|
|
419
437
|
rule<RV extends AnyRuleValue>(
|
|
420
438
|
build: (r: QueryRuleScope<Rels, Classes>) => RV
|
|
421
|
-
): Query<Rels, RowOf<RV>, Flatten<P & ParamsOf<RV>>, Classes
|
|
439
|
+
): Query<Rels, RowOf<RV>, Flatten<P & ParamsOf<RV>>, Classes, HeadOf<RV>>
|
|
422
440
|
interior<const Builds extends readonly InteriorBuild<Rels, Classes>[]>(
|
|
423
441
|
name: string,
|
|
424
442
|
...builds: Builds
|
|
@@ -436,7 +454,7 @@ type QueryReachStart<
|
|
|
436
454
|
> = {
|
|
437
455
|
rule<RV extends AnyRuleValue>(
|
|
438
456
|
build: (r: QueryRuleScope<Rels, Classes>) => RV
|
|
439
|
-
): Query<Rels, RowOf<RV>, Flatten<P & ParamsOf<RV>>, Classes
|
|
457
|
+
): Query<Rels, RowOf<RV>, Flatten<P & ParamsOf<RV>>, Classes, HeadOf<RV>>
|
|
440
458
|
}
|
|
441
459
|
|
|
442
460
|
const termOps: TermOps = Object.freeze({
|
|
@@ -481,8 +499,8 @@ interface ResolvedBindings {
|
|
|
481
499
|
|
|
482
500
|
/**
|
|
483
501
|
* The MINT slot of a variable, the runtime twin of {@link MintSlotOf}: (i)
|
|
484
|
-
* verifies the mint owner
|
|
485
|
-
*
|
|
502
|
+
* verifies the mint owner agrees with the schema's ordered declaration — a
|
|
503
|
+
* conflicting declaration is refused, naming its label — and (ii)
|
|
486
504
|
* returns the descriptor it was minted at plus the law-computed class read
|
|
487
505
|
* off the schema's frozen class map. Because {@link fieldJoins} is an
|
|
488
506
|
* equality, judging every binding position against this one slot makes all
|
|
@@ -495,16 +513,16 @@ function mintSlotOf(context: ChainContext, ref: AnyVar): ClassedField {
|
|
|
495
513
|
// the descriptor it projected and the carrier class that survived
|
|
496
514
|
// the projection (aggregate outputs are bare derived scalars).
|
|
497
515
|
const column = facade.columns.get(ref.column)
|
|
498
|
-
if (column === undefined || (facade.source.schema as AnySchema
|
|
516
|
+
if (column === undefined || !schemasAgree(facade.source.schema as AnySchema, context.theory)) {
|
|
499
517
|
throw new AuthoringError({
|
|
500
518
|
message: `the variable ${ref.label} was minted from a query template schema ${context.theory.name} does not own`
|
|
501
519
|
})
|
|
502
520
|
}
|
|
503
521
|
return column.slot ?? { field: ref.field, class: undefined }
|
|
504
522
|
}
|
|
505
|
-
if (context.theory.relations[ref.owner.name]
|
|
523
|
+
if (!membersAgree(context.theory.relations[ref.owner.name], ref.owner)) {
|
|
506
524
|
throw new AuthoringError({
|
|
507
|
-
message: `the variable ${ref.label} was minted from a relation schema ${context.theory.name} does not declare —
|
|
525
|
+
message: `the variable ${ref.label} was minted from a relation schema ${context.theory.name} does not declare — use an equivalent ordered declaration`
|
|
508
526
|
})
|
|
509
527
|
}
|
|
510
528
|
return { field: ref.field, class: context.classes[ref.owner.name]?.[ref.column] }
|
|
@@ -532,7 +550,7 @@ function membershipSet(
|
|
|
532
550
|
})
|
|
533
551
|
}
|
|
534
552
|
const seen = new Set<string>()
|
|
535
|
-
const members = value
|
|
553
|
+
const members = arrayValue(context, value, function memberName(context, member) {
|
|
536
554
|
if (typeof member !== "string") {
|
|
537
555
|
throw literalShapeError(context, `a ${roster.name} handle name (string)`, member)
|
|
538
556
|
}
|
|
@@ -548,22 +566,30 @@ function membershipSet(
|
|
|
548
566
|
return { name: `∈ ${roster.name} ${JSON.stringify(key)}`, members: Object.freeze(members) }
|
|
549
567
|
}
|
|
550
568
|
|
|
569
|
+
function queryRecord(context: string, input: unknown): Readonly<Record<string, unknown>> {
|
|
570
|
+
return recordValue(context, input, typeof input === "object" && input !== null ? Object.keys(input) : [])
|
|
571
|
+
}
|
|
572
|
+
|
|
551
573
|
function resolveBindings(
|
|
552
574
|
context: ChainContext,
|
|
553
575
|
label: string,
|
|
554
|
-
|
|
555
|
-
bindings:
|
|
576
|
+
input: MatchOwner,
|
|
577
|
+
bindings: unknown,
|
|
556
578
|
joins: (a: ClassedField, b: ClassedField) => boolean = fieldJoins
|
|
557
579
|
): ResolvedBindings {
|
|
580
|
+
const relation = context.theory.relations[input.name]
|
|
581
|
+
if (relation === undefined || !membersAgree(relation, input)) {
|
|
582
|
+
throw new AuthoringError({
|
|
583
|
+
message: `${label}: schema ${context.theory.name} does not declare this ordered relation`
|
|
584
|
+
})
|
|
585
|
+
}
|
|
586
|
+
// Retain the schema-owned declaration, not mutable caller metadata.
|
|
558
587
|
const entries: BindingEntry[] = []
|
|
559
588
|
const vars: AnyVar[] = []
|
|
560
589
|
const uses: ParamUse[] = []
|
|
561
590
|
const relationClasses = context.classes[relation.name]
|
|
562
591
|
const ordered = sealedFieldsOf(relation)
|
|
563
|
-
for (const [fieldName, value] of Object.entries(bindings)) {
|
|
564
|
-
if (value === undefined) {
|
|
565
|
-
continue
|
|
566
|
-
}
|
|
592
|
+
for (const [fieldName, value] of Object.entries(queryRecord(label, bindings))) {
|
|
567
593
|
const declared = ordered.find(function byName(candidate) {
|
|
568
594
|
return candidate.name === fieldName
|
|
569
595
|
})
|
|
@@ -627,7 +653,10 @@ function resolveBindings(
|
|
|
627
653
|
})
|
|
628
654
|
)
|
|
629
655
|
} else {
|
|
630
|
-
bound = Object.freeze({
|
|
656
|
+
bound = Object.freeze({
|
|
657
|
+
kind: "literal" as const,
|
|
658
|
+
value: ownLiteral(`${label}.${fieldName}`, declared.field, value, "binding")
|
|
659
|
+
})
|
|
631
660
|
}
|
|
632
661
|
entries.push(Object.freeze({ field: fieldName, data: declared.field, class: fieldClass, term: bound }))
|
|
633
662
|
}
|
|
@@ -683,9 +712,22 @@ function sideUses(op: CmpKind, side: CmpTermData, sibling: CmpTermData, uses: Pa
|
|
|
683
712
|
}
|
|
684
713
|
|
|
685
714
|
function condDataOf(cond: AnyCond, uses: ParamUse[]): CondData {
|
|
715
|
+
queryRecord("query condition", cond)
|
|
686
716
|
if (cond.cond === "cmp") {
|
|
687
|
-
|
|
688
|
-
|
|
717
|
+
let lhs = cmpTermDataOf(cond.lhs)
|
|
718
|
+
let rhs = cmpTermDataOf(cond.rhs)
|
|
719
|
+
if (lhs.kind === "literal" && rhs.kind === "var") {
|
|
720
|
+
lhs = Object.freeze({
|
|
721
|
+
kind: "literal",
|
|
722
|
+
value: ownLiteral("comparison literal", rhs.ref.field, lhs.value, cond.op)
|
|
723
|
+
})
|
|
724
|
+
}
|
|
725
|
+
if (rhs.kind === "literal" && lhs.kind === "var") {
|
|
726
|
+
rhs = Object.freeze({
|
|
727
|
+
kind: "literal",
|
|
728
|
+
value: ownLiteral("comparison literal", lhs.ref.field, rhs.value, cond.op)
|
|
729
|
+
})
|
|
730
|
+
}
|
|
689
731
|
sideUses(cond.op, lhs, rhs, uses)
|
|
690
732
|
sideUses(cond.op, rhs, lhs, uses)
|
|
691
733
|
if (cond.op === "allen") {
|
|
@@ -705,8 +747,8 @@ function condDataOf(cond: AnyCond, uses: ParamUse[]): CondData {
|
|
|
705
747
|
return Object.freeze({ kind: "cmp" as const, op: { kind: cond.op }, lhs, rhs })
|
|
706
748
|
}
|
|
707
749
|
if (cond.cond === "tree") {
|
|
708
|
-
const children = cond.children
|
|
709
|
-
return condDataOf(child, uses)
|
|
750
|
+
const children = arrayValue("query condition children", cond.children, function lowerChild(_context, child) {
|
|
751
|
+
return condDataOf(child as AnyCond, uses)
|
|
710
752
|
})
|
|
711
753
|
return Object.freeze({ kind: "tree" as const, op: cond.op, children: Object.freeze(children) })
|
|
712
754
|
}
|
|
@@ -716,25 +758,22 @@ function condDataOf(cond: AnyCond, uses: ParamUse[]): CondData {
|
|
|
716
758
|
}
|
|
717
759
|
|
|
718
760
|
function advanceWhere(context: ChainContext, state: RuleBuildState, cond: AnyCond): RuleBuildState {
|
|
761
|
+
queryRecord("query condition", cond)
|
|
719
762
|
if (typeof cond !== "object" || cond === null || !("cond" in cond)) {
|
|
720
763
|
throw new AuthoringError({ message: "where() takes a comparison, an and()/or() tree, or a negated atom" })
|
|
721
764
|
}
|
|
722
765
|
if (cond.cond === "notInterior") {
|
|
723
|
-
|
|
724
|
-
Object.entries(cond.bindings ?? {}).filter(function defined([, value]) {
|
|
725
|
-
return value !== undefined
|
|
726
|
-
})
|
|
727
|
-
)
|
|
728
|
-
return notInteriorAdvance(context, state, cond.name, bindings)
|
|
766
|
+
return notInteriorAdvance(context, state, cond.name, queryRecord("negated interior bindings", cond.bindings))
|
|
729
767
|
}
|
|
730
768
|
if (cond.cond === "not") {
|
|
731
769
|
const relation: MatchOwner = cond.relation
|
|
732
|
-
const
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
770
|
+
const resolved = resolveBindings(
|
|
771
|
+
context,
|
|
772
|
+
`negated relation ${relation.name}`,
|
|
773
|
+
relation,
|
|
774
|
+
cond.bindings,
|
|
775
|
+
fieldAntiJoins
|
|
736
776
|
)
|
|
737
|
-
const resolved = resolveBindings(context, `negated relation ${relation.name}`, relation, bindings, fieldAntiJoins)
|
|
738
777
|
return Object.freeze({
|
|
739
778
|
items: Object.freeze([...state.items, Object.freeze({ kind: "negated" as const, atom: resolved.atom })]),
|
|
740
779
|
bound: state.bound,
|
|
@@ -757,10 +796,7 @@ function advanceInterior(
|
|
|
757
796
|
kind: "interior" | "negatedInterior"
|
|
758
797
|
): RuleBuildState {
|
|
759
798
|
const resolved: Array<{ readonly key: string; readonly ref: AnyVar }> = []
|
|
760
|
-
for (const [key, value] of Object.entries(bindings)) {
|
|
761
|
-
if (value === undefined) {
|
|
762
|
-
continue
|
|
763
|
-
}
|
|
799
|
+
for (const [key, value] of Object.entries(queryRecord(`interior ${target.name} bindings`, bindings))) {
|
|
764
800
|
if (!isTerm(value) || value[term] !== "var") {
|
|
765
801
|
throw new AuthoringError({
|
|
766
802
|
message: `interior ${target.name}: position ${key} takes a variable — bind literals and params through where()/match()`
|
|
@@ -1177,28 +1213,43 @@ interface ImportEntry {
|
|
|
1177
1213
|
|
|
1178
1214
|
const importEntries = new WeakMap<object, ImportEntry>()
|
|
1179
1215
|
const importTables = new WeakSet<InteriorData>()
|
|
1216
|
+
const importOrigins = new WeakMap<InteriorData, InteriorData>()
|
|
1217
|
+
|
|
1218
|
+
function snapshotQueryData<A>(input: A): A {
|
|
1219
|
+
return snapshotData(input, (source, snapshot) => {
|
|
1220
|
+
// Detached imports retain their authoring identity for deduplication.
|
|
1221
|
+
if (importTables.has(source as InteriorData)) {
|
|
1222
|
+
const table = snapshot as InteriorData
|
|
1223
|
+
importTables.add(table)
|
|
1224
|
+
importOrigins.set(table, importOrigins.get(source as InteriorData) ?? (source as InteriorData))
|
|
1225
|
+
const inner = importInner.get(source as InteriorData)
|
|
1226
|
+
if (inner !== undefined) importInner.set(table, inner)
|
|
1227
|
+
}
|
|
1228
|
+
})
|
|
1229
|
+
}
|
|
1180
1230
|
|
|
1181
1231
|
function importEntryOf(context: ChainContext, imported: AnyQuery): ImportEntry {
|
|
1182
1232
|
const cached = importEntries.get(imported)
|
|
1183
1233
|
if (cached !== undefined) {
|
|
1184
|
-
if (imported.schema
|
|
1234
|
+
if (!schemasAgree(imported.schema, context.theory)) {
|
|
1185
1235
|
throw new AuthoringError({
|
|
1186
1236
|
message: `${contextLabel(context)}: the imported query belongs to schema ${imported.schema.name}, not ${context.theory.name} — templates compose within one schema`
|
|
1187
1237
|
})
|
|
1188
1238
|
}
|
|
1189
1239
|
return cached
|
|
1190
1240
|
}
|
|
1191
|
-
if (imported.schema
|
|
1241
|
+
if (!schemasAgree(imported.schema, context.theory)) {
|
|
1192
1242
|
throw new AuthoringError({
|
|
1193
1243
|
message: `${contextLabel(context)}: the imported query belongs to schema ${imported.schema.name}, not ${context.theory.name} — templates compose within one schema`
|
|
1194
1244
|
})
|
|
1195
1245
|
}
|
|
1196
|
-
|
|
1246
|
+
const data = imported.data
|
|
1247
|
+
if (data.kind === "reach") {
|
|
1197
1248
|
throw new AuthoringError({
|
|
1198
1249
|
message: `${contextLabel(context)}: a recursive query is not importable as a relation expression yet — declare the recursion on the consuming query (C05 boundary, recorded)`
|
|
1199
1250
|
})
|
|
1200
1251
|
}
|
|
1201
|
-
const userParams =
|
|
1252
|
+
const userParams = data.params.filter(function userSupplied(entry) {
|
|
1202
1253
|
return entry.membership === undefined
|
|
1203
1254
|
})
|
|
1204
1255
|
if (userParams.length > 0) {
|
|
@@ -1211,10 +1262,10 @@ function importEntryOf(context: ChainContext, imported: AnyQuery): ImportEntry {
|
|
|
1211
1262
|
const facade = importFacadeOf(imported)
|
|
1212
1263
|
const table: InteriorData = Object.freeze({
|
|
1213
1264
|
name: facade.owner.name,
|
|
1214
|
-
finds:
|
|
1215
|
-
rules:
|
|
1265
|
+
finds: data.finds,
|
|
1266
|
+
rules: data.rules
|
|
1216
1267
|
})
|
|
1217
|
-
const entry: ImportEntry = Object.freeze({ table, inner:
|
|
1268
|
+
const entry: ImportEntry = Object.freeze({ table, inner: data.interiors })
|
|
1218
1269
|
importEntries.set(imported, entry)
|
|
1219
1270
|
importTables.add(table)
|
|
1220
1271
|
importInner.set(table, entry.inner)
|
|
@@ -1242,8 +1293,9 @@ function referencedImports(
|
|
|
1242
1293
|
continue
|
|
1243
1294
|
}
|
|
1244
1295
|
const target = item.target as InteriorData
|
|
1245
|
-
|
|
1246
|
-
|
|
1296
|
+
const origin = importOrigins.get(target) ?? target
|
|
1297
|
+
if (importTables.has(target) && !seen.has(origin)) {
|
|
1298
|
+
seen.add(origin)
|
|
1247
1299
|
out.push(target)
|
|
1248
1300
|
}
|
|
1249
1301
|
}
|
|
@@ -1280,7 +1332,8 @@ function spliceImports(
|
|
|
1280
1332
|
const names = new Set<string>()
|
|
1281
1333
|
const added = new Set<InteriorData>()
|
|
1282
1334
|
function add(table: InteriorData): void {
|
|
1283
|
-
|
|
1335
|
+
const origin = importOrigins.get(table) ?? table
|
|
1336
|
+
if (added.has(origin)) {
|
|
1284
1337
|
return
|
|
1285
1338
|
}
|
|
1286
1339
|
if (names.has(table.name)) {
|
|
@@ -1288,7 +1341,7 @@ function spliceImports(
|
|
|
1288
1341
|
message: `query: the derived-table name ${table.name} appears twice after import splicing — rename the colliding interior (names are unique)`
|
|
1289
1342
|
})
|
|
1290
1343
|
}
|
|
1291
|
-
added.add(
|
|
1344
|
+
added.add(origin)
|
|
1292
1345
|
names.add(table.name)
|
|
1293
1346
|
spliced.push(table)
|
|
1294
1347
|
}
|
|
@@ -1330,15 +1383,13 @@ function notInteriorAdvance(
|
|
|
1330
1383
|
|
|
1331
1384
|
function findColumns(context: ChainContext, entries: Readonly<Record<string, unknown>>): FindColumn[] {
|
|
1332
1385
|
const columns: FindColumn[] = []
|
|
1386
|
+
queryRecord("query find", entries)
|
|
1333
1387
|
// Nonrecursive derived stages (interiors) may emit aggregate/computed
|
|
1334
1388
|
// outputs (C05: "do not leave Interior.rules projection only"); ONLY the
|
|
1335
1389
|
// recursive feedback cycle stays projection-only — no aggregate,
|
|
1336
1390
|
// arithmetic-created value or negation flows through it.
|
|
1337
1391
|
const recName = context.kind === "rec-base" || context.kind === "rec-arm" ? context.self.name : undefined
|
|
1338
1392
|
for (const [name, entry] of Object.entries(entries)) {
|
|
1339
|
-
if (entry === undefined) {
|
|
1340
|
-
continue
|
|
1341
|
-
}
|
|
1342
1393
|
if (recName !== undefined && !(isTerm(entry) && entry[term] === "var")) {
|
|
1343
1394
|
throw new AuthoringError({
|
|
1344
1395
|
message: `rec ${recName}: a rec head projects bound variables only — no aggregate or arithmetic-created value flows through the feedback cycle (compute over the finished set in the main rules)`
|
|
@@ -1540,7 +1591,7 @@ function paramRegistryOf(
|
|
|
1540
1591
|
if (existing.anchor !== undefined && use.anchor !== undefined) {
|
|
1541
1592
|
const registered = rosterOf(existing.anchor)
|
|
1542
1593
|
const anchored = rosterOf(use.anchor)
|
|
1543
|
-
if (registered
|
|
1594
|
+
if (!rostersAgree(registered, anchored)) {
|
|
1544
1595
|
throw new AuthoringError({
|
|
1545
1596
|
message: `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`
|
|
1546
1597
|
})
|
|
@@ -1627,7 +1678,7 @@ function alignedHeadOf(label: string, rules: readonly RuleData[]): readonly Find
|
|
|
1627
1678
|
}
|
|
1628
1679
|
rule.finds.forEach(function verifyClosedSlice(column, position) {
|
|
1629
1680
|
const lead = first.finds[position]
|
|
1630
|
-
if (lead !== undefined && column.closed
|
|
1681
|
+
if (lead !== undefined && !rostersAgree(column.closed, lead.closed)) {
|
|
1631
1682
|
throw new AuthoringError({
|
|
1632
1683
|
message: `every rule of ${label} derives the same head — the head column ${lead.name} is ${renderClosedSlice(lead.closed)} in rule 0 but ${renderClosedSlice(column.closed)} in rule ${index} (one column decodes through one roster)`
|
|
1633
1684
|
})
|
|
@@ -1671,11 +1722,17 @@ function afterMainError(what: string): Error {
|
|
|
1671
1722
|
}
|
|
1672
1723
|
|
|
1673
1724
|
function makeRawQuery(
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1725
|
+
inputTheory: AnySchema,
|
|
1726
|
+
inputInteriors: readonly InteriorData[],
|
|
1727
|
+
inputRec: RecData | undefined,
|
|
1728
|
+
inputRules: readonly RuleData[]
|
|
1678
1729
|
): RawQuery {
|
|
1730
|
+
const { theory, interiors, rec, rules } = snapshotQueryData({
|
|
1731
|
+
theory: inputTheory,
|
|
1732
|
+
interiors: inputInteriors,
|
|
1733
|
+
rec: inputRec,
|
|
1734
|
+
rules: inputRules
|
|
1735
|
+
})
|
|
1679
1736
|
const mergedFinds = alignedHeadOf("a query", rules)
|
|
1680
1737
|
const first = rules[0]
|
|
1681
1738
|
if (first === undefined) {
|
|
@@ -1704,8 +1761,12 @@ function makeRawQuery(
|
|
|
1704
1761
|
params
|
|
1705
1762
|
})
|
|
1706
1763
|
const value: RawQuery = {
|
|
1707
|
-
schema
|
|
1708
|
-
|
|
1764
|
+
get schema() {
|
|
1765
|
+
return snapshotData(theory)
|
|
1766
|
+
},
|
|
1767
|
+
get data() {
|
|
1768
|
+
return snapshotQueryData(data)
|
|
1769
|
+
},
|
|
1709
1770
|
rule(build) {
|
|
1710
1771
|
const built = build(makeRawScope({ kind: "query", classes: theory.classes, theory, ...env }))
|
|
1711
1772
|
return makeRawQuery(theory, interiors, rec, [...rules, built.rule])
|
|
@@ -1727,13 +1788,19 @@ function makeRawQuery(
|
|
|
1727
1788
|
return value
|
|
1728
1789
|
}
|
|
1729
1790
|
|
|
1730
|
-
function makeQuery<
|
|
1791
|
+
function makeQuery<
|
|
1792
|
+
Rels extends SchemaRelations,
|
|
1793
|
+
Row,
|
|
1794
|
+
P extends ParamsRecord,
|
|
1795
|
+
Classes extends SchemaClasses,
|
|
1796
|
+
Head extends HeadShape
|
|
1797
|
+
>(
|
|
1731
1798
|
theory: Schema<Rels, Classes>,
|
|
1732
1799
|
interiors: readonly InteriorData[],
|
|
1733
1800
|
rec: RecData | undefined,
|
|
1734
1801
|
rules: readonly RuleData[]
|
|
1735
|
-
): Query<Rels, Row, P, Classes> {
|
|
1736
|
-
return makeRawQuery(theory, interiors, rec, rules) as unknown as Query<Rels, Row, P, Classes>
|
|
1802
|
+
): Query<Rels, Row, P, Classes, Head> {
|
|
1803
|
+
return makeRawQuery(theory, interiors, rec, rules) as unknown as Query<Rels, Row, P, Classes, Head>
|
|
1737
1804
|
}
|
|
1738
1805
|
|
|
1739
1806
|
function collectInterior<Rels extends SchemaRelations, Classes extends SchemaClasses>(
|
|
@@ -1799,9 +1866,9 @@ function collectRec<Rels extends SchemaRelations, Classes extends SchemaClasses>
|
|
|
1799
1866
|
const sealedRec: RecData["rec"] = [firstRec, ...rec.slice(1)]
|
|
1800
1867
|
const recData: RecData = Object.freeze({
|
|
1801
1868
|
name,
|
|
1802
|
-
finds: sealedFinds,
|
|
1803
|
-
base: sealedBase,
|
|
1804
|
-
rec: sealedRec
|
|
1869
|
+
finds: Object.freeze(sealedFinds),
|
|
1870
|
+
base: Object.freeze(sealedBase),
|
|
1871
|
+
rec: Object.freeze(sealedRec)
|
|
1805
1872
|
})
|
|
1806
1873
|
return recData
|
|
1807
1874
|
}
|
|
@@ -1852,9 +1919,11 @@ function makeQueryStart<Rels extends SchemaRelations, Classes extends SchemaClas
|
|
|
1852
1919
|
},
|
|
1853
1920
|
rule<RV extends AnyRuleValue>(
|
|
1854
1921
|
build: (r: QueryRuleScope<Rels, Classes>) => RV
|
|
1855
|
-
): Query<Rels, RowOf<RV>, Flatten<P & ParamsOf<RV>>, Classes
|
|
1922
|
+
): Query<Rels, RowOf<RV>, Flatten<P & ParamsOf<RV>>, Classes, HeadOf<RV>> {
|
|
1856
1923
|
const built = build(makeQueryRuleScope<Rels, Classes>(theory, env))
|
|
1857
|
-
return makeQuery<Rels, RowOf<RV>, Flatten<P & ParamsOf<RV>>, Classes
|
|
1924
|
+
return makeQuery<Rels, RowOf<RV>, Flatten<P & ParamsOf<RV>>, Classes, HeadOf<RV>>(theory, interiors, undefined, [
|
|
1925
|
+
built.rule
|
|
1926
|
+
])
|
|
1858
1927
|
}
|
|
1859
1928
|
}
|
|
1860
1929
|
Object.freeze(start)
|
|
@@ -1870,9 +1939,11 @@ function makeQueryReachStart<Rels extends SchemaRelations, Classes extends Schem
|
|
|
1870
1939
|
const start = {
|
|
1871
1940
|
rule<RV extends AnyRuleValue>(
|
|
1872
1941
|
build: (r: QueryRuleScope<Rels, Classes>) => RV
|
|
1873
|
-
): Query<Rels, RowOf<RV>, Flatten<P & ParamsOf<RV>>, Classes
|
|
1942
|
+
): Query<Rels, RowOf<RV>, Flatten<P & ParamsOf<RV>>, Classes, HeadOf<RV>> {
|
|
1874
1943
|
const built = build(makeQueryRuleScope<Rels, Classes>(theory, env))
|
|
1875
|
-
return makeQuery<Rels, RowOf<RV>, Flatten<P & ParamsOf<RV>>, Classes
|
|
1944
|
+
return makeQuery<Rels, RowOf<RV>, Flatten<P & ParamsOf<RV>>, Classes, HeadOf<RV>>(theory, interiors, rec, [
|
|
1945
|
+
built.rule
|
|
1946
|
+
])
|
|
1876
1947
|
}
|
|
1877
1948
|
}
|
|
1878
1949
|
Object.freeze(start)
|
|
@@ -1882,112 +1953,21 @@ function makeQueryReachStart<Rels extends SchemaRelations, Classes extends Schem
|
|
|
1882
1953
|
function query<Rels extends SchemaRelations, Classes extends SchemaClasses>(
|
|
1883
1954
|
theory: Schema<Rels, Classes>
|
|
1884
1955
|
): QueryStart<Rels, Classes> {
|
|
1885
|
-
return makeQueryStart<Rels, Classes, Record<never, never>>(theory, [])
|
|
1886
|
-
}
|
|
1887
|
-
|
|
1888
|
-
function taggedHandleId(
|
|
1889
|
-
context: string,
|
|
1890
|
-
closed: { readonly name: string; readonly handles: readonly string[] },
|
|
1891
|
-
value: unknown
|
|
1892
|
-
): TaggedValue {
|
|
1893
|
-
if (typeof value !== "string") {
|
|
1894
|
-
throw literalShapeError(context, `a ${closed.name} handle name (string)`, value)
|
|
1895
|
-
}
|
|
1896
|
-
const id = closed.handles.indexOf(value)
|
|
1897
|
-
if (id < 0) {
|
|
1898
|
-
throw new AuthoringError({
|
|
1899
|
-
message: `${context}: "${value}" is not a handle of ${closed.name} — the roster is ${closed.handles.join(", ")}`
|
|
1900
|
-
})
|
|
1901
|
-
}
|
|
1902
|
-
return { kind: "u64", value: BigInt(id) }
|
|
1956
|
+
return makeQueryStart<Rels, Classes, Record<never, never>>(schemaDescriptor(theory), [])
|
|
1903
1957
|
}
|
|
1904
1958
|
|
|
1905
1959
|
function taggedAtElementDomain(context: string, element: "u64" | "i64" | "f64", value: unknown): TaggedValue {
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
if (Number.isNaN(value.start) || Number.isNaN(value.end) || !(value.start < value.end)) {
|
|
1912
|
-
throw literalShapeError(context, "a nonempty float interval with non-NaN endpoints", value)
|
|
1913
|
-
}
|
|
1914
|
-
return {
|
|
1915
|
-
kind: "intervalF64",
|
|
1916
|
-
start: Object.is(value.start, -0) ? 0 : value.start,
|
|
1917
|
-
end: Object.is(value.end, -0) ? 0 : value.end
|
|
1918
|
-
}
|
|
1919
|
-
}
|
|
1920
|
-
throw literalShapeError(context, "number (point) or { start, end } numbers (interval)", value)
|
|
1921
|
-
}
|
|
1922
|
-
if (typeof value === "bigint") {
|
|
1923
|
-
if (element === "u64") {
|
|
1924
|
-
return { kind: "u64", value }
|
|
1925
|
-
}
|
|
1926
|
-
return { kind: "i64", value }
|
|
1927
|
-
}
|
|
1928
|
-
if (isIntervalValue(value)) {
|
|
1929
|
-
if (element === "u64") {
|
|
1930
|
-
return { kind: "intervalU64", start: value.start, end: value.end }
|
|
1931
|
-
}
|
|
1932
|
-
return { kind: "intervalI64", start: value.start, end: value.end }
|
|
1933
|
-
}
|
|
1934
|
-
throw literalShapeError(context, "bigint (point) or { start, end } (interval)", value)
|
|
1960
|
+
const field: AnyField =
|
|
1961
|
+
typeof value === "number" || typeof value === "bigint"
|
|
1962
|
+
? { u64: u64Field, i64: i64Field, f64: f64Field }[element]
|
|
1963
|
+
: { kind: "interval", element, width: undefined }
|
|
1964
|
+
return taggedValueOf(context, field, value)
|
|
1935
1965
|
}
|
|
1936
1966
|
|
|
1937
1967
|
function taggedLiteral(context: string, field: AnyField, value: unknown): TaggedValue {
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
}
|
|
1942
|
-
switch (field.kind) {
|
|
1943
|
-
case "bool": {
|
|
1944
|
-
if (typeof value !== "boolean") {
|
|
1945
|
-
throw literalShapeError(context, "boolean", value)
|
|
1946
|
-
}
|
|
1947
|
-
return { kind: "bool", value }
|
|
1948
|
-
}
|
|
1949
|
-
case "u64": {
|
|
1950
|
-
if (typeof value !== "bigint") {
|
|
1951
|
-
throw literalShapeError(context, "bigint", value)
|
|
1952
|
-
}
|
|
1953
|
-
return { kind: "u64", value }
|
|
1954
|
-
}
|
|
1955
|
-
case "i64": {
|
|
1956
|
-
if (typeof value !== "bigint") {
|
|
1957
|
-
throw literalShapeError(context, "bigint", value)
|
|
1958
|
-
}
|
|
1959
|
-
return { kind: "i64", value }
|
|
1960
|
-
}
|
|
1961
|
-
case "f64": {
|
|
1962
|
-
if (typeof value !== "number") {
|
|
1963
|
-
throw literalShapeError(context, "number", value)
|
|
1964
|
-
}
|
|
1965
|
-
return { kind: "f64", value }
|
|
1966
|
-
}
|
|
1967
|
-
case "uuid": {
|
|
1968
|
-
if (!Uuid.isUuid(value)) {
|
|
1969
|
-
throw literalShapeError(context, "a UUID (canonical UUID text)", value)
|
|
1970
|
-
}
|
|
1971
|
-
return { kind: "uuid", value }
|
|
1972
|
-
}
|
|
1973
|
-
case "str": {
|
|
1974
|
-
if (typeof value !== "string") {
|
|
1975
|
-
throw literalShapeError(context, "string", value)
|
|
1976
|
-
}
|
|
1977
|
-
if (!value.isWellFormed()) {
|
|
1978
|
-
throw literalShapeError(context, "well-formed string", value)
|
|
1979
|
-
}
|
|
1980
|
-
return { kind: "string", value }
|
|
1981
|
-
}
|
|
1982
|
-
case "bytes": {
|
|
1983
|
-
if (!(value instanceof Uint8Array)) {
|
|
1984
|
-
throw literalShapeError(context, "Uint8Array", value)
|
|
1985
|
-
}
|
|
1986
|
-
return { kind: "fixedBytes", value }
|
|
1987
|
-
}
|
|
1988
|
-
case "interval":
|
|
1989
|
-
return taggedAtElementDomain(context, field.element, value)
|
|
1990
|
-
}
|
|
1968
|
+
return field.kind === "interval"
|
|
1969
|
+
? taggedAtElementDomain(context, field.element, value)
|
|
1970
|
+
: taggedValueOf(context, field, value)
|
|
1991
1971
|
}
|
|
1992
1972
|
|
|
1993
1973
|
/**
|
|
@@ -1996,7 +1976,7 @@ function taggedLiteral(context: string, field: AnyField, value: unknown): Tagged
|
|
|
1996
1976
|
* contributes its element domain, a scalar sibling its own type. At
|
|
1997
1977
|
* `pointIn` the operand order is interval-left, point-right, so an
|
|
1998
1978
|
* interval-shaped literal beside a scalar element-typed sibling is the LEGAL
|
|
1999
|
-
* interval operand of `pointIn(t,
|
|
1979
|
+
* interval operand of `pointIn(t, { start, end })`; under every other operator an
|
|
2000
1980
|
* interval shape against a scalar sibling stays refused.
|
|
2001
1981
|
*/
|
|
2002
1982
|
function taggedCmpLiteral(context: string, sibling: AnyField, value: unknown, op: CmpKind | "binding"): TaggedValue {
|
|
@@ -2017,6 +1997,14 @@ function taggedCmpLiteral(context: string, sibling: AnyField, value: unknown, op
|
|
|
2017
1997
|
return taggedLiteral(context, sibling, value)
|
|
2018
1998
|
}
|
|
2019
1999
|
|
|
2000
|
+
/** Validate and own a literal when the query is authored, before caller mutation. */
|
|
2001
|
+
function ownLiteral(context: string, field: AnyField, value: unknown, op: CmpKind | "binding"): unknown {
|
|
2002
|
+
const tagged = taggedCmpLiteral(context, field, value, op)
|
|
2003
|
+
if (rosterOf(field) !== undefined) return value
|
|
2004
|
+
if ("value" in tagged) return tagged.value
|
|
2005
|
+
return Object.freeze({ start: tagged.start, end: tagged.end })
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2020
2008
|
interface LowerContext {
|
|
2021
2009
|
readonly theory: AnySchema
|
|
2022
2010
|
readonly relationIds: ReadonlyMap<string, number>
|
|
@@ -2054,7 +2042,7 @@ function paramIdOf(ctx: LowerContext, name: string): number {
|
|
|
2054
2042
|
|
|
2055
2043
|
function lowerAtom(ctx: LowerContext, atom: AtomData, ids: VarIds): AtomIr {
|
|
2056
2044
|
const member = ctx.theory.relations[atom.relation.name]
|
|
2057
|
-
if (member
|
|
2045
|
+
if (!membersAgree(member, atom.relation)) {
|
|
2058
2046
|
throw new AuthoringError({
|
|
2059
2047
|
message: `query lowering: relation ${atom.relation.name} is not the relation value schema ${ctx.theory.name} declares`
|
|
2060
2048
|
})
|
|
@@ -2306,20 +2294,21 @@ function lowerRule(ctx: LowerContext, rule: RuleData): RuleIr {
|
|
|
2306
2294
|
|
|
2307
2295
|
function lowerQuery(q: AnyQuery): ParsedQuery {
|
|
2308
2296
|
const theory = q.schema
|
|
2297
|
+
const data = q.data
|
|
2309
2298
|
const relationIds = new Map<string, number>()
|
|
2310
2299
|
Object.keys(theory.relations).forEach(function assignOrdinal(name, index) {
|
|
2311
2300
|
relationIds.set(name, index)
|
|
2312
2301
|
})
|
|
2313
2302
|
const interiorIds = new Map<string, number>()
|
|
2314
|
-
|
|
2303
|
+
data.interiors.forEach(function assignInteriorId(interior, index) {
|
|
2315
2304
|
interiorIds.set(interior.name, index)
|
|
2316
2305
|
})
|
|
2317
|
-
if (
|
|
2318
|
-
interiorIds.set(
|
|
2306
|
+
if (data.kind === "reach") {
|
|
2307
|
+
interiorIds.set(data.rec.name, data.interiors.length)
|
|
2319
2308
|
}
|
|
2320
2309
|
const paramIds = new Map<string, number>()
|
|
2321
2310
|
const params = new Map<string, ParamEntry>()
|
|
2322
|
-
|
|
2311
|
+
data.params.forEach(function assignParamId(entry, index) {
|
|
2323
2312
|
if (entry.anchor === undefined) {
|
|
2324
2313
|
throw new AuthoringError({
|
|
2325
2314
|
message: `query param ${entry.name} has no field-anchored use — bind it in an atom or compare it against a bound variable`
|
|
@@ -2329,7 +2318,7 @@ function lowerQuery(q: AnyQuery): ParsedQuery {
|
|
|
2329
2318
|
params.set(entry.name, entry)
|
|
2330
2319
|
})
|
|
2331
2320
|
const ctx: LowerContext = { theory, relationIds, interiorIds, paramIds, params }
|
|
2332
|
-
const interiors =
|
|
2321
|
+
const interiors = data.interiors.map(function lowerInterior(interior) {
|
|
2333
2322
|
return {
|
|
2334
2323
|
head: interior.finds.map(headTermOf),
|
|
2335
2324
|
rules: interior.rules.map(function lowerInteriorRule(rule) {
|
|
@@ -2337,22 +2326,22 @@ function lowerQuery(q: AnyQuery): ParsedQuery {
|
|
|
2337
2326
|
})
|
|
2338
2327
|
}
|
|
2339
2328
|
})
|
|
2340
|
-
const head =
|
|
2341
|
-
const rules =
|
|
2329
|
+
const head = data.finds.map(headTermOf)
|
|
2330
|
+
const rules = data.rules.map(function lowerMainRule(rule) {
|
|
2342
2331
|
return lowerRule(ctx, rule)
|
|
2343
2332
|
})
|
|
2344
|
-
if (
|
|
2333
|
+
if (data.kind === "cq") {
|
|
2345
2334
|
return parseQueryIr({ kind: "cq", interiors, head, rules })
|
|
2346
2335
|
}
|
|
2347
2336
|
return parseQueryIr({
|
|
2348
2337
|
kind: "reach",
|
|
2349
2338
|
interiors,
|
|
2350
2339
|
rec: {
|
|
2351
|
-
head:
|
|
2352
|
-
base:
|
|
2340
|
+
head: data.rec.finds.map(headTermOf),
|
|
2341
|
+
base: data.rec.base.map(function lowerBase(rule) {
|
|
2353
2342
|
return lowerRule(ctx, rule)
|
|
2354
2343
|
}),
|
|
2355
|
-
rec:
|
|
2344
|
+
rec: data.rec.rec.map(function lowerRecArm(rule) {
|
|
2356
2345
|
return lowerRule(ctx, rule)
|
|
2357
2346
|
})
|
|
2358
2347
|
},
|
|
@@ -2364,6 +2353,7 @@ function lowerQuery(q: AnyQuery): ParsedQuery {
|
|
|
2364
2353
|
export type {
|
|
2365
2354
|
AnyQuery,
|
|
2366
2355
|
AnyRuleValue,
|
|
2356
|
+
ChainContext,
|
|
2367
2357
|
Query,
|
|
2368
2358
|
QueryData,
|
|
2369
2359
|
QueryParams,
|
|
@@ -2378,4 +2368,4 @@ export type {
|
|
|
2378
2368
|
RuleValue,
|
|
2379
2369
|
TermOps
|
|
2380
2370
|
}
|
|
2381
|
-
export { lowerQuery, query, taggedCmpLiteral }
|
|
2371
|
+
export { alignedHeadOf, EMPTY_RULE, lowerQuery, makeRawChain, makeRawQuery, query, taggedCmpLiteral }
|