@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
@@ -51,11 +51,11 @@ class SQReference extends source_query_element_1.SourceQueryElement {
51
51
  return query;
52
52
  }
53
53
  else {
54
- this.sqLog(`Illegal reference to '${entry.as || entry.name}', query expected`);
54
+ this.sqLog(`cannot-use-${entry.type}-as-query`, `Illegal reference to '${entry.as || entry.name}', query expected`);
55
55
  }
56
56
  }
57
57
  else {
58
- this.ref.log(`Reference to undefined object '${this.ref.refString}'`);
58
+ this.ref.logError('source-or-query-not-found', `Reference to undefined object '${this.ref.refString}'`);
59
59
  this.errored = true;
60
60
  }
61
61
  return;
@@ -70,13 +70,13 @@ class SQReference extends source_query_element_1.SourceQueryElement {
70
70
  }
71
71
  const entry = this.ref.getNamed();
72
72
  if (!entry) {
73
- this.ref.log(`Reference to undefined object '${this.ref.refString}'`);
73
+ this.ref.logError('source-not-found', `Reference to undefined object '${this.ref.refString}'`);
74
74
  this.errored = true;
75
75
  return;
76
76
  }
77
77
  if (entry.type === 'query') {
78
78
  if (this.args !== undefined) {
79
- this.ref.log('Arguments cannot be passed to queries');
79
+ this.ref.logError('illegal-query-argument', 'Arguments cannot be passed to queries');
80
80
  }
81
81
  const existingQuery = new query_reference_1.QueryReference(this.ref);
82
82
  this.asSource = new query_source_1.QuerySource(existingQuery);
@@ -85,7 +85,7 @@ class SQReference extends source_query_element_1.SourceQueryElement {
85
85
  this.asSource = new named_source_1.NamedSource(this.ref, undefined, this.args);
86
86
  }
87
87
  else {
88
- this.sqLog(`Expected '${this.ref.refString}' to be of type query or source, not '${entry.type}'`);
88
+ this.sqLog(`cannot-use-${entry.type}-as-source`, `Expected '${this.ref.refString}' to be of type query or source, not '${entry.type}'`);
89
89
  return;
90
90
  }
91
91
  this.has({ asSource: this.asSource });
@@ -43,10 +43,10 @@ class SQRefine extends source_query_element_1.SourceQueryElement {
43
43
  getQuery() {
44
44
  if (this.toRefine.isSource()) {
45
45
  if (this.toRefine instanceof sq_reference_1.SQReference) {
46
- this.sqLog(`Cannot add view refinements to '${this.toRefine.ref.refString}' because it is a source`);
46
+ this.sqLog('illegal-refinement-of-source', `Cannot add view refinements to '${this.toRefine.ref.refString}' because it is a source`);
47
47
  }
48
48
  else {
49
- this.sqLog('Cannot add view refinements to a source');
49
+ this.sqLog('illegal-refinement-of-source', 'Cannot add view refinements to a source');
50
50
  }
51
51
  return;
52
52
  }
@@ -51,7 +51,7 @@ class SQSource extends source_query_element_1.SourceQueryElement {
51
51
  return rawQuery;
52
52
  }
53
53
  else {
54
- this.sqLog('This source cannot be used as a query');
54
+ this.sqLog('invalid-source-as-query', 'This source cannot be used as a query');
55
55
  }
56
56
  }
57
57
  }
@@ -49,7 +49,7 @@ class SQLString extends malloy_element_1.MalloyElement {
49
49
  el.parent = this;
50
50
  }
51
51
  else {
52
- el.log('This element is not legal inside an SQL string');
52
+ el.logError('invalid-sql-source-interpolation', 'This element is not legal inside an SQL string');
53
53
  }
54
54
  }
55
55
  sqlPhrases() {
@@ -64,7 +64,7 @@ class SQLString extends malloy_element_1.MalloyElement {
64
64
  ret.push(queryObject.query());
65
65
  }
66
66
  else {
67
- el.sqLog('Cannot expand into a query');
67
+ el.sqLog('failed-to-expand-sql-source', 'Cannot expand into a query');
68
68
  }
69
69
  }
70
70
  }
