@malloydata/malloy 0.0.405 → 0.0.406

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.
@@ -161,6 +161,26 @@ class GivenDeclaration extends malloy_element_1.MalloyElement {
161
161
  operators: [...bad].sort().join(', '),
162
162
  });
163
163
  }
164
+ // An inline default may fold an unsupplied `$REF` to that
165
+ // given's declared default, so every reachable regular default
166
+ // must also be inline-reducible. The closure is flat; inline
167
+ // members self-validate at their own declaration, so only
168
+ // regular ones need checking here.
169
+ for (const g of givenUsage !== null && givenUsage !== void 0 ? givenUsage : []) {
170
+ const refDecl = doc.documentGivens.get(g.id);
171
+ if (!refDecl || refDecl.inline || refDecl.default === undefined) {
172
+ continue;
173
+ }
174
+ const refBad = new Set();
175
+ collectInlineBadOps(refDecl.default, refBad);
176
+ if (refBad.size > 0) {
177
+ this.default.logError('inline-bad-operator-in-ref', {
178
+ name: this.name,
179
+ refName: refDecl.name,
180
+ operators: [...refBad].sort().join(', '),
181
+ });
182
+ }
183
+ }
164
184
  }
165
185
  }
166
186
  }
@@ -315,6 +315,11 @@ type MessageParameterTypes = {
315
315
  name: string;
316
316
  operators: string;
317
317
  };
318
+ 'inline-bad-operator-in-ref': {
319
+ name: string;
320
+ refName: string;
321
+ operators: string;
322
+ };
318
323
  'invalid-given-modifier': {
319
324
  modifier: string;
320
325
  };
