@malloydata/malloy 0.0.190 → 0.0.191-dev240925232958

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 (90) hide show
  1. package/dist/lang/ast/expressions/constant-expression.js +4 -1
  2. package/dist/lang/ast/expressions/expr-aggregate-function.js +25 -14
  3. package/dist/lang/ast/expressions/expr-alternation-tree.js +1 -3
  4. package/dist/lang/ast/expressions/expr-cast.js +1 -1
  5. package/dist/lang/ast/expressions/expr-coalesce.js +1 -1
  6. package/dist/lang/ast/expressions/expr-func.js +20 -31
  7. package/dist/lang/ast/expressions/expr-granular-time.js +1 -1
  8. package/dist/lang/ast/expressions/expr-id-reference.js +1 -3
  9. package/dist/lang/ast/expressions/expr-props.js +8 -8
  10. package/dist/lang/ast/expressions/expr-time-extract.js +7 -13
  11. package/dist/lang/ast/expressions/expr-ungroup.js +4 -8
  12. package/dist/lang/ast/expressions/for-range.js +2 -3
  13. package/dist/lang/ast/expressions/function-ordering.js +6 -6
  14. package/dist/lang/ast/expressions/partial-compare.js +1 -3
  15. package/dist/lang/ast/expressions/pick-when.js +30 -20
  16. package/dist/lang/ast/expressions/range.js +1 -1
  17. package/dist/lang/ast/expressions/top-by.js +1 -1
  18. package/dist/lang/ast/field-space/dynamic-space.js +1 -1
  19. package/dist/lang/ast/field-space/index-field-space.js +6 -6
  20. package/dist/lang/ast/field-space/parameter-space.js +14 -3
  21. package/dist/lang/ast/field-space/project-field-space.js +1 -1
  22. package/dist/lang/ast/field-space/query-spaces.d.ts +2 -1
  23. package/dist/lang/ast/field-space/query-spaces.js +7 -6
  24. package/dist/lang/ast/field-space/reference-field.js +1 -1
  25. package/dist/lang/ast/field-space/refined-space.js +3 -3
  26. package/dist/lang/ast/field-space/static-space.js +11 -2
  27. package/dist/lang/ast/parameters/has-parameter.js +4 -4
  28. package/dist/lang/ast/query-builders/index-builder.js +4 -4
  29. package/dist/lang/ast/query-builders/project-builder.js +2 -2
  30. package/dist/lang/ast/query-builders/reduce-builder.js +5 -5
  31. package/dist/lang/ast/query-elements/anonymous-query.js +1 -1
  32. package/dist/lang/ast/query-elements/query-reference.js +2 -2
  33. package/dist/lang/ast/query-items/field-declaration.js +7 -4
  34. package/dist/lang/ast/query-items/field-references.js +1 -1
  35. package/dist/lang/ast/query-items/typecheck_utils.js +31 -11
  36. package/dist/lang/ast/query-properties/filters.js +4 -4
  37. package/dist/lang/ast/query-properties/ordering.js +3 -3
  38. package/dist/lang/ast/query-properties/qop-desc.js +2 -2
  39. package/dist/lang/ast/query-properties/top.js +5 -5
  40. package/dist/lang/ast/query-utils.js +1 -1
  41. package/dist/lang/ast/source-elements/named-source.d.ts +2 -2
  42. package/dist/lang/ast/source-elements/named-source.js +12 -13
  43. package/dist/lang/ast/source-elements/refined-source.js +9 -7
  44. package/dist/lang/ast/source-elements/sql-source.js +5 -5
  45. package/dist/lang/ast/source-elements/table-source.js +2 -2
  46. package/dist/lang/ast/source-properties/joins.js +6 -6
  47. package/dist/lang/ast/source-properties/renames.js +3 -3
  48. package/dist/lang/ast/source-query-elements/source-query-element.d.ts +2 -1
  49. package/dist/lang/ast/source-query-elements/source-query-element.js +3 -2
  50. package/dist/lang/ast/source-query-elements/sq-arrow.js +2 -2
  51. package/dist/lang/ast/source-query-elements/sq-extend.js +1 -1
  52. package/dist/lang/ast/source-query-elements/sq-reference.js +5 -5
  53. package/dist/lang/ast/source-query-elements/sq-refine.js +2 -2
  54. package/dist/lang/ast/source-query-elements/sq-source.js +1 -1
  55. package/dist/lang/ast/sql-elements/sql-string.js +2 -2
  56. package/dist/lang/ast/statements/define-query.js +2 -2
  57. package/dist/lang/ast/statements/define-source.js +3 -3
  58. package/dist/lang/ast/statements/import-statement.js +7 -7
  59. package/dist/lang/ast/struct-utils.js +1 -1
  60. package/dist/lang/ast/types/expression-def.js +21 -22
  61. package/dist/lang/ast/types/lookup-result.d.ts +5 -1
  62. package/dist/lang/ast/types/malloy-element.d.ts +8 -5
  63. package/dist/lang/ast/types/malloy-element.js +33 -27
  64. package/dist/lang/ast/view-elements/qop-desc-view.js +3 -3
  65. package/dist/lang/ast/view-elements/reference-view.js +6 -6
  66. package/dist/lang/ast/view-elements/refine-utils.js +6 -6
  67. package/dist/lang/ast/view-elements/view-arrow.js +1 -1
  68. package/dist/lang/ast/view-elements/view-refine.js +1 -1
  69. package/dist/lang/malloy-to-ast.d.ts +7 -7
  70. package/dist/lang/malloy-to-ast.js +56 -55
  71. package/dist/lang/parse-log.d.ts +323 -6
  72. package/dist/lang/parse-log.js +62 -16
  73. package/dist/lang/parse-malloy.d.ts +5 -3
  74. package/dist/lang/parse-malloy.js +22 -31
  75. package/dist/lang/test/annotation.spec.js +4 -4
  76. package/dist/lang/test/expressions.spec.js +93 -74
  77. package/dist/lang/test/imports.spec.js +5 -5
  78. package/dist/lang/test/lenses.spec.js +18 -18
  79. package/dist/lang/test/literals.spec.js +1 -1
  80. package/dist/lang/test/locations.spec.js +5 -5
  81. package/dist/lang/test/parameters.spec.js +34 -34
  82. package/dist/lang/test/parse-expects.d.ts +10 -28
  83. package/dist/lang/test/parse-expects.js +38 -33
  84. package/dist/lang/test/parse.spec.js +49 -49
  85. package/dist/lang/test/query.spec.js +82 -82
  86. package/dist/lang/test/source.spec.js +6 -6
  87. package/dist/lang/test/test-translator.d.ts +19 -0
  88. package/dist/lang/test/test-translator.js +20 -1
  89. package/dist/tags.js +4 -2
  90. package/package.json +1 -1