@@ -37,12 +37,12 @@ class DefineQuery extends malloy_element_1.MalloyElement {
37
37
  execute(doc) {
38
38
  const existing = doc.getEntry(this.name);
39
39
  if (existing) {
40
- this.log(`'${this.name}' is already defined, cannot redefine`);
40
+ this.logError('query-definition-name-conflict', `'${this.name}' is already defined, cannot redefine`);
41
41
  return;
42
42
  }
43
43
  const queryEl = this.queryExpr.getQuery();
44
44
  if (!queryEl) {
45
- this.queryExpr.sqLog('Cannot define a query from this expression');
45
+ this.queryExpr.sqLog('query-definition-from-non-query', 'Cannot define a query from this expression');
46
46
  return;
47
47
  }
48
48
  const entry = {
@@ -46,7 +46,7 @@ class DefineSource extends malloy_element_1.MalloyElement {
46
46
  execute(doc) {
47
47
  var _a;
48
48
  if (doc.modelEntry(this.name)) {
49
- this.log(`Cannot redefine '${this.name}'`);
49
+ this.logError('source-definition-name-conflict', `Cannot redefine '${this.name}'`);
50
50
  }
51
51
  else {
52
52
  const theSource = (_a = this.sourceExpr) === null || _a === void 0 ? void 0 : _a.getSource();
@@ -79,7 +79,7 @@ class DefineSource extends malloy_element_1.MalloyElement {
79
79
  const out = [];
80
80
  for (const parameter of this.parameters) {
81
81
  if (parameter.name in exists) {
82
- parameter.log(`Cannot redefine parameter \`${parameter.name}\``);
82
+ parameter.logError('parameter-name-conflict', `Cannot redefine parameter \`${parameter.name}\``);
83
83
  }
84
84
  exists[parameter.name] = true;
85
85
  out.push(parameter);
@@ -89,7 +89,7 @@ class DefineSource extends malloy_element_1.MalloyElement {
89
89
  validateParameterShadowing(parameters, structDef) {
90
90
  for (const parameter of parameters) {
91
91
  if (structDef.fields.find(field => { var _a; return ((_a = field.as) !== null && _a !== void 0 ? _a : field.name) === parameter.name; })) {
92
- parameter.log(`Illegal shadowing of field \`${parameter.name}\` by parameter with the same name`);
92
+ parameter.logError('parameter-shadowing-field', `Illegal shadowing of field \`${parameter.name}\` by parameter with the same name`);
93
93
  }
94
94
  }
95
95
  }
@@ -61,7 +61,7 @@ class ImportStatement extends malloy_element_1.ListOf {
61
61
  this.fullURL = decodeURI(new URL(url, baseURL).toString());
62
62
  }
63
63
  catch (e) {
64
- this.log('Invalid URI in import statement');
64
+ this.logError('invalid-import-url', 'Invalid URL in import statement');
65
65
  }
66
66
  }
67
67
  needs() {
@@ -79,7 +79,7 @@ class ImportStatement extends malloy_element_1.ListOf {
79
79
  execute(doc) {
80
80
  const trans = this.translator();
81
81
  if (!trans) {
82
- this.log('Cannot import without translation context');
82
+ this.logError('no-translator-for-import', 'Cannot import without translation context');
83
83
  }
84
84
  else if (this.fullURL) {
85
85
  const src = trans.root.importZone.getEntry(this.fullURL);
@@ -90,7 +90,7 @@ class ImportStatement extends malloy_element_1.ListOf {
90
90
  for (const importOne of this.list) {
91
91
  const fetchName = importOne.from || importOne;
92
92
  if (doc.getEntry(importOne.text)) {
93
- importOne.log(`Cannot redefine '${importOne.text}'`);
93
+ importOne.logError('name-conflict-on-selective-import', `Cannot redefine '${importOne.text}'`);
94
94
  }
95
95
  else if (importable[fetchName.text]) {
96
96
  const importMe = { ...importable[fetchName.text] };
@@ -100,7 +100,7 @@ class ImportStatement extends malloy_element_1.ListOf {
100
100
  doc.setEntry(importOne.text, { entry: importMe, exported: false });
101
101
  }
102
102
  else {
103
- fetchName.log(`Cannot find '${fetchName.text}', not imported`);
103
+ fetchName.logError('selective-import-not-found', `Cannot find '${fetchName.text}', not imported`);
104
104
  }
105
105
  }
106
106
  }
@@ -108,7 +108,7 @@ class ImportStatement extends malloy_element_1.ListOf {
108
108
  // import everything
109
109
  for (const [importing, entry] of Object.entries(trans.getChildExports(this.fullURL))) {
110
110
  if (doc.getEntry(importing)) {
111
- this.log(`Cannot redefine '${importing}'`);
111
+ this.logError('name-conflict-on-indiscriminate-import', `Cannot redefine '${importing}'`);
112
112
  }
113
113
  else {
114
114
  doc.setEntry(importing, { entry, exported: false });
@@ -117,10 +117,10 @@ class ImportStatement extends malloy_element_1.ListOf {
117
117
  }
118
118
  }
119
119
  else if (src.status === 'error') {
120
- this.log(`import failed: '${src.message}'`);
120
+ this.logError('failed-import', `import failed: '${src.message}'`);
121
121
  }
122
122
  else {
123
- this.log(`import failed with status: '${src.status}'`);
123
+ this.logError('failed-import', `import failed with status: '${src.status}'`);
124
124
  }
125
125
  }
126
126
  }
@@ -40,7 +40,7 @@ function opOutputStruct(logTo, inputStruct, opDesc) {
40
40
  return malloy_query_1.Segment.nextStructDef(inputStruct, opDesc);
41
41
  }
42
42
  catch (e) {
43
- logTo.log(`INTERNAL ERROR model/Segment.nextStructDef: ${e.message}\n` +
43
+ logTo.logError('failed-to-compute-output-schema', `INTERNAL ERROR model/Segment.nextStructDef: ${e.message}\n` +
44
44
  `QUERY: ${(0, util_1.inspect)(opDesc, { breakLength: 72, depth: Infinity })}`);
45
45
  }
46
46
  }
@@ -66,9 +66,14 @@ class ExpressionDef extends malloy_element_1.MalloyElement {
66
66
  */
67
67
  typeCheck(eNode, eVal) {
68
68
  if (eVal.dataType !== 'error' && !fragtype_utils_1.FT.in(eVal, this.legalChildTypes)) {
69
- eNode.log(eVal.dataType === 'sql native'
70
- ? `'${this.elementType}' Can't be used with unsupported SQL native type '${eVal.rawType}'[unsupported-sql-native-type-not-allowed-in-expression]`
71
- : `'${this.elementType}' Can't use type ${fragtype_utils_1.FT.inspect(eVal)}`);
69
+ if (eVal.dataType === 'sql native') {
70
+ eNode.logError('sql-native-not-allowed-in-expression', {
71
+ rawType: eVal.rawType,
72
+ });
73
+ }
74
+ else {
75
+ eNode.logError('expression-type-error', `'${this.elementType}' Can't use type ${fragtype_utils_1.FT.inspect(eVal)}`);
76
+ }
72
77
  return false;
73
78
  }
74
79
  return true;
@@ -114,7 +119,7 @@ class ExprDuration extends ExpressionDef {
114
119
  if ((0, malloy_types_1.isTimeFieldType)(lhs.dataType) && (op === '+' || op === '-')) {
115
120
  const num = this.n.getExpression(fs);
116
121
  if (!fragtype_utils_1.FT.typeEq(num, fragtype_utils_1.FT.numberT)) {
117
- this.log(`Duration units needs number not '${num.dataType}`);
122
+ this.logError('invalid-duration-quantity', `Duration quantity needs number not '${num.dataType}`);
118
123
  return (0, ast_utils_1.errorFor)('illegal unit expression');
119
124
  }
120
125
  let resultGranularity;
@@ -142,8 +147,7 @@ class ExprDuration extends ExpressionDef {
142
147
  }, resultGranularity);
143
148
  }
144
149
  else {
145
- this.log(`Cannot offset date by ${this.timeframe}`);
146
- return (0, ast_utils_1.errorFor)('ofsset date error');
150
+ return this.loggedErrorExpr('invalid-timeframe-for-time-offset', `Cannot offset date by ${this.timeframe}`);
147
151
  }
148
152
  }
149
153
  return super.apply(fs, op, left);
@@ -196,7 +200,7 @@ function timeCompare(left, lhs, op, rhs) {
196
200
  if ((leftIsTime || rightIsTime) &&
197
201
  lhs.dataType !== 'null' &&
198
202
  rhs.dataType !== 'null') {
199
- left.log(`Cannot compare a ${lhs.dataType} to a ${rhs.dataType}`);
203
+ left.logError('time-comparison-type-mismatch', `Cannot compare a ${lhs.dataType} to a ${rhs.dataType}`);
200
204
  return { node: 'false' };
201
205
  }
202
206
  return undefined;
@@ -319,15 +323,14 @@ function numeric(fs, left, op, right) {
319
323
  if (err)
320
324
  return err;
321
325
  const noGo = unsupportError(left, lhs, right, rhs);
322
- if (noGo) {
326
+ if (noGo)
323
327
  return noGo;
324
- }
325
328
  const expressionType = (0, malloy_types_1.maxExpressionType)(lhs.expressionType, rhs.expressionType);
326
329
  if (lhs.dataType !== 'number') {
327
- left.log(`The '${op}' operator requires a number, not a '${lhs.dataType}'`);
330
+ left.logError('arithmetic-operation-type-mismatch', `The '${op}' operator requires a number, not a '${lhs.dataType}'`);
328
331
  }
329
332
  else if (rhs.dataType !== 'number') {
330
- right.log(`The '${op}' operator requires a number, not a '${rhs.dataType}'`);
333
+ right.logError('arithmetic-operation-type-mismatch', `The '${op}' operator requires a number, not a '${rhs.dataType}'`);
331
334
  }
332
335
  else {
333
336
  return {
@@ -360,8 +363,7 @@ function delta(fs, left, op, right) {
360
363
  duration = new ExprDuration(right, 'day');
361
364
  }
362
365
  else {
363
- left.log(`Can not offset time by '${rhs.dataType}'`);
364
- return (0, ast_utils_1.errorFor)(`time plus ${rhs.dataType}`);
366
+ return left.loggedErrorExpr('time-offset-type-mismatch', `Can not offset time by '${rhs.dataType}'`);
365
367
  }
366
368
  }
367
369
  return duration.apply(fs, op, left);
@@ -396,18 +398,16 @@ function applyBinary(fs, left, op, right) {
396
398
  const num = left.getExpression(fs);
397
399
  const denom = right.getExpression(fs);
398
400
  const noGo = unsupportError(left, num, right, denom);
399
- if (noGo) {
400
- left.log(`Cannot use '${op}' with sql native type`);
401
+ if (noGo)
401
402
  return noGo;
402
- }
403
403
  const err = errorCascade('number', num, denom);
404
404
  if (err)
405
405
  return err;
406
406
  if (num.dataType !== 'number') {
407
- left.log('Numerator must be a number');
407
+ left.logError('arithmetic-operation-type-mismatch', 'Numerator must be a number');
408
408
  }
409
409
  else if (denom.dataType !== 'number') {
410
- right.log('Denominator must be a number');
410
+ right.logError('arithmetic-operation-type-mismatch', 'Denominator must be a number');
411
411
  }
412
412
  else {
413
413
  const divmod = {
@@ -423,8 +423,7 @@ function applyBinary(fs, left, op, right) {
423
423
  }
424
424
  return (0, ast_utils_1.errorFor)('divide type mismatch');
425
425
  }
426
- left.log(`Cannot use ${op} operator here`);
427
- return (0, ast_utils_1.errorFor)('applybinary bad operator');
426
+ return left.loggedErrorExpr('unexpected-binary-operator', `Cannot use ${op} operator here`);
428
427
  }
429
428
  exports.applyBinary = applyBinary;
430
429
  function errorCascade(dataType, ...es) {
@@ -448,12 +447,12 @@ function unsupportError(l, lhs, r, rhs) {
448
447
  evalSpace: (0, malloy_types_1.mergeEvalSpaces)(lhs.evalSpace, rhs.evalSpace),
449
448
  };
450
449
  if (lhs.dataType === 'sql native') {
451
- l.log(`Unsupported SQL native type '${lhs.rawType}' not allowed in expression[unsupported-sql-native-type-not-allowed-in-expression]`);
450
+ l.logError('sql-native-not-allowed-in-expression', { rawType: lhs.rawType });
452
451
  ret.dataType = rhs.dataType;
453
452
  return ret;
454
453
  }
455
454
  if (rhs.dataType === 'sql native') {
456
- r.log(`Unsupported SQL native type '${rhs.rawType}' not allowed in expression[unsupported-sql-native-type-not-allowed-in-expression]`);
455
+ r.logError('sql-native-not-allowed-in-expression', { rawType: rhs.rawType });
457
456
  return ret;
458
457
  }
459
458
  return undefined;
@@ -1,4 +1,5 @@
1
1
  import { StructRelationship } from '../../../model';
2
+ import { MessageCode } from '../../parse-log';
2
3
  import { SpaceEntry } from './space-entry';
3
4
  export interface LookupFound {
4
5
  found: SpaceEntry;
@@ -10,7 +11,10 @@ export interface LookupFound {
10
11
  isOutputField: boolean;
11
12
  }
12
13
  export interface LookupError {
13
- error: string;
14
+ error: {
15
+ message: string;
16
+ code: MessageCode;
17
+ };
14
18
  found: undefined;
15
19
  }
16
20
  export type LookupResult = LookupFound | LookupError;
@@ -1,9 +1,10 @@
1
1
  import { Annotation, DocumentLocation, DocumentReference, ModelDef, ModelAnnotation, NamedModelObject, Query, SQLBlockStructDef, StructDef } from '../../../model/malloy_types';
2
2
  import { Tag } from '../../../tags';
3
- import { LogSeverity, MessageLogger } from '../../parse-log';
3
+ import { LogMessageOptions, MessageLogger, MessageParameterType, MessageCode } from '../../parse-log';
4
4
  import { MalloyTranslation } from '../../parse-malloy';
5
5
  import { ModelDataRequest } from '../../translate-response';
6
6
  import { DocumentCompileResult } from './document-compile-result';
7
+ import { ExprValue } from './expr-value';
7
8
  import { ModelEntry } from './model-entry';
8
9
  import { NameSpace } from './name-space';
9
10
  import { Noteable, extendNoteMethod } from './noteable';
@@ -38,10 +39,12 @@ export declare abstract class MalloyElement {
38
39
  setTranslator(x: MalloyTranslation): void;
39
40
  addReference(reference: DocumentReference): void;
40
41
  private get sourceURL();
41
- errorsExist(): boolean;
42
42
  private readonly logged;
43
- log(message: string, severity?: LogSeverity): void;
44
- logger(): MessageLogger | undefined;
43
+ private log;
44
+ logError<T extends MessageCode>(code: T, parameters: MessageParameterType<T>, options?: Omit<LogMessageOptions, 'severity'>): T;
45
+ logWarning<T extends MessageCode>(code: T, parameters: MessageParameterType<T>, options?: Omit<LogMessageOptions, 'severity'>): T;
46
+ loggedErrorExpr<T extends MessageCode>(code: T, parameters: MessageParameterType<T>, options?: LogMessageOptions): ExprValue;
47
+ get logger(): MessageLogger;
45
48
  /**
46
49
  * Mostly for debugging / testing. A string-y version of this object which
47
50
  * is used to ask "are these two AST segments equal". Formatted so that
@@ -54,7 +57,7 @@ export declare abstract class MalloyElement {
54
57
  private varInfo;
55
58
  protected internalError(msg: string): Error;
56
59
  needs(doc: Document): ModelDataRequest | undefined;
57
- inExperiment(experimentID: string, silent?: boolean): boolean;
60
+ inExperiment(experimentId: string, silent?: boolean): boolean;
58
61
  }
59
62
  export declare class Unimplemented extends MalloyElement {
60
63
  elementType: string;
@@ -26,6 +26,8 @@ exports.Document = exports.DocStatementList = exports.ListOf = exports.isDocStat
26
26
  const dialect_1 = require("../../../dialect");
27
27
  const malloy_types_1 = require("../../../model/malloy_types");
28
28
  const tags_1 = require("../../../tags");
29
+ const parse_log_1 = require("../../parse-log");
30
+ const ast_utils_1 = require("../ast-utils");
29
31
  const dialect_name_space_1 = require("./dialect-name-space");
30
32
  const global_name_space_1 = require("./global-name-space");
31
33
  const noteable_1 = require("./noteable");
@@ -166,35 +168,40 @@ class MalloyElement {
166
168
  const trans = this.translator();
167
169
  return (trans === null || trans === void 0 ? void 0 : trans.sourceURL) || '(missing)';
168
170
  }
169
- errorsExist() {
170
- const logger = this.logger();
171
- if (logger) {
172
- return logger.hasErrors();
173
- }
174
- return false;
175
- }
176
- log(message, severity = 'error') {
171
+ log(code, parameters, options) {
172
+ const log = (0, parse_log_1.makeLogMessage)(code, parameters, {
173
+ at: this.location,
174
+ ...options,
175
+ });
177
176
  if (this.codeLocation) {
178
177
  /*
179
178
  * If this element has a location, then don't report the same
180
179
  * error message at the same location more than once
181
180
  */
182
- if (this.logged.has(message)) {
183
- return;
181
+ if (this.logged.has(log.message)) {
182
+ return code;
184
183
  }
185
- this.logged.add(message);
186
- }
187
- const msg = { at: this.location, message, severity };
188
- const logTo = this.logger();
189
- if (logTo) {
190
- logTo.log(msg);
191
- return;
184
+ this.logged.add(log.message);
192
185
  }
193
- throw new Error(`Translation error (without error reporter):\n${JSON.stringify(msg, null, 2)}`);
186
+ this.logger.log(log);
187
+ return code;
194
188
  }
195
- logger() {
189
+ logError(code, parameters, options) {
190
+ return this.log(code, parameters, { severity: 'error', ...options });
191
+ }
192
+ logWarning(code, parameters, options) {
193
+ return this.log(code, parameters, { severity: 'warn', ...options });
194
+ }
195
+ loggedErrorExpr(code, parameters, options) {
196
+ return (0, ast_utils_1.errorFor)(this.logError(code, parameters, options));
197
+ }
198
+ get logger() {
196
199
  var _a;
197
- return (_a = this.translator()) === null || _a === void 0 ? void 0 : _a.root.logger;
200
+ const logger = (_a = this.translator()) === null || _a === void 0 ? void 0 : _a.root.logger;
201
+ if (logger === undefined) {
202
+ throw new Error('Attempted to access logger without a translator');
203
+ }
204
+ return logger;
198
205
  }
199
206
  /**
200
207
  * Mostly for debugging / testing. A string-y version of this object which
@@ -254,8 +261,7 @@ class MalloyElement {
254
261
  return extra;
255
262
  }
256
263
  internalError(msg) {
257
- this.log(`INTERNAL ERROR IN TRANSLATION: ${msg}`);
258
- return new Error(msg);
264
+ return new Error(`INTERNAL ERROR IN TRANSLATION: ${msg}`);
259
265
  }
260
266
  needs(doc) {
261
267
  for (const child of this.walk()) {
@@ -264,15 +270,15 @@ class MalloyElement {
264
270
  return childNeeds;
265
271
  }
266
272
  }
267
- inExperiment(experimentID, silent = false) {
273
+ inExperiment(experimentId, silent = false) {
268
274
  var _a;
269
275
  const experimental = (_a = this.translator()) === null || _a === void 0 ? void 0 : _a.compilerFlags.tag('experimental');
270
- const enabled = experimental && (experimental.bare() || experimental.has(experimentID));
276
+ const enabled = experimental && (experimental.bare() || experimental.has(experimentId));
271
277
  if (enabled) {
272
278
  return true;
273
279
  }
274
280
  if (!silent) {
275
- this.log(`Experimental flag '${experimentID}' is not set, feature not available`);
281
+ this.logError('experiment-not-enabled', { experimentId });
276
282
  }
277
283
  return false;
278
284
  }
@@ -551,7 +557,7 @@ class Document extends MalloyElement {
551
557
  setEntry(str, ent) {
552
558
  // TODO this error message is going to be in the wrong place everywhere...
553
559
  if (this.globalNameSpace.getEntry(str) !== undefined) {
554
- this.log(`Cannot redefine '${str}', which is in global namespace`);
560
+ this.logError('name-conflict-with-global', `Cannot redefine '${str}', which is in global namespace`);
555
561
  }
556
562
  if (ent.entry.type === 'struct') {
557
563
  this.checkExperimentalDialect(this, ent.entry.dialect);
@@ -570,7 +576,7 @@ class Document extends MalloyElement {
570
576
  t.firstReferenceToDialect(dialect) &&
571
577
  (0, dialect_1.getDialect)(dialect).experimental &&
572
578
  !t.experimentalDialectEnabled(dialect)) {
573
- me.log(`Requires compiler flag '##! experimental.dialect.${dialect}'`, 'error');
579
+ me.logError('experimental-dialect-not-enabled', { dialect });
574
580
  }
575
581
  }
576
582
  getDialectNamespace(dialectName) {
@@ -49,7 +49,7 @@ class QOpDescView extends view_1.View {
49
49
  }
50
50
  getOp(inputFS, isNestIn, qOpDesc, refineThis) {
51
51
  if ((0, malloy_types_1.isRawSegment)(refineThis)) {
52
- this.log('A raw query cannot be refined');
52
+ this.logError('refinement-of-raw-query', 'A raw query cannot be refined');
53
53
  return refineThis;
54
54
  }
55
55
  qOpDesc.refineFrom(refineThis);
@@ -72,13 +72,13 @@ class QOpDescView extends view_1.View {
72
72
  headRefinements.push(qop);
73
73
  break;
74
74
  case query_property_interface_1.LegalRefinementStage.Single:
75
- qop.log('Illegal in refinement of a query with more than one stage');
75
+ qop.logError('illegal-multistage-refinement-operation', 'Illegal in refinement of a query with more than one stage');
76
76
  break;
77
77
  case query_property_interface_1.LegalRefinementStage.Tail:
78
78
  tailRefinements.push(qop);
79
79
  break;
80
80
  default:
81
- qop.log('Illegal query refinement');
81
+ qop.logError('illegal-refinement-operation', 'Illegal query refinement');
82
82
  }
83
83
  }
84
84
  if (headRefinements.notEmpty()) {
@@ -60,7 +60,7 @@ class ReferenceView extends view_1.View {
60
60
  };
61
61
  };
62
62
  if (!lookup.found) {
63
- this.log(`\`${this.reference.refString}\` is not defined`);
63
+ this.logError('view-not-found', `\`${this.reference.refString}\` is not defined`);
64
64
  return oops();
65
65
  }
66
66
  if (!(lookup.found instanceof space_field_1.SpaceField)) {
@@ -95,10 +95,10 @@ class ReferenceView extends view_1.View {
95
95
  else if ((0, malloy_types_1.isTurtleDef)(fieldDef)) {
96
96
  if (this.reference.list.length > 1) {
97
97
  if (forRefinement) {
98
- this.log('Cannot use view from join as refinement');
98
+ this.logError('refinement-with-joined-view', 'Cannot use view from join as refinement');
99
99
  }
100
100
  else {
101
- this.log('Cannot use view from join');
101
+ this.logError('nest-of-joined-view', 'Cannot use view from join');
102
102
  }
103
103
  return oops();
104
104
  }
@@ -111,10 +111,10 @@ class ReferenceView extends view_1.View {
111
111
  }
112
112
  else {
113
113
  if (forRefinement) {
114
- this.reference.log(`named refinement \`${this.reference.refString}\` must be a view, found a ${fieldDef.type}`);
114
+ this.reference.logError('refinement-with-source', `named refinement \`${this.reference.refString}\` must be a view, found a ${fieldDef.type}`);
115
115
  }
116
116
  else {
117
- this.reference.log('This operation is not supported');
117
+ this.reference.logError('nest-of-source', 'This operation is not supported');
118
118
  }
119
119
  return oops();
120
120
  }
@@ -126,7 +126,7 @@ class ReferenceView extends view_1.View {
126
126
  if (error)
127
127
  return;
128
128
  if (pipeline.length !== 1) {
129
- this.reference.log(`named refinement \`${this.reference.refString}\` must have exactly one stage`);
129
+ this.reference.logError('refinement-with-multistage-view', `named refinement \`${this.reference.refString}\` must have exactly one stage`);
130
130
  return;
131
131
  }
132
132
  return pipeline[0];
@@ -29,14 +29,14 @@ function refine(logTo, refineTo, refineFrom) {
29
29
  var _a, _b;
30
30
  // TODO we should probably support this
31
31
  if (refineTo.length !== 1) {
32
- logTo.log('Named refinements of multi-stage views are not supported');
32
+ logTo.logError('refinement-with-multistage-view', 'Named refinements of multi-stage views are not supported');
33
33
  // TODO better error pipeline?
34
34
  return refineTo;
35
35
  }
36
36
  const to = { ...refineTo[0] };
37
37
  const from = refineFrom;
38
38
  if ((0, model_1.isRawSegment)(to)) {
39
- logTo.log('Cannot refine raw query, must add an explicit query stage');
39
+ logTo.logError('refinement-of-raw-query', 'Cannot refine raw query, must add an explicit query stage');
40
40
  }
41
41
  else {
42
42
  // TODO need to disallow partial + index for now to make the types happy
@@ -44,7 +44,7 @@ function refine(logTo, refineTo, refineFrom) {
44
44
  to.type = from.type;
45
45
  }
46
46
  else if (from.type !== to.type) {
47
- logTo.log(`cannot refine ${to.type} view with ${from.type} view`);
47
+ logTo.logError('mismatched-view-types-for-refinement', `cannot refine ${to.type} view with ${from.type} view`);
48
48
  }
49
49
  if (from.type !== 'index' && to.type !== 'index' && from.type !== 'raw') {
50
50
  if (from.orderBy !== undefined || from.by !== undefined) {
@@ -57,7 +57,7 @@ function refine(logTo, refineTo, refineFrom) {
57
57
  }
58
58
  }
59
59
  else {
60
- logTo.log('refinement cannot override existing ordering');
60
+ logTo.logError('ordering-overridden-in-refinement', 'refinement cannot override existing ordering');
61
61
  }
62
62
  }
63
63
  if (from.limit !== undefined) {
@@ -65,7 +65,7 @@ function refine(logTo, refineTo, refineFrom) {
65
65
  to.limit = from.limit;
66
66
  }
67
67
  else {
68
- logTo.log('refinement cannot override existing limit');
68
+ logTo.logError('limit-overridden-in-refinement', 'refinement cannot override existing limit');
69
69
  }
70
70
  }
71
71
  }
@@ -90,7 +90,7 @@ function refine(logTo, refineTo, refineFrom) {
90
90
  }
91
91
  to.queryFields = [...to.queryFields, ...nonOverlappingFields];
92
92
  if (overlappingFields.length > 0) {
93
- logTo.log(`overlapping fields in refinement: ${overlappingFields.map(field_utils_1.nameFromDef)}`);
93
+ logTo.logError('name-conflict-in-refinement', `overlapping fields in refinement: ${overlappingFields.map(field_utils_1.nameFromDef)}`);
94
94
  }
95
95
  }
96
96
  else if (from.type === 'index' && to.type === 'index') {
@@ -48,7 +48,7 @@ class ViewArrow extends view_1.View {
48
48
  };
49
49
  }
50
50
  refine(_inputFS, _pipeline, _isNestIn) {
51
- this.log('A multi-segment view cannot be used as a refinement');
51
+ this.logError('refinement-with-multistage-view', 'A multi-segment view cannot be used as a refinement');
52
52
  return [];
53
53
  }
54
54
  getImplicitName() {
@@ -54,7 +54,7 @@ class ViewRefine extends view_1.View {
54
54
  refine(inputFS, pipeline, isNestIn) {
55
55
  const refineFrom = this.pipeline(inputFS, isNestIn);
56
56
  if (refineFrom.length !== 1) {
57
- this.refinement.log('refinement must have exactly one stage');
57
+ this.refinement.logError('refinement-with-multistage-view', 'refinement must have exactly one stage');
58
58
  // TODO better error pipeline?
59
59
  return pipeline;
60
60
  }