@@ -82,6 +82,7 @@ exports.MESSAGE_FORMATTERS = {
82
82
  'in-given-type-mismatch': e => `\`in\` left-hand side type \`${e.lhsType}\` does not match the array element type \`${e.elementType}\``,
83
83
  'inline-no-default': e => `inline given \`${e.name}\` must have a value — there is nothing to inline without one`,
84
84
  'inline-bad-operator': e => `inline given \`${e.name}\` uses operator(s) not allowed in inline expressions: ${e.operators}`,
85
+ 'inline-bad-operator-in-ref': e => `inline given \`${e.name}\` references \`${e.refName}\`, whose default uses operator(s) not allowed in inline expressions: ${e.operators}`,
85
86
  'invalid-given-modifier': e => `Unknown modifier \`${e.modifier}\` on \`given:\` declaration; the only modifier allowed here is \`inline\``,
86
87
  'restricted-construct-forbidden': e => ({
87
88
  message: e,
@@ -166,45 +166,37 @@ class ImportsAndTablesStep {
166
166
  return { timingInfo: parseReq.timingInfo };
167
167
  }
168
168
  let allMissing = {};
169
- // Validate each table reference against its dialect's grammar (if
170
- // the dialect is resolved) and register the canonical entry. Bad
171
- // paths are silently dropped the AST step re-validates and logs
172
- // an error at the precise source range. Entries whose dialect
173
- // isn't resolved yet are preserved unchanged and re-processed on a
174
- // later step.
175
- {
176
- const refs = this.parseReferences.tables;
177
- const canonical = {};
178
- for (const rawKey in refs) {
179
- const info = refs[rawKey];
180
- let { tablePath } = info;
181
- const dialectName = that.root.connectionDialectZone.get(info.connectionName);
182
- if (dialectName !== undefined) {
183
- const result = (0, dialect_1.getDialect)(dialectName).sqlValidateTableName(tablePath);
184
- if (!result.ok)
185
- continue;
186
- tablePath = result.canonical;
187
- }
188
- const key = `${info.connectionName}:${tablePath}`;
189
- canonical[key] = { ...info, tablePath };
190
- that.root.schemaZone.reference(key, {
191
- url: that.sourceURL,
192
- range: info.firstReference,
193
- });
194
- }
195
- this.parseReferences.tables = canonical;
169
+ // A path can only be canonicalized once its dialect is known; references
170
+ // whose dialect isn't resolved yet are skipped and re-scanned next round.
171
+ // Invalid paths are dropped here and re-reported by the AST step.
172
+ // tableRequests feeds the missing-table loop below.
173
+ const tableRequests = {};
174
+ for (const rawKey in this.parseReferences.tables) {
175
+ const info = this.parseReferences.tables[rawKey];
176
+ const dialectName = that.root.connectionDialectZone.get(info.connectionName);
177
+ if (dialectName === undefined)
178
+ continue;
179
+ const result = (0, dialect_1.getDialect)(dialectName).sqlValidateTableName(info.tablePath);
180
+ if (!result.ok)
181
+ continue;
182
+ const key = `${info.connectionName}:${result.canonical}`;
183
+ tableRequests[key] = {
184
+ connectionName: info.connectionName,
185
+ tablePath: result.canonical,
186
+ };
187
+ that.root.schemaZone.reference(key, {
188
+ url: that.sourceURL,
189
+ range: info.firstReference,
190
+ });
196
191
  }
197
192
  const missingTables = that.root.schemaZone.getUndefined();
198
193
  if (missingTables) {
199
194
  const tables = {};
200
195
  for (const key of missingTables) {
201
- const info = this.parseReferences.tables[key];
202
- if (info === undefined)
196
+ const request = tableRequests[key];
197
+ if (request === undefined)
203
198
  continue;
204
- tables[key] = {
205
- connectionName: info.connectionName,
206
- tablePath: info.tablePath,
207
- };
199
+ tables[key] = request;
208
200
  }
209
201
  if (Object.keys(tables).length > 0) {
210
202
  allMissing = { tables };
@@ -30,6 +30,7 @@ const filter_compilers_1 = require("./filter_compilers");
30
30
  const utils_1 = require("./utils");
31
31
  const query_node_1 = require("./query_node");
32
32
  const malloy_compile_error_1 = require("./malloy_compile_error");
33
+ const given_binding_1 = require("./given_binding");
33
34
  const NUMERIC_DECIMAL_PRECISION = 9;
34
35
  function sqlSumDistinct(dialect, sqlExp, sqlDistintKey) {
35
36
  const precision = 9;
@@ -628,13 +629,10 @@ function generateParameterFragment(resultSet, context, expr, state) {
628
629
  * and the `'inGiven'` SQL-emit case ($ARR in `expr in $ARR`).
629
630
  */
630
631
  function resolveGivenBoundExpr(context, ref) {
631
- var _a, _b;
632
- const supplied = (_b = (_a = context.prepareResultOptions) === null || _a === void 0 ? void 0 : _a.resolvedGivens) === null || _b === void 0 ? void 0 : _b.get(ref.id);
633
- if (supplied !== undefined)
634
- return supplied;
635
- const decl = context.getModel().givens[ref.id];
636
- if ((decl === null || decl === void 0 ? void 0 : decl.default) !== undefined)
637
- return decl.default;
632
+ var _a;
633
+ const value = (0, given_binding_1.lookupGivenValue)((_a = context.prepareResultOptions) === null || _a === void 0 ? void 0 : _a.resolvedGivens, context.getModel().givens, ref.id);
634
+ if (value !== undefined)
635
+ return value;
638
636
  throw new malloy_compile_error_1.MalloyCompileError(unsatisfiedGivenMessage(ref.refName), 'compiler-given-no-value', ref.at);
639
637
  }
640
638
  function generateGivenFragment(resultSet, context, expr, state) {
@@ -1,4 +1,13 @@
1
- import type { Expr, GivenID, GivenValue, ModelDef } from './malloy_types';
1
+ import type { Expr, Given, GivenID, GivenValue, ModelDef } from './malloy_types';
2
+ /**
3
+ * The one definition of "what value does a given reference stand for":
4
+ * the caller-supplied value if one is bound, otherwise the declaration
5
+ * default. Returns undefined when neither exists — callers decide how to
6
+ * report that, since the right diagnostic differs by site (a bind-time
7
+ * inline fold vs. SQL emission). Shared by `evaluateInlineGivens` here
8
+ * and `resolveGivenBoundExpr` in the compiler.
9
+ */
10
+ export declare function lookupGivenValue(bound: Map<GivenID, Expr> | undefined, givens: Record<GivenID, Given> | undefined, id: GivenID): Expr | undefined;
2
11
  export declare function resolveSuppliedGivens(supplied: Record<string, GivenValue> | undefined, modelDef: ModelDef | undefined): Map<GivenID, Expr>;
3
12
  /**
4
13
  * Evaluate every `inline` given's default to a literal Expr and add it
@@ -10,8 +19,11 @@ export declare function resolveSuppliedGivens(supplied: Record<string, GivenValu
10
19
  * `inline-no-default` error at declaration time.
11
20
  *
12
21
  * Iteration follows `modelDef.contents` insertion order, which (by
13
- * Malloy's no-forward-refs rule) is also topological order: if inline
14
- * A's default references inline B, B's declaration came first and is
15
- * already in the map by the time A runs.
22
+ * Malloy's no-forward-refs rule) is also topological order. An inline
23
+ * default that references another inline given relies on this: the
24
+ * referenced one was declared first, so it is already folded into
25
+ * `bound` by the time this one reads it. A reference to a regular given
26
+ * resolves straight to its supplied value or declaration default (see
27
+ * `resolveGiven`), which needs no ordering.
16
28
  */
17
29
  export declare function evaluateInlineGivens(bound: Map<GivenID, Expr>, modelDef: ModelDef | undefined): Map<GivenID, Expr>;
@@ -4,6 +4,7 @@
4
4
  * SPDX-License-Identifier: MIT
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.lookupGivenValue = lookupGivenValue;
7
8
  exports.resolveSuppliedGivens = resolveSuppliedGivens;
8
9
  exports.evaluateInlineGivens = evaluateInlineGivens;
9
10
  const luxon_1 = require("luxon");
@@ -11,6 +12,18 @@ const closest_match_1 = require("../util/closest_match");
11
12
  const inline_expr_1 = require("./inline_expr");
12
13
  const malloy_types_1 = require("./malloy_types");
13
14
  const malloy_compile_error_1 = require("./malloy_compile_error");
15
+ /**
16
+ * The one definition of "what value does a given reference stand for":
17
+ * the caller-supplied value if one is bound, otherwise the declaration
18
+ * default. Returns undefined when neither exists — callers decide how to
19
+ * report that, since the right diagnostic differs by site (a bind-time
20
+ * inline fold vs. SQL emission). Shared by `evaluateInlineGivens` here
21
+ * and `resolveGivenBoundExpr` in the compiler.
22
+ */
23
+ function lookupGivenValue(bound, givens, id) {
24
+ var _a, _b;
25
+ return (_a = bound === null || bound === void 0 ? void 0 : bound.get(id)) !== null && _a !== void 0 ? _a : (_b = givens === null || givens === void 0 ? void 0 : givens[id]) === null || _b === void 0 ? void 0 : _b.default;
26
+ }
14
27
  function resolveSuppliedGivens(supplied, modelDef) {
15
28
  var _a;
16
29
  const out = new Map();
@@ -57,15 +70,28 @@ function resolveSuppliedGivens(supplied, modelDef) {
57
70
  * `inline-no-default` error at declaration time.
58
71
  *
59
72
  * Iteration follows `modelDef.contents` insertion order, which (by
60
- * Malloy's no-forward-refs rule) is also topological order: if inline
61
- * A's default references inline B, B's declaration came first and is
62
- * already in the map by the time A runs.
73
+ * Malloy's no-forward-refs rule) is also topological order. An inline
74
+ * default that references another inline given relies on this: the
75
+ * referenced one was declared first, so it is already folded into
76
+ * `bound` by the time this one reads it. A reference to a regular given
77
+ * resolves straight to its supplied value or declaration default (see
78
+ * `resolveGiven`), which needs no ordering.
63
79
  */
64
80
  function evaluateInlineGivens(bound, modelDef) {
65
81
  var _a;
66
82
  if (!modelDef)
67
83
  return bound;
68
84
  const givens = (_a = modelDef.givens) !== null && _a !== void 0 ? _a : {};
85
+ // A `$REF` in an inline default resolves to its supplied value, else
86
+ // its declaration default; a reference with neither is a caller error.
87
+ // `bound` is read live, so a given folded earlier in the loop is
88
+ // visible to one folded later.
89
+ const resolveGiven = (id, refName) => {
90
+ const v = lookupGivenValue(bound, givens, id);
91
+ if (v !== undefined)
92
+ return v;
93
+ throw new malloy_compile_error_1.MalloyCompileError(`Inline given depends on '${refName}', which has no supplied value and no default. Supply a value for '${refName}' or give it a declaration default.`, 'runtime-given-unsatisfied-inline', undefined);
94
+ };
69
95
  for (const [, entry] of Object.entries(modelDef.contents)) {
70
96
  if (entry.type !== 'given')
71
97
  continue;
@@ -76,7 +102,7 @@ function evaluateInlineGivens(bound, modelDef) {
76
102
  continue;
77
103
  if (decl.default === undefined)
78
104
  continue;
79
- bound.set(entry.id, (0, inline_expr_1.inlineExpr)(decl.default, bound));
105
+ bound.set(entry.id, (0, inline_expr_1.inlineExpr)(decl.default, resolveGiven));
80
106
  }
81
107
  return bound;
82
108
  }
@@ -18,13 +18,19 @@ export declare const INLINE_OPS: ReadonlySet<string>;
18
18
  */
19
19
  export declare const INLINE_LEAVES: ReadonlySet<string>;
20
20
  /**
21
- * Bind-time evaluator for `inline` given defaults. Walks the Expr tree,
22
- * recursing on bound values for given-refs, and returns a literal Expr
23
- * (string/number/boolean/null/arrayLiteral).
21
+ * Bind-time evaluator for `inline` given defaults. Walks the Expr tree
22
+ * and returns a literal Expr (string/number/boolean/null/arrayLiteral).
23
+ *
24
+ * A pure folder: it does not know how given references get their values.
25
+ * `resolveGiven` is handed a given reference's id and surface name (both
26
+ * a `$NAME` ref and the array of an `expr in $NAME`) and returns the Expr
27
+ * that reference stands for — caller-supplied value, declaration default,
28
+ * or a thrown error if neither. That policy lives with the caller
29
+ * (`given_binding.ts`), not here — see `lookupGivenValue`.
24
30
  *
25
31
  * Throws on any node outside `INLINE_OPS ∪ INLINE_LEAVES`. The
26
32
  * translator's pre-flight check should have rejected such defaults
27
33
  * already, so a throw here flags a compiler bug rather than a caller
28
34
  * error.
29
35
  */
30
- export declare function inlineExpr(e: Expr, bound: Map<GivenID, Expr>): Expr;
36
+ export declare function inlineExpr(e: Expr, resolveGiven: (id: GivenID, refName: string) => Expr): Expr;
@@ -45,16 +45,22 @@ exports.INLINE_LEAVES = new Set([
45
45
  'given',
46
46
  ]);
47
47
  /**
48
- * Bind-time evaluator for `inline` given defaults. Walks the Expr tree,
49
- * recursing on bound values for given-refs, and returns a literal Expr
50
- * (string/number/boolean/null/arrayLiteral).
48
+ * Bind-time evaluator for `inline` given defaults. Walks the Expr tree
49
+ * and returns a literal Expr (string/number/boolean/null/arrayLiteral).
50
+ *
51
+ * A pure folder: it does not know how given references get their values.
52
+ * `resolveGiven` is handed a given reference's id and surface name (both
53
+ * a `$NAME` ref and the array of an `expr in $NAME`) and returns the Expr
54
+ * that reference stands for — caller-supplied value, declaration default,
55
+ * or a thrown error if neither. That policy lives with the caller
56
+ * (`given_binding.ts`), not here — see `lookupGivenValue`.
51
57
  *
52
58
  * Throws on any node outside `INLINE_OPS ∪ INLINE_LEAVES`. The
53
59
  * translator's pre-flight check should have rejected such defaults
54
60
  * already, so a throw here flags a compiler bug rather than a caller
55
61
  * error.
56
62
  */
57
- function inlineExpr(e, bound) {
63
+ function inlineExpr(e, resolveGiven) {
58
64
  switch (e.node) {
59
65
  case 'stringLiteral':
60
66
  case 'numberLiteral':
@@ -63,23 +69,18 @@ function inlineExpr(e, bound) {
63
69
  case 'null':
64
70
  case 'arrayLiteral':
65
71
  return e;
66
- case 'given': {
67
- const v = bound.get(e.id);
68
- if (v === undefined) {
69
- throw new Error(`inlineExpr: given '${e.refName}' has no bound value and no default — translator should have caught this earlier`);
70
- }
71
- return inlineExpr(v, bound);
72
- }
72
+ case 'given':
73
+ return inlineExpr(resolveGiven(e.id, e.refName), resolveGiven);
73
74
  case '()':
74
- return inlineExpr(e.e, bound);
75
+ return inlineExpr(e.e, resolveGiven);
75
76
  case 'not': {
76
- const inner = inlineExpr(e.e, bound);
77
+ const inner = inlineExpr(e.e, resolveGiven);
77
78
  return toBoolLiteral(!exprAsBool(inner));
78
79
  }
79
80
  case 'and':
80
81
  case 'or': {
81
- const left = exprAsBool(inlineExpr(e.kids.left, bound));
82
- const right = exprAsBool(inlineExpr(e.kids.right, bound));
82
+ const left = exprAsBool(inlineExpr(e.kids.left, resolveGiven));
83
+ const right = exprAsBool(inlineExpr(e.kids.right, resolveGiven));
83
84
  return toBoolLiteral(e.node === 'and' ? left && right : left || right);
84
85
  }
85
86
  case '=':
@@ -88,23 +89,20 @@ function inlineExpr(e, bound) {
88
89
  case '<':
89
90
  case '>=':
90
91
  case '<=': {
91
- const left = inlineExpr(e.kids.left, bound);
92
- const right = inlineExpr(e.kids.right, bound);
92
+ const left = inlineExpr(e.kids.left, resolveGiven);
93
+ const right = inlineExpr(e.kids.right, resolveGiven);
93
94
  return toBoolLiteral(compareLiterals(e.node, left, right));
94
95
  }
95
96
  case 'inGiven': {
96
- const lhs = inlineExpr(e.e, bound);
97
- const arrBound = bound.get(e.givenRef.id);
98
- if (arrBound === undefined) {
99
- throw new Error(`inlineExpr: given '${e.givenRef.refName}' has no bound value and no default — translator should have caught this earlier`);
100
- }
101
- if (arrBound.node === 'null') {
97
+ const lhs = inlineExpr(e.e, resolveGiven);
98
+ const arr = inlineExpr(resolveGiven(e.givenRef.id, e.givenRef.refName), resolveGiven);
99
+ if (arr.node === 'null') {
102
100
  return toBoolLiteral(e.not);
103
101
  }
104
- if (arrBound.node !== 'arrayLiteral') {
105
- throw new Error(`inlineExpr: 'inGiven' bound to '${arrBound.node}', expected 'arrayLiteral'`);
102
+ if (arr.node !== 'arrayLiteral') {
103
+ throw new Error(`inlineExpr: 'inGiven' resolved to '${arr.node}', expected 'arrayLiteral'`);
106
104
  }
107
- const found = arrBound.kids.values.some(v => compareLiterals('=', lhs, inlineExpr(v, bound)));
105
+ const found = arr.kids.values.some(v => compareLiterals('=', lhs, inlineExpr(v, resolveGiven)));
108
106
  return toBoolLiteral(e.not ? !found : found);
109
107
  }
110
108
  default:
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const MALLOY_VERSION = "0.0.405";
1
+ export declare const MALLOY_VERSION = "0.0.406";
package/dist/version.js CHANGED
@@ -2,5 +2,5 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MALLOY_VERSION = void 0;
4
4
  // generated with 'generate-version-file' script; do not edit manually
5
- exports.MALLOY_VERSION = '0.0.405';
5
+ exports.MALLOY_VERSION = '0.0.406';
6
6
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy",
3
- "version": "0.0.405",
3
+ "version": "0.0.406",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./dist/index.js",
@@ -51,9 +51,9 @@
51
51
  "generate-version-file": "VERSION=$(npm pkg get version --workspaces=false | tr -d \\\")\necho \"// generated with 'generate-version-file' script; do not edit manually\\nexport const MALLOY_VERSION = '$VERSION';\" > src/version.ts"
52
52
  },
53
53
  "dependencies": {
54
- "@malloydata/malloy-filter": "0.0.405",
55
- "@malloydata/malloy-interfaces": "0.0.405",
56
- "@malloydata/malloy-tag": "0.0.405",
54
+ "@malloydata/malloy-filter": "0.0.406",
55
+ "@malloydata/malloy-interfaces": "0.0.406",
56
+ "@malloydata/malloy-tag": "0.0.406",
57
57
  "@noble/hashes": "^1.8.0",
58
58
  "antlr4ts": "^0.5.0-alpha.4",
59
59
  "assert": "^2.0.0",