@@ -4,7 +4,7 @@ import { AbstractParseTreeVisitor } from 'antlr4ts/tree/AbstractParseTreeVisitor
4
4
  import { MalloyParserVisitor } from './lib/Malloy/MalloyParserVisitor';
5
5
  import * as parse from './lib/Malloy/MalloyParser';
6
6
  import * as ast from './ast';
7
- import { LogSeverity, MessageLogger } from './parse-log';
7
+ import { LogMessageOptions, LogSeverity, MessageCode, MessageLogger, MessageParameterType } from './parse-log';
8
8
  import { MalloyParseInfo } from './malloy-parse-info';
9
9
  import { FieldDeclarationConstructor } from './ast';
10
10
  import { HasString, HasID } from './parse-utils';
@@ -39,20 +39,20 @@ export declare class MalloyToAST extends AbstractParseTreeVisitor<ast.MalloyElem
39
39
  * are not compatible.
40
40
  * @return an error object to throw.
41
41
  */
42
- protected internalError(cx: ParserRuleContext, msg: string): Error;
42
+ protected internalError(cx: ParserRuleContext, message: string): Error;
43
43
  /**
44
44
  * Log an error message relative to an AST node
45
45
  */
46
- protected astError(el: ast.MalloyElement, str: string, sev?: LogSeverity): void;
46
+ protected astError<T extends MessageCode>(el: ast.MalloyElement, code: T, data: MessageParameterType<T>, options?: LogMessageOptions): void;
47
47
  protected getLocation(cx: ParserRuleContext): DocumentLocation;
48
48
  /**
49
49
  * Log an error message relative to a parse node
50
50
  */
51
- protected contextError(cx: ParserRuleContext, msg: string, sev?: LogSeverity): void;
52
- protected warnWithReplacement(message: string, range: DocumentRange, replacement: string): void;
53
- protected inExperiment(experimentID: string, cx: ParserRuleContext): boolean;
51
+ protected contextError<T extends MessageCode>(cx: ParserRuleContext, code: T, data: MessageParameterType<T>, options?: LogMessageOptions): void;
52
+ protected warnWithReplacement<T extends MessageCode>(code: T, data: MessageParameterType<T>, range: DocumentRange, replacement: string): void;
53
+ protected inExperiment(experimentId: string, cx: ParserRuleContext): boolean;
54
54
  protected m4Severity(): LogSeverity | false;
55
- protected m4advisory(cx: ParserRuleContext, msg: string): void;
55
+ protected m4advisory<T extends MessageCode>(cx: ParserRuleContext, code: T, data: MessageParameterType<T>): void;
56
56
  protected only<T extends ast.MalloyElement>(els: ast.MalloyElement[], isGood: (el: ast.MalloyElement) => T | false, desc: string): T[];
57
57
  protected getNumber(term: ParseTree): number;
58
58
  protected getFieldName(cx: HasID): ast.FieldName;
@@ -50,6 +50,7 @@ exports.MalloyToAST = void 0;
50
50
  const antlr4ts_1 = require("antlr4ts");
51
51
  const AbstractParseTreeVisitor_1 = require("antlr4ts/tree/AbstractParseTreeVisitor");
52
52
  const ast = __importStar(require("./ast"));
53
+ const parse_log_1 = require("./parse-log");
53
54
  const Interval_1 = require("antlr4ts/misc/Interval");
54
55
  const ast_1 = require("./ast");
55
56
  const parse_utils_1 = require("./parse-utils");
@@ -93,16 +94,15 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
93
94
  * are not compatible.
94
95
  * @return an error object to throw.
95
96
  */
96
- internalError(cx, msg) {
97
- const tmsg = `Internal Translator Error: ${msg}`;
98
- this.contextError(cx, tmsg);
99
- return new Error(tmsg);
97
+ internalError(cx, message) {
98
+ this.contextError(cx, 'internal-translator-error', { message });
99
+ return new Error(`Internal Translator Error: ${message}`);
100
100
  }
101
101
  /**
102
102
  * Log an error message relative to an AST node
103
103
  */
104
- astError(el, str, sev = 'error') {
105
- this.msgLog.log({ message: str, at: el.location, severity: sev });
104
+ astError(el, code, data, options) {
105
+ this.msgLog.log((0, parse_log_1.makeLogMessage)(code, data, { at: el.location, ...options }));
106
106
  }
107
107
  getLocation(cx) {
108
108
  return {
@@ -113,28 +113,26 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
113
113
  /**
114
114
  * Log an error message relative to a parse node
115
115
  */
116
- contextError(cx, msg, sev = 'error') {
117
- this.msgLog.log({
118
- message: msg,
116
+ contextError(cx, code, data, options) {
117
+ this.msgLog.log((0, parse_log_1.makeLogMessage)(code, data, {
119
118
  at: this.getLocation(cx),
120
- severity: sev,
121
- });
119
+ ...options,
120
+ }));
122
121
  }
123
- warnWithReplacement(message, range, replacement) {
124
- this.msgLog.log({
125
- message,
122
+ warnWithReplacement(code, data, range, replacement) {
123
+ this.msgLog.log((0, parse_log_1.makeLogMessage)(code, data, {
126
124
  at: { url: this.parseInfo.sourceURL, range },
127
125
  severity: 'warn',
128
126
  replacement,
129
- });
127
+ }));
130
128
  }
131
- inExperiment(experimentID, cx) {
129
+ inExperiment(experimentId, cx) {
132
130
  const experimental = this.compilerFlags.tag('experimental');
133
131
  if (experimental &&
134
- (experimental.bare() || experimental.has(experimentID))) {
132
+ (experimental.bare() || experimental.has(experimentId))) {
135
133
  return true;
136
134
  }
137
- this.contextError(cx, `Experimental flag '${experimentID}' required to enable this feature`);
135
+ this.contextError(cx, 'experiment-not-enabled', { experimentId });
138
136
  return false;
139
137
  }
140
138
  m4Severity() {
@@ -144,10 +142,10 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
144
142
  }
145
143
  return false;
146
144
  }
147
- m4advisory(cx, msg) {
145
+ m4advisory(cx, code, data) {
148
146
  const m4 = this.m4Severity();
149
147
  if (m4) {
150
- this.contextError(cx, msg, m4);
148
+ this.contextError(cx, code, data, { severity: m4 });
151
149
  }
152
150
  }
153
151
  only(els, isGood, desc) {
@@ -158,7 +156,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
158
156
  acceptable.push(checked);
159
157
  }
160
158
  else if (!(el instanceof IgnoredElement)) {
161
- this.astError(el, `Parser enountered unexpected statement type '${el.elementType}' when it needed '${desc}'`);
159
+ this.astError(el, 'unexpected-statement-in-translation', `Parser enountered unexpected statement type '${el.elementType}' when it needed '${desc}'`);
162
160
  }
163
161
  }
164
162
  return acceptable;
@@ -205,14 +203,14 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
205
203
  }
206
204
  getFilterShortcut(cx) {
207
205
  const el = this.getFilterElement(cx.fieldExpr());
208
- this.m4advisory(cx, 'Filter shortcut `{? condition }` is deprecated; use `{ where: condition } instead');
206
+ this.m4advisory(cx, 'filter-shortcut', 'Filter shortcut `{? condition }` is deprecated; use `{ where: condition } instead');
209
207
  return new ast.Filter([el]);
210
208
  }
211
209
  getPlainStringFrom(cx) {
212
210
  const [result, errors] = (0, parse_utils_1.getPlainString)(cx);
213
211
  for (const error of errors) {
214
212
  if (error instanceof antlr4ts_1.ParserRuleContext) {
215
- this.contextError(error, '%{ query } illegal in this string');
213
+ this.contextError(error, 'illegal-query-interpolation-outside-sql-block', '%{ query } illegal in this string');
216
214
  }
217
215
  }
218
216
  return result || '';
@@ -220,7 +218,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
220
218
  makeSqlString(pcx, sqlStr) {
221
219
  for (const part of pcx.sqlInterpolation()) {
222
220
  if (part.CLOSE_CODE()) {
223
- this.m4advisory(part, 'Use %{ ... } instead of %{ ... }%');
221
+ this.m4advisory(part, 'percent-terminated-query-interpolation', 'Use %{ ... } instead of %{ ... }%');
224
222
  }
225
223
  }
226
224
  for (const part of (0, parse_utils_1.getStringParts)(pcx)) {
@@ -244,7 +242,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
244
242
  parseTime(pcx, parse) {
245
243
  let def = parse(pcx.text);
246
244
  if (!def) {
247
- this.contextError(pcx, 'Time data parse error');
245
+ this.contextError(pcx, 'failed-to-parse-time-literal', 'Time data parse error');
248
246
  // return a value node so the parse can continue
249
247
  def = new ast.LiteralTimestamp({ text: pcx.text });
250
248
  }
@@ -321,7 +319,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
321
319
  visitTableFunction(pcx) {
322
320
  const tableURI = this.getPlainStringFrom(pcx.tableURI());
323
321
  const el = this.astAt(new ast.TableFunctionSource(tableURI), pcx);
324
- this.m4advisory(pcx, "`table('connection_name:table_path')` is deprecated; use `connection_name.table('table_path')`");
322
+ this.m4advisory(pcx, 'table-function', "`table('connection_name:table_path')` is deprecated; use `connection_name.table('table_path')`");
325
323
  return el;
326
324
  }
327
325
  visitTableMethod(pcx) {
@@ -354,11 +352,11 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
354
352
  if (join instanceof ast.ExpressionJoin) {
355
353
  join.joinType = 'many';
356
354
  if (join.joinOn === undefined) {
357
- this.contextError(pcx, 'join_many: requires ON expression');
355
+ this.contextError(pcx, 'missing-on-in-join-many', 'join_many: requires ON expression');
358
356
  }
359
357
  }
360
358
  else if (join instanceof ast.KeyJoin) {
361
- this.contextError(pcx, 'Foreign key join not legal in join_many:');
359
+ this.contextError(pcx, 'foreign-key-in-join-many', 'Foreign key join not legal in join_many:');
362
360
  }
363
361
  }
364
362
  }
@@ -391,7 +389,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
391
389
  join.joinType = 'cross';
392
390
  }
393
391
  else {
394
- join.log('Foreign key join not legal in join_cross:');
392
+ join.logError('foreign-key-in-join-cross', 'Foreign key join not legal in join_cross:');
395
393
  }
396
394
  }
397
395
  }
@@ -419,7 +417,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
419
417
  }
420
418
  visitQueryJoinStatement(pcx) {
421
419
  const result = this.astAt(this.visit(pcx.joinStatement()), pcx);
422
- this.m4advisory(pcx, 'Joins in queries are deprecated, move into an `extend:` block.');
420
+ this.m4advisory(pcx, 'join-statement-in-view', 'Joins in queries are deprecated, move into an `extend:` block.');
423
421
  return result;
424
422
  }
425
423
  visitJoinOn(pcx) {
@@ -432,7 +430,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
432
430
  join.matrixOperation = mop;
433
431
  }
434
432
  else {
435
- this.contextError(pcx, 'Internal Error: Unknown matrixOperation');
433
+ this.contextError(pcx, 'unknown-matrix-operation', 'Internal Error: Unknown matrixOperation');
436
434
  }
437
435
  if (onCx) {
438
436
  join.joinOn = this.getFieldExpr(onCx);
@@ -489,7 +487,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
489
487
  const defs = this.getFieldDefs(pcx.defList().fieldDef(), ast.DeclareFieldDeclaration);
490
488
  const stmt = new ast.DeclareFields(defs);
491
489
  const result = this.astAt(stmt, pcx);
492
- this.m4advisory(pcx, '`declare:` is deprecated; use `dimension:` or `measure:` inside a source or `extend:` block');
490
+ this.m4advisory(pcx, 'declare', '`declare:` is deprecated; use `dimension:` or `measure:` inside a source or `extend:` block');
493
491
  return result;
494
492
  }
495
493
  visitExploreRenameDef(pcx) {
@@ -528,7 +526,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
528
526
  const blockNotes = this.getNotes(pcx.tags());
529
527
  queryDefs.extendNote({ blockNotes });
530
528
  if (pcx.QUERY()) {
531
- this.m4advisory(pcx, 'Use view: inside of a source instead of query:');
529
+ this.m4advisory(pcx, 'query-in-source', 'Use view: inside of a source instead of query:');
532
530
  }
533
531
  return queryDefs;
534
532
  }
@@ -553,7 +551,9 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
553
551
  const timezone = this.getPlainStringFrom(cx);
554
552
  const timezoneStatement = this.astAt(new ast.TimezoneStatement(timezone), cx.string());
555
553
  if (!timezoneStatement.isValid) {
556
- this.astError(timezoneStatement, `Invalid timezone: ${timezoneStatement.tz}`);
554
+ this.astError(timezoneStatement, 'invalid-timezone', {
555
+ timezone: timezoneStatement.tz,
556
+ });
557
557
  }
558
558
  return this.astAt(timezoneStatement, cx);
559
559
  }
@@ -623,7 +623,8 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
623
623
  expr = new ast.ExprSum(undefined, ref);
624
624
  }
625
625
  else {
626
- this.contextError(agg, `\`${aggFunc}\` is not legal in a reference-only aggregation`);
626
+ // TODO this error doesn't have any tests
627
+ this.contextError(agg, 'invalid-reference-only-aggregation', `\`${aggFunc}\` is not legal in a reference-only aggregation`);
627
628
  return ref;
628
629
  }
629
630
  }
@@ -659,7 +660,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
659
660
  }
660
661
  visitProjectStatement(pcx) {
661
662
  if (pcx.PROJECT()) {
662
- this.m4advisory(pcx, 'project: keyword is deprecated, use select:');
663
+ this.m4advisory(pcx, 'project', 'project: keyword is deprecated, use select:');
663
664
  }
664
665
  const stmt = this.visitFieldCollection(pcx.fieldCollection());
665
666
  stmt.extendNote({ blockNotes: this.getNotes(pcx.tags()) });
@@ -746,7 +747,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
746
747
  const topN = this.getNumber(pcx.INTEGER_LITERAL());
747
748
  let top;
748
749
  if (byCx) {
749
- this.m4advisory(byCx, 'by clause of top statement unupported. Use order_by instead');
750
+ this.m4advisory(byCx, 'top-by', 'by clause of top statement unupported. Use order_by instead');
750
751
  const nameCx = byCx.fieldName();
751
752
  if (nameCx) {
752
753
  const name = this.getFieldName(nameCx);
@@ -789,7 +790,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
789
790
  const notes = this.getNotes(pcx.topLevelAnonQueryDef().tags());
790
791
  const blockNotes = this.getNotes(pcx.tags());
791
792
  theQuery.extendNote({ notes, blockNotes });
792
- this.m4advisory(defCx, 'Anonymous `query:` statements are deprecated, use `run:` instead');
793
+ this.m4advisory(defCx, 'anonymous-query', 'Anonymous `query:` statements are deprecated, use `run:` instead');
793
794
  return this.astAt(theQuery, pcx);
794
795
  }
795
796
  visitRunStatement(pcx) {
@@ -819,7 +820,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
819
820
  else {
820
821
  const implicitName = vExpr.getImplicitName();
821
822
  if (implicitName === undefined) {
822
- this.contextError(pcx, '`nest:` view requires a name (add `nest_name is ...`)');
823
+ this.contextError(pcx, 'anonymous-nest', '`nest:` view requires a name (add `nest_name is ...`)');
823
824
  }
824
825
  name = implicitName !== null && implicitName !== void 0 ? implicitName : '__unnamed__';
825
826
  }
@@ -946,14 +947,14 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
946
947
  const exprDef = pcx.fieldExpr();
947
948
  if (pcx.aggregate().COUNT()) {
948
949
  if (exprDef) {
949
- this.contextError(exprDef, 'Expression illegal inside path.count()');
950
+ this.contextError(exprDef, 'count-expression-with-locality', 'Expression illegal inside path.count()');
950
951
  }
951
952
  return new ast.ExprCount(source);
952
953
  }
953
954
  const expr = exprDef ? this.getFieldExpr(exprDef) : undefined;
954
955
  if (aggFunc === 'min' || aggFunc === 'max') {
955
956
  if (expr) {
956
- this.contextError(pcx, this.symmetricAggregateUsageError(aggFunc));
957
+ this.contextError(pcx, 'invalid-symmetric-aggregate', this.symmetricAggregateUsageError(aggFunc));
957
958
  }
958
959
  else {
959
960
  const idRef = this.astAt(new ast.ExprIdReference(path), pathCx);
@@ -969,7 +970,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
969
970
  return new ast.ExprSum(expr, source);
970
971
  }
971
972
  else {
972
- this.contextError(pcx, `Cannot parse aggregate function ${aggFunc}`);
973
+ this.contextError(pcx, 'aggregate-parse-error', `Cannot parse aggregate function ${aggFunc}`);
973
974
  }
974
975
  return new ast.ExprNULL();
975
976
  }
@@ -992,7 +993,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
992
993
  const source = undefined;
993
994
  const aggFunc = pcx.aggregate().text.toLowerCase();
994
995
  if (pcx.STAR()) {
995
- this.m4advisory(pcx, `* illegal inside ${aggFunc}()`);
996
+ this.m4advisory(pcx, 'wildcard-in-aggregate', `* illegal inside ${aggFunc}()`);
996
997
  }
997
998
  if (aggFunc === 'count') {
998
999
  return this.astAt(expr ? new ast.ExprCountDistinct(expr) : new ast.ExprCount(), pcx);
@@ -1002,7 +1003,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
1002
1003
  return new ast.ExprMin(expr);
1003
1004
  }
1004
1005
  else {
1005
- this.contextError(pcx, this.symmetricAggregateUsageError(aggFunc));
1006
+ this.contextError(pcx, 'invalid-symmetric-aggregate', this.symmetricAggregateUsageError(aggFunc));
1006
1007
  }
1007
1008
  }
1008
1009
  else if (aggFunc === 'max') {
@@ -1010,12 +1011,12 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
1010
1011
  return new ast.ExprMax(expr);
1011
1012
  }
1012
1013
  else {
1013
- this.contextError(pcx, this.symmetricAggregateUsageError(aggFunc));
1014
+ this.contextError(pcx, 'invalid-symmetric-aggregate', this.symmetricAggregateUsageError(aggFunc));
1014
1015
  }
1015
1016
  }
1016
1017
  else {
1017
1018
  if (expr === undefined) {
1018
- this.contextError(pcx, this.asymmetricAggregateUsageError(aggFunc));
1019
+ this.contextError(pcx, 'invalid-asymmetric-aggregate', this.asymmetricAggregateUsageError(aggFunc));
1019
1020
  return new ast.ExprNULL();
1020
1021
  }
1021
1022
  const explicitSource = pcx.SOURCE_KW() !== undefined;
@@ -1098,13 +1099,13 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
1098
1099
  rawType = rawRawType;
1099
1100
  }
1100
1101
  else {
1101
- this.contextError(pcx, `'#' assertion for unknown type '${rawRawType}'`);
1102
+ this.contextError(pcx, 'unexpected-malloy-type', `'#' assertion for unknown type '${rawRawType}'`);
1102
1103
  rawType = undefined;
1103
1104
  }
1104
1105
  }
1105
1106
  let fn = (0, parse_utils_1.getOptionalId)(pcx) || ((_b = pcx.timeframe()) === null || _b === void 0 ? void 0 : _b.text);
1106
1107
  if (fn === undefined) {
1107
- this.contextError(pcx, 'Function name error');
1108
+ this.contextError(pcx, 'failed-to-parse-function-name', 'Function name error');
1108
1109
  fn = 'FUNCTION_NAME_ERROR';
1109
1110
  }
1110
1111
  if (ast.ExprTimeExtract.extractor(fn)) {
@@ -1205,11 +1206,11 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
1205
1206
  return tags;
1206
1207
  }
1207
1208
  visitIgnoredObjectAnnotations(pcx) {
1208
- this.contextError(pcx, 'Object annotation not connected to any object');
1209
+ this.contextError(pcx, 'orphaned-object-annotation', 'Object annotation not connected to any object');
1209
1210
  return new IgnoredElement(pcx.text);
1210
1211
  }
1211
1212
  visitIgnoredModelAnnotations(pcx) {
1212
- this.contextError(pcx, 'Model annotations not allowed at this scope');
1213
+ this.contextError(pcx, 'misplaced-model-annotation', 'Model annotations not allowed at this scope');
1213
1214
  return new IgnoredElement(pcx.text);
1214
1215
  }
1215
1216
  visitDefExploreAnnotation(pcx) {
@@ -1244,7 +1245,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
1244
1245
  if (result instanceof ast.SourceQueryElement) {
1245
1246
  return result;
1246
1247
  }
1247
- this.contextError(cx, `Expected a source/query expression, not '${result.elementType}'`);
1248
+ this.contextError(cx, 'unexpected-non-source-query-expression-node', `Expected a source/query expression, not '${result.elementType}'`);
1248
1249
  return new ErrorNode();
1249
1250
  }
1250
1251
  visitSQExtendedSource(pcx) {
@@ -1318,10 +1319,10 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
1318
1319
  const wholeRange = this.parseInfo.rangeFromContext(pcx);
1319
1320
  if (pcx.NOT()) {
1320
1321
  op = '!~';
1321
- this.warnWithReplacement("Use Malloy operator '!~' instead of 'NOT LIKE'", wholeRange, `${left.text} !~ ${right.text}`);
1322
+ this.warnWithReplacement('sql-not-like', "Use Malloy operator '!~' instead of 'NOT LIKE'", wholeRange, `${left.text} !~ ${right.text}`);
1322
1323
  }
1323
1324
  else {
1324
- this.warnWithReplacement("Use Malloy operator '~' instead of 'LIKE'", wholeRange, `${left.text} ~ ${right.text}`);
1325
+ this.warnWithReplacement('sql-like', "Use Malloy operator '~' instead of 'LIKE'", wholeRange, `${left.text} ~ ${right.text}`);
1325
1326
  }
1326
1327
  return this.astAt(new ast.ExprCompare(this.getFieldExpr(left), op, this.getFieldExpr(right)), pcx);
1327
1328
  }
@@ -1331,10 +1332,10 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
1331
1332
  const wholeRange = this.parseInfo.rangeFromContext(pcx);
1332
1333
  if (pcx.NOT()) {
1333
1334
  op = '!=';
1334
- this.warnWithReplacement("Use '!= NULL' to check for NULL instead of 'IS NOT NULL'", wholeRange, `${expr.text} != null`);
1335
+ this.warnWithReplacement('sql-is-not-null', "Use '!= NULL' to check for NULL instead of 'IS NOT NULL'", wholeRange, `${expr.text} != null`);
1335
1336
  }
1336
1337
  else {
1337
- this.warnWithReplacement("Use '= NULL' to check for NULL instead of 'IS NULL'", wholeRange, `${expr.text} = null`);
1338
+ this.warnWithReplacement('sql-is-null', "Use '= NULL' to check for NULL instead of 'IS NULL'", wholeRange, `${expr.text} = null`);
1338
1339
  }
1339
1340
  const nullExpr = new ast.ExprNULL();
1340
1341
  return this.astAt(new ast.ExprCompare(this.getFieldExpr(expr), op, nullExpr), pcx);