@malloydata/malloy 0.0.43-dev230621191254 → 0.0.43-dev230622144455

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.
@@ -33,7 +33,7 @@ exports.errorFor = void 0;
33
33
  */
34
34
  function errorFor(reason) {
35
35
  return {
36
- dataType: 'unknown',
36
+ dataType: 'error',
37
37
  expressionType: 'scalar',
38
38
  value: [`_ERROR_${reason.replace(/ /g, '_')}`],
39
39
  evalSpace: 'constant',
@@ -48,8 +48,12 @@ class ExprCoalesce extends expression_def_1.ExpressionDef {
48
48
  * SQL, but I decided that is will happen when the "expressions are true
49
49
  * trees" rewrite happens.
50
50
  */
51
+ if (!fragtype_utils_1.FT.typeEq(maybeNull, whenNull)) {
52
+ this.log(`Mismatched types for coalesce (${maybeNull.dataType}, ${whenNull.dataType})`);
53
+ }
51
54
  return {
52
55
  ...whenNull,
56
+ dataType: maybeNull.dataType === 'error' ? whenNull.dataType : maybeNull.dataType,
53
57
  expressionType: (0, model_1.maxExpressionType)(maybeNull.expressionType, whenNull.expressionType),
54
58
  value: (0, model_1.mkExpr) `COALESCE(${maybeNull.value},${whenNull.value})`,
55
59
  evalSpace: (0, model_1.mergeEvalSpaces)(maybeNull.evalSpace, whenNull.evalSpace),
@@ -70,21 +70,11 @@ class ExprFunc extends expression_def_1.ExpressionDef {
70
70
  const func = (_c = this.modelEntry(this.name.toLowerCase())) === null || _c === void 0 ? void 0 : _c.entry;
71
71
  if (func === undefined) {
72
72
  this.log(`Unknown function '${this.name}'. Use '${this.name}!(...)' to call a SQL function directly.`);
73
- return {
74
- dataType: 'unknown',
75
- expressionType: 'scalar',
76
- value: [],
77
- evalSpace: 'constant',
78
- };
73
+ return (0, ast_utils_1.errorFor)('unknown function');
79
74
  }
80
75
  else if (func.type !== 'function') {
81
76
  this.log(`Cannot call '${this.name}', which is of type ${func.type}`);
82
- return {
83
- dataType: 'unknown',
84
- expressionType: 'scalar',
85
- value: [],
86
- evalSpace: 'constant',
87
- };
77
+ return (0, ast_utils_1.errorFor)('called non function');
88
78
  }
89
79
  // Find the 'implicit argument' for aggregate functions called like `some_join.some_field.agg(...args)`
90
80
  // where the full arg list is `(some_field, ...args)`.
@@ -127,12 +117,7 @@ class ExprFunc extends expression_def_1.ExpressionDef {
127
117
  this.log(`No matching overload for function ${this.name}(${argExprs
128
118
  .map(e => e.dataType)
129
119
  .join(', ')})`);
130
- return {
131
- dataType: 'unknown',
132
- expressionType: 'scalar',
133
- value: [],
134
- evalSpace: 'constant',
135
- };
120
+ return (0, ast_utils_1.errorFor)('no matching overload');
136
121
  }
137
122
  const { overload, expressionTypeErrors, evalSpaceErrors, nullabilityErrors } = result;
138
123
  // Report errors for expression type mismatch
@@ -171,12 +156,7 @@ class ExprFunc extends expression_def_1.ExpressionDef {
171
156
  this.log(`Cannot call function ${this.name}(${argExprs
172
157
  .map(e => e.dataType)
173
158
  .join(', ')}) with source`);
174
- return {
175
- dataType: 'unknown',
176
- expressionType,
177
- value: [],
178
- evalSpace: 'constant',
179
- };
159
+ return (0, ast_utils_1.errorFor)('cannot call with source');
180
160
  }
181
161
  const funcCall = [
182
162
  {
@@ -189,12 +169,7 @@ class ExprFunc extends expression_def_1.ExpressionDef {
189
169
  ];
190
170
  if (type.dataType === 'any') {
191
171
  this.log(`Invalid return type ${type.dataType} for function '${this.name}'`);
192
- return {
193
- dataType: 'unknown',
194
- expressionType,
195
- value: [],
196
- evalSpace: 'constant',
197
- };
172
+ return (0, ast_utils_1.errorFor)('invalid return type');
198
173
  }
199
174
  const maxEvalSpace = (0, malloy_types_1.mergeEvalSpaces)(...argExprs.map(e => e.evalSpace));
200
175
  // If the merged eval space of all args is constant, the result is constant.
@@ -240,10 +215,7 @@ function findOverload(func, args) {
240
215
  // does not make sense to limit function calls to not allow nulls, since have
241
216
  // so little control over nullability.
242
217
  arg.dataType === 'null' ||
243
- // TODO I've included this because it means that errors cascade a bit less...
244
- // I think we may want to add an `error` type for nodes generated from errors,
245
- // then make `error` propagate without generating more errors.
246
- arg.dataType === 'unknown';
218
+ arg.dataType === 'error';
247
219
  // Check expression type errors
248
220
  if (paramT.expressionType) {
249
221
  const expressionTypeMatch = (0, malloy_types_1.isExpressionTypeLEQ)(arg.expressionType, paramT.expressionType);
@@ -70,8 +70,20 @@ class ExprGranularTime extends expression_def_1.ExpressionDef {
70
70
  }
71
71
  return tsVal;
72
72
  }
73
- this.log(`Cannot do time truncation on type '${exprVal.dataType}'`);
74
- return (0, ast_utils_1.errorFor)('granularity typecheck');
73
+ if (exprVal.dataType !== 'error') {
74
+ this.log(`Cannot do time truncation on type '${exprVal.dataType}'`);
75
+ }
76
+ const returnType = exprVal.dataType === 'error'
77
+ ? (0, malloy_types_1.isDateUnit)(timeframe)
78
+ ? 'date'
79
+ : 'timestamp'
80
+ : exprVal.dataType;
81
+ return {
82
+ ...exprVal,
83
+ dataType: returnType,
84
+ value: (0, ast_utils_1.errorFor)('granularity typecheck').value,
85
+ evalSpace: 'constant',
86
+ };
75
87
  }
76
88
  apply(fs, op, left) {
77
89
  return this.getRange(fs).apply(fs, op, left);
@@ -37,9 +37,9 @@ class ExprMinus extends expression_def_1.ExpressionDef {
37
37
  const expr = this.expr.getExpression(fs);
38
38
  if (this.typeCheck(this.expr, expr)) {
39
39
  if (expr.value.length > 1) {
40
- return { ...expr, value: ['-(', ...expr.value, ')'] };
40
+ return { ...expr, dataType: 'number', value: ['-(', ...expr.value, ')'] };
41
41
  }
42
- return { ...expr, value: ['-', ...expr.value] };
42
+ return { ...expr, dataType: 'number', value: ['-', ...expr.value] };
43
43
  }
44
44
  return (0, ast_utils_1.errorFor)('negate requires number');
45
45
  }
@@ -54,6 +54,16 @@ class ExprTimeExtract extends expression_def_1.ExpressionDef {
54
54
  if (from instanceof range_1.Range) {
55
55
  let first = from.first.getExpression(fs);
56
56
  let last = from.last.getExpression(fs);
57
+ const expressionType = (0, malloy_types_1.maxExpressionType)(first.expressionType, last.expressionType);
58
+ const evalSpace = (0, malloy_types_1.mergeEvalSpaces)(first.evalSpace, last.evalSpace);
59
+ if (first.dataType === 'error' || last.dataType === 'error') {
60
+ return {
61
+ dataType: 'number',
62
+ expressionType,
63
+ evalSpace,
64
+ value: (0, ast_utils_1.errorFor)('extract from error').value,
65
+ };
66
+ }
57
67
  if (!(0, malloy_types_1.isTimeFieldType)(first.dataType)) {
58
68
  from.first.log(`Can't extract ${extractTo} from '${first.dataType}'`);
59
69
  return (0, ast_utils_1.errorFor)(`${extractTo} bad type ${first.dataType}`);
@@ -95,8 +105,8 @@ class ExprTimeExtract extends expression_def_1.ExpressionDef {
95
105
  }
96
106
  return {
97
107
  dataType: 'number',
98
- expressionType: (0, malloy_types_1.maxExpressionType)(first.expressionType, last.expressionType),
99
- evalSpace: (0, malloy_types_1.mergeEvalSpaces)(first.evalSpace, last.evalSpace),
108
+ expressionType,
109
+ evalSpace,
100
110
  value: [
101
111
  {
102
112
  type: 'dialect',
@@ -125,8 +135,16 @@ class ExprTimeExtract extends expression_def_1.ExpressionDef {
125
135
  ],
126
136
  };
127
137
  }
128
- this.log(`${this.extractText}() requires time type, not '${argV.dataType}'`);
129
- return (0, ast_utils_1.errorFor)(`${this.extractText} bad type ${argV.dataType}`);
138
+ if (argV.dataType !== 'error') {
139
+ this.log(`${this.extractText}() requires time type, not '${argV.dataType}'`);
140
+ }
141
+ return {
142
+ dataType: 'number',
143
+ expressionType: argV.expressionType,
144
+ evalSpace: argV.evalSpace,
145
+ value: (0, ast_utils_1.errorFor)(`${this.extractText} bad type ${argV.dataType}`)
146
+ .value,
147
+ };
130
148
  }
131
149
  }
132
150
  throw this.internalError(`Illegal extraction unit '${this.extractText}'`);
@@ -7,7 +7,7 @@ export declare class ForRange extends ExpressionDef {
7
7
  readonly duration: ExpressionDef;
8
8
  readonly timeframe: Timeframe;
9
9
  elementType: string;
10
- legalChildTypes: import("../../..").TypeDesc[];
10
+ legalChildTypes: import("../../../model").TypeDesc[];
11
11
  constructor(from: ExpressionDef, duration: ExpressionDef, timeframe: Timeframe);
12
12
  apply(fs: FieldSpace, op: string, expr: ExpressionDef): ExprValue;
13
13
  requestExpression(_fs: FieldSpace): undefined;
@@ -23,6 +23,7 @@
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.ForRange = void 0;
26
+ const model_1 = require("../../../model");
26
27
  const ast_utils_1 = require("../ast-utils");
27
28
  const fragtype_utils_1 = require("../fragtype-utils");
28
29
  const time_utils_1 = require("../time-utils");
@@ -46,8 +47,15 @@ class ForRange extends expression_def_1.ExpressionDef {
46
47
  }
47
48
  const nV = this.duration.getExpression(fs);
48
49
  if (nV.dataType !== 'number') {
49
- this.log(`FOR duration count must be a number, not '${nV.dataType}'`);
50
- return (0, ast_utils_1.errorFor)('FOR not number');
50
+ if (nV.dataType !== 'error') {
51
+ this.log(`FOR duration count must be a number, not '${nV.dataType}'`);
52
+ }
53
+ return {
54
+ dataType: 'boolean',
55
+ evalSpace: (0, model_1.mergeEvalSpaces)(startV.evalSpace, checkV.evalSpace),
56
+ expressionType: (0, model_1.maxExpressionType)(startV.expressionType, checkV.expressionType),
57
+ value: (0, ast_utils_1.errorFor)('for not number').value,
58
+ };
51
59
  }
52
60
  const units = this.timeframe.text;
53
61
  // If the duration resolution is smaller than date, we have
@@ -30,7 +30,11 @@ const expression_def_1 = require("../types/expression-def");
30
30
  const malloy_element_1 = require("../types/malloy-element");
31
31
  const utils_1 = require("./utils");
32
32
  function typeCoalesce(ev1, ev2) {
33
- return ev1 === undefined || ev1.dataType === 'null' ? ev2 : ev1;
33
+ return ev1 === undefined ||
34
+ ev1.dataType === 'null' ||
35
+ ev1.dataType === 'error'
36
+ ? ev2
37
+ : ev1;
34
38
  }
35
39
  class Pick extends expression_def_1.ExpressionDef {
36
40
  constructor(choices, elsePick) {
@@ -292,14 +292,11 @@ class QuerySpace extends refined_space_1.RefinedSpace {
292
292
  }
293
293
  else {
294
294
  const typeDesc = field.typeDesc();
295
- // Filter out fields whose type is unknown, which means that a totally bad field
296
- // isn't sent to the compiler, where it will wig out. The downside is that in
297
- // subsequent stages of a query, the field is not in the input struct. But since
298
- // the error message says "Cannot define x which has unknown type", it makes sense
299
- // that it wouldn't be "defined" in later stages.
295
+ // Filter out fields whose type is 'error', which means that a totally bad field
296
+ // isn't sent to the compiler, where it will wig out.
300
297
  // TODO Figure out how to make errors generated by `canContain` go in the right place,
301
298
  // maybe by adding a logable element to SpaceFields.
302
- if (typeDesc.dataType !== 'unknown' && this.canContain(typeDesc)) {
299
+ if (typeDesc.dataType !== 'error' && this.canContain(typeDesc)) {
303
300
  fields.push(fieldQueryDef);
304
301
  }
305
302
  }
@@ -45,7 +45,7 @@ class ReferenceField extends space_field_1.SpaceField {
45
45
  if (this.res !== undefined && this.res.found) {
46
46
  return this.res.found.typeDesc();
47
47
  }
48
- return { dataType: 'unknown', expressionType: 'scalar', evalSpace: 'input' };
48
+ return { dataType: 'error', expressionType: 'scalar', evalSpace: 'input' };
49
49
  }
50
50
  }
51
51
  exports.ReferenceField = ReferenceField;
@@ -21,7 +21,7 @@ export declare class FT {
21
21
  * @param right Right type
22
22
  * @param nullOk True if a NULL is an acceptable match
23
23
  */
24
- static typeEq(left: TypeDesc, right: TypeDesc, nullOk?: boolean): boolean;
24
+ static typeEq(left: TypeDesc, right: TypeDesc, nullOk?: boolean, errorOk?: boolean): boolean;
25
25
  /**
26
26
  *
27
27
  * For error messages, returns a comma seperated list of readable names
@@ -59,10 +59,11 @@ class FT {
59
59
  * @param right Right type
60
60
  * @param nullOk True if a NULL is an acceptable match
61
61
  */
62
- static typeEq(left, right, nullOk = false) {
62
+ static typeEq(left, right, nullOk = false, errorOk = true) {
63
63
  const maybeEq = left.dataType === right.dataType;
64
64
  const nullEq = nullOk && (left.dataType === 'null' || right.dataType === 'null');
65
- return maybeEq || nullEq;
65
+ const errorEq = errorOk && (left.dataType === 'error' || right.dataType === 'error');
66
+ return maybeEq || nullEq || errorEq;
66
67
  }
67
68
  /**
68
69
  *
@@ -37,7 +37,7 @@ class ConstantParameter extends has_parameter_1.HasParameter {
37
37
  this.log(`Unexpected expression type '${cVal.dataType}'`);
38
38
  return {
39
39
  value: ['XXX-type-mismatch-error-XXX'],
40
- type: 'string',
40
+ type: 'error',
41
41
  name: this.name,
42
42
  constant: true,
43
43
  };
@@ -66,8 +66,8 @@ class FieldDeclaration extends malloy_element_1.MalloyElement {
66
66
  catch (error) {
67
67
  this.log(`Cannot define '${exprName}', ${error.message}`);
68
68
  return {
69
- name: `error_defining_${exprName}`,
70
- type: 'string',
69
+ name: exprName,
70
+ type: 'error',
71
71
  };
72
72
  }
73
73
  const compressValue = (0, utils_1.compressExpr)(exprValue.value);
@@ -96,17 +96,14 @@ class FieldDeclaration extends malloy_element_1.MalloyElement {
96
96
  }
97
97
  const circularDef = exprFS instanceof DefSpace && exprFS.foundCircle;
98
98
  if (!circularDef) {
99
- if (exprValue.dataType === 'unknown') {
100
- this.log(`Cannot define '${exprName}', value has unknown type`);
101
- }
102
- else {
99
+ if (exprValue.dataType !== 'error') {
103
100
  const badType = fragtype_utils_1.FT.inspect(exprValue);
104
101
  this.log(`Cannot define '${exprName}', unexpected type: ${badType}`);
105
102
  }
106
103
  }
107
104
  return {
108
- name: `error_defining_${exprName}`,
109
- type: 'string',
105
+ name: exprName,
106
+ type: 'error',
110
107
  };
111
108
  }
112
109
  }
@@ -67,7 +67,7 @@ class ExpressionDef extends malloy_element_1.MalloyElement {
67
67
  * @param eVal ...list of expressions that must match legalChildTypes
68
68
  */
69
69
  typeCheck(eNode, eVal) {
70
- if (!fragtype_utils_1.FT.in(eVal, this.legalChildTypes)) {
70
+ if (eVal.dataType !== 'error' && !fragtype_utils_1.FT.in(eVal, this.legalChildTypes)) {
71
71
  eNode.log(`'${this.elementType}' Can't use type ${fragtype_utils_1.FT.inspect(eVal)}`);
72
72
  return false;
73
73
  }
@@ -232,6 +232,9 @@ function nullCompare(left, op, right) {
232
232
  function equality(fs, left, op, right) {
233
233
  const lhs = left.getExpression(fs);
234
234
  const rhs = right.getExpression(fs);
235
+ const err = errorCascade('boolean', lhs, rhs);
236
+ if (err)
237
+ return err;
235
238
  // Unsupported types can be compare with null
236
239
  const checkUnsupport = lhs.dataType === 'unsupported' || rhs.dataType === 'unsupported';
237
240
  if (checkUnsupport) {
@@ -245,7 +248,7 @@ function equality(fs, left, op, right) {
245
248
  }
246
249
  }
247
250
  let value = timeCompare(left, lhs, op, rhs) || (0, utils_1.compose)(lhs.value, op, rhs.value);
248
- if (lhs.dataType !== 'unknown' && rhs.dataType !== 'unknown') {
251
+ if (lhs.dataType !== 'error' && rhs.dataType !== 'error') {
249
252
  switch (op) {
250
253
  case '~':
251
254
  case '!~': {
@@ -285,6 +288,9 @@ function equality(fs, left, op, right) {
285
288
  function compare(fs, left, op, right) {
286
289
  const lhs = left.getExpression(fs);
287
290
  const rhs = right.getExpression(fs);
291
+ const err = errorCascade('boolean', lhs, rhs);
292
+ if (err)
293
+ return err;
288
294
  const expressionType = (0, malloy_types_1.maxExpressionType)(lhs.expressionType, rhs.expressionType);
289
295
  const noCompare = unsupportError(left, lhs, right, rhs);
290
296
  if (noCompare) {
@@ -312,6 +318,9 @@ function allAre(oneType, ...values) {
312
318
  function numeric(fs, left, op, right) {
313
319
  const lhs = left.getExpression(fs);
314
320
  const rhs = right.getExpression(fs);
321
+ const err = errorCascade('number', lhs, rhs);
322
+ if (err)
323
+ return err;
315
324
  const noGo = unsupportError(left, lhs, right, rhs);
316
325
  if (noGo) {
317
326
  return noGo;
@@ -335,7 +344,11 @@ function delta(fs, left, op, right) {
335
344
  if (noGo) {
336
345
  return noGo;
337
346
  }
338
- if ((0, malloy_types_1.isTimeFieldType)(lhs.dataType)) {
347
+ const timeLHS = (0, malloy_types_1.isTimeFieldType)(lhs.dataType);
348
+ const err = errorCascade(timeLHS ? 'error' : 'number', lhs, rhs);
349
+ if (err)
350
+ return err;
351
+ if (timeLHS) {
339
352
  let duration = right;
340
353
  if (rhs.dataType !== 'duration') {
341
354
  if ((0, granular_result_1.isGranularResult)(lhs)) {
@@ -385,6 +398,9 @@ function applyBinary(fs, left, op, right) {
385
398
  left.log('Cannot operate with unsupported type');
386
399
  return noGo;
387
400
  }
401
+ const err = errorCascade('number', num, denom);
402
+ if (err)
403
+ return err;
388
404
  if (num.dataType !== 'number') {
389
405
  left.log('Numerator for division must be a number');
390
406
  }
@@ -411,6 +427,16 @@ function applyBinary(fs, left, op, right) {
411
427
  return (0, ast_utils_1.errorFor)('applybinary bad operator');
412
428
  }
413
429
  exports.applyBinary = applyBinary;
430
+ function errorCascade(dataType, ...es) {
431
+ if (es.some(e => e.dataType === 'error')) {
432
+ return {
433
+ dataType,
434
+ expressionType: (0, malloy_types_1.maxOfExpressionTypes)(es.map(e => e.expressionType)),
435
+ value: ["'cascading error'"],
436
+ evalSpace: (0, malloy_types_1.mergeEvalSpaces)(...es.map(e => e.evalSpace)),
437
+ };
438
+ }
439
+ }
414
440
  /**
415
441
  * Return an error if a binary operation includes unsupported types.
416
442
  */
@@ -297,14 +297,6 @@ class RunList extends ListOf {
297
297
  }
298
298
  executeList(doc) {
299
299
  while (this.execCursor < this.elements.length) {
300
- if (doc.errorsExist()) {
301
- // TODO make a better way to stop cascading errors -- this way means that if there are
302
- // actual different errors in multiple places, we only see the first one. A better way
303
- // might be to make things that have error pass all kinds of typechecks, which prevents
304
- // the real cascade.
305
- // This stops cascading errors
306
- return;
307
- }
308
300
  const el = this.elements[this.execCursor];
309
301
  if (isDocStatement(el)) {
310
302
  const resp = el.execute(doc);
@@ -40,7 +40,7 @@ class AbstractParameter extends SpaceParam {
40
40
  return this.astParam.parameter();
41
41
  }
42
42
  typeDesc() {
43
- const type = this.astParam.type || 'unknown';
43
+ const type = this.astParam.type || 'error';
44
44
  // TODO Not sure whether params are considered "input space". It seems like they
45
45
  // could be input or constant, depending on usage.
46
46
  return { dataType: type, expressionType: 'scalar', evalSpace: 'input' };
@@ -1,4 +1,7 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  /*
4
7
  * Copyright 2023 Google LLC
@@ -24,30 +27,31 @@ Object.defineProperty(exports, "__esModule", { value: true });
24
27
  */
25
28
  require("./parse-expects");
26
29
  const test_translator_1 = require("./test-translator");
30
+ const escapeRegExp_1 = __importDefault(require("lodash/escapeRegExp"));
27
31
  describe('import:', () => {
28
32
  test('simple source', () => {
29
33
  const docParse = new test_translator_1.TestTranslator('import "child"');
30
34
  const xr = docParse.unresolved();
31
- expect(docParse).toBeErrorless();
35
+ expect(docParse).toParse();
32
36
  expect(xr).toEqual({ urls: ['internal://test/langtests/child'] });
33
37
  docParse.update({
34
38
  urls: { 'internal://test/langtests/child': 'source: aa is a' },
35
39
  });
36
- expect(docParse).modelCompiled();
40
+ expect(docParse).toTranslate();
37
41
  const aa = docParse.getSourceDef('aa');
38
42
  expect(aa).toBeDefined();
39
43
  });
40
44
  test('simple query', () => {
41
45
  const docParse = new test_translator_1.TestTranslator('import "child"');
42
46
  const xr = docParse.unresolved();
43
- expect(docParse).toBeErrorless();
47
+ expect(docParse).toParse();
44
48
  expect(xr).toEqual({ urls: ['internal://test/langtests/child'] });
45
49
  docParse.update({
46
50
  urls: {
47
51
  'internal://test/langtests/child': 'query: aq is a->{ project: * }',
48
52
  },
49
53
  });
50
- expect(docParse).modelCompiled();
54
+ expect(docParse).toTranslate();
51
55
  const aq = docParse.getQuery('aq');
52
56
  expect(aq).toBeDefined();
53
57
  });
@@ -59,7 +63,7 @@ source: newSrc is a {
59
63
  }
60
64
  `);
61
65
  const xr = docParse.unresolved();
62
- expect(docParse).toBeErrorless();
66
+ expect(docParse).toParse();
63
67
  expect(xr).toEqual({ urls: ['internal://test/langtests/child'] });
64
68
  docParse.update({
65
69
  urls: {
@@ -70,7 +74,7 @@ source: botProjQSrc is from(->botProjQ)
70
74
  `,
71
75
  },
72
76
  });
73
- expect(docParse).modelCompiled();
77
+ expect(docParse).toTranslate();
74
78
  const newSrc = docParse.getSourceDef('newSrc');
75
79
  const f = newSrc === null || newSrc === void 0 ? void 0 : newSrc.fields.find(f => f.name === 'b');
76
80
  expect(f === null || f === void 0 ? void 0 : f.type).toBe('struct');
@@ -85,7 +89,7 @@ source: botProjQSrc is from(->botProjQ)
85
89
  test('missing import', () => {
86
90
  const docParse = new test_translator_1.TestTranslator('import "child"');
87
91
  const xr = docParse.unresolved();
88
- expect(docParse).toBeErrorless();
92
+ expect(docParse).toParse();
89
93
  expect(xr).toEqual({ urls: ['internal://test/langtests/child'] });
90
94
  const reportedError = 'ENOWAY: No way to find your child';
91
95
  docParse.update({
@@ -94,8 +98,7 @@ source: botProjQSrc is from(->botProjQ)
94
98
  },
95
99
  });
96
100
  docParse.translate();
97
- expect(docParse).not.toBeErrorless();
98
- expect(docParse.prettyErrors()).toContain(reportedError);
101
+ expect(docParse).translationToFailWith(new RegExp((0, escapeRegExp_1.default)(reportedError)));
99
102
  });
100
103
  test('chained imports', () => {
101
104
  const docParse = new test_translator_1.TestTranslator('import "child"');
@@ -103,12 +106,12 @@ source: botProjQSrc is from(->botProjQ)
103
106
  urls: { 'internal://test/langtests/child': 'import "grandChild"' },
104
107
  });
105
108
  const xr = docParse.unresolved();
106
- expect(docParse).toBeErrorless();
109
+ expect(docParse).toParse();
107
110
  expect(xr).toEqual({ urls: ['internal://test/langtests/grandChild'] });
108
111
  });
109
112
  test('relative imports', () => {
110
113
  const docParse = new test_translator_1.TestTranslator('import "../parent.malloy"');
111
- expect(docParse).modelParsed();
114
+ expect(docParse).toParse();
112
115
  const xr = docParse.unresolved();
113
116
  expect(xr).toEqual({ urls: ['internal://test/parent.malloy'] });
114
117
  docParse.update({
@@ -116,11 +119,11 @@ source: botProjQSrc is from(->botProjQ)
116
119
  'internal://test/parent.malloy': "source: aa is table('aTable')",
117
120
  },
118
121
  });
119
- expect(docParse).modelCompiled();
122
+ expect(docParse).toTranslate();
120
123
  });
121
124
  test('relative imports with errors', () => {
122
125
  const docParse = new test_translator_1.TestTranslator('import "../parent.malloy"');
123
- expect(docParse).modelParsed();
126
+ expect(docParse).toParse();
124
127
  const xr = docParse.unresolved();
125
128
  expect(xr).toEqual({ urls: ['internal://test/parent.malloy'] });
126
129
  docParse.update({
@@ -131,7 +134,7 @@ source: botProjQSrc is from(->botProjQ)
131
134
  }`,
132
135
  },
133
136
  });
134
- expect(docParse).compileToFailWith("Cannot redefine 'astr'");
137
+ expect(docParse).translationToFailWith("Cannot redefine 'astr'");
135
138
  });
136
139
  test('source references expanded when not exported', () => {
137
140
  const srcFiles = {
@@ -145,7 +148,7 @@ source: botProjQSrc is from(->botProjQ)
145
148
  import "middle"
146
149
  `);
147
150
  fullModel.update({ urls: srcFiles });
148
- expect(fullModel).modelCompiled();
151
+ expect(fullModel).toTranslate();
149
152
  const ms = fullModel.getSourceDef('midSrc');
150
153
  expect(ms).toBeDefined();
151
154
  if (ms) {
@@ -3,13 +3,39 @@ declare type ErrorSpec = string | RegExp;
3
3
  declare global {
4
4
  namespace jest {
5
5
  interface Matchers<R> {
6
- modelParsed(): R;
7
- toBeErrorless(): R;
8
- toCompile(): R;
9
- modelCompiled(): R;
10
- expressionCompiled(): R;
6
+ /**
7
+ * expect(X).toParse()
8
+ *
9
+ * Passes if the source parses to an AST without errors.
10
+ *
11
+ * X can be a MarkedSource, a string, or a model. If it is a marked
12
+ * source, the errors which are found must match the locations of
13
+ * the markings.
14
+ */
15
+ toParse(): R;
16
+ /**
17
+ * expect(X).toTranslate()
18
+ *
19
+ * Passes if the source compiles to code which could be used to
20
+ * generate SQL.
21
+ *
22
+ * X can be a MarkedSource, a string, or a model. If it is a marked
23
+ * source, the errors which are found must match the locations of
24
+ * the markings.
25
+ */
26
+ toTranslate(): R;
11
27
  toReturnType(tp: string): R;
12
- compileToFailWith(...expectedErrors: ErrorSpec[]): R;
28
+ /**
29
+ * expect(X).translateToFailWith(expectedErrors)
30
+ *
31
+ * X can be a MarkedSource, a string, or a model. If it is a marked
32
+ * source, the errors which are found must match the locations of
33
+ * the markings.
34
+ *
35
+ * @param expectedErrors varargs list of strings which must match
36
+ * exactly, or regular expressions.
37
+ */
38
+ translationToFailWith(...expectedErrors: ErrorSpec[]): R;
13
39
  isLocationIn(at: DocumentLocation, txt: string): R;
14
40
  }
15
41
  }