@malloydata/malloy 0.0.391 → 0.0.393
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/dist/api/asynchronous.js +0 -3
- package/dist/api/foundation/compile.d.ts +1 -1
- package/dist/api/foundation/config.d.ts +80 -8
- package/dist/api/foundation/config.js +151 -69
- package/dist/api/foundation/config_compile.js +27 -35
- package/dist/api/foundation/config_discover.js +5 -9
- package/dist/api/foundation/config_overlays.d.ts +6 -0
- package/dist/api/foundation/config_overlays.js +12 -0
- package/dist/api/foundation/config_resolve.d.ts +4 -1
- package/dist/api/foundation/config_resolve.js +64 -4
- package/dist/api/foundation/core.d.ts +75 -2
- package/dist/api/foundation/core.js +104 -6
- package/dist/api/foundation/index.d.ts +2 -0
- package/dist/api/foundation/readers.js +1 -1
- package/dist/api/foundation/runtime.d.ts +68 -2
- package/dist/api/foundation/runtime.js +212 -10
- package/dist/api/foundation/types.d.ts +2 -1
- package/dist/index.d.ts +3 -1
- package/dist/lang/ast/ast-utils.js +0 -1
- package/dist/lang/ast/expressions/expr-aggregate-function.d.ts +1 -1
- package/dist/lang/ast/expressions/expr-aggregate-function.js +9 -8
- package/dist/lang/ast/expressions/expr-coalesce.d.ts +1 -1
- package/dist/lang/ast/expressions/expr-coalesce.js +2 -3
- package/dist/lang/ast/expressions/expr-count-distinct.js +1 -1
- package/dist/lang/ast/expressions/expr-count.js +6 -4
- package/dist/lang/ast/expressions/expr-filter-expr.js +0 -1
- package/dist/lang/ast/expressions/expr-func.js +9 -4
- package/dist/lang/ast/expressions/expr-given.d.ts +18 -0
- package/dist/lang/ast/expressions/expr-given.js +69 -0
- package/dist/lang/ast/expressions/expr-granular-time.d.ts +1 -1
- package/dist/lang/ast/expressions/expr-id-reference.js +3 -2
- package/dist/lang/ast/expressions/expr-now.js +0 -1
- package/dist/lang/ast/expressions/expr-props.d.ts +132 -132
- package/dist/lang/ast/expressions/expr-props.js +2 -2
- package/dist/lang/ast/expressions/expr-ungroup.d.ts +1 -1
- package/dist/lang/ast/expressions/expr-ungroup.js +4 -4
- package/dist/lang/ast/expressions/for-range.d.ts +1 -1
- package/dist/lang/ast/expressions/function-ordering.d.ts +1 -1
- package/dist/lang/ast/expressions/function-ordering.js +2 -2
- package/dist/lang/ast/expressions/time-literal.d.ts +3 -3
- package/dist/lang/ast/field-space/include-utils.js +2 -2
- package/dist/lang/ast/field-space/index-field-space.js +18 -23
- package/dist/lang/ast/field-space/passthrough-space.d.ts +1 -1
- package/dist/lang/ast/field-space/query-spaces.d.ts +6 -2
- package/dist/lang/ast/field-space/query-spaces.js +29 -19
- package/dist/lang/ast/field-space/reference-field.js +1 -1
- package/dist/lang/ast/field-space/rename-space-field.d.ts +1 -1
- package/dist/lang/ast/field-space/rename-space-field.js +2 -2
- package/dist/lang/ast/field-space/struct-space-field-base.js +2 -3
- package/dist/lang/ast/index.d.ts +2 -0
- package/dist/lang/ast/index.js +2 -0
- package/dist/lang/ast/query-builders/index-builder.d.ts +1 -1
- package/dist/lang/ast/query-builders/index-builder.js +4 -3
- package/dist/lang/ast/query-builders/reduce-builder.d.ts +2 -2
- package/dist/lang/ast/query-builders/reduce-builder.js +4 -5
- package/dist/lang/ast/query-elements/query-arrow.js +3 -2
- package/dist/lang/ast/query-elements/query-base.d.ts +1 -1
- package/dist/lang/ast/query-elements/query-base.js +1 -1
- package/dist/lang/ast/query-elements/query-refine.js +3 -1
- package/dist/lang/ast/query-items/field-declaration.js +2 -2
- package/dist/lang/ast/query-properties/drill.js +6 -6
- package/dist/lang/ast/query-properties/filters.js +2 -2
- package/dist/lang/ast/query-properties/nest.js +3 -3
- package/dist/lang/ast/source-elements/composite-source.js +5 -3
- package/dist/lang/ast/source-elements/named-source.js +4 -0
- package/dist/lang/ast/source-elements/sql-source.js +2 -2
- package/dist/lang/ast/source-elements/table-source.js +3 -1
- package/dist/lang/ast/source-properties/join.js +4 -4
- package/dist/lang/ast/source-query-elements/sq-reference.js +2 -1
- package/dist/lang/ast/statements/define-given.d.ts +23 -0
- package/dist/lang/ast/statements/define-given.js +163 -0
- package/dist/lang/ast/statements/import-statement.js +72 -9
- package/dist/lang/ast/typedesc-utils.d.ts +3 -1
- package/dist/lang/ast/typedesc-utils.js +4 -47
- package/dist/lang/ast/types/expr-value.js +2 -3
- package/dist/lang/ast/types/expression-def.d.ts +2 -2
- package/dist/lang/ast/types/expression-def.js +2 -2
- package/dist/lang/ast/types/malloy-element.d.ts +5 -15
- package/dist/lang/ast/types/malloy-element.js +113 -1
- package/dist/lang/ast/types/space-field.js +7 -9
- package/dist/lang/ast/view-elements/reference-view.js +6 -5
- package/dist/lang/ast/view-elements/refine-utils.js +1 -1
- package/dist/lang/composite-source-utils.d.ts +30 -15
- package/dist/lang/composite-source-utils.js +234 -64
- package/dist/lang/lib/Malloy/MalloyLexer.d.ts +171 -169
- package/dist/lang/lib/Malloy/MalloyLexer.js +1194 -1178
- package/dist/lang/lib/Malloy/MalloyParser.d.ts +408 -334
- package/dist/lang/lib/Malloy/MalloyParser.js +3062 -2561
- package/dist/lang/lib/Malloy/MalloyParserListener.d.ts +68 -0
- package/dist/lang/lib/Malloy/MalloyParserVisitor.d.ts +43 -0
- package/dist/lang/malloy-to-ast.d.ts +13 -1
- package/dist/lang/malloy-to-ast.js +90 -11
- package/dist/lang/parse-log.d.ts +8 -0
- package/dist/lang/prettify/filter-type.d.ts +3 -0
- package/dist/lang/prettify/filter-type.js +38 -0
- package/dist/lang/prettify/formatter.js +6 -0
- package/dist/lang/prettify/inline-renderer.js +20 -0
- package/dist/lang/prettify/rules.d.ts +1 -1
- package/dist/lang/prettify/rules.js +1 -0
- package/dist/lang/prettify/sections.js +2 -0
- package/dist/lang/prettify/tokens.js +2 -0
- package/dist/lang/test/expr-to-str.js +2 -0
- package/dist/lang/test/parse-expects.d.ts +1 -0
- package/dist/lang/test/parse-expects.js +27 -10
- package/dist/model/constant_expression_compiler.js +1 -0
- package/dist/model/expression_compiler.d.ts +2 -1
- package/dist/model/expression_compiler.js +41 -1
- package/dist/model/given_binding.d.ts +2 -0
- package/dist/model/given_binding.js +204 -0
- package/dist/model/index.d.ts +1 -1
- package/dist/model/index.js +2 -1
- package/dist/model/malloy_types.d.ts +163 -36
- package/dist/model/malloy_types.js +97 -0
- package/dist/model/query_model_contract.d.ts +2 -1
- package/dist/model/query_model_impl.d.ts +2 -1
- package/dist/model/query_model_impl.js +7 -0
- package/dist/model/query_node.d.ts +2 -1
- package/dist/model/source_def_utils.d.ts +2 -1
- package/dist/model/source_def_utils.js +4 -0
- package/dist/model/utils.d.ts +14 -1
- package/dist/model/utils.js +41 -0
- package/dist/to_stable.js +1 -1
- package/dist/util/closest_match.d.ts +9 -0
- package/dist/util/closest_match.js +47 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright Contributors to the Malloy project
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.DefineGivens = exports.GivenDeclaration = void 0;
|
|
8
|
+
const malloy_types_1 = require("../../../model/malloy_types");
|
|
9
|
+
const source_def_utils_1 = require("../../../model/source_def_utils");
|
|
10
|
+
const utils_1 = require("../../../model/utils");
|
|
11
|
+
const expression_def_1 = require("../types/expression-def");
|
|
12
|
+
const malloy_element_1 = require("../types/malloy-element");
|
|
13
|
+
const noteable_1 = require("../types/noteable");
|
|
14
|
+
// `filter<T>` defaults can't be type-checked via TD.eq — the filter
|
|
15
|
+
// expression value shape doesn't match an atomic typeDef. Catch only
|
|
16
|
+
// the gross kind mismatch here; inner-content validation (filter
|
|
17
|
+
// syntax + T compatibility) belongs to the filter machinery at use
|
|
18
|
+
// site.
|
|
19
|
+
function filterTypeMismatch(declared, constVal) {
|
|
20
|
+
return ((declared.type === 'filter expression') !==
|
|
21
|
+
(constVal.type === 'filter expression'));
|
|
22
|
+
}
|
|
23
|
+
class GivenDeclaration extends malloy_element_1.MalloyElement {
|
|
24
|
+
constructor(name, typeDef, defaultExpr) {
|
|
25
|
+
super();
|
|
26
|
+
this.name = name;
|
|
27
|
+
this.typeDef = typeDef;
|
|
28
|
+
this.elementType = 'given';
|
|
29
|
+
this.isNoteableObj = true;
|
|
30
|
+
this.extendNote = noteable_1.extendNoteMethod;
|
|
31
|
+
if (defaultExpr) {
|
|
32
|
+
this.default = defaultExpr;
|
|
33
|
+
this.has({ default: defaultExpr });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
varInfo() {
|
|
37
|
+
return ` ${this.name} :: ${(0, utils_1.typeDefToString)(this.typeDef)}`;
|
|
38
|
+
}
|
|
39
|
+
execute(doc) {
|
|
40
|
+
var _a, _b, _c, _d, _e;
|
|
41
|
+
if (this.typeDef.type === 'error')
|
|
42
|
+
return;
|
|
43
|
+
if (doc.modelEntry(this.name)) {
|
|
44
|
+
this.logError('given-definition-name-conflict', `Cannot redefine '${this.name}'`);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// Default expression. ConstantExpression evaluates through a
|
|
48
|
+
// ConstantFieldSpace that errors on every name lookup, so any
|
|
49
|
+
// non-constant subexpression (field refs, aggregates, etc.) gets
|
|
50
|
+
// logged here. `$NAME` references bypass that field space (see
|
|
51
|
+
// GivenReference.getExpression) so given-to-given refs in defaults
|
|
52
|
+
// are allowed.
|
|
53
|
+
let defaultExpr;
|
|
54
|
+
let givenUsage;
|
|
55
|
+
if (this.default) {
|
|
56
|
+
const constVal = this.default.constantValue();
|
|
57
|
+
if (constVal.type !== 'error') {
|
|
58
|
+
// `X :: timestamp is @2024-01-01` works because date literals
|
|
59
|
+
// carry a morphic.timestamp. Date/timestamp are the only
|
|
60
|
+
// MorphicType targets — other declared types fall through to
|
|
61
|
+
// the TD.eq check below.
|
|
62
|
+
const morphed = this.typeDef.type === 'date' || this.typeDef.type === 'timestamp'
|
|
63
|
+
? (0, expression_def_1.getMorphicValue)(constVal, this.typeDef.type)
|
|
64
|
+
: undefined;
|
|
65
|
+
// `filter<T>` defaults are filter-expression literals — their
|
|
66
|
+
// shape doesn't match an atomic typeDef, so type-check there is
|
|
67
|
+
// owned by the filter machinery, not us. `null` is implicitly
|
|
68
|
+
// accepted for any declared type.
|
|
69
|
+
if (constVal.type !== 'null' &&
|
|
70
|
+
morphed === undefined &&
|
|
71
|
+
(filterTypeMismatch(this.typeDef, constVal) ||
|
|
72
|
+
!malloy_types_1.TD.eq(this.typeDef, constVal))) {
|
|
73
|
+
const actual = malloy_types_1.TD.isAtomic(constVal)
|
|
74
|
+
? (0, utils_1.typeDefToString)(constVal)
|
|
75
|
+
: constVal.type;
|
|
76
|
+
this.default.logError('parameter-default-does-not-match-declared-type', `Default value of type \`${actual}\` does not match declared type \`${(0, utils_1.typeDefToString)(this.typeDef)}\``);
|
|
77
|
+
}
|
|
78
|
+
else if (this.typeDef.type === 'filter expression' &&
|
|
79
|
+
constVal.type === 'filter expression') {
|
|
80
|
+
// We know T at the declaration site, so validate the filter
|
|
81
|
+
// literal here even though the filter machinery will check it
|
|
82
|
+
// again at use site. Catches bad filter syntax early.
|
|
83
|
+
(0, expression_def_1.checkFilterExpression)(this.default, this.typeDef.filterType, constVal.value);
|
|
84
|
+
}
|
|
85
|
+
defaultExpr = (_a = morphed === null || morphed === void 0 ? void 0 : morphed.value) !== null && _a !== void 0 ? _a : constVal.value;
|
|
86
|
+
// Build the transitive closure of givens reachable through this
|
|
87
|
+
// default's expression. We only include direct refs PLUS each
|
|
88
|
+
// referenced given's already-precomputed transitive (which is
|
|
89
|
+
// present because Malloy requires `$X` to resolve to a given
|
|
90
|
+
// declared earlier or imported). The satisfiability check then
|
|
91
|
+
// becomes a flat iteration — no recursion at check time.
|
|
92
|
+
//
|
|
93
|
+
// For `A :: number is $B + 1` where `B :: number is $C`:
|
|
94
|
+
// - B's givenUsage was computed when B was declared = [C]
|
|
95
|
+
// - A's givenUsage = [B] ∪ B.givenUsage = [B, C]
|
|
96
|
+
const directRefs = (0, malloy_types_1.givenUsageFrom)(constVal.refSummary);
|
|
97
|
+
if (directRefs.length > 0) {
|
|
98
|
+
const seen = new Set();
|
|
99
|
+
const closure = [];
|
|
100
|
+
for (const g of directRefs) {
|
|
101
|
+
if (seen.has(g.id))
|
|
102
|
+
continue;
|
|
103
|
+
seen.add(g.id);
|
|
104
|
+
closure.push(g);
|
|
105
|
+
const refDecl = doc.documentGivens.get(g.id);
|
|
106
|
+
for (const t of (_b = refDecl === null || refDecl === void 0 ? void 0 : refDecl.givenUsage) !== null && _b !== void 0 ? _b : []) {
|
|
107
|
+
if (seen.has(t.id))
|
|
108
|
+
continue;
|
|
109
|
+
seen.add(t.id);
|
|
110
|
+
closure.push(t);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
givenUsage = closure;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const id = (0, source_def_utils_1.mkGivenID)(this.name, (_c = this.location) === null || _c === void 0 ? void 0 : _c.url);
|
|
118
|
+
const defaultText = defaultExpr !== undefined ? (_d = this.default) === null || _d === void 0 ? void 0 : _d.code : undefined;
|
|
119
|
+
const givenIR = {
|
|
120
|
+
name: this.name,
|
|
121
|
+
type: this.typeDef,
|
|
122
|
+
default: defaultExpr,
|
|
123
|
+
defaultText,
|
|
124
|
+
givenUsage,
|
|
125
|
+
location: this.location,
|
|
126
|
+
annotation: this.note,
|
|
127
|
+
};
|
|
128
|
+
doc.documentGivens.set(id, givenIR);
|
|
129
|
+
const entry = { type: 'given', name: this.name, id };
|
|
130
|
+
doc.setEntry(this.name, { entry, exported: true });
|
|
131
|
+
// Declaration-site reference. `location` covers the whole
|
|
132
|
+
// declaration; `definition.location` points at the same place.
|
|
133
|
+
// Self-referential by design — parallel to how fieldReference
|
|
134
|
+
// behaves on field declarations.
|
|
135
|
+
this.addReference({
|
|
136
|
+
type: 'givenReference',
|
|
137
|
+
text: this.name,
|
|
138
|
+
location: (_e = this.location) !== null && _e !== void 0 ? _e : {
|
|
139
|
+
url: '',
|
|
140
|
+
range: {
|
|
141
|
+
start: { line: 0, character: 0 },
|
|
142
|
+
end: { line: 0, character: 0 },
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
definition: {
|
|
146
|
+
type: (0, utils_1.typeDefToString)(this.typeDef),
|
|
147
|
+
annotation: this.note,
|
|
148
|
+
location: this.location,
|
|
149
|
+
defaultText,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
exports.GivenDeclaration = GivenDeclaration;
|
|
155
|
+
class DefineGivens extends malloy_element_1.DocStatementList {
|
|
156
|
+
constructor(givens) {
|
|
157
|
+
super(givens);
|
|
158
|
+
this.elementType = 'defineGivens';
|
|
159
|
+
this.givens = givens;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
exports.DefineGivens = DefineGivens;
|
|
163
|
+
//# sourceMappingURL=define-given.js.map
|
|
@@ -27,6 +27,7 @@ const malloy_element_1 = require("../types/malloy-element");
|
|
|
27
27
|
const malloy_types_1 = require("../../../model/malloy_types");
|
|
28
28
|
const source_def_utils_1 = require("../../../model/source_def_utils");
|
|
29
29
|
const persist_utils_1 = require("../../../model/persist_utils");
|
|
30
|
+
const utils_1 = require("../../../model/utils");
|
|
30
31
|
/** Walk BuildNode tree and collect all sourceIDs */
|
|
31
32
|
function collectSourceIDs(nodes, into) {
|
|
32
33
|
for (const node of nodes) {
|
|
@@ -87,6 +88,7 @@ class ImportStatement extends malloy_element_1.ListOf {
|
|
|
87
88
|
return undefined;
|
|
88
89
|
}
|
|
89
90
|
execute(doc) {
|
|
91
|
+
var _a;
|
|
90
92
|
const trans = this.translator();
|
|
91
93
|
if (!trans) {
|
|
92
94
|
this.logError('no-translator-for-import', 'Cannot import without translation context');
|
|
@@ -98,7 +100,10 @@ class ImportStatement extends malloy_element_1.ListOf {
|
|
|
98
100
|
const importedModel = trans.importModelDef(this.fullURL);
|
|
99
101
|
if (importedModel) {
|
|
100
102
|
const importAll = this.empty();
|
|
101
|
-
|
|
103
|
+
// `Map` rather than plain object — keys are user-provided names and
|
|
104
|
+
// a plain object would expose Object.prototype keys (`toString`,
|
|
105
|
+
// etc.).
|
|
106
|
+
const explicitImport = new Map();
|
|
102
107
|
for (const importOne of this.list) {
|
|
103
108
|
const dstName = importOne.text;
|
|
104
109
|
const srcName = importOne.from ? importOne.from.text : dstName;
|
|
@@ -109,20 +114,64 @@ class ImportStatement extends malloy_element_1.ListOf {
|
|
|
109
114
|
importOne.logError('name-conflict-on-selective-import', `Cannot redefine '${dstName}'`);
|
|
110
115
|
}
|
|
111
116
|
else {
|
|
112
|
-
|
|
113
|
-
|
|
117
|
+
const existing = explicitImport.get(srcName);
|
|
118
|
+
if (existing) {
|
|
119
|
+
existing.push(dstName);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
explicitImport.set(srcName, [dstName]);
|
|
114
123
|
}
|
|
115
|
-
explicitImport[srcName].push(dstName);
|
|
116
124
|
}
|
|
117
125
|
}
|
|
118
126
|
const neededSourceIDs = new Set();
|
|
119
127
|
for (const srcName of importedModel.exports) {
|
|
120
|
-
const pickedNames = explicitImport
|
|
121
|
-
const dstNames = pickedNames
|
|
128
|
+
const pickedNames = explicitImport.get(srcName);
|
|
129
|
+
const dstNames = pickedNames !== null && pickedNames !== void 0 ? pickedNames : (importAll ? [srcName] : []);
|
|
122
130
|
for (const dstName of dstNames) {
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
131
|
+
const sourceEntry = (0, malloy_types_1.safeRecordGet)(importedModel.contents, srcName);
|
|
132
|
+
// Non-selective collision: an entry would shadow something
|
|
133
|
+
// already in the importer's namespace (a local declaration or
|
|
134
|
+
// an entry brought in by a previous import). Selective
|
|
135
|
+
// collisions are caught by the per-item check at the top of
|
|
136
|
+
// the loop; here we re-check because the auto-surface path
|
|
137
|
+
// bypasses that loop. Applies to every entry kind.
|
|
138
|
+
if (!pickedNames && doc.getEntry(dstName)) {
|
|
139
|
+
this.logError('name-conflict-on-indiscriminate-import', `Cannot redefine '${dstName}'`);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if ((sourceEntry === null || sourceEntry === void 0 ? void 0 : sourceEntry.type) === 'given') {
|
|
143
|
+
const givenEntry = {
|
|
144
|
+
type: 'given',
|
|
145
|
+
name: dstName,
|
|
146
|
+
id: sourceEntry.id,
|
|
147
|
+
};
|
|
148
|
+
doc.setEntry(dstName, { entry: givenEntry, exported: false });
|
|
149
|
+
// Selective import-site reference. `location` covers
|
|
150
|
+
// the `NAME` token in the selective list;
|
|
151
|
+
// `definition.location` points at the canonical
|
|
152
|
+
// `given:` declaration in the imported file. Only
|
|
153
|
+
// emitted on the selective form — non-selective
|
|
154
|
+
// auto-surface has no per-name source position.
|
|
155
|
+
if (pickedNames) {
|
|
156
|
+
const givenIR = (_a = importedModel.givens) === null || _a === void 0 ? void 0 : _a[sourceEntry.id];
|
|
157
|
+
const importOne = this.list.find(item => item.text === dstName);
|
|
158
|
+
if (importOne && givenIR) {
|
|
159
|
+
this.addReference({
|
|
160
|
+
type: 'givenReference',
|
|
161
|
+
text: dstName,
|
|
162
|
+
location: importOne.location,
|
|
163
|
+
definition: {
|
|
164
|
+
type: (0, utils_1.typeDefToString)(givenIR.type),
|
|
165
|
+
annotation: givenIR.annotation,
|
|
166
|
+
location: givenIR.location,
|
|
167
|
+
defaultText: givenIR.defaultText,
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const importMe = { ...sourceEntry };
|
|
126
175
|
importMe.as = dstName;
|
|
127
176
|
doc.setEntry(dstName, { entry: importMe, exported: false });
|
|
128
177
|
// Collect dependencies for persistable sources
|
|
@@ -132,6 +181,20 @@ class ImportStatement extends malloy_element_1.ListOf {
|
|
|
132
181
|
}
|
|
133
182
|
}
|
|
134
183
|
}
|
|
184
|
+
// Always copy the imported model's given declarations into the
|
|
185
|
+
// local givens map, regardless of whether each given is surfaced
|
|
186
|
+
// into the namespace. This guarantees that any givenRef in
|
|
187
|
+
// imported IR (sources/queries that reference `$X`) resolves to
|
|
188
|
+
// a declaration at SQL-emission time. The import-time
|
|
189
|
+
// satisfiability check that catches unsatisfiable refs lands
|
|
190
|
+
// with the refSummary work.
|
|
191
|
+
if (importedModel.givens) {
|
|
192
|
+
for (const [id, given] of Object.entries(importedModel.givens)) {
|
|
193
|
+
if (!doc.documentGivens.has(id)) {
|
|
194
|
+
doc.documentGivens.set(id, given);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
135
198
|
// Register hidden dependencies from child's registry
|
|
136
199
|
for (const sourceID of neededSourceIDs) {
|
|
137
200
|
if (!(sourceID in doc.documentSrcRegistry)) {
|
|
@@ -43,7 +43,9 @@ export declare function typeEq(left: TypeDesc, right: TypeDesc, nullOk?: boolean
|
|
|
43
43
|
export declare function inspect(...types: (TypeDesc | undefined)[]): string;
|
|
44
44
|
/**
|
|
45
45
|
* Used when using a TypeDesc or TypeDesc-like interface to
|
|
46
|
-
* create a field, don't copy the non type fields.
|
|
46
|
+
* create a field, don't copy the non type fields. Thin wrapper over
|
|
47
|
+
* `TD.atomicDef`; exists in `lang/` so callers can pass a `TypeDesc`
|
|
48
|
+
* (translator-side) without leaking that type into the model layer.
|
|
47
49
|
*/
|
|
48
50
|
export declare function atomicDef(td: AtomicTypeDef | TypeDesc): AtomicTypeDef;
|
|
49
51
|
export declare function parameterTypeDesc(p: Parameter, evalSpace: EvalSpace): TypeDesc;
|
|
@@ -40,7 +40,6 @@ dataType, expressionType = 'scalar', evalSpace = 'constant') {
|
|
|
40
40
|
type: dataType,
|
|
41
41
|
expressionType,
|
|
42
42
|
evalSpace,
|
|
43
|
-
fieldUsage: [],
|
|
44
43
|
};
|
|
45
44
|
}
|
|
46
45
|
exports.nullT = mkTypeDesc('null');
|
|
@@ -120,53 +119,12 @@ function inspect(...types) {
|
|
|
120
119
|
}
|
|
121
120
|
/**
|
|
122
121
|
* Used when using a TypeDesc or TypeDesc-like interface to
|
|
123
|
-
* create a field, don't copy the non type fields.
|
|
122
|
+
* create a field, don't copy the non type fields. Thin wrapper over
|
|
123
|
+
* `TD.atomicDef`; exists in `lang/` so callers can pass a `TypeDesc`
|
|
124
|
+
* (translator-side) without leaking that type into the model layer.
|
|
124
125
|
*/
|
|
125
126
|
function atomicDef(td) {
|
|
126
|
-
|
|
127
|
-
switch (td.type) {
|
|
128
|
-
case 'array': {
|
|
129
|
-
return (0, model_1.isRepeatedRecord)(td)
|
|
130
|
-
? {
|
|
131
|
-
type: 'array',
|
|
132
|
-
elementTypeDef: td.elementTypeDef,
|
|
133
|
-
fields: td.fields,
|
|
134
|
-
}
|
|
135
|
-
: {
|
|
136
|
-
type: 'array',
|
|
137
|
-
elementTypeDef: td.elementTypeDef,
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
case 'record': {
|
|
141
|
-
return { type: 'record', fields: td.fields };
|
|
142
|
-
}
|
|
143
|
-
case 'number': {
|
|
144
|
-
return td.numberType
|
|
145
|
-
? { type: 'number', numberType: td.numberType }
|
|
146
|
-
: { type: 'number' };
|
|
147
|
-
}
|
|
148
|
-
case 'sql native': {
|
|
149
|
-
return td.rawType
|
|
150
|
-
? { type: 'sql native', rawType: td.rawType }
|
|
151
|
-
: { type: 'sql native' };
|
|
152
|
-
}
|
|
153
|
-
case 'timestamp': {
|
|
154
|
-
return {
|
|
155
|
-
type: 'timestamp',
|
|
156
|
-
...(td.timeframe === undefined ? {} : { timeframe: td.timeframe }),
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
case 'timestamptz': {
|
|
160
|
-
return {
|
|
161
|
-
type: 'timestamptz',
|
|
162
|
-
...(td.timeframe === undefined ? {} : { timeframe: td.timeframe }),
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
default:
|
|
166
|
-
return { type: td.type };
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return { type: 'error' };
|
|
127
|
+
return model_1.TD.atomicDef(td);
|
|
170
128
|
}
|
|
171
129
|
function parameterTypeDesc(p, evalSpace) {
|
|
172
130
|
const t = p.type;
|
|
@@ -177,7 +135,6 @@ function parameterTypeDesc(p, evalSpace) {
|
|
|
177
135
|
...theType,
|
|
178
136
|
expressionType: 'scalar',
|
|
179
137
|
evalSpace,
|
|
180
|
-
fieldUsage: [],
|
|
181
138
|
};
|
|
182
139
|
}
|
|
183
140
|
//# sourceMappingURL=typedesc-utils.js.map
|
|
@@ -37,7 +37,7 @@ function computedExprValue({ value, dataType, from, }) {
|
|
|
37
37
|
value,
|
|
38
38
|
expressionType: (0, model_1.maxOfExpressionTypes)(from.map(e => e.expressionType)),
|
|
39
39
|
evalSpace: (0, model_1.mergeEvalSpaces)(...from.map(e => e.evalSpace)),
|
|
40
|
-
|
|
40
|
+
refSummary: (0, composite_source_utils_1.mergeRefSummaries)(...from.map(e => e.refSummary)),
|
|
41
41
|
ungroupings: mergeUngroupings(...from.map(e => e.ungroupings)),
|
|
42
42
|
requiresGroupBy: mergeGroupedBys(...from.map(e => e.requiresGroupBy)),
|
|
43
43
|
};
|
|
@@ -49,7 +49,7 @@ function computedTimeResult({ value, dataType, from, timeframe, }) {
|
|
|
49
49
|
expressionType: xv.expressionType,
|
|
50
50
|
evalSpace: xv.evalSpace,
|
|
51
51
|
value: xv.value,
|
|
52
|
-
|
|
52
|
+
refSummary: (0, composite_source_utils_1.mergeRefSummaries)(...from.map(e => e.refSummary)),
|
|
53
53
|
ungroupings: mergeUngroupings(...from.map(e => e.ungroupings)),
|
|
54
54
|
requiresGroupBy: mergeGroupedBys(...from.map(e => e.requiresGroupBy)),
|
|
55
55
|
};
|
|
@@ -76,7 +76,6 @@ function literalTimeResult({ value, dataType, timeframe, }) {
|
|
|
76
76
|
expressionType: xv.expressionType,
|
|
77
77
|
evalSpace: xv.evalSpace,
|
|
78
78
|
value: xv.value,
|
|
79
|
-
fieldUsage: [],
|
|
80
79
|
};
|
|
81
80
|
if (timeframe) {
|
|
82
81
|
y.timeframe = timeframe;
|
|
@@ -27,7 +27,7 @@ export declare abstract class ExpressionDef extends MalloyElement {
|
|
|
27
27
|
* @param space Namespace for looking up field references
|
|
28
28
|
*/
|
|
29
29
|
abstract getExpression(fs: FieldSpace): ExprValue;
|
|
30
|
-
legalChildTypes: import("
|
|
30
|
+
legalChildTypes: import("../../..").TypeDesc[];
|
|
31
31
|
/**
|
|
32
32
|
* Some operators want to give the right hand value a chance to
|
|
33
33
|
* rewrite itself. This requests a translation for a rewrite,
|
|
@@ -66,7 +66,7 @@ export declare class ExprDuration extends ExpressionDef {
|
|
|
66
66
|
readonly n: ExpressionDef;
|
|
67
67
|
readonly timeframe: TimestampUnit;
|
|
68
68
|
elementType: string;
|
|
69
|
-
legalChildTypes: import("
|
|
69
|
+
legalChildTypes: import("../../..").TypeDesc[];
|
|
70
70
|
constructor(n: ExpressionDef, timeframe: TimestampUnit);
|
|
71
71
|
apply(fs: FieldSpace, op: BinaryMalloyOperator, left: ExpressionDef): ExprValue;
|
|
72
72
|
getExpression(fs: FieldSpace): ExprValue;
|
|
@@ -296,8 +296,8 @@ function equality(fs, left, op, right) {
|
|
|
296
296
|
while (actualFilter.node === '()') {
|
|
297
297
|
actualFilter = actualFilter.e;
|
|
298
298
|
}
|
|
299
|
-
if (actualFilter.node !== 'parameter') {
|
|
300
|
-
//
|
|
299
|
+
if (actualFilter.node !== 'parameter' && actualFilter.node !== 'given') {
|
|
300
|
+
// Parameter and given values are checked when supplied
|
|
301
301
|
checkFilterExpression(right, lhs.type, actualFilter);
|
|
302
302
|
}
|
|
303
303
|
const filterMatch = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Annotation, DocumentLocation, DocumentReference, ModelDef, ModelAnnotation, NamedModelObject, Query, SourceID, SourceRegistryValue, StructDef } from '../../../model/malloy_types';
|
|
1
|
+
import type { Annotation, DocumentLocation, DocumentReference, Given, ModelDef, ModelAnnotation, NamedModelObject, Query, SourceID, SourceRegistryValue, StructDef } from '../../../model/malloy_types';
|
|
2
2
|
import { Tag } from '@malloydata/malloy-tag';
|
|
3
3
|
import type { LogMessageOptions, MessageLogger, MessageParameterType, MessageCode } from '../../parse-log';
|
|
4
4
|
import type { MalloyTranslation } from '../../parse-malloy';
|
|
@@ -55,7 +55,7 @@ export declare abstract class MalloyElement {
|
|
|
55
55
|
toString(): string;
|
|
56
56
|
private stringify;
|
|
57
57
|
walk(): Generator<MalloyElement>;
|
|
58
|
-
|
|
58
|
+
protected varInfo(): string;
|
|
59
59
|
protected internalError(msg: string): Error;
|
|
60
60
|
needs(doc: Document): ModelDataRequest | undefined;
|
|
61
61
|
inExperiment(experimentId: string, silent?: boolean): boolean;
|
|
@@ -104,24 +104,12 @@ export declare class DocStatementList extends ListOf<DocStatement | DocStatement
|
|
|
104
104
|
noteCursor: number;
|
|
105
105
|
executeList(doc: Document): ModelDataRequest;
|
|
106
106
|
}
|
|
107
|
-
/**
|
|
108
|
-
* The Document class is a little weird because we might need to bounce back
|
|
109
|
-
* to the requestor, which might be on the other side of a wire, to get
|
|
110
|
-
* back some schema information. The intended translation of a Document
|
|
111
|
-
* is to call initModelDef(), and then to call modelDataRequest() until it
|
|
112
|
-
* returns undefined. At any time you can call modelDef to get the model
|
|
113
|
-
* as it exists so far, but the translation is not complete until
|
|
114
|
-
* modelDataRequest() returns undefined;
|
|
115
|
-
*
|
|
116
|
-
* TODO probably modelRequest should be the method and you call it
|
|
117
|
-
* until it returns a model with no additional data needed ...
|
|
118
|
-
* that can be tomorrow
|
|
119
|
-
*/
|
|
120
107
|
export declare class Document extends MalloyElement implements NameSpace {
|
|
121
108
|
elementType: string;
|
|
122
109
|
globalNameSpace: NameSpace;
|
|
123
110
|
documentModel: Map<string, ModelEntry>;
|
|
124
111
|
documentSrcRegistry: Record<SourceID, SourceRegistryValue>;
|
|
112
|
+
documentGivens: Map<string, Given>;
|
|
125
113
|
queryList: Query[];
|
|
126
114
|
statements: DocStatementList;
|
|
127
115
|
didInitModel: boolean;
|
|
@@ -133,6 +121,8 @@ export declare class Document extends MalloyElement implements NameSpace {
|
|
|
133
121
|
compile(): DocumentCompileResult;
|
|
134
122
|
private modelAnnotationTodoList;
|
|
135
123
|
rememberToAddModelAnnotations(sd: StructDef): void;
|
|
124
|
+
private checkGivenAliasCollisions;
|
|
125
|
+
private checkQueryGivenSatisfiability;
|
|
136
126
|
hasAnnotation(): boolean;
|
|
137
127
|
currentModelAnnotation(): ModelAnnotation | undefined;
|
|
138
128
|
modelDef(): ModelDef;
|
|
@@ -432,6 +432,20 @@ function annotationID(a) {
|
|
|
432
432
|
* until it returns a model with no additional data needed ...
|
|
433
433
|
* that can be tomorrow
|
|
434
434
|
*/
|
|
435
|
+
function unsatisfiedGivenMessage(label, decl, id) {
|
|
436
|
+
var _a, _b;
|
|
437
|
+
// We always have the GivenID; we may or may not have the declaration
|
|
438
|
+
// (we should — every given referenced should have been copied into
|
|
439
|
+
// documentGivens at import time — but defend against it being absent).
|
|
440
|
+
const surface = (_a = decl === null || decl === void 0 ? void 0 : decl.name) !== null && _a !== void 0 ? _a : id;
|
|
441
|
+
const where = ((_b = decl === null || decl === void 0 ? void 0 : decl.location) === null || _b === void 0 ? void 0 : _b.url)
|
|
442
|
+
? ` (declared in ${decl.location.url})`
|
|
443
|
+
: '';
|
|
444
|
+
return (`${label} references given \`${surface}\`${where}, ` +
|
|
445
|
+
'which is not surfaced in this model and has no default. ' +
|
|
446
|
+
`Either import it (e.g. \`import { ${surface} } from "..."\`) ` +
|
|
447
|
+
'or supply a default at the declaration site.');
|
|
448
|
+
}
|
|
435
449
|
class Document extends MalloyElement {
|
|
436
450
|
constructor(statements) {
|
|
437
451
|
super();
|
|
@@ -439,6 +453,7 @@ class Document extends MalloyElement {
|
|
|
439
453
|
this.globalNameSpace = new global_name_space_1.GlobalNameSpace();
|
|
440
454
|
this.documentModel = new Map();
|
|
441
455
|
this.documentSrcRegistry = {};
|
|
456
|
+
this.documentGivens = new Map();
|
|
442
457
|
this.queryList = [];
|
|
443
458
|
this.didInitModel = false;
|
|
444
459
|
this.modelWasModified = false;
|
|
@@ -455,6 +470,7 @@ class Document extends MalloyElement {
|
|
|
455
470
|
}
|
|
456
471
|
this.documentModel = new Map();
|
|
457
472
|
this.documentSrcRegistry = {};
|
|
473
|
+
this.documentGivens = new Map();
|
|
458
474
|
this.queryList = [];
|
|
459
475
|
if (extendingModelDef) {
|
|
460
476
|
if (extendingModelDef.annotation) {
|
|
@@ -465,11 +481,17 @@ class Document extends MalloyElement {
|
|
|
465
481
|
if ((0, malloy_types_1.isSourceDef)(entry) ||
|
|
466
482
|
entry.type === 'query' ||
|
|
467
483
|
entry.type === 'function' ||
|
|
468
|
-
entry.type === 'userType'
|
|
484
|
+
entry.type === 'userType' ||
|
|
485
|
+
entry.type === 'given') {
|
|
469
486
|
const exported = extendingModelDef.exports.includes(nm);
|
|
470
487
|
this.setEntry(nm, { entry, exported });
|
|
471
488
|
}
|
|
472
489
|
}
|
|
490
|
+
if (extendingModelDef.givens) {
|
|
491
|
+
for (const [id, given] of Object.entries(extendingModelDef.givens)) {
|
|
492
|
+
this.documentGivens.set(id, given);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
473
495
|
}
|
|
474
496
|
this.didInitModel = true;
|
|
475
497
|
}
|
|
@@ -477,6 +499,8 @@ class Document extends MalloyElement {
|
|
|
477
499
|
const needs = this.statements.executeList(this);
|
|
478
500
|
const modelDef = this.modelDef();
|
|
479
501
|
if (needs === undefined) {
|
|
502
|
+
this.checkGivenAliasCollisions();
|
|
503
|
+
this.checkQueryGivenSatisfiability();
|
|
480
504
|
for (const q of this.queryList) {
|
|
481
505
|
if (q.modelAnnotation === undefined && modelDef.annotation) {
|
|
482
506
|
q.modelAnnotation = modelDef.annotation;
|
|
@@ -501,6 +525,82 @@ class Document extends MalloyElement {
|
|
|
501
525
|
rememberToAddModelAnnotations(sd) {
|
|
502
526
|
this.modelAnnotationTodoList.push(sd);
|
|
503
527
|
}
|
|
528
|
+
checkGivenAliasCollisions() {
|
|
529
|
+
var _a, _b;
|
|
530
|
+
const byId = new Map();
|
|
531
|
+
for (const [name, m] of this.documentModel) {
|
|
532
|
+
if (m.entry.type !== 'given')
|
|
533
|
+
continue;
|
|
534
|
+
const list = byId.get(m.entry.id);
|
|
535
|
+
if (list) {
|
|
536
|
+
list.push(name);
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
byId.set(m.entry.id, [name]);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
for (const [id, names] of byId) {
|
|
543
|
+
if (names.length < 2)
|
|
544
|
+
continue;
|
|
545
|
+
const decl = this.documentGivens.get(id);
|
|
546
|
+
const sourceName = (_a = decl === null || decl === void 0 ? void 0 : decl.name) !== null && _a !== void 0 ? _a : names[0];
|
|
547
|
+
const where = ((_b = decl === null || decl === void 0 ? void 0 : decl.location) === null || _b === void 0 ? void 0 : _b.url)
|
|
548
|
+
? ` (declared in ${decl.location.url})`
|
|
549
|
+
: '';
|
|
550
|
+
const sorted = [...names].sort();
|
|
551
|
+
this.logError('given-alias-collision', `Given \`${sourceName}\`${where} is surfaced under multiple names ` +
|
|
552
|
+
`[${sorted.join(', ')}] in this model. ` +
|
|
553
|
+
'Surfacing the same given under two names is ambiguous at supply ' +
|
|
554
|
+
'time. To expose it under a second name, declare a local given ' +
|
|
555
|
+
`with a default-chain reference: \`given: NEW_NAME :: T is $${sourceName}\`.`);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
checkQueryGivenSatisfiability() {
|
|
559
|
+
// Always runs at end-of-compile, not gated on imports — a notebook cell
|
|
560
|
+
// that calls `extendModel` with a prior modelDef inherits that model's
|
|
561
|
+
// queries and givens, and a query inherited from cell N can become
|
|
562
|
+
// unsatisfiable in cell N+1 if the satisfying given is removed (or
|
|
563
|
+
// never re-supplied). Cheap when there's nothing to check.
|
|
564
|
+
const namespaceGivens = new Set();
|
|
565
|
+
for (const m of this.documentModel.values()) {
|
|
566
|
+
if (m.entry.type === 'given')
|
|
567
|
+
namespaceGivens.add(m.entry.id);
|
|
568
|
+
}
|
|
569
|
+
const checkOne = (q, label) => {
|
|
570
|
+
var _a;
|
|
571
|
+
const usage = q.givenUsage;
|
|
572
|
+
if (!usage || usage.length === 0)
|
|
573
|
+
return;
|
|
574
|
+
// Build the full set of ids the query transitively needs: each id
|
|
575
|
+
// in Q.givenUsage, plus each id's precomputed default-chain closure
|
|
576
|
+
// (Given.givenUsage). Since the closure is already transitive, no
|
|
577
|
+
// recursion at check time.
|
|
578
|
+
const allIds = new Set();
|
|
579
|
+
for (const g of usage) {
|
|
580
|
+
allIds.add(g.id);
|
|
581
|
+
const decl = this.documentGivens.get(g.id);
|
|
582
|
+
for (const t of (_a = decl === null || decl === void 0 ? void 0 : decl.givenUsage) !== null && _a !== void 0 ? _a : [])
|
|
583
|
+
allIds.add(t.id);
|
|
584
|
+
}
|
|
585
|
+
for (const id of allIds) {
|
|
586
|
+
if (namespaceGivens.has(id))
|
|
587
|
+
continue;
|
|
588
|
+
const decl = this.documentGivens.get(id);
|
|
589
|
+
if ((decl === null || decl === void 0 ? void 0 : decl.default) !== undefined)
|
|
590
|
+
continue;
|
|
591
|
+
this.logError('unsatisfied-given-in-query', unsatisfiedGivenMessage(label, decl, id), { at: q.location });
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
// Named queries in the namespace (locally defined OR imported).
|
|
595
|
+
for (const [name, m] of this.documentModel) {
|
|
596
|
+
if (m.entry.type === 'query')
|
|
597
|
+
checkOne(m.entry, `Query '${name}'`);
|
|
598
|
+
}
|
|
599
|
+
// `run:` statements.
|
|
600
|
+
for (const q of this.queryList) {
|
|
601
|
+
checkOne(q, 'run: statement');
|
|
602
|
+
}
|
|
603
|
+
}
|
|
504
604
|
hasAnnotation() {
|
|
505
605
|
return ((this.annotation.notes && this.annotation.notes.length > 0) ||
|
|
506
606
|
this.annotation.inherits !== undefined);
|
|
@@ -536,9 +636,21 @@ class Document extends MalloyElement {
|
|
|
536
636
|
def.contents[name] = newEntry;
|
|
537
637
|
}
|
|
538
638
|
}
|
|
639
|
+
else if (entryDef.type === 'given') {
|
|
640
|
+
if (modelEntry.exported) {
|
|
641
|
+
def.exports.push(name);
|
|
642
|
+
}
|
|
643
|
+
def.contents[name] = { ...entryDef };
|
|
644
|
+
}
|
|
539
645
|
}
|
|
540
646
|
// Copy the accumulated sourceRegistry
|
|
541
647
|
def.sourceRegistry = { ...this.documentSrcRegistry };
|
|
648
|
+
if (this.documentGivens.size > 0) {
|
|
649
|
+
def.givens = {};
|
|
650
|
+
for (const [id, given] of this.documentGivens) {
|
|
651
|
+
def.givens[id] = given;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
542
654
|
return def;
|
|
543
655
|
}
|
|
544
656
|
getEntry(str) {
|