@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
@@ -28,51 +28,59 @@ function typecheckProject(type, logTo) {
28
28
  if (type.dataType === 'turtle' || !(0, model_1.expressionIsScalar)(type.expressionType)) {
29
29
  let useInstead;
30
30
  let kind;
31
+ let code;
31
32
  if (type.dataType === 'turtle') {
32
33
  useInstead = 'a nest';
33
34
  kind = 'a view';
35
+ code = 'select-of-view';
34
36
  }
35
37
  else if ((0, model_1.expressionIsAnalytic)(type.expressionType)) {
36
38
  useInstead = 'a calculate';
37
39
  kind = 'an analytic';
40
+ code = 'select-of-analytic';
38
41
  }
39
42
  else if ((0, model_1.expressionIsAggregate)(type.expressionType)) {
40
43
  useInstead = 'an aggregate';
41
44
  kind = 'an aggregate';
45
+ code = 'select-of-aggregate';
42
46
  }
43
47
  else {
44
48
  throw new Error(`Unexpected expression type ${type.expressionType} not handled here`);
45
49
  }
46
- logTo.log(`Cannot use ${kind} field in a select operation, did you mean to use ${useInstead} operation instead?`);
50
+ logTo.logError(code, `Cannot use ${kind} field in a select operation, did you mean to use ${useInstead} operation instead?`);
47
51
  }
48
52
  }
49
53
  exports.typecheckProject = typecheckProject;
50
54
  function typecheckIndex(type, logTo) {
51
55
  if (type.dataType === 'turtle' || !(0, model_1.expressionIsScalar)(type.expressionType)) {
52
56
  let kind;
57
+ let code;
53
58
  if (type.dataType === 'turtle') {
54
59
  kind = 'a view';
60
+ code = 'index-of-view';
55
61
  }
56
62
  else if ((0, model_1.expressionIsAnalytic)(type.expressionType)) {
57
63
  kind = 'an analytic';
64
+ code = 'index-of-analytic';
58
65
  }
59
66
  else if ((0, model_1.expressionIsAggregate)(type.expressionType)) {
60
67
  kind = 'an aggregate';
68
+ code = 'index-of-aggregate';
61
69
  }
62
70
  else {
63
71
  throw new Error(`Unexpected expression type ${type.expressionType} not handled here`);
64
72
  }
65
- logTo.log(`Cannot use ${kind} field in an index operation`);
73
+ logTo.logError(code, `Cannot use ${kind} field in an index operation`);
66
74
  }
67
75
  }
68
76
  exports.typecheckIndex = typecheckIndex;
69
77
  function typecheckDimension(type, logTo) {
70
78
  if (!(0, model_1.expressionIsScalar)(type.expressionType)) {
71
79
  if ((0, model_1.expressionIsAggregate)(type.expressionType)) {
72
- logTo.log('Cannot use an aggregate field in a dimension declaration, did you mean to use a measure declaration instead?');
80
+ logTo.logError('aggregate-in-dimension', 'Cannot use an aggregate field in a dimension declaration, did you mean to use a measure declaration instead?');
73
81
  }
74
82
  else if ((0, model_1.expressionIsAnalytic)(type.expressionType)) {
75
- logTo.log('Cannot use an analytic field in a dimension declaration');
83
+ logTo.logError('analytic-in-dimension', 'Cannot use an analytic field in a dimension declaration');
76
84
  }
77
85
  else {
78
86
  throw new Error(`Unexpected expression type ${type.expressionType} not handled here`);
@@ -83,10 +91,10 @@ exports.typecheckDimension = typecheckDimension;
83
91
  function typecheckMeasure(type, logTo) {
84
92
  if (!(0, model_1.expressionIsAggregate)(type.expressionType)) {
85
93
  if ((0, model_1.expressionIsScalar)(type.expressionType)) {
86
- logTo.log('Cannot use a scalar field in a measure declaration, did you mean to use a dimension declaration instead?');
94
+ logTo.logError('scalar-in-measure', 'Cannot use a scalar field in a measure declaration, did you mean to use a dimension declaration instead?');
87
95
  }
88
96
  else if ((0, model_1.expressionIsAnalytic)(type.expressionType)) {
89
- logTo.log('Cannot use an analytic field in a measure declaration');
97
+ logTo.logError('analytic-in-measure', 'Cannot use an analytic field in a measure declaration');
90
98
  }
91
99
  else {
92
100
  throw new Error(`Unexpected expression type ${type.expressionType} not handled here`);
@@ -96,10 +104,10 @@ function typecheckMeasure(type, logTo) {
96
104
  exports.typecheckMeasure = typecheckMeasure;
97
105
  function typecheckDeclare(type, logTo) {
98
106
  if (type.dataType === 'turtle') {
99
- logTo.log('Query fields cannot be used in a declare block');
107
+ logTo.logError('view-in-declare', 'Views cannot be used in a declare block');
100
108
  }
101
109
  else if ((0, model_1.expressionIsAnalytic)(type.expressionType)) {
102
- logTo.log('Analytic expressions can not be used in a declare block');
110
+ logTo.logError('analytic-in-declare', 'Analytic expressions can not be used in a declare block');
103
111
  }
104
112
  }
105
113
  exports.typecheckDeclare = typecheckDeclare;
@@ -108,22 +116,26 @@ function typecheckCalculate(type, logTo) {
108
116
  !(0, model_1.expressionIsAnalytic)(type.expressionType)) {
109
117
  let useInstead;
110
118
  let kind;
119
+ let code;
111
120
  if (type.dataType === 'turtle') {
112
121
  useInstead = 'a nest';
113
122
  kind = 'a view';
123
+ code = 'calculate-of-view';
114
124
  }
115
125
  else if ((0, model_1.expressionIsAggregate)(type.expressionType)) {
116
126
  useInstead = 'an aggregate';
117
127
  kind = 'an aggregate';
128
+ code = 'calculate-of-aggregate';
118
129
  }
119
130
  else if ((0, model_1.expressionIsScalar)(type.expressionType)) {
120
131
  useInstead = 'a group_by or select';
121
132
  kind = 'a scalar';
133
+ code = 'calculate-of-scalar';
122
134
  }
123
135
  else {
124
136
  throw new Error(`Unexpected expression type ${type.expressionType} not handled here`);
125
137
  }
126
- logTo.log(`Cannot use ${kind} field in a calculate operation, did you mean to use ${useInstead} operation instead?`);
138
+ logTo.logError(code, `Cannot use ${kind} field in a calculate operation, did you mean to use ${useInstead} operation instead?`);
127
139
  }
128
140
  }
129
141
  exports.typecheckCalculate = typecheckCalculate;
@@ -132,22 +144,26 @@ function typecheckAggregate(type, logTo) {
132
144
  !(0, model_1.expressionIsAggregate)(type.expressionType)) {
133
145
  let useInstead;
134
146
  let kind;
147
+ let code;
135
148
  if (type.dataType === 'turtle') {
136
149
  useInstead = 'a nest';
137
150
  kind = 'a view';
151
+ code = 'aggregate-of-view';
138
152
  }
139
153
  else if ((0, model_1.expressionIsAnalytic)(type.expressionType)) {
140
154
  useInstead = 'a calculate';
141
155
  kind = 'an analytic';
156
+ code = 'aggregate-of-analytic';
142
157
  }
143
158
  else if ((0, model_1.expressionIsScalar)(type.expressionType)) {
144
159
  useInstead = 'a group_by or select';
145
160
  kind = 'a scalar';
161
+ code = 'aggregate-of-scalar';
146
162
  }
147
163
  else {
148
164
  throw new Error(`Unexpected expression type ${type} not handled here`);
149
165
  }
150
- logTo.log(`Cannot use ${kind} field in an aggregate operation, did you mean to use ${useInstead} operation instead?`);
166
+ logTo.logError(code, `Cannot use ${kind} field in an aggregate operation, did you mean to use ${useInstead} operation instead?`);
151
167
  }
152
168
  }
153
169
  exports.typecheckAggregate = typecheckAggregate;
@@ -155,22 +171,26 @@ function typecheckGroupBy(type, logTo) {
155
171
  if (type.dataType === 'turtle' || !(0, model_1.expressionIsScalar)(type.expressionType)) {
156
172
  let useInstead;
157
173
  let kind;
174
+ let code;
158
175
  if (type.dataType === 'turtle') {
159
176
  useInstead = 'a nest';
160
177
  kind = 'a view';
178
+ code = 'group-by-view';
161
179
  }
162
180
  else if ((0, model_1.expressionIsAnalytic)(type.expressionType)) {
163
181
  useInstead = 'a calculate';
164
182
  kind = 'an analytic';
183
+ code = 'group-by-analytic';
165
184
  }
166
185
  else if ((0, model_1.expressionIsAggregate)(type.expressionType)) {
167
186
  useInstead = 'an aggregate';
168
187
  kind = 'an aggregate';
188
+ code = 'group-by-aggregate';
169
189
  }
170
190
  else {
171
191
  throw new Error(`Unexpected expression type ${type.expressionType} not handled here`);
172
192
  }
173
- logTo.log(`Cannot use ${kind} field in a group_by operation, did you mean to use ${useInstead} operation instead?`);
193
+ logTo.logError(code, `Cannot use ${kind} field in a group_by operation, did you mean to use ${useInstead} operation instead?`);
174
194
  }
175
195
  }
176
196
  exports.typecheckGroupBy = typecheckGroupBy;
@@ -36,7 +36,7 @@ class FilterElement extends malloy_element_1.MalloyElement {
36
36
  filterCondition(fs) {
37
37
  const exprVal = this.expr.getExpression(fs);
38
38
  if (exprVal.dataType !== 'boolean') {
39
- this.expr.log('Filter expression must have boolean value');
39
+ this.expr.logError('non-boolean-filter', 'Filter expression must have boolean value');
40
40
  return {
41
41
  node: 'filterCondition',
42
42
  code: this.exprSrc,
@@ -81,17 +81,17 @@ class Filter extends malloy_element_1.ListOf {
81
81
  const isAnalytic = (0, malloy_types_1.expressionIsAnalytic)(fExpr.expressionType);
82
82
  if (this.havingClause) {
83
83
  if (isAnalytic) {
84
- oneElement.log('Analytic expressions are not allowed in `having:`');
84
+ oneElement.logError('analytic-in-having', 'Analytic expressions are not allowed in `having:`');
85
85
  continue;
86
86
  }
87
87
  }
88
88
  else {
89
89
  if (isAnalytic) {
90
- oneElement.log('Analytic expressions are not allowed in `where:`');
90
+ oneElement.logError('analytic-in-where', 'Analytic expressions are not allowed in `where:`');
91
91
  continue;
92
92
  }
93
93
  else if (isAggregate) {
94
- oneElement.log('Aggregate expressions are not allowed in `where:`; use `having:`');
94
+ oneElement.logError('aggregate-in-where', 'Aggregate expressions are not allowed in `where:`; use `having:`');
95
95
  }
96
96
  }
97
97
  }
@@ -48,13 +48,13 @@ class OrderBy extends malloy_element_1.MalloyElement {
48
48
  const output = fs.outputSpace();
49
49
  const entry = this.field.getField(output);
50
50
  if (entry.error) {
51
- this.field.log(entry.error);
51
+ this.field.logError(entry.error.code, entry.error.message);
52
52
  }
53
53
  if (!entry.found || !entry.isOutputField) {
54
- this.log(`Unknown field ${this.field.refString} in output space`);
54
+ this.logError('order-by-not-found-in-output', `Unknown field ${this.field.refString} in output space`);
55
55
  }
56
56
  if ((0, malloy_types_1.expressionIsAnalytic)((_a = entry.found) === null || _a === void 0 ? void 0 : _a.typeDesc().expressionType)) {
57
- this.log(`Illegal order by of analytic field ${this.field.refString}`);
57
+ this.logError('order-by-analytic', `Illegal order by of analytic field ${this.field.refString}`);
58
58
  }
59
59
  }
60
60
  const orderElement = { field: this.modelField };
@@ -55,7 +55,7 @@ class QOpDesc extends malloy_element_1.ListOf {
55
55
  if (el.forceQueryClass) {
56
56
  if (guessType) {
57
57
  if (guessType !== el.forceQueryClass) {
58
- el.log(`Not legal in ${guessType} query`);
58
+ el.logError(`illegal-${guessType}-operation`, `Not legal in ${guessType} query`);
59
59
  }
60
60
  }
61
61
  else {
@@ -65,7 +65,7 @@ class QOpDesc extends malloy_element_1.ListOf {
65
65
  needsExplicitQueryClass || (needsExplicitQueryClass = (_a = el.needsExplicitQueryClass) !== null && _a !== void 0 ? _a : false);
66
66
  }
67
67
  if (guessType === undefined && needsExplicitQueryClass) {
68
- this.log("Can't determine view type (`group_by` / `aggregate` / `nest`, `project`, `index`)");
68
+ this.logError('ambiguous-view-type', {});
69
69
  guessType = query_property_interface_1.QueryClass.Project;
70
70
  }
71
71
  this.opClass = guessType;
@@ -47,13 +47,13 @@ class Top extends malloy_element_1.MalloyElement {
47
47
  const output = fs.outputSpace();
48
48
  const entry = this.by.getField(output);
49
49
  if (entry.error) {
50
- this.by.log(entry.error);
50
+ this.by.logError(entry.error.code, entry.error.message);
51
51
  }
52
52
  if (!entry.found || !entry.isOutputField) {
53
- this.by.log(`Unknown field ${this.by.refString} in output space`);
53
+ this.by.logError('top-by-not-found-in-output', `Unknown field ${this.by.refString} in output space`);
54
54
  }
55
55
  if ((0, malloy_types_1.expressionIsAnalytic)((_a = entry.found) === null || _a === void 0 ? void 0 : _a.typeDesc().expressionType)) {
56
- this.by.log(`Illegal order by of analytic field ${this.by.refString}`);
56
+ this.by.logError('top-by-analytic', `Illegal order by of analytic field ${this.by.refString}`);
57
57
  }
58
58
  }
59
59
  return { by: 'name', name: this.by.refString };
@@ -61,10 +61,10 @@ class Top extends malloy_element_1.MalloyElement {
61
61
  else {
62
62
  const byExpr = this.by.getExpression(fs);
63
63
  if ((0, malloy_types_1.expressionIsAggregate)(byExpr.expressionType)) {
64
- this.by.log('top by expression must be an aggregate');
64
+ this.by.logError('top-by-aggregate', 'top by expression must not be an aggregate');
65
65
  }
66
66
  if (byExpr.evalSpace === 'output') {
67
- this.by.log('top by expression must be an output expression');
67
+ this.by.logError('top-by-not-in-output', 'top by expression must be an output expression');
68
68
  }
69
69
  return { by: 'expression', e: byExpr.value };
70
70
  }
@@ -39,7 +39,7 @@ function detectAndRemovePartialStages(pipeline, logTo) {
39
39
  }
40
40
  }
41
41
  if (hasPartials) {
42
- logTo.log("Can't determine view type (`group_by` / `aggregate` / `nest`, `project`, `index`)");
42
+ logTo.logError('ambiguous-view-type', {});
43
43
  }
44
44
  return cleaned;
45
45
  }
@@ -2,7 +2,7 @@ import { Argument, InvokedStructRef, StructDef } from '../../../model/malloy_typ
2
2
  import { Source } from './source';
3
3
  import { ModelEntryReference } from '../types/malloy-element';
4
4
  import { Argument as HasArgument } from '../parameters/argument';
5
- import { LogSeverity } from '../../parse-log';
5
+ import { LogMessageOptions, MessageCode, MessageParameterType } from '../../parse-log';
6
6
  import { ParameterSpace } from '../field-space/parameter-space';
7
7
  import { HasParameter } from '../parameters/has-parameter';
8
8
  export declare class NamedSource extends Source {
@@ -13,7 +13,7 @@ export declare class NamedSource extends Source {
13
13
  constructor(ref: ModelEntryReference | string, sourceArguments: Record<string, Argument> | undefined, args: HasArgument[] | undefined);
14
14
  get refName(): string;
15
15
  structRef(parameterSpace: ParameterSpace | undefined): InvokedStructRef;
16
- refLog(message: string, severity?: LogSeverity): void;
16
+ refLogError<T extends MessageCode>(code: T, parameters: MessageParameterType<T>, options?: Omit<LogMessageOptions, 'severity'>): void;
17
17
  modelStruct(): StructDef | undefined;
18
18
  private evaluateArgumentsForRef;
19
19
  private evaluateArguments;
@@ -61,12 +61,12 @@ class NamedSource extends source_1.Source {
61
61
  sourceArguments: this.evaluateArgumentsForRef(parameterSpace),
62
62
  };
63
63
  }
64
- refLog(message, severity) {
64
+ refLogError(code, parameters, options) {
65
65
  if (typeof this.ref === 'string') {
66
- this.log(message, severity);
66
+ this.logError(code, parameters, options);
67
67
  }
68
68
  else {
69
- this.ref.log(message, severity);
69
+ this.ref.logError(code, parameters, options);
70
70
  }
71
71
  }
72
72
  modelStruct() {
@@ -74,24 +74,23 @@ class NamedSource extends source_1.Source {
74
74
  const modelEnt = this.modelEntry(this.ref);
75
75
  const entry = modelEnt === null || modelEnt === void 0 ? void 0 : modelEnt.entry;
76
76
  if (!entry) {
77
- const undefMsg = `Undefined source '${this.refName}'`;
78
- (this.ref instanceof malloy_element_1.ModelEntryReference ? this.ref : this).log(undefMsg);
77
+ this.refLogError('source-not-found', `Undefined source '${this.refName}'`);
79
78
  return;
80
79
  }
81
80
  if (entry.type === 'query') {
82
- this.log(`Cannot construct a source from a query '${this.refName}'`);
81
+ this.logError('invalid-source-from-query', `Cannot construct a source from a query '${this.refName}'`);
83
82
  return;
84
83
  }
85
84
  else if (entry.type === 'function') {
86
- this.log(`Cannot construct a source from a function '${this.refName}'`);
85
+ this.logError('invalid-source-from-function', `Cannot construct a source from a function '${this.refName}'`);
87
86
  return;
88
87
  }
89
88
  else if (entry.type === 'connection') {
90
- this.log(`Cannot construct a source from a connection '${this.refName}'`);
89
+ this.logError('invalid-source-from-connection', `Cannot construct a source from a connection '${this.refName}'`);
91
90
  return;
92
91
  }
93
92
  else if ((0, malloy_types_1.isSQLBlockStruct)(entry) && entry.declaredSQLBlock) {
94
- this.log(`Must use 'from_sql()' for sql source '${this.refName}'`);
93
+ this.logError('invalid-source-from-sql-block', `Must use 'from_sql()' for sql source '${this.refName}'`);
95
94
  return;
96
95
  }
97
96
  else {
@@ -115,18 +114,18 @@ class NamedSource extends source_1.Source {
115
114
  ? argument.value.fieldReference
116
115
  : undefined);
117
116
  if (id === undefined) {
118
- argument.value.log('Parameterized source arguments must be named with `parameter_name is`');
117
+ argument.value.logError('unnamed-source-argument', 'Parameterized source arguments must be named with `parameter_name is`');
119
118
  continue;
120
119
  }
121
120
  const name = id.outputName;
122
121
  if (passedNames.has(name)) {
123
- argument.log(`Cannot pass argument for \`${name}\` more than once`);
122
+ argument.logError('duplicate-source-argument', `Cannot pass argument for \`${name}\` more than once`);
124
123
  continue;
125
124
  }
126
125
  passedNames.add(name);
127
126
  const parameter = (parametersIn !== null && parametersIn !== void 0 ? parametersIn : {})[name];
128
127
  if (!parameter) {
129
- id.log(`\`${this.refName}\` has no declared parameter named \`${id.refString}\``);
128
+ id.logError('source-parameter-not-found', `\`${this.refName}\` has no declared parameter named \`${id.refString}\``);
130
129
  }
131
130
  else {
132
131
  const paramSpace = parameterSpace !== null && parameterSpace !== void 0 ? parameterSpace : new parameter_space_1.ParameterSpace(parametersOut !== null && parametersOut !== void 0 ? parametersOut : []);
@@ -147,7 +146,7 @@ class NamedSource extends source_1.Source {
147
146
  outArguments[paramName] = { ...parametersIn[paramName] };
148
147
  }
149
148
  else {
150
- this.refLog(`Argument not provided for required parameter \`${paramName}\``);
149
+ this.refLogError('missing-source-argument', `Argument not provided for required parameter \`${paramName}\``);
151
150
  }
152
151
  }
153
152
  }
@@ -64,15 +64,17 @@ class RefinedSource extends source_1.Source {
64
64
  const errTo = el;
65
65
  if (el instanceof primary_key_1.PrimaryKey) {
66
66
  if (primaryKey) {
67
- primaryKey.log('Primary key already defined');
68
- el.log('Primary key redefined');
67
+ const code = 'multiple-primary-keys';
68
+ primaryKey.logError(code, 'Primary key already defined');
69
+ el.logError(code, 'Primary key redefined');
69
70
  }
70
71
  primaryKey = el;
71
72
  }
72
73
  else if (el instanceof field_list_edit_1.FieldListEdit) {
73
74
  if (fieldListEdit) {
74
- fieldListEdit.log('Too many accept/except statements');
75
- el.log('Too many accept/except statements');
75
+ const code = 'multiple-field-list-edits';
76
+ fieldListEdit.logError(code, 'Too many accept/except statements');
77
+ el.logError(code, 'Too many accept/except statements');
76
78
  }
77
79
  fieldListEdit = el;
78
80
  }
@@ -89,7 +91,7 @@ class RefinedSource extends source_1.Source {
89
91
  newTimezone = el.tz;
90
92
  }
91
93
  else {
92
- errTo.log(`Unexpected source property: '${errTo.elementType}'`);
94
+ errTo.logError('unexpected-source-property', `Unexpected source property: '${errTo.elementType}'`);
93
95
  }
94
96
  }
95
97
  const paramSpace = pList ? new parameter_space_1.ParameterSpace(pList) : undefined;
@@ -111,7 +113,7 @@ class RefinedSource extends source_1.Source {
111
113
  if (primaryKey) {
112
114
  const keyDef = primaryKey.field.getField(fs);
113
115
  if (keyDef.error) {
114
- primaryKey.log(keyDef.error);
116
+ primaryKey.logError(keyDef.error.code, keyDef.error.message);
115
117
  }
116
118
  }
117
119
  const retStruct = fs.structDef();
@@ -121,7 +123,7 @@ class RefinedSource extends source_1.Source {
121
123
  for (const el of filter.list) {
122
124
  const fc = el.filterCondition(fs);
123
125
  if ((0, malloy_types_1.expressionIsCalculation)(fc.expressionType)) {
124
- el.log("Can't use aggregate computations in top level filters");
126
+ el.logError('aggregate-in-source-filter', "Can't use aggregate computations in top level filters");
125
127
  }
126
128
  else {
127
129
  filterList.push(fc);
@@ -55,7 +55,7 @@ class SQLSource extends source_1.Source {
55
55
  (_a = this.namespace()) === null || _a === void 0 ? void 0 : _a.setEntry(name, { entry: { type: 'connection', name }, exported: true }, true);
56
56
  }
57
57
  else if (connection.entry.type !== 'connection') {
58
- this.connectionName.log(`${this.connectionName.refString} is not a connection`);
58
+ this.connectionName.logError('invalid-connection-for-sql-source', `${this.connectionName.refString} is not a connection`);
59
59
  this.connectionNameInvalid = true;
60
60
  return false;
61
61
  }
@@ -72,7 +72,7 @@ class SQLSource extends source_1.Source {
72
72
  const sql = this.sqlBlock();
73
73
  const sqlDefEntry = (_a = this.translator()) === null || _a === void 0 ? void 0 : _a.root.sqlQueryZone;
74
74
  if (!sqlDefEntry) {
75
- this.log("Cant't look up schema for sql block");
75
+ this.logError('failed-to-fetch-sql-source-schema', "Cant't look up schema for sql block");
76
76
  return;
77
77
  }
78
78
  sqlDefEntry.reference(sql.name, this.location);
@@ -94,7 +94,7 @@ class SQLSource extends source_1.Source {
94
94
  }
95
95
  const sqlDefEntry = (_a = this.translator()) === null || _a === void 0 ? void 0 : _a.root.sqlQueryZone;
96
96
  if (!sqlDefEntry) {
97
- this.log("Cant't look up schema for sql block");
97
+ this.logError('failed-to-fetch-sql-source-schema', "Cant't look up schema for sql block");
98
98
  return error_factory_1.ErrorFactory.structDef;
99
99
  }
100
100
  const sql = this.sqlBlock();
@@ -102,7 +102,7 @@ class SQLSource extends source_1.Source {
102
102
  const lookup = sqlDefEntry.getEntry(sql.name);
103
103
  if (lookup.status === 'error') {
104
104
  const msgLines = lookup.message.split(/\r?\n/);
105
- this.select.log('Invalid SQL, ' + msgLines.join('\n '));
105
+ this.select.logError('invalid-sql-source', 'Invalid SQL, ' + msgLines.join('\n '));
106
106
  return error_factory_1.ErrorFactory.structDef;
107
107
  }
108
108
  else if (lookup.status === 'present') {
@@ -120,7 +120,7 @@ class SQLSource extends source_1.Source {
120
120
  return locStruct;
121
121
  }
122
122
  else {
123
- this.log('`connection_name.sql(...)` can currently only be used in top level source/query definitions');
123
+ this.logError('non-top-level-sql-source', '`connection_name.sql(...)` can currently only be used in top level source/query definitions');
124
124
  return error_factory_1.ErrorFactory.structDef;
125
125
  }
126
126
  }
@@ -59,7 +59,7 @@ class TableSource extends source_1.Source {
59
59
  msg = tableDefEntry.message;
60
60
  }
61
61
  }
62
- this.log(msg);
62
+ this.logError('failed-to-fetch-table-schema', msg);
63
63
  return error_factory_1.ErrorFactory.structDef;
64
64
  }
65
65
  }
@@ -80,7 +80,7 @@ class TableMethodSource extends TableSource {
80
80
  (_a = this.namespace()) === null || _a === void 0 ? void 0 : _a.setEntry(name, { entry: { type: 'connection', name }, exported: true }, true);
81
81
  }
82
82
  else if (connection.entry.type !== 'connection') {
83
- this.connectionName.log(`${this.connectionName.refString} is not a connection`);
83
+ this.connectionName.logError('invalid-connection-for-table-source', `${this.connectionName.refString} is not a connection`);
84
84
  return undefined;
85
85
  }
86
86
  return {
@@ -42,7 +42,7 @@ class Join extends malloy_element_1.MalloyElement {
42
42
  getStructDefFromExpr(parameterSpace) {
43
43
  const source = this.sourceExpr.getSource();
44
44
  if (!source) {
45
- this.sourceExpr.sqLog('Cannot great a source to join from');
45
+ this.sourceExpr.sqLog('invalid-join-source', 'Cannot create a source to join from');
46
46
  return error_factory_1.ErrorFactory.structDef;
47
47
  }
48
48
  return source.structDef(parameterSpace);
@@ -105,15 +105,15 @@ class KeyJoin extends Join {
105
105
  return;
106
106
  }
107
107
  else {
108
- this.log(`join_one: with type mismatch with primary key: ${exprX.dataType}/${pkey.type}`);
108
+ this.logError('join-on-primary-key-type-mismatch', `join_one: with type mismatch with primary key: ${exprX.dataType}/${pkey.type}`);
109
109
  }
110
110
  }
111
111
  else {
112
- this.log(`join_one: Primary key '${pkey}' not found in source`);
112
+ this.logError('join-primary-key-not-found', `join_one: Primary key '${pkey}' not found in source`);
113
113
  }
114
114
  }
115
115
  else {
116
- this.log('join_one: Cannot use with unless source has a primary key');
116
+ this.logError('join-with-without-primary-key', 'join_one: Cannot use with unless source has a primary key');
117
117
  }
118
118
  }
119
119
  }
@@ -140,7 +140,7 @@ class ExpressionJoin extends Join {
140
140
  }
141
141
  const exprX = this.expr.getExpression(outer);
142
142
  if (exprX.dataType !== 'boolean') {
143
- this.log('join conditions must be boolean expressions');
143
+ this.logError('non-boolean-join-on', 'join conditions must be boolean expressions');
144
144
  return;
145
145
  }
146
146
  const joinRel = inStruct.structRelationship;
@@ -152,7 +152,7 @@ class ExpressionJoin extends Join {
152
152
  var _a;
153
153
  const source = this.sourceExpr.getSource();
154
154
  if (!source) {
155
- this.sourceExpr.sqLog('Cannot create a source to join from');
155
+ this.sourceExpr.sqLog('invalid-join-source', 'Cannot create a source to join from');
156
156
  return error_factory_1.ErrorFactory.structDef;
157
157
  }
158
158
  const sourceDef = source.structDef(parameterSpace);
@@ -36,7 +36,7 @@ class RenameField extends malloy_element_1.MalloyElement {
36
36
  }
37
37
  makeEntry(fs) {
38
38
  if (this.oldName.refString === this.newName) {
39
- this.log("Can't rename field to itself");
39
+ this.logError('invalid-rename-with-same-name', "Can't rename field to itself");
40
40
  return;
41
41
  }
42
42
  const oldValue = this.oldName.getField(fs);
@@ -45,11 +45,11 @@ class RenameField extends malloy_element_1.MalloyElement {
45
45
  fs.renameEntry(this.oldName.refString, this.newName, new rename_space_field_1.RenameSpaceField(oldValue.found, this.newName, this.location));
46
46
  }
47
47
  else {
48
- this.log(`'${this.oldName}' cannot be renamed`);
48
+ this.logError('failed-rename', `'${this.oldName}' cannot be renamed`);
49
49
  }
50
50
  }
51
51
  else {
52
- this.log(`Can't rename '${this.oldName}', no such field`);
52
+ this.logError('rename-field-not-found', `Can't rename '${this.oldName}', no such field`);
53
53
  }
54
54
  }
55
55
  }
@@ -1,6 +1,7 @@
1
1
  import { QueryElement } from '../types/query-element';
2
2
  import { MalloyElement } from '../types/malloy-element';
3
3
  import { Source } from '../source-elements/source';
4
+ import { LogMessageOptions, MessageCode, MessageParameterType } from '../../parse-log';
4
5
  /**
5
6
  * An AST element which can be treated as either a source or a query
6
7
  * depending on context. For instance, an `SQReference` represents
@@ -12,6 +13,6 @@ export declare abstract class SourceQueryElement extends MalloyElement {
12
13
  getSource(): Source | undefined;
13
14
  getQuery(): QueryElement | undefined;
14
15
  isSource(): boolean;
15
- sqLog(message: string): void;
16
+ sqLog<T extends MessageCode>(code: T, parameters: MessageParameterType<T>, options?: LogMessageOptions): T;
16
17
  isErrorFree(): boolean;
17
18
  }
@@ -44,11 +44,12 @@ class SourceQueryElement extends malloy_element_1.MalloyElement {
44
44
  isSource() {
45
45
  return false;
46
46
  }
47
- sqLog(message) {
47
+ sqLog(code, parameters, options) {
48
48
  if (this.isErrorFree()) {
49
- this.log(message);
49
+ this.logError(code, parameters, options);
50
50
  }
51
51
  this.errored = true;
52
+ return code;
52
53
  }
53
54
  isErrorFree() {
54
55
  if (this.errored) {
@@ -48,7 +48,7 @@ class SQArrow extends source_query_element_1.SourceQueryElement {
48
48
  ? this.applyTo.getSource()
49
49
  : this.applyTo.getQuery();
50
50
  if (lhs === undefined) {
51
- this.sqLog('Could not get LHS of arrow operation');
51
+ this.sqLog('failed-to-compute-arrow-source', 'Could not get LHS of arrow operation');
52
52
  return;
53
53
  }
54
54
  const arr = new query_arrow_1.QueryArrow(lhs, this.operation);
@@ -58,7 +58,7 @@ class SQArrow extends source_query_element_1.SourceQueryElement {
58
58
  getSource() {
59
59
  const query = this.getQuery();
60
60
  if (!query) {
61
- this.sqLog("Couldn't comprehend query well enough to make a source");
61
+ this.sqLog('failed-to-compute-source-from-query', "Couldn't comprehend query well enough to make a source");
62
62
  return;
63
63
  }
64
64
  const asSource = new query_source_1.QuerySource(query);
@@ -49,7 +49,7 @@ class SQExtend extends source_query_element_1.SourceQueryElement {
49
49
  this.has({ asSource: this.asSource });
50
50
  return this.asSource;
51
51
  }
52
- this.sqLog('Could not compute source to extend');
52
+ this.sqLog('failed-to-compute-source-to-extend', 'Could not compute source to extend');
53
53
  }
54
54
  isSource() {
55
55
  return true;