@malloydata/malloy 0.0.395 → 0.0.396

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.
Files changed (44) hide show
  1. package/dist/api/foundation/compile.d.ts +7 -6
  2. package/dist/api/foundation/compile.js +22 -6
  3. package/dist/api/foundation/runtime.d.ts +85 -5
  4. package/dist/api/foundation/runtime.js +183 -13
  5. package/dist/api/foundation/types.d.ts +2 -0
  6. package/dist/lang/ast/expressions/expr-func.js +30 -11
  7. package/dist/lang/ast/expressions/expr-given.js +1 -0
  8. package/dist/lang/ast/field-space/reference-field.js +1 -1
  9. package/dist/lang/ast/source-elements/sql-source.js +4 -0
  10. package/dist/lang/ast/source-elements/table-source.js +4 -0
  11. package/dist/lang/ast/statements/define-given.d.ts +1 -0
  12. package/dist/lang/ast/statements/define-given.js +7 -0
  13. package/dist/lang/ast/statements/import-statement.js +4 -0
  14. package/dist/lang/ast/types/annotation-elements.d.ts +1 -0
  15. package/dist/lang/ast/types/annotation-elements.js +10 -3
  16. package/dist/lang/ast/types/malloy-element.d.ts +1 -0
  17. package/dist/lang/ast/types/malloy-element.js +4 -0
  18. package/dist/lang/malloy-to-ast.d.ts +2 -1
  19. package/dist/lang/malloy-to-ast.js +11 -1
  20. package/dist/lang/parse-log.d.ts +1 -0
  21. package/dist/lang/parse-log.js +4 -0
  22. package/dist/lang/parse-malloy.d.ts +4 -1
  23. package/dist/lang/parse-malloy.js +26 -4
  24. package/dist/lang/test/test-translator.d.ts +19 -5
  25. package/dist/lang/test/test-translator.js +15 -12
  26. package/dist/lang/zone.d.ts +2 -0
  27. package/dist/lang/zone.js +10 -0
  28. package/dist/model/constant_expression_compiler.js +14 -5
  29. package/dist/model/expression_compiler.js +19 -17
  30. package/dist/model/field_instance.js +7 -3
  31. package/dist/model/given_binding.js +26 -21
  32. package/dist/model/index.d.ts +1 -0
  33. package/dist/model/index.js +3 -1
  34. package/dist/model/malloy_compile_error.d.ts +13 -0
  35. package/dist/model/malloy_compile_error.js +23 -0
  36. package/dist/model/malloy_types.d.ts +2 -0
  37. package/dist/model/query_model_impl.js +2 -1
  38. package/dist/model/query_node.d.ts +5 -5
  39. package/dist/model/query_node.js +21 -16
  40. package/dist/model/query_query.js +23 -11
  41. package/dist/model/sql_compiled.js +6 -3
  42. package/dist/version.d.ts +1 -1
  43. package/dist/version.js +1 -1
  44. package/package.json +4 -4
@@ -41,12 +41,19 @@ class ModelAnnotation extends ObjectAnnotation {
41
41
  super(...arguments);
42
42
  this.elementType = 'modelAnnotation';
43
43
  }
44
+ getCompilerFlagNotes() {
45
+ return this.notes.filter(note => note.text.match(COMPILER_FLAG_PREFIX));
46
+ }
44
47
  getCompilerFlagLines() {
45
- return this.notes
46
- .filter(note => note.text.match(COMPILER_FLAG_PREFIX))
47
- .map(note => note.text);
48
+ return this.getCompilerFlagNotes().map(note => note.text);
48
49
  }
49
50
  execute(doc) {
51
+ if (this.isRestricted()) {
52
+ for (const note of this.getCompilerFlagNotes()) {
53
+ const line = note.text.replace(/\n$/, '');
54
+ this.logError('restricted-construct-forbidden', `\`${line}\` cannot be used in a restricted query — compiler-flag annotations are not permitted.`, { at: note.at });
55
+ }
56
+ }
50
57
  if (doc.annotation.notes === undefined) {
51
58
  doc.annotation.notes = [];
52
59
  }
@@ -37,6 +37,7 @@ export declare abstract class MalloyElement {
37
37
  */
38
38
  kupuna(): MalloyElement;
39
39
  translator(): MalloyTranslation | undefined;
40
+ isRestricted(): boolean;
40
41
  setTranslator(x: MalloyTranslation): void;
41
42
  addReference(reference: DocumentReference): void;
42
43
  private get sourceURL();
@@ -158,6 +158,10 @@ class MalloyElement {
158
158
  }
159
159
  return undefined;
160
160
  }
161
+ isRestricted() {
162
+ var _a, _b;
163
+ return (_b = (_a = this.translator()) === null || _a === void 0 ? void 0 : _a.root.restrictedMode) !== null && _b !== void 0 ? _b : false;
164
+ }
161
165
  setTranslator(x) {
162
166
  this.xlate = x;
163
167
  }
@@ -35,10 +35,11 @@ type HasAnnotations = ParserRuleContext & {
35
35
  export declare class MalloyToAST extends AbstractParseTreeVisitor<ast.MalloyElement> implements MalloyParserVisitor<ast.MalloyElement> {
36
36
  readonly parseInfo: MalloyParseInfo;
37
37
  readonly msgLog: MessageLogger;
38
+ readonly restrictedMode: boolean;
38
39
  readonly timer: Timer;
39
40
  private compilerFlagSrc;
40
41
  private compilerFlagTag?;
41
- constructor(parseInfo: MalloyParseInfo, msgLog: MessageLogger, compilerFlagSrc: string[]);
42
+ constructor(parseInfo: MalloyParseInfo, msgLog: MessageLogger, compilerFlagSrc: string[], restrictedMode?: boolean);
42
43
  getCompilerFlags(): Tag;
43
44
  run(): {
44
45
  ast: ast.MalloyElement;
@@ -92,10 +92,11 @@ const LEGAL_FILTER_TYPES = 'string, number, boolean, date, timestamp, timestampt
92
92
  * AST from an ANTLR parse tree.
93
93
  */
94
94
  class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
95
- constructor(parseInfo, msgLog, compilerFlagSrc) {
95
+ constructor(parseInfo, msgLog, compilerFlagSrc, restrictedMode = false) {
96
96
  super();
97
97
  this.parseInfo = parseInfo;
98
98
  this.msgLog = msgLog;
99
+ this.restrictedMode = restrictedMode;
99
100
  this.timer = new timing_1.Timer('generate_ast');
100
101
  const parseCompilerFlagsTimer = new timing_1.Timer('parse_compiler_flags');
101
102
  this.compilerFlagSrc = [...DEFAULT_COMPILER_FLAGS, ...compilerFlagSrc];
@@ -1498,6 +1499,15 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
1498
1499
  }
1499
1500
  updateCompilerFlags(tags) {
1500
1501
  const parseCompilerFlagsTimer = new timing_1.Timer('parse_compiler_flags');
1502
+ if (this.restrictedMode) {
1503
+ // `##!` lines in restricted text never become active flags. The
1504
+ // user-facing rejection is logged at execute time (see
1505
+ // ModelAnnotation.execute) so it doesn't short-circuit ASTStep,
1506
+ // which lets every other restricted-mode violation in the same
1507
+ // compile log its own diagnostic.
1508
+ this.timer.contribute([parseCompilerFlagsTimer.stop()]);
1509
+ return;
1510
+ }
1501
1511
  const newLines = tags.getCompilerFlagLines();
1502
1512
  if (newLines.length > 0) {
1503
1513
  const oldLength = this.compilerFlagSrc.length;
@@ -430,6 +430,7 @@ type MessageParameterTypes = {
430
430
  'missing-required-group-by': string;
431
431
  'invalid-partition-composite': string;
432
432
  'integer-literal-out-of-range': string;
433
+ 'restricted-construct-forbidden': string;
433
434
  };
434
435
  export declare const MESSAGE_FORMATTERS: PartialErrorCodeMessageMap;
435
436
  export type MessageCode = keyof MessageParameterTypes;
@@ -101,6 +101,10 @@ exports.MESSAGE_FORMATTERS = {
101
101
  'inline-no-default': e => `inline given \`${e.name}\` must have a value — there is nothing to inline without one`,
102
102
  'inline-bad-operator': e => `inline given \`${e.name}\` uses operator(s) not allowed in inline expressions: ${e.operators}`,
103
103
  'invalid-given-modifier': e => `Unknown modifier \`${e.modifier}\` on \`given:\` declaration; the only modifier allowed here is \`inline\``,
104
+ 'restricted-construct-forbidden': e => ({
105
+ message: e,
106
+ tag: 'restricted-mode',
107
+ }),
104
108
  };
105
109
  function makeLogMessage(code, parameters, options) {
106
110
  var _a, _b, _c, _d, _e;
@@ -193,6 +193,7 @@ export declare class MalloyChildTranslator extends MalloyTranslation {
193
193
  */
194
194
  export declare class MalloyTranslator extends MalloyTranslation {
195
195
  private readonly eventStream;
196
+ readonly restrictedMode: boolean;
196
197
  schemaZone: Zone<SourceDef>;
197
198
  importZone: Zone<string>;
198
199
  pretranslatedModels: Map<string, ModelDef>;
@@ -200,8 +201,10 @@ export declare class MalloyTranslator extends MalloyTranslation {
200
201
  connectionDialectZone: Zone<string>;
201
202
  logger: BaseMessageLogger;
202
203
  readonly root: MalloyTranslator;
203
- constructor(rootURL: string, importURL?: string | null, preload?: ParseUpdate | null, eventStream?: EventStream | null);
204
+ constructor(rootURL: string, importURL?: string | null, preload?: ParseUpdate | null, eventStream?: EventStream | null, restrictedMode?: boolean);
204
205
  update(dd: ParseUpdate): void;
206
+ private lockZonesIfRestricted;
207
+ translate(extendingModel?: ModelDef): TranslateResponse;
205
208
  logError<T extends MessageCode>(code: T, parameters: MessageParameterType<T>, options?: Omit<LogMessageOptions, 'severity'>): T;
206
209
  }
207
210
  interface ErrorData {
@@ -139,6 +139,12 @@ class ImportsAndTablesStep {
139
139
  if (parseReq.parse === undefined) {
140
140
  return parseReq;
141
141
  }
142
+ // Every reference this step would register — connection dialects,
143
+ // imports, table schemas — is for a construct that's forbidden in
144
+ // restricted code.
145
+ if (that.root.restrictedMode) {
146
+ return { timingInfo: parseReq.timingInfo };
147
+ }
142
148
  if (!this.parseReferences) {
143
149
  this.parseReferences = (0, find_external_references_1.findReferences)(that, parseReq.parse.tokenStream, parseReq.parse.root);
144
150
  // Register connection dialects and imports immediately. Table
@@ -278,7 +284,7 @@ class ASTStep {
278
284
  throw new Error('TRANSLATOR INTERNAL ERROR: Translator parse response had no errors, but also no parser');
279
285
  }
280
286
  stepTimer.incorporate(parseResponse.timingInfo);
281
- const secondPass = new malloy_to_ast_1.MalloyToAST(parse, that.root.logger, that.compilerFlagSrc);
287
+ const secondPass = new malloy_to_ast_1.MalloyToAST(parse, that.root.logger, that.compilerFlagSrc, that.root.restrictedMode);
282
288
  const { ast: newAST, compilerFlagSrc, timingInfo } = secondPass.run();
283
289
  stepTimer.contribute([timingInfo]);
284
290
  that.compilerFlagSrc = compilerFlagSrc;
@@ -466,10 +472,13 @@ class TranslateStep {
466
472
  modelWasModified: false,
467
473
  };
468
474
  }
469
- // begin with the compiler flags of the model we are extending
475
+ // Layer the extending model's compiler flags on top of whatever was
476
+ // there already. In production the array starts empty so push vs.
477
+ // overwrite produce the same result; the push lets constructor-time
478
+ // seeding (e.g. TestTranslator's compilerFlags option) survive.
470
479
  if (extendingModel && !this.importedAnnotations) {
471
480
  const parseCompilerFlagsTimer = new timing_1.Timer('parse_compiler_flags');
472
- that.compilerFlagSrc = (0, annotation_1.annotationToTaglines)(extendingModel.annotation, /^##! /);
481
+ that.compilerFlagSrc.push(...(0, annotation_1.annotationToTaglines)(extendingModel.annotation, /^##! /));
473
482
  stepTimer.contribute([parseCompilerFlagsTimer.stop()]);
474
483
  this.importedAnnotations = true;
475
484
  }
@@ -821,9 +830,10 @@ exports.MalloyChildTranslator = MalloyChildTranslator;
821
830
  * no need to call again, the translation is finished or error'd.
822
831
  */
823
832
  class MalloyTranslator extends MalloyTranslation {
824
- constructor(rootURL, importURL = null, preload = null, eventStream = null) {
833
+ constructor(rootURL, importURL = null, preload = null, eventStream = null, restrictedMode = false) {
825
834
  super(rootURL, importURL);
826
835
  this.eventStream = eventStream;
836
+ this.restrictedMode = restrictedMode;
827
837
  this.schemaZone = new zone_1.Zone();
828
838
  this.importZone = new zone_1.Zone();
829
839
  this.pretranslatedModels = new Map();
@@ -845,6 +855,18 @@ class MalloyTranslator extends MalloyTranslation {
845
855
  this.pretranslatedModels.set(url, dd.translations[url]);
846
856
  }
847
857
  }
858
+ lockZonesIfRestricted() {
859
+ if (!this.restrictedMode)
860
+ return;
861
+ this.schemaZone.lock();
862
+ this.importZone.lock();
863
+ this.sqlQueryZone.lock();
864
+ this.connectionDialectZone.lock();
865
+ }
866
+ translate(extendingModel) {
867
+ this.lockZonesIfRestricted();
868
+ return super.translate(extendingModel);
869
+ }
848
870
  logError(code, parameters, options) {
849
871
  this.logger.log((0, parse_log_1.makeLogMessage)(code, parameters, { severity: 'error', ...options }));
850
872
  return code;
@@ -42,12 +42,27 @@ export declare class TestChildTranslator extends MalloyChildTranslator {
42
42
  translate(): TranslateResponse;
43
43
  addChild(url: string): void;
44
44
  }
45
+ export interface TestTranslatorOptions {
46
+ rootRule?: string;
47
+ importBaseURL?: string | null;
48
+ eventStream?: EventStream | null;
49
+ internalModel?: ModelDef;
50
+ restrictedMode?: boolean;
51
+ /**
52
+ * Each entry is a compiler-flag tag fragment — the content that
53
+ * would follow `##! ` in a Malloy source annotation. Rendered to
54
+ * `##! <flag>\n` and pushed onto compilerFlagSrc before any
55
+ * TranslateStep seeding, so the flags are active during AST-build
56
+ * inExperiment() checks.
57
+ */
58
+ compilerFlags?: string[];
59
+ }
45
60
  export declare class TestTranslator extends MalloyTranslator {
46
61
  readonly testSrc: string;
47
62
  allDialectsEnabled: boolean;
48
63
  testRoot?: TestRoot;
49
64
  internalModel: ModelDef;
50
- constructor(testSrc: string, importBaseURL?: string | null, eventStream?: EventStream | null, rootRule?: string, internalModel?: ModelDef);
65
+ constructor(testSrc: string, options?: TestTranslatorOptions);
51
66
  translate(): TranslateResponse;
52
67
  addChild(url: string): void;
53
68
  ast(): MalloyElement | undefined;
@@ -64,7 +79,7 @@ export declare class TestTranslator extends MalloyTranslator {
64
79
  export declare class BetaExpression extends TestTranslator {
65
80
  readonly sourceName: string;
66
81
  private compiled?;
67
- constructor(src: string, model?: ModelDef, sourceName?: string);
82
+ constructor(src: string, model?: ModelDef, sourceName?: string, options?: Omit<TestTranslatorOptions, 'rootRule' | 'internalModel'>);
68
83
  private testFS;
69
84
  compile(): void;
70
85
  generated(): ExprValue;
@@ -85,12 +100,11 @@ interface HasTranslator<TT extends TestTranslator> extends MarkedSource {
85
100
  }
86
101
  export declare function expr(unmarked: TemplateStringsArray, ...marked: string[]): HasTranslator<BetaExpression>;
87
102
  export declare function model(unmarked: TemplateStringsArray, ...marked: string[]): HasTranslator<TestTranslator>;
88
- export declare function makeModelFunc(options: {
89
- model?: ModelDef;
103
+ export declare function makeModelFunc(options: TestTranslatorOptions & {
90
104
  prefix?: string;
91
105
  wrap?: (code: string) => string;
92
106
  }): (unmarked: TemplateStringsArray, ...marked: string[]) => HasTranslator<TestTranslator>;
93
- export declare function makeExprFunc(model: ModelDef, sourceName: string): (unmarked: TemplateStringsArray, ...marked: string[]) => HasTranslator<TestTranslator>;
107
+ export declare function makeExprFunc(model?: ModelDef, sourceName?: string, options?: Omit<TestTranslatorOptions, 'rootRule' | 'internalModel'>): (unmarked: TemplateStringsArray, ...marked: string[]) => HasTranslator<TestTranslator>;
94
108
  export declare function markSource(unmarked: TemplateStringsArray, ...marked: string[]): MarkedSource;
95
109
  export declare function getSelectOneStruct(sqlBlock: SQLSourceRequest): {
96
110
  [key: string]: SQLSourceDef;
@@ -330,8 +330,9 @@ class TestChildTranslator extends parse_malloy_1.MalloyChildTranslator {
330
330
  exports.TestChildTranslator = TestChildTranslator;
331
331
  const testURI = 'internal://test/langtests/root.malloy';
332
332
  class TestTranslator extends parse_malloy_1.MalloyTranslator {
333
- constructor(testSrc, importBaseURL = null, eventStream = null, rootRule = 'malloyDocument', internalModel) {
334
- super(testURI, importBaseURL, null, eventStream);
333
+ constructor(testSrc, options = {}) {
334
+ var _a, _b, _c, _d, _e;
335
+ super(testURI, (_a = options.importBaseURL) !== null && _a !== void 0 ? _a : null, null, (_b = options.eventStream) !== null && _b !== void 0 ? _b : null, (_c = options.restrictedMode) !== null && _c !== void 0 ? _c : false);
335
336
  this.testSrc = testSrc;
336
337
  this.allDialectsEnabled = true;
337
338
  /*
@@ -415,16 +416,19 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
415
416
  },
416
417
  },
417
418
  };
418
- this.grammarRule = rootRule;
419
+ this.grammarRule = (_d = options.rootRule) !== null && _d !== void 0 ? _d : 'malloyDocument';
419
420
  this.importZone.define(testURI, testSrc);
420
- if (internalModel !== undefined) {
421
- this.internalModel = internalModel;
421
+ if (options.internalModel !== undefined) {
422
+ this.internalModel = options.internalModel;
422
423
  }
423
424
  for (const actualSchema of exports.mockSchema) {
424
425
  this.schemaZone.define(`${actualSchema.connection}:${actualSchema.tablePath}`, actualSchema);
425
426
  }
426
427
  this.connectionDialectZone.define('_db_', exports.TEST_DIALECT);
427
428
  this.connectionDialectZone.define('_bq_', 'standardsql');
429
+ for (const flag of (_e = options.compilerFlags) !== null && _e !== void 0 ? _e : []) {
430
+ this.compilerFlagSrc.push(`##! ${flag}\n`);
431
+ }
428
432
  }
429
433
  translate() {
430
434
  return super.translate(this.internalModel);
@@ -532,8 +536,8 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
532
536
  exports.TestTranslator = TestTranslator;
533
537
  TestTranslator.inspectCompile = false;
534
538
  class BetaExpression extends TestTranslator {
535
- constructor(src, model, sourceName = 'ab') {
536
- super(src, null, null, 'debugExpr', model);
539
+ constructor(src, model, sourceName = 'ab', options = {}) {
540
+ super(src, { ...options, rootRule: 'debugExpr', internalModel: model });
537
541
  this.sourceName = sourceName;
538
542
  }
539
543
  testFS() {
@@ -627,21 +631,20 @@ function model(unmarked, ...marked) {
627
631
  }
628
632
  function makeModelFunc(options) {
629
633
  return function model(unmarked, ...marked) {
630
- var _a;
631
634
  const ms = markSource(unmarked, ...marked);
635
+ const { prefix, wrap, ...ttOptions } = options;
632
636
  return {
633
637
  ...ms,
634
- translator: new TestTranslator(((_a = options.prefix) !== null && _a !== void 0 ? _a : '') +
635
- (options.wrap ? options.wrap(ms.code) : ms.code), null, null, undefined, options === null || options === void 0 ? void 0 : options.model),
638
+ translator: new TestTranslator((prefix !== null && prefix !== void 0 ? prefix : '') + (wrap ? wrap(ms.code) : ms.code), ttOptions),
636
639
  };
637
640
  };
638
641
  }
639
- function makeExprFunc(model, sourceName) {
642
+ function makeExprFunc(model, sourceName = 'ab', options = {}) {
640
643
  return function expr(unmarked, ...marked) {
641
644
  const ms = markSource(unmarked, ...marked);
642
645
  return {
643
646
  ...ms,
644
- translator: new BetaExpression(ms.code, model, sourceName),
647
+ translator: new BetaExpression(ms.code, model, sourceName, options),
645
648
  };
646
649
  };
647
650
  }
@@ -28,7 +28,9 @@ export type ZoneEntry<T> = EntryPresent<T> | ReferenceEntry | EntryErrored;
28
28
  export declare class Zone<TValue> {
29
29
  zone: Map<string, ZoneEntry<TValue>>;
30
30
  location: Record<string, DocumentLocation>;
31
+ private locked;
31
32
  constructor();
33
+ lock(): void;
32
34
  get(str: string): TValue | undefined;
33
35
  getEntry(str: string): ZoneEntry<TValue>;
34
36
  /**
package/dist/lang/zone.js CHANGED
@@ -33,8 +33,12 @@ exports.Zone = void 0;
33
33
  class Zone {
34
34
  constructor() {
35
35
  this.location = {};
36
+ this.locked = false;
36
37
  this.zone = new Map();
37
38
  }
39
+ lock() {
40
+ this.locked = true;
41
+ }
38
42
  get(str) {
39
43
  const zst = this.zone.get(str);
40
44
  if ((zst === null || zst === void 0 ? void 0 : zst.status) === 'present') {
@@ -57,6 +61,8 @@ class Zone {
57
61
  * @param val
58
62
  */
59
63
  define(str, val) {
64
+ if (this.locked)
65
+ return;
60
66
  this.zone.set(str, { status: 'present', value: val });
61
67
  }
62
68
  /**
@@ -65,6 +71,8 @@ class Zone {
65
71
  * @param loc The location of the reference
66
72
  */
67
73
  reference(str, loc) {
74
+ if (this.locked)
75
+ return;
68
76
  const zst = this.zone.get(str);
69
77
  if ((zst === null || zst === void 0 ? void 0 : zst.status) === undefined) {
70
78
  this.zone.set(str, { status: 'reference', firstReference: loc });
@@ -89,6 +97,8 @@ class Zone {
89
97
  * @param errorData Pass on errors encountered during fetch
90
98
  */
91
99
  updateFrom(updateData, errorData) {
100
+ if (this.locked)
101
+ return;
92
102
  if (updateData) {
93
103
  for (const [updateKey, updateVal] of Object.entries(updateData)) {
94
104
  if (updateVal !== undefined) {
@@ -14,11 +14,20 @@ const field_instance_1 = require("./field_instance");
14
14
  * Used to distinguish expected errors from unexpected ones.
15
15
  */
16
16
  class ConstantExpressionError extends Error {
17
- constructor(message) {
17
+ constructor(message, at) {
18
18
  super(message);
19
+ this.at = at;
19
20
  this.name = 'ConstantExpressionError';
20
21
  }
21
22
  }
23
+ function locSuffix(at) {
24
+ if (!at)
25
+ return '';
26
+ // Display 1-based line/column; range positions are 0-based.
27
+ const line = at.range.start.line + 1;
28
+ const col = at.range.start.character + 1;
29
+ return ` at ${at.url}:${line}:${col}`;
30
+ }
22
31
  /**
23
32
  * Minimal FieldInstanceResultRoot for constant expressions.
24
33
  * This serves as both the result set and its own root, providing
@@ -106,11 +115,11 @@ class ConstantQueryStruct extends query_node_1.QueryStruct {
106
115
  /**
107
116
  * These methods should not be called for constant expressions
108
117
  */
109
- getFieldByName(path) {
110
- throw new ConstantExpressionError(`Illegal reference to '${path.join('.')}' in constant expressions`);
118
+ getFieldByName(path, at) {
119
+ throw new ConstantExpressionError(`Illegal reference to '${path.join('.')}' in constant expressions${locSuffix(at)}`, at);
111
120
  }
112
- getStructByName(path) {
113
- throw new ConstantExpressionError(`Illegal reference to '${path.join('.')}' in constant expressions`);
121
+ getStructByName(path, at) {
122
+ throw new ConstantExpressionError(`Illegal reference to '${path.join('.')}' in constant expressions${locSuffix(at)}`, at);
114
123
  }
115
124
  getSQLIdentifier() {
116
125
  throw new ConstantExpressionError('Constant expressions do not need SQL identifiers');
@@ -29,6 +29,7 @@ const field_instance_1 = require("./field_instance");
29
29
  const filter_compilers_1 = require("./filter_compilers");
30
30
  const utils_1 = require("./utils");
31
31
  const query_node_1 = require("./query_node");
32
+ const malloy_compile_error_1 = require("./malloy_compile_error");
32
33
  const NUMERIC_DECIMAL_PRECISION = 9;
33
34
  function sqlSumDistinct(dialect, sqlExp, sqlDistintKey) {
34
35
  const precision = 9;
@@ -186,7 +187,7 @@ function compileExpr(resultSet, context, expr, state = new utils_1.GenerateState
186
187
  return `${expr.kids.e.sql} ${expr.not ? 'NOT IN' : 'IN'} (${oneOf})`;
187
188
  }
188
189
  case 'inGiven': {
189
- const bound = resolveGivenBoundExpr(context, expr.givenRef.id, expr.givenRef.refName);
190
+ const bound = resolveGivenBoundExpr(context, expr.givenRef);
190
191
  // null binding collapses to empty-set semantics — not the SQL
191
192
  // `IN (NULL)` shape, which has confusing NULL-membership rules.
192
193
  if (bound.node === 'null') {
@@ -280,27 +281,27 @@ function generateAppliedFilter(context, filterMatchExpr, qi) {
280
281
  filterExpr = argument.value;
281
282
  }
282
283
  else {
283
- throw new Error(`Parameter ${name} was expected to be a filter expression`);
284
+ throw new malloy_compile_error_1.MalloyCompileError(`Parameter '${name}' has no value, but a filter expression is required here.`, 'compiler-filter-parameter-no-value', filterExpr.at);
284
285
  }
285
286
  }
286
287
  if (filterExpr.node === 'given') {
287
- const id = filterExpr.id;
288
- const supplied = (_c = (_b = context.prepareResultOptions) === null || _b === void 0 ? void 0 : _b.resolvedGivens) === null || _c === void 0 ? void 0 : _c.get(id);
288
+ const supplied = (_c = (_b = context.prepareResultOptions) === null || _b === void 0 ? void 0 : _b.resolvedGivens) === null || _c === void 0 ? void 0 : _c.get(filterExpr.id);
289
289
  if (supplied !== undefined) {
290
290
  filterExpr = supplied;
291
291
  }
292
292
  else {
293
- const decl = context.getModel().givens[id];
293
+ const decl = context.getModel().givens[filterExpr.id];
294
294
  if ((decl === null || decl === void 0 ? void 0 : decl.default) !== undefined) {
295
295
  filterExpr = decl.default;
296
296
  }
297
297
  else {
298
- throw new Error(unsatisfiedGivenMessage(filterExpr.refName));
298
+ throw new malloy_compile_error_1.MalloyCompileError(unsatisfiedGivenMessage(filterExpr.refName), 'compiler-given-no-value', filterExpr.at);
299
299
  }
300
300
  }
301
301
  }
302
302
  if (filterExpr.node !== 'filterLiteral') {
303
- throw new Error('Can only use filter expression literals or parameters as filter expressions');
303
+ throw new malloy_compile_error_1.MalloyCompileError("Filter context requires a filter-expression literal (e.g., `f'...'`) " +
304
+ `or a parameter that resolves to one; got node '${filterExpr.node}'.`, 'compiler-filter-not-literal', undefined);
304
305
  }
305
306
  const filterSrc = filterExpr.filterSrc;
306
307
  let fParse;
@@ -320,10 +321,11 @@ function generateAppliedFilter(context, filterMatchExpr, qi) {
320
321
  fParse = malloy_filter_1.TemporalFilterExpression.parse(filterSrc);
321
322
  break;
322
323
  default:
323
- throw new Error(`unsupported filter type ${filterMatchExpr.dataType}`);
324
+ throw new malloy_compile_error_1.MalloyCompileError(`Filter type '${filterMatchExpr.dataType}' is not supported. ` +
325
+ 'Supported filter types: string, number, boolean, date, timestamp, timestamptz.', 'compiler-filter-type-unsupported', undefined);
324
326
  }
325
327
  if (fParse.log.length > 0) {
326
- throw new Error(`Filter expression parse error: ${fParse.log[0]}`);
328
+ throw new malloy_compile_error_1.MalloyCompileError(`Filter expression parse error: ${fParse.log[0]}.`, 'compiler-filter-parse-error', undefined);
327
329
  }
328
330
  return filter_compilers_1.FilterCompilers.compile(filterMatchExpr.dataType, fParse.parsed, filterMatchExpr.kids.expr.sql || '', context.dialect, qi);
329
331
  }
@@ -581,7 +583,7 @@ function generateFunctionCallExpression(resultSet, context, frag, state) {
581
583
  }
582
584
  function generateFieldFragment(resultSet, context, expr, state) {
583
585
  // find the structDef and return the path to the field...
584
- const fieldRef = context.getFieldByName(expr.path);
586
+ const fieldRef = context.getFieldByName(expr.path, expr.at);
585
587
  if ((0, malloy_types_1.hasExpression)(fieldRef.fieldDef)) {
586
588
  const ret = exprToSQL(resultSet, fieldRef.parent, fieldRef.fieldDef.e, state);
587
589
  return `(${ret})`;
@@ -614,7 +616,7 @@ function generateParameterFragment(resultSet, context, expr, state) {
614
616
  if (argument === null || argument === void 0 ? void 0 : argument.value) {
615
617
  return exprToSQL(resultSet, context, argument.value, state);
616
618
  }
617
- throw new Error(`Can't generate SQL, no value for ${expr.path}`);
619
+ throw new malloy_compile_error_1.MalloyCompileError(`Parameter '${expr.path.join('.')}' has no value supplied.`, 'compiler-parameter-no-value', expr.at);
618
620
  }
619
621
  /**
620
622
  * Resolve a given to the Expr that should stand in for it at SQL emit:
@@ -625,20 +627,20 @@ function generateParameterFragment(resultSet, context, expr, state) {
625
627
  * Shared by `generateGivenFragment` ($NAME directly in an expression)
626
628
  * and the `'inGiven'` SQL-emit case ($ARR in `expr in $ARR`).
627
629
  */
628
- function resolveGivenBoundExpr(context, id, refName) {
630
+ function resolveGivenBoundExpr(context, ref) {
629
631
  var _a, _b;
630
- const supplied = (_b = (_a = context.prepareResultOptions) === null || _a === void 0 ? void 0 : _a.resolvedGivens) === null || _b === void 0 ? void 0 : _b.get(id);
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);
631
633
  if (supplied !== undefined)
632
634
  return supplied;
633
- const decl = context.getModel().givens[id];
635
+ const decl = context.getModel().givens[ref.id];
634
636
  if ((decl === null || decl === void 0 ? void 0 : decl.default) !== undefined)
635
637
  return decl.default;
636
- throw new Error(unsatisfiedGivenMessage(refName));
638
+ throw new malloy_compile_error_1.MalloyCompileError(unsatisfiedGivenMessage(ref.refName), 'compiler-given-no-value', ref.at);
637
639
  }
638
640
  function generateGivenFragment(resultSet, context, expr, state) {
639
641
  // The bound expr may itself be a `$OTHER`-bearing expression; recursive
640
642
  // compile handles default chains.
641
- const bound = resolveGivenBoundExpr(context, expr.id, expr.refName);
643
+ const bound = resolveGivenBoundExpr(context, expr);
642
644
  return exprToSQL(resultSet, context, bound, state);
643
645
  }
644
646
  function unsatisfiedGivenMessage(refName) {
@@ -663,7 +665,7 @@ function generateDimFragment(resultSet, context, expr, state) {
663
665
  }
664
666
  function generateUngroupedFragment(resultSet, context, expr, state) {
665
667
  if (state.totalGroupSet !== -1) {
666
- throw new Error('Already in ALL. Cannot nest within an all calcuation.');
668
+ throw new malloy_compile_error_1.MalloyCompileError("Cannot nest 'all()' or 'exclude()' inside another 'all()' calculation.", 'compiler-nested-ungroup', undefined);
667
669
  }
668
670
  let totalGroupSet;
669
671
  let ungroupSet;
@@ -6,6 +6,7 @@
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.FieldInstanceResultRoot = exports.FieldInstanceResult = exports.FieldInstanceField = void 0;
8
8
  exports.sqlFullChildReference = sqlFullChildReference;
9
+ const malloy_compile_error_1 = require("./malloy_compile_error");
9
10
  const malloy_types_1 = require("./malloy_types");
10
11
  const utils_1 = require("./utils");
11
12
  const join_instance_1 = require("./join_instance");
@@ -139,7 +140,8 @@ class FieldInstanceResult {
139
140
  const fi = this.allFields.get(as);
140
141
  if (fi) {
141
142
  if (fi.type === 'query') {
142
- throw new Error(`Redefinition of field ${field.fieldDef.name} as struct`);
143
+ throw new malloy_compile_error_1.MalloyCompileError(`Field '${field.fieldDef.name}' is already defined as a nested view; ` +
144
+ 'cannot also use it as a scalar field in this output.', 'compiler-field-redefined-as-struct', field.fieldDef.location);
143
145
  }
144
146
  const fif = fi;
145
147
  if (fif.fieldUsage.type === 'result') {
@@ -148,7 +150,8 @@ class FieldInstanceResult {
148
150
  return;
149
151
  }
150
152
  else {
151
- throw new Error(`Ambiguous output field name '${field.fieldDef.name}'.`);
153
+ throw new malloy_compile_error_1.MalloyCompileError(`Output field name '${field.fieldDef.name}' is ambiguous — ` +
154
+ 'defined more than once at the query output level.', 'compiler-ambiguous-output-name', field.fieldDef.location);
152
155
  }
153
156
  }
154
157
  }
@@ -373,7 +376,8 @@ class FieldInstanceResult {
373
376
  // verify that all names specified are available in the current scope.
374
377
  for (const fieldName of (ungroupSet === null || ungroupSet === void 0 ? void 0 : ungroupSet.fields) || []) {
375
378
  if (inScopeFieldNames.indexOf(fieldName) === -1) {
376
- throw new Error(`${ungroupSet === null || ungroupSet === void 0 ? void 0 : ungroupSet.type}(): unknown field name "${fieldName}" or name not in scope.`);
379
+ throw new malloy_compile_error_1.MalloyCompileError(`'${ungroupSet === null || ungroupSet === void 0 ? void 0 : ungroupSet.type}()' references field '${fieldName}', ` +
380
+ 'which is not defined or not in scope at this nesting level.', 'compiler-ungroup-field-unknown', undefined);
377
381
  }
378
382
  }
379
383
  return ret;