@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/dist/query/lower.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { sealedFieldsOf } from "#closed.ts";
|
|
1
|
+
import { membersAgree, sealedFieldsOf } from "#closed.ts";
|
|
2
2
|
import { AuthoringError, SdkInvariantError } from "#errors.ts";
|
|
3
|
-
import { assertDeclarationOrderKey, f64 as f64Field, isFloatIntervalValue, isIntervalValue, literalShapeError, rosterOf, u64 as u64Field } from "#fields.ts";
|
|
3
|
+
import { assertDeclarationOrderKey, f64 as f64Field, i64 as i64Field, isFloatIntervalValue, isIntervalValue, literalShapeError, rosterOf, rostersAgree, u64 as u64Field } from "#fields.ts";
|
|
4
|
+
import { snapshotData } from "#immutable.ts";
|
|
4
5
|
import { allen, and, eq, ge, gt, le, lt, ne, not, or, pointIn } from "#query/atom.ts";
|
|
5
6
|
import { computeFieldOf, computeVarsOf, isComputeExpr, MAX_COMPUTE_DEPTH } from "#query/compute.ts";
|
|
6
7
|
import { count, max, mean, min, pack, sum } from "#query/find.ts";
|
|
7
8
|
import { parseQueryIr } from "#query/parse-ir.ts";
|
|
8
9
|
import { fieldAntiJoins, fieldJoins, headFieldJoins, importFacadeOf, importFacadeOfOwner, inferred, isImportedSource, isTerm, labelImport, makeParam, makeSetParam, renderFieldKind, term } from "#query/scope.ts";
|
|
9
10
|
import { literalWireOf } from "#scalar.ts";
|
|
10
|
-
import {
|
|
11
|
+
import { schemaDescriptor, schemasAgree } from "#schema.ts";
|
|
12
|
+
import { arrayValue, recordValue, taggedValueOf } from "#values.ts";
|
|
11
13
|
const termOps = Object.freeze({
|
|
12
14
|
param: makeParam,
|
|
13
15
|
inSet: makeSetParam,
|
|
@@ -36,8 +38,8 @@ const EMPTY_RULE = Object.freeze({
|
|
|
36
38
|
});
|
|
37
39
|
/**
|
|
38
40
|
* The MINT slot of a variable, the runtime twin of {@link MintSlotOf}: (i)
|
|
39
|
-
* verifies the mint owner
|
|
40
|
-
*
|
|
41
|
+
* verifies the mint owner agrees with the schema's ordered declaration — a
|
|
42
|
+
* conflicting declaration is refused, naming its label — and (ii)
|
|
41
43
|
* returns the descriptor it was minted at plus the law-computed class read
|
|
42
44
|
* off the schema's frozen class map. Because {@link fieldJoins} is an
|
|
43
45
|
* equality, judging every binding position against this one slot makes all
|
|
@@ -50,16 +52,16 @@ function mintSlotOf(context, ref) {
|
|
|
50
52
|
// the descriptor it projected and the carrier class that survived
|
|
51
53
|
// the projection (aggregate outputs are bare derived scalars).
|
|
52
54
|
const column = facade.columns.get(ref.column);
|
|
53
|
-
if (column === undefined || facade.source.schema
|
|
55
|
+
if (column === undefined || !schemasAgree(facade.source.schema, context.theory)) {
|
|
54
56
|
throw new AuthoringError({
|
|
55
57
|
message: `the variable ${ref.label} was minted from a query template schema ${context.theory.name} does not own`
|
|
56
58
|
});
|
|
57
59
|
}
|
|
58
60
|
return column.slot ?? { field: ref.field, class: undefined };
|
|
59
61
|
}
|
|
60
|
-
if (context.theory.relations[ref.owner.name]
|
|
62
|
+
if (!membersAgree(context.theory.relations[ref.owner.name], ref.owner)) {
|
|
61
63
|
throw new AuthoringError({
|
|
62
|
-
message: `the variable ${ref.label} was minted from a relation schema ${context.theory.name} does not declare —
|
|
64
|
+
message: `the variable ${ref.label} was minted from a relation schema ${context.theory.name} does not declare — use an equivalent ordered declaration`
|
|
63
65
|
});
|
|
64
66
|
}
|
|
65
67
|
return { field: ref.field, class: context.classes[ref.owner.name]?.[ref.column] };
|
|
@@ -82,7 +84,7 @@ function membershipSet(context, field, value) {
|
|
|
82
84
|
});
|
|
83
85
|
}
|
|
84
86
|
const seen = new Set();
|
|
85
|
-
const members = value
|
|
87
|
+
const members = arrayValue(context, value, function memberName(context, member) {
|
|
86
88
|
if (typeof member !== "string") {
|
|
87
89
|
throw literalShapeError(context, `a ${roster.name} handle name (string)`, member);
|
|
88
90
|
}
|
|
@@ -97,16 +99,23 @@ function membershipSet(context, field, value) {
|
|
|
97
99
|
const key = [...members].sort();
|
|
98
100
|
return { name: `∈ ${roster.name} ${JSON.stringify(key)}`, members: Object.freeze(members) };
|
|
99
101
|
}
|
|
100
|
-
function
|
|
102
|
+
function queryRecord(context, input) {
|
|
103
|
+
return recordValue(context, input, typeof input === "object" && input !== null ? Object.keys(input) : []);
|
|
104
|
+
}
|
|
105
|
+
function resolveBindings(context, label, input, bindings, joins = fieldJoins) {
|
|
106
|
+
const relation = context.theory.relations[input.name];
|
|
107
|
+
if (relation === undefined || !membersAgree(relation, input)) {
|
|
108
|
+
throw new AuthoringError({
|
|
109
|
+
message: `${label}: schema ${context.theory.name} does not declare this ordered relation`
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// Retain the schema-owned declaration, not mutable caller metadata.
|
|
101
113
|
const entries = [];
|
|
102
114
|
const vars = [];
|
|
103
115
|
const uses = [];
|
|
104
116
|
const relationClasses = context.classes[relation.name];
|
|
105
117
|
const ordered = sealedFieldsOf(relation);
|
|
106
|
-
for (const [fieldName, value] of Object.entries(bindings)) {
|
|
107
|
-
if (value === undefined) {
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
118
|
+
for (const [fieldName, value] of Object.entries(queryRecord(label, bindings))) {
|
|
110
119
|
const declared = ordered.find(function byName(candidate) {
|
|
111
120
|
return candidate.name === fieldName;
|
|
112
121
|
});
|
|
@@ -166,7 +175,10 @@ function resolveBindings(context, label, relation, bindings, joins = fieldJoins)
|
|
|
166
175
|
}));
|
|
167
176
|
}
|
|
168
177
|
else {
|
|
169
|
-
bound = Object.freeze({
|
|
178
|
+
bound = Object.freeze({
|
|
179
|
+
kind: "literal",
|
|
180
|
+
value: ownLiteral(`${label}.${fieldName}`, declared.field, value, "binding")
|
|
181
|
+
});
|
|
170
182
|
}
|
|
171
183
|
entries.push(Object.freeze({ field: fieldName, data: declared.field, class: fieldClass, term: bound }));
|
|
172
184
|
}
|
|
@@ -211,9 +223,22 @@ function sideUses(op, side, sibling, uses) {
|
|
|
211
223
|
}));
|
|
212
224
|
}
|
|
213
225
|
function condDataOf(cond, uses) {
|
|
226
|
+
queryRecord("query condition", cond);
|
|
214
227
|
if (cond.cond === "cmp") {
|
|
215
|
-
|
|
216
|
-
|
|
228
|
+
let lhs = cmpTermDataOf(cond.lhs);
|
|
229
|
+
let rhs = cmpTermDataOf(cond.rhs);
|
|
230
|
+
if (lhs.kind === "literal" && rhs.kind === "var") {
|
|
231
|
+
lhs = Object.freeze({
|
|
232
|
+
kind: "literal",
|
|
233
|
+
value: ownLiteral("comparison literal", rhs.ref.field, lhs.value, cond.op)
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
if (rhs.kind === "literal" && lhs.kind === "var") {
|
|
237
|
+
rhs = Object.freeze({
|
|
238
|
+
kind: "literal",
|
|
239
|
+
value: ownLiteral("comparison literal", lhs.ref.field, rhs.value, cond.op)
|
|
240
|
+
});
|
|
241
|
+
}
|
|
217
242
|
sideUses(cond.op, lhs, rhs, uses);
|
|
218
243
|
sideUses(cond.op, rhs, lhs, uses);
|
|
219
244
|
if (cond.op === "allen") {
|
|
@@ -233,7 +258,7 @@ function condDataOf(cond, uses) {
|
|
|
233
258
|
return Object.freeze({ kind: "cmp", op: { kind: cond.op }, lhs, rhs });
|
|
234
259
|
}
|
|
235
260
|
if (cond.cond === "tree") {
|
|
236
|
-
const children = cond.children
|
|
261
|
+
const children = arrayValue("query condition children", cond.children, function lowerChild(_context, child) {
|
|
237
262
|
return condDataOf(child, uses);
|
|
238
263
|
});
|
|
239
264
|
return Object.freeze({ kind: "tree", op: cond.op, children: Object.freeze(children) });
|
|
@@ -243,21 +268,16 @@ function condDataOf(cond, uses) {
|
|
|
243
268
|
});
|
|
244
269
|
}
|
|
245
270
|
function advanceWhere(context, state, cond) {
|
|
271
|
+
queryRecord("query condition", cond);
|
|
246
272
|
if (typeof cond !== "object" || cond === null || !("cond" in cond)) {
|
|
247
273
|
throw new AuthoringError({ message: "where() takes a comparison, an and()/or() tree, or a negated atom" });
|
|
248
274
|
}
|
|
249
275
|
if (cond.cond === "notInterior") {
|
|
250
|
-
|
|
251
|
-
return value !== undefined;
|
|
252
|
-
}));
|
|
253
|
-
return notInteriorAdvance(context, state, cond.name, bindings);
|
|
276
|
+
return notInteriorAdvance(context, state, cond.name, queryRecord("negated interior bindings", cond.bindings));
|
|
254
277
|
}
|
|
255
278
|
if (cond.cond === "not") {
|
|
256
279
|
const relation = cond.relation;
|
|
257
|
-
const
|
|
258
|
-
return value !== undefined;
|
|
259
|
-
}));
|
|
260
|
-
const resolved = resolveBindings(context, `negated relation ${relation.name}`, relation, bindings, fieldAntiJoins);
|
|
280
|
+
const resolved = resolveBindings(context, `negated relation ${relation.name}`, relation, cond.bindings, fieldAntiJoins);
|
|
261
281
|
return Object.freeze({
|
|
262
282
|
items: Object.freeze([...state.items, Object.freeze({ kind: "negated", atom: resolved.atom })]),
|
|
263
283
|
bound: state.bound,
|
|
@@ -274,10 +294,7 @@ function advanceWhere(context, state, cond) {
|
|
|
274
294
|
}
|
|
275
295
|
function advanceInterior(state, target, bindings, kind) {
|
|
276
296
|
const resolved = [];
|
|
277
|
-
for (const [key, value] of Object.entries(bindings)) {
|
|
278
|
-
if (value === undefined) {
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
297
|
+
for (const [key, value] of Object.entries(queryRecord(`interior ${target.name} bindings`, bindings))) {
|
|
281
298
|
if (!isTerm(value) || value[term] !== "var") {
|
|
282
299
|
throw new AuthoringError({
|
|
283
300
|
message: `interior ${target.name}: position ${key} takes a variable — bind literals and params through where()/match()`
|
|
@@ -621,27 +638,42 @@ function interiorAdvance(context, state, name, bindings) {
|
|
|
621
638
|
}
|
|
622
639
|
const importEntries = new WeakMap();
|
|
623
640
|
const importTables = new WeakSet();
|
|
641
|
+
const importOrigins = new WeakMap();
|
|
642
|
+
function snapshotQueryData(input) {
|
|
643
|
+
return snapshotData(input, (source, snapshot) => {
|
|
644
|
+
// Detached imports retain their authoring identity for deduplication.
|
|
645
|
+
if (importTables.has(source)) {
|
|
646
|
+
const table = snapshot;
|
|
647
|
+
importTables.add(table);
|
|
648
|
+
importOrigins.set(table, importOrigins.get(source) ?? source);
|
|
649
|
+
const inner = importInner.get(source);
|
|
650
|
+
if (inner !== undefined)
|
|
651
|
+
importInner.set(table, inner);
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
}
|
|
624
655
|
function importEntryOf(context, imported) {
|
|
625
656
|
const cached = importEntries.get(imported);
|
|
626
657
|
if (cached !== undefined) {
|
|
627
|
-
if (imported.schema
|
|
658
|
+
if (!schemasAgree(imported.schema, context.theory)) {
|
|
628
659
|
throw new AuthoringError({
|
|
629
660
|
message: `${contextLabel(context)}: the imported query belongs to schema ${imported.schema.name}, not ${context.theory.name} — templates compose within one schema`
|
|
630
661
|
});
|
|
631
662
|
}
|
|
632
663
|
return cached;
|
|
633
664
|
}
|
|
634
|
-
if (imported.schema
|
|
665
|
+
if (!schemasAgree(imported.schema, context.theory)) {
|
|
635
666
|
throw new AuthoringError({
|
|
636
667
|
message: `${contextLabel(context)}: the imported query belongs to schema ${imported.schema.name}, not ${context.theory.name} — templates compose within one schema`
|
|
637
668
|
});
|
|
638
669
|
}
|
|
639
|
-
|
|
670
|
+
const data = imported.data;
|
|
671
|
+
if (data.kind === "reach") {
|
|
640
672
|
throw new AuthoringError({
|
|
641
673
|
message: `${contextLabel(context)}: a recursive query is not importable as a relation expression yet — declare the recursion on the consuming query (C05 boundary, recorded)`
|
|
642
674
|
});
|
|
643
675
|
}
|
|
644
|
-
const userParams =
|
|
676
|
+
const userParams = data.params.filter(function userSupplied(entry) {
|
|
645
677
|
return entry.membership === undefined;
|
|
646
678
|
});
|
|
647
679
|
if (userParams.length > 0) {
|
|
@@ -654,10 +686,10 @@ function importEntryOf(context, imported) {
|
|
|
654
686
|
const facade = importFacadeOf(imported);
|
|
655
687
|
const table = Object.freeze({
|
|
656
688
|
name: facade.owner.name,
|
|
657
|
-
finds:
|
|
658
|
-
rules:
|
|
689
|
+
finds: data.finds,
|
|
690
|
+
rules: data.rules
|
|
659
691
|
});
|
|
660
|
-
const entry = Object.freeze({ table, inner:
|
|
692
|
+
const entry = Object.freeze({ table, inner: data.interiors });
|
|
661
693
|
importEntries.set(imported, entry);
|
|
662
694
|
importTables.add(table);
|
|
663
695
|
importInner.set(table, entry.inner);
|
|
@@ -679,8 +711,9 @@ function referencedImports(interiors, rec, rules) {
|
|
|
679
711
|
continue;
|
|
680
712
|
}
|
|
681
713
|
const target = item.target;
|
|
682
|
-
|
|
683
|
-
|
|
714
|
+
const origin = importOrigins.get(target) ?? target;
|
|
715
|
+
if (importTables.has(target) && !seen.has(origin)) {
|
|
716
|
+
seen.add(origin);
|
|
684
717
|
out.push(target);
|
|
685
718
|
}
|
|
686
719
|
}
|
|
@@ -712,7 +745,8 @@ function spliceImports(interiors, rec, rules) {
|
|
|
712
745
|
const names = new Set();
|
|
713
746
|
const added = new Set();
|
|
714
747
|
function add(table) {
|
|
715
|
-
|
|
748
|
+
const origin = importOrigins.get(table) ?? table;
|
|
749
|
+
if (added.has(origin)) {
|
|
716
750
|
return;
|
|
717
751
|
}
|
|
718
752
|
if (names.has(table.name)) {
|
|
@@ -720,7 +754,7 @@ function spliceImports(interiors, rec, rules) {
|
|
|
720
754
|
message: `query: the derived-table name ${table.name} appears twice after import splicing — rename the colliding interior (names are unique)`
|
|
721
755
|
});
|
|
722
756
|
}
|
|
723
|
-
added.add(
|
|
757
|
+
added.add(origin);
|
|
724
758
|
names.add(table.name);
|
|
725
759
|
spliced.push(table);
|
|
726
760
|
}
|
|
@@ -749,15 +783,13 @@ function notInteriorAdvance(context, state, name, bindings) {
|
|
|
749
783
|
}
|
|
750
784
|
function findColumns(context, entries) {
|
|
751
785
|
const columns = [];
|
|
786
|
+
queryRecord("query find", entries);
|
|
752
787
|
// Nonrecursive derived stages (interiors) may emit aggregate/computed
|
|
753
788
|
// outputs (C05: "do not leave Interior.rules projection only"); ONLY the
|
|
754
789
|
// recursive feedback cycle stays projection-only — no aggregate,
|
|
755
790
|
// arithmetic-created value or negation flows through it.
|
|
756
791
|
const recName = context.kind === "rec-base" || context.kind === "rec-arm" ? context.self.name : undefined;
|
|
757
792
|
for (const [name, entry] of Object.entries(entries)) {
|
|
758
|
-
if (entry === undefined) {
|
|
759
|
-
continue;
|
|
760
|
-
}
|
|
761
793
|
if (recName !== undefined && !(isTerm(entry) && entry[term] === "var")) {
|
|
762
794
|
throw new AuthoringError({
|
|
763
795
|
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)`
|
|
@@ -912,7 +944,7 @@ function paramRegistryOf(interiors, rec, rules) {
|
|
|
912
944
|
if (existing.anchor !== undefined && use.anchor !== undefined) {
|
|
913
945
|
const registered = rosterOf(existing.anchor);
|
|
914
946
|
const anchored = rosterOf(use.anchor);
|
|
915
|
-
if (registered
|
|
947
|
+
if (!rostersAgree(registered, anchored)) {
|
|
916
948
|
throw new AuthoringError({
|
|
917
949
|
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`
|
|
918
950
|
});
|
|
@@ -984,7 +1016,7 @@ function alignedHeadOf(label, rules) {
|
|
|
984
1016
|
}
|
|
985
1017
|
rule.finds.forEach(function verifyClosedSlice(column, position) {
|
|
986
1018
|
const lead = first.finds[position];
|
|
987
|
-
if (lead !== undefined && column.closed
|
|
1019
|
+
if (lead !== undefined && !rostersAgree(column.closed, lead.closed)) {
|
|
988
1020
|
throw new AuthoringError({
|
|
989
1021
|
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)`
|
|
990
1022
|
});
|
|
@@ -1025,7 +1057,13 @@ function afterMainError(what) {
|
|
|
1025
1057
|
message: `query: ${what} after a main rule is unwritable — declaration order is interiors, then rec, then main`
|
|
1026
1058
|
});
|
|
1027
1059
|
}
|
|
1028
|
-
function makeRawQuery(
|
|
1060
|
+
function makeRawQuery(inputTheory, inputInteriors, inputRec, inputRules) {
|
|
1061
|
+
const { theory, interiors, rec, rules } = snapshotQueryData({
|
|
1062
|
+
theory: inputTheory,
|
|
1063
|
+
interiors: inputInteriors,
|
|
1064
|
+
rec: inputRec,
|
|
1065
|
+
rules: inputRules
|
|
1066
|
+
});
|
|
1029
1067
|
const mergedFinds = alignedHeadOf("a query", rules);
|
|
1030
1068
|
const first = rules[0];
|
|
1031
1069
|
if (first === undefined) {
|
|
@@ -1053,8 +1091,12 @@ function makeRawQuery(theory, interiors, rec, rules) {
|
|
|
1053
1091
|
params
|
|
1054
1092
|
});
|
|
1055
1093
|
const value = {
|
|
1056
|
-
schema
|
|
1057
|
-
|
|
1094
|
+
get schema() {
|
|
1095
|
+
return snapshotData(theory);
|
|
1096
|
+
},
|
|
1097
|
+
get data() {
|
|
1098
|
+
return snapshotQueryData(data);
|
|
1099
|
+
},
|
|
1058
1100
|
rule(build) {
|
|
1059
1101
|
const built = build(makeRawScope({ kind: "query", classes: theory.classes, theory, ...env }));
|
|
1060
1102
|
return makeRawQuery(theory, interiors, rec, [...rules, built.rule]);
|
|
@@ -1129,9 +1171,9 @@ function collectRec(theory, interiors, name, baseBuilds, recBuilds) {
|
|
|
1129
1171
|
const sealedRec = [firstRec, ...rec.slice(1)];
|
|
1130
1172
|
const recData = Object.freeze({
|
|
1131
1173
|
name,
|
|
1132
|
-
finds: sealedFinds,
|
|
1133
|
-
base: sealedBase,
|
|
1134
|
-
rec: sealedRec
|
|
1174
|
+
finds: Object.freeze(sealedFinds),
|
|
1175
|
+
base: Object.freeze(sealedBase),
|
|
1176
|
+
rec: Object.freeze(sealedRec)
|
|
1135
1177
|
});
|
|
1136
1178
|
return recData;
|
|
1137
1179
|
}
|
|
@@ -1164,7 +1206,9 @@ function makeQueryStart(theory, interiors) {
|
|
|
1164
1206
|
},
|
|
1165
1207
|
rule(build) {
|
|
1166
1208
|
const built = build(makeQueryRuleScope(theory, env));
|
|
1167
|
-
return makeQuery(theory, interiors, undefined, [
|
|
1209
|
+
return makeQuery(theory, interiors, undefined, [
|
|
1210
|
+
built.rule
|
|
1211
|
+
]);
|
|
1168
1212
|
}
|
|
1169
1213
|
};
|
|
1170
1214
|
Object.freeze(start);
|
|
@@ -1175,112 +1219,27 @@ function makeQueryReachStart(theory, interiors, rec) {
|
|
|
1175
1219
|
const start = {
|
|
1176
1220
|
rule(build) {
|
|
1177
1221
|
const built = build(makeQueryRuleScope(theory, env));
|
|
1178
|
-
return makeQuery(theory, interiors, rec, [
|
|
1222
|
+
return makeQuery(theory, interiors, rec, [
|
|
1223
|
+
built.rule
|
|
1224
|
+
]);
|
|
1179
1225
|
}
|
|
1180
1226
|
};
|
|
1181
1227
|
Object.freeze(start);
|
|
1182
1228
|
return start;
|
|
1183
1229
|
}
|
|
1184
1230
|
function query(theory) {
|
|
1185
|
-
return makeQueryStart(theory, []);
|
|
1186
|
-
}
|
|
1187
|
-
function taggedHandleId(context, closed, value) {
|
|
1188
|
-
if (typeof value !== "string") {
|
|
1189
|
-
throw literalShapeError(context, `a ${closed.name} handle name (string)`, value);
|
|
1190
|
-
}
|
|
1191
|
-
const id = closed.handles.indexOf(value);
|
|
1192
|
-
if (id < 0) {
|
|
1193
|
-
throw new AuthoringError({
|
|
1194
|
-
message: `${context}: "${value}" is not a handle of ${closed.name} — the roster is ${closed.handles.join(", ")}`
|
|
1195
|
-
});
|
|
1196
|
-
}
|
|
1197
|
-
return { kind: "u64", value: BigInt(id) };
|
|
1231
|
+
return makeQueryStart(schemaDescriptor(theory), []);
|
|
1198
1232
|
}
|
|
1199
1233
|
function taggedAtElementDomain(context, element, value) {
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
if (isFloatIntervalValue(value)) {
|
|
1205
|
-
if (Number.isNaN(value.start) || Number.isNaN(value.end) || !(value.start < value.end)) {
|
|
1206
|
-
throw literalShapeError(context, "a nonempty float interval with non-NaN endpoints", value);
|
|
1207
|
-
}
|
|
1208
|
-
return {
|
|
1209
|
-
kind: "intervalF64",
|
|
1210
|
-
start: Object.is(value.start, -0) ? 0 : value.start,
|
|
1211
|
-
end: Object.is(value.end, -0) ? 0 : value.end
|
|
1212
|
-
};
|
|
1213
|
-
}
|
|
1214
|
-
throw literalShapeError(context, "number (point) or { start, end } numbers (interval)", value);
|
|
1215
|
-
}
|
|
1216
|
-
if (typeof value === "bigint") {
|
|
1217
|
-
if (element === "u64") {
|
|
1218
|
-
return { kind: "u64", value };
|
|
1219
|
-
}
|
|
1220
|
-
return { kind: "i64", value };
|
|
1221
|
-
}
|
|
1222
|
-
if (isIntervalValue(value)) {
|
|
1223
|
-
if (element === "u64") {
|
|
1224
|
-
return { kind: "intervalU64", start: value.start, end: value.end };
|
|
1225
|
-
}
|
|
1226
|
-
return { kind: "intervalI64", start: value.start, end: value.end };
|
|
1227
|
-
}
|
|
1228
|
-
throw literalShapeError(context, "bigint (point) or { start, end } (interval)", value);
|
|
1234
|
+
const field = typeof value === "number" || typeof value === "bigint"
|
|
1235
|
+
? { u64: u64Field, i64: i64Field, f64: f64Field }[element]
|
|
1236
|
+
: { kind: "interval", element, width: undefined };
|
|
1237
|
+
return taggedValueOf(context, field, value);
|
|
1229
1238
|
}
|
|
1230
1239
|
function taggedLiteral(context, field, value) {
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
}
|
|
1235
|
-
switch (field.kind) {
|
|
1236
|
-
case "bool": {
|
|
1237
|
-
if (typeof value !== "boolean") {
|
|
1238
|
-
throw literalShapeError(context, "boolean", value);
|
|
1239
|
-
}
|
|
1240
|
-
return { kind: "bool", value };
|
|
1241
|
-
}
|
|
1242
|
-
case "u64": {
|
|
1243
|
-
if (typeof value !== "bigint") {
|
|
1244
|
-
throw literalShapeError(context, "bigint", value);
|
|
1245
|
-
}
|
|
1246
|
-
return { kind: "u64", value };
|
|
1247
|
-
}
|
|
1248
|
-
case "i64": {
|
|
1249
|
-
if (typeof value !== "bigint") {
|
|
1250
|
-
throw literalShapeError(context, "bigint", value);
|
|
1251
|
-
}
|
|
1252
|
-
return { kind: "i64", value };
|
|
1253
|
-
}
|
|
1254
|
-
case "f64": {
|
|
1255
|
-
if (typeof value !== "number") {
|
|
1256
|
-
throw literalShapeError(context, "number", value);
|
|
1257
|
-
}
|
|
1258
|
-
return { kind: "f64", value };
|
|
1259
|
-
}
|
|
1260
|
-
case "uuid": {
|
|
1261
|
-
if (!Uuid.isUuid(value)) {
|
|
1262
|
-
throw literalShapeError(context, "a UUID (canonical UUID text)", value);
|
|
1263
|
-
}
|
|
1264
|
-
return { kind: "uuid", value };
|
|
1265
|
-
}
|
|
1266
|
-
case "str": {
|
|
1267
|
-
if (typeof value !== "string") {
|
|
1268
|
-
throw literalShapeError(context, "string", value);
|
|
1269
|
-
}
|
|
1270
|
-
if (!value.isWellFormed()) {
|
|
1271
|
-
throw literalShapeError(context, "well-formed string", value);
|
|
1272
|
-
}
|
|
1273
|
-
return { kind: "string", value };
|
|
1274
|
-
}
|
|
1275
|
-
case "bytes": {
|
|
1276
|
-
if (!(value instanceof Uint8Array)) {
|
|
1277
|
-
throw literalShapeError(context, "Uint8Array", value);
|
|
1278
|
-
}
|
|
1279
|
-
return { kind: "fixedBytes", value };
|
|
1280
|
-
}
|
|
1281
|
-
case "interval":
|
|
1282
|
-
return taggedAtElementDomain(context, field.element, value);
|
|
1283
|
-
}
|
|
1240
|
+
return field.kind === "interval"
|
|
1241
|
+
? taggedAtElementDomain(context, field.element, value)
|
|
1242
|
+
: taggedValueOf(context, field, value);
|
|
1284
1243
|
}
|
|
1285
1244
|
/**
|
|
1286
1245
|
* Tags one host literal at a COMPARISON or PARAM position, where the SIBLING
|
|
@@ -1288,7 +1247,7 @@ function taggedLiteral(context, field, value) {
|
|
|
1288
1247
|
* contributes its element domain, a scalar sibling its own type. At
|
|
1289
1248
|
* `pointIn` the operand order is interval-left, point-right, so an
|
|
1290
1249
|
* interval-shaped literal beside a scalar element-typed sibling is the LEGAL
|
|
1291
|
-
* interval operand of `pointIn(t,
|
|
1250
|
+
* interval operand of `pointIn(t, { start, end })`; under every other operator an
|
|
1292
1251
|
* interval shape against a scalar sibling stays refused.
|
|
1293
1252
|
*/
|
|
1294
1253
|
function taggedCmpLiteral(context, sibling, value, op) {
|
|
@@ -1306,6 +1265,15 @@ function taggedCmpLiteral(context, sibling, value, op) {
|
|
|
1306
1265
|
}
|
|
1307
1266
|
return taggedLiteral(context, sibling, value);
|
|
1308
1267
|
}
|
|
1268
|
+
/** Validate and own a literal when the query is authored, before caller mutation. */
|
|
1269
|
+
function ownLiteral(context, field, value, op) {
|
|
1270
|
+
const tagged = taggedCmpLiteral(context, field, value, op);
|
|
1271
|
+
if (rosterOf(field) !== undefined)
|
|
1272
|
+
return value;
|
|
1273
|
+
if ("value" in tagged)
|
|
1274
|
+
return tagged.value;
|
|
1275
|
+
return Object.freeze({ start: tagged.start, end: tagged.end });
|
|
1276
|
+
}
|
|
1309
1277
|
function freshVarIds() {
|
|
1310
1278
|
const assigned = new Map();
|
|
1311
1279
|
return {
|
|
@@ -1329,7 +1297,7 @@ function paramIdOf(ctx, name) {
|
|
|
1329
1297
|
}
|
|
1330
1298
|
function lowerAtom(ctx, atom, ids) {
|
|
1331
1299
|
const member = ctx.theory.relations[atom.relation.name];
|
|
1332
|
-
if (member
|
|
1300
|
+
if (!membersAgree(member, atom.relation)) {
|
|
1333
1301
|
throw new AuthoringError({
|
|
1334
1302
|
message: `query lowering: relation ${atom.relation.name} is not the relation value schema ${ctx.theory.name} declares`
|
|
1335
1303
|
});
|
|
@@ -1563,20 +1531,21 @@ function lowerRule(ctx, rule) {
|
|
|
1563
1531
|
}
|
|
1564
1532
|
function lowerQuery(q) {
|
|
1565
1533
|
const theory = q.schema;
|
|
1534
|
+
const data = q.data;
|
|
1566
1535
|
const relationIds = new Map();
|
|
1567
1536
|
Object.keys(theory.relations).forEach(function assignOrdinal(name, index) {
|
|
1568
1537
|
relationIds.set(name, index);
|
|
1569
1538
|
});
|
|
1570
1539
|
const interiorIds = new Map();
|
|
1571
|
-
|
|
1540
|
+
data.interiors.forEach(function assignInteriorId(interior, index) {
|
|
1572
1541
|
interiorIds.set(interior.name, index);
|
|
1573
1542
|
});
|
|
1574
|
-
if (
|
|
1575
|
-
interiorIds.set(
|
|
1543
|
+
if (data.kind === "reach") {
|
|
1544
|
+
interiorIds.set(data.rec.name, data.interiors.length);
|
|
1576
1545
|
}
|
|
1577
1546
|
const paramIds = new Map();
|
|
1578
1547
|
const params = new Map();
|
|
1579
|
-
|
|
1548
|
+
data.params.forEach(function assignParamId(entry, index) {
|
|
1580
1549
|
if (entry.anchor === undefined) {
|
|
1581
1550
|
throw new AuthoringError({
|
|
1582
1551
|
message: `query param ${entry.name} has no field-anchored use — bind it in an atom or compare it against a bound variable`
|
|
@@ -1586,7 +1555,7 @@ function lowerQuery(q) {
|
|
|
1586
1555
|
params.set(entry.name, entry);
|
|
1587
1556
|
});
|
|
1588
1557
|
const ctx = { theory, relationIds, interiorIds, paramIds, params };
|
|
1589
|
-
const interiors =
|
|
1558
|
+
const interiors = data.interiors.map(function lowerInterior(interior) {
|
|
1590
1559
|
return {
|
|
1591
1560
|
head: interior.finds.map(headTermOf),
|
|
1592
1561
|
rules: interior.rules.map(function lowerInteriorRule(rule) {
|
|
@@ -1594,22 +1563,22 @@ function lowerQuery(q) {
|
|
|
1594
1563
|
})
|
|
1595
1564
|
};
|
|
1596
1565
|
});
|
|
1597
|
-
const head =
|
|
1598
|
-
const rules =
|
|
1566
|
+
const head = data.finds.map(headTermOf);
|
|
1567
|
+
const rules = data.rules.map(function lowerMainRule(rule) {
|
|
1599
1568
|
return lowerRule(ctx, rule);
|
|
1600
1569
|
});
|
|
1601
|
-
if (
|
|
1570
|
+
if (data.kind === "cq") {
|
|
1602
1571
|
return parseQueryIr({ kind: "cq", interiors, head, rules });
|
|
1603
1572
|
}
|
|
1604
1573
|
return parseQueryIr({
|
|
1605
1574
|
kind: "reach",
|
|
1606
1575
|
interiors,
|
|
1607
1576
|
rec: {
|
|
1608
|
-
head:
|
|
1609
|
-
base:
|
|
1577
|
+
head: data.rec.finds.map(headTermOf),
|
|
1578
|
+
base: data.rec.base.map(function lowerBase(rule) {
|
|
1610
1579
|
return lowerRule(ctx, rule);
|
|
1611
1580
|
}),
|
|
1612
|
-
rec:
|
|
1581
|
+
rec: data.rec.rec.map(function lowerRecArm(rule) {
|
|
1613
1582
|
return lowerRule(ctx, rule);
|
|
1614
1583
|
})
|
|
1615
1584
|
},
|
|
@@ -1617,5 +1586,5 @@ function lowerQuery(q) {
|
|
|
1617
1586
|
rules
|
|
1618
1587
|
});
|
|
1619
1588
|
}
|
|
1620
|
-
export { lowerQuery, query, taggedCmpLiteral };
|
|
1589
|
+
export { alignedHeadOf, EMPTY_RULE, lowerQuery, makeRawChain, makeRawQuery, query, taggedCmpLiteral };
|
|
1621
1590
|
//# sourceMappingURL=lower.js.map
|