@malloydata/malloy 0.0.80-dev230830211713 → 0.0.80-dev230903192621

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.
@@ -0,0 +1,415 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright 2023 Google LLC
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining
6
+ * a copy of this software and associated documentation files
7
+ * (the "Software"), to deal in the Software without restriction,
8
+ * including without limitation the rights to use, copy, modify, merge,
9
+ * publish, distribute, sublicense, and/or sell copies of the Software,
10
+ * and to permit persons to whom the Software is furnished to do so,
11
+ * subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be
14
+ * included in all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ const model_1 = require("../../model");
26
+ const test_translator_1 = require("./test-translator");
27
+ require("./parse-expects");
28
+ describe('expressions', () => {
29
+ describe('timeframes', () => {
30
+ const timeframes = [
31
+ 'second',
32
+ 'minute',
33
+ 'hour',
34
+ 'day',
35
+ 'week',
36
+ 'month',
37
+ 'quarter',
38
+ 'year',
39
+ ];
40
+ test.each(timeframes.map(x => [x]))('truncate %s', unit => {
41
+ expect(new test_translator_1.BetaExpression(`ats.${unit}`)).toParse();
42
+ });
43
+ // mtoy todo units missing: implement, or document
44
+ const diffable = ['second', 'minute', 'hour', 'day'];
45
+ test.each(diffable.map(x => [x]))('timestamp difference - %s', unit => {
46
+ expect(new test_translator_1.BetaExpression(`${unit}(@2021 to ats)`)).toParse();
47
+ });
48
+ test.each(diffable.map(x => [x]))('timestamp difference - %s', unit => {
49
+ expect(new test_translator_1.BetaExpression(`${unit}(ats to @2030)`)).toParse();
50
+ });
51
+ });
52
+ test('field name', () => {
53
+ expect((0, test_translator_1.expr) `astr`).toTranslate();
54
+ });
55
+ test('function call', () => {
56
+ expect((0, test_translator_1.expr) `concat('foo')`).toTranslate();
57
+ });
58
+ describe('operators', () => {
59
+ test('addition', () => {
60
+ expect((0, test_translator_1.expr) `42 + 7`).toTranslate();
61
+ });
62
+ test('subtraction', () => {
63
+ expect((0, test_translator_1.expr) `42 - 7`).toTranslate();
64
+ });
65
+ test('multiplication', () => {
66
+ expect((0, test_translator_1.expr) `42 * 7`).toTranslate();
67
+ });
68
+ test('mod', () => {
69
+ expect((0, test_translator_1.expr) `42 % 7`).toTranslate();
70
+ });
71
+ test('division', () => {
72
+ expect((0, test_translator_1.expr) `42 / 7`).toTranslate();
73
+ });
74
+ test('unary negation', () => {
75
+ expect((0, test_translator_1.expr) `- ai`).toTranslate();
76
+ });
77
+ test('equal', () => {
78
+ expect((0, test_translator_1.expr) `42 = 7`).toTranslate();
79
+ });
80
+ test('not equal', () => {
81
+ expect((0, test_translator_1.expr) `42 != 7`).toTranslate();
82
+ });
83
+ test('greater than', () => {
84
+ expect((0, test_translator_1.expr) `42 > 7`).toTranslate();
85
+ });
86
+ test('greater than or equal', () => {
87
+ expect((0, test_translator_1.expr) `42 >= 7`).toTranslate();
88
+ });
89
+ test('less than or equal', () => {
90
+ expect((0, test_translator_1.expr) `42 <= 7`).toTranslate();
91
+ });
92
+ test('less than', () => {
93
+ expect((0, test_translator_1.expr) `42 < 7`).toTranslate();
94
+ });
95
+ test('match', () => {
96
+ expect((0, test_translator_1.expr) `'forty-two' ~ 'fifty-four'`).toTranslate();
97
+ });
98
+ test('not match', () => {
99
+ expect((0, test_translator_1.expr) `'forty-two' !~ 'fifty-four'`).toTranslate();
100
+ });
101
+ test('apply', () => {
102
+ expect((0, test_translator_1.expr) `'forty-two' ? 'fifty-four'`).toTranslate();
103
+ });
104
+ test('not', () => {
105
+ expect((0, test_translator_1.expr) `not true`).toTranslate();
106
+ });
107
+ test('and', () => {
108
+ expect((0, test_translator_1.expr) `true and false`).toTranslate();
109
+ });
110
+ test('or', () => {
111
+ expect((0, test_translator_1.expr) `true or false`).toTranslate();
112
+ });
113
+ test('null-check (??)', () => {
114
+ expect((0, test_translator_1.expr) `ai ?? 7`).toTranslate();
115
+ });
116
+ test('coalesce type mismatch', () => {
117
+ expect(new test_translator_1.BetaExpression('ai ?? @2003')).translationToFailWith('Mismatched types for coalesce (number, date)');
118
+ });
119
+ test('disallow date OP number', () => {
120
+ expect(new test_translator_1.BetaExpression('@2001 = 7')).translationToFailWith('Cannot compare a date to a number');
121
+ });
122
+ test('disallow date OP timestamp', () => {
123
+ expect(new test_translator_1.BetaExpression('ad = ats')).translationToFailWith('Cannot compare a date to a timestamp');
124
+ });
125
+ test('disallow interval from date to timestamp', () => {
126
+ expect(new test_translator_1.BetaExpression('days(ad to ats)')).translationToFailWith('Cannot measure from date to timestamp');
127
+ });
128
+ test('comparison promotes date literal to timestamp', () => {
129
+ expect((0, test_translator_1.expr) `@2001 = ats`).toTranslate();
130
+ });
131
+ test('can apply range to date', () => {
132
+ expect((0, test_translator_1.expr) `ad ? @2001 for 1 day`).toTranslate();
133
+ });
134
+ const noOffset = ['second', 'minute', 'hour'];
135
+ test.each(noOffset.map(x => [x]))('disallow date delta %s', unit => {
136
+ expect(new test_translator_1.BetaExpression(`ad + 10 ${unit}s`)).translationToFailWith(`Cannot offset date by ${unit}`);
137
+ });
138
+ test('apply with parens', () => {
139
+ expect((0, test_translator_1.expr) `ai ? (> 1 & < 100)`).toTranslate();
140
+ });
141
+ });
142
+ test('filtered measure', () => {
143
+ expect((0, test_translator_1.expr) `acount {? astr = 'why?' }`).toTranslate();
144
+ });
145
+ test('filtered ungrouped aggregate', () => {
146
+ expect(`
147
+ query: a -> {
148
+ group_by: ai
149
+ aggregate: x is all(avg(ai)) { where: true }
150
+ }
151
+ `).toTranslate();
152
+ });
153
+ test('shortcut filtered measure m4warning', () => {
154
+ expect(`
155
+ ##! m4warnings
156
+ run: a -> {
157
+ group_by: ai
158
+ aggregate: x is avg(ai) {? astr = 'why?' }
159
+ }
160
+ `).toTranslateWithWarnings('Filter shortcut `{? condition }` is deprecated; use `{ where: condition } instead');
161
+ });
162
+ test('correctly flags filtered scalar', () => {
163
+ const e = new test_translator_1.BetaExpression('ai { where: true }');
164
+ expect(e).translationToFailWith('Filtered expression requires an aggregate computation');
165
+ });
166
+ test('correctly flags filtered analytic', () => {
167
+ expect((0, test_translator_1.markSource) `
168
+ query: a -> {
169
+ group_by: ai
170
+ calculate: l is lag(ai) { where: true }
171
+ }
172
+ `).translationToFailWith('Filtered expression requires an aggregate computation');
173
+ });
174
+ describe('aggregate forms', () => {
175
+ test('count', () => {
176
+ expect((0, test_translator_1.expr) `count()`).toTranslate();
177
+ });
178
+ test('count distinct', () => {
179
+ expect((0, test_translator_1.expr) `count(distinct astr)`).toTranslate();
180
+ });
181
+ test('join.count()', () => {
182
+ expect((0, test_translator_1.expr) `b.count()`).toTranslate();
183
+ });
184
+ for (const f of ['sum', 'min', 'max', 'avg']) {
185
+ const fOfT = `${f}(af)`;
186
+ test(fOfT, () => {
187
+ expect(new test_translator_1.BetaExpression(fOfT)).toTranslate();
188
+ });
189
+ if (f !== 'min' && f !== 'max') {
190
+ const joinDot = `b.af.${f}()`;
191
+ test(joinDot, () => {
192
+ expect(new test_translator_1.BetaExpression(joinDot)).toTranslate();
193
+ });
194
+ const joinAgg = `b.${f}(af)`;
195
+ test(joinAgg, () => {
196
+ expect(new test_translator_1.BetaExpression(joinAgg)).toTranslate();
197
+ });
198
+ }
199
+ }
200
+ });
201
+ describe('pick statements', () => {
202
+ test('full', () => {
203
+ expect((0, test_translator_1.expr) `
204
+ pick 'the answer' when ai = 42
205
+ pick 'the questionable answer' when ai = 54
206
+ else 'random'
207
+ `).toTranslate();
208
+ });
209
+ test('applied', () => {
210
+ expect((0, test_translator_1.expr) `
211
+ astr ?
212
+ pick 'the answer' when = '42'
213
+ pick 'the questionable answer' when = '54'
214
+ else 'random'
215
+ `).toTranslate();
216
+ });
217
+ test('filtering', () => {
218
+ expect((0, test_translator_1.expr) `astr ? pick 'missing value' when NULL`).toTranslate();
219
+ });
220
+ test('null branch with else', () => {
221
+ expect("astr ? pick null when = '42' else 3").toReturnType('number');
222
+ });
223
+ test('null branch no else', () => {
224
+ expect("astr ? pick null when = '42'").toReturnType('string');
225
+ });
226
+ test('null branch no apply', () => {
227
+ expect('pick null when 1 = 1 else 3').toReturnType('number');
228
+ });
229
+ test('tiering', () => {
230
+ expect((0, test_translator_1.expr) `
231
+ ai ?
232
+ pick 1 when < 10
233
+ pick 10 when < 100
234
+ pick 100 when < 1000
235
+ else 10000
236
+ `).toTranslate();
237
+ });
238
+ test('transforming', () => {
239
+ expect((0, test_translator_1.expr) `
240
+ ai ?
241
+ pick 'small' when < 10
242
+ pick 'medium' when < 100
243
+ else 'large'
244
+ `).toTranslate();
245
+ });
246
+ test('when single values', () => {
247
+ expect((0, test_translator_1.expr) `
248
+ ai ?
249
+ pick 'one' when 1
250
+ else 'a lot'
251
+ `).toTranslate();
252
+ });
253
+ test('n-ary without else', () => {
254
+ expect(`
255
+ source: na is a + { dimension: d is
256
+ pick 7 when true and true
257
+ }
258
+ `).translationToFailWith("pick incomplete, missing 'else'");
259
+ });
260
+ test('n-ary with mismatch when clauses', () => {
261
+ expect((0, test_translator_1.markSource) `
262
+ source: na is a + { dimension: d is
263
+ pick 7 when true and true
264
+ pick '7' when true or true
265
+ else 7
266
+ }
267
+ `).translationToFailWith("pick type 'string', expected 'number'");
268
+ });
269
+ test('n-ary with mismatched else clause', () => {
270
+ expect((0, test_translator_1.markSource) `
271
+ source: na is a + { dimension: d is
272
+ pick 7 when true and true
273
+ else '7'
274
+ }
275
+ `).translationToFailWith("else type 'string', expected 'number'");
276
+ });
277
+ test('applied else mismatch', () => {
278
+ expect((0, test_translator_1.markSource) `
279
+ source: na is a + { dimension: d is
280
+ 7 ? pick 7 when 7 else 'not seven'
281
+ }
282
+ `).translationToFailWith("else type 'string', expected 'number'");
283
+ });
284
+ test('applied default mismatch', () => {
285
+ expect((0, test_translator_1.markSource) `
286
+ source: na is a + { dimension: d is
287
+ 7 ? pick 'seven' when 7
288
+ }
289
+ `).translationToFailWith("pick default type 'number', expected 'string'");
290
+ });
291
+ test('applied when mismatch', () => {
292
+ expect((0, test_translator_1.markSource) `
293
+ source: na is a + { dimension: d is
294
+ 7 ? pick 'seven' when 7 pick 6 when 6
295
+ }
296
+ `).translationToFailWith("pick type 'number', expected 'string'");
297
+ });
298
+ });
299
+ test('paren and applied div', () => {
300
+ var _a, _b;
301
+ const modelSrc = 'query: z is a -> { group_by: x is 1+(3/4) }';
302
+ const m = new test_translator_1.TestTranslator(modelSrc);
303
+ expect(m).toTranslate();
304
+ const queryDef = (_b = (_a = m.translate()) === null || _a === void 0 ? void 0 : _a.translated) === null || _b === void 0 ? void 0 : _b.modelDef.contents['z'];
305
+ expect(queryDef).toBeDefined();
306
+ expect(queryDef === null || queryDef === void 0 ? void 0 : queryDef.type).toBe('query');
307
+ if (queryDef && queryDef.type === 'query') {
308
+ const x = queryDef.pipeline[0].fields[0];
309
+ if (typeof x !== 'string' &&
310
+ !(0, model_1.isFilteredAliasedName)(x) &&
311
+ (0, model_1.isFieldTypeDef)(x) &&
312
+ x.type === 'number' &&
313
+ x.e) {
314
+ expect(x).toMatchObject({
315
+ 'e': [
316
+ {
317
+ 'function': 'numberLiteral',
318
+ 'literal': '1',
319
+ 'type': 'dialect',
320
+ },
321
+ // TODO not sure why there are TWO sets of parentheses... A previous version of this test
322
+ // just checked that there were ANY parens, so that went under the radar. Not fixing now.
323
+ '+((',
324
+ {
325
+ 'denominator': [
326
+ {
327
+ 'function': 'numberLiteral',
328
+ 'literal': '4',
329
+ 'type': 'dialect',
330
+ },
331
+ ],
332
+ 'function': 'div',
333
+ 'numerator': [
334
+ {
335
+ 'function': 'numberLiteral',
336
+ 'literal': '3',
337
+ 'type': 'dialect',
338
+ },
339
+ ],
340
+ 'type': 'dialect',
341
+ },
342
+ '))',
343
+ ],
344
+ });
345
+ }
346
+ else {
347
+ fail('expression with parens compiled oddly');
348
+ }
349
+ }
350
+ });
351
+ test.each([
352
+ ['ats', 'timestamp'],
353
+ ['ad', 'date'],
354
+ ['ai', 'number'],
355
+ ['astr', 'string'],
356
+ ['abool', 'boolean'],
357
+ ])('Can compare field %s (type %s) to NULL', (name, _datatype) => {
358
+ expect((0, test_translator_1.expr) `${name} = NULL`).toTranslate();
359
+ });
360
+ });
361
+ describe('unspported fields in schema', () => {
362
+ test('unsupported reference in result allowed', () => {
363
+ const uModel = new test_translator_1.TestTranslator('query: a->{ group_by: aun }');
364
+ expect(uModel).toTranslate();
365
+ });
366
+ test('unsupported reference can be compared to NULL', () => {
367
+ const uModel = new test_translator_1.TestTranslator('query: a->{ where: aun != NULL; project: * }');
368
+ expect(uModel).toTranslate();
369
+ });
370
+ test('flag unsupported equality', () => {
371
+ // because we don't know if the two unsupported types are comparable
372
+ const uModel = new test_translator_1.TestTranslator('query: ab->{ where: aun = b.aun project: * }');
373
+ expect(uModel).translationToFailWith('Unsupported type not allowed in expression');
374
+ });
375
+ test('flag unsupported compare', () => {
376
+ // because we don't know if the two unsupported types are comparable
377
+ const uModel = new test_translator_1.TestTranslator('query: ab->{ where: aun > b.aun project: * }');
378
+ expect(uModel).translationToFailWith('Unsupported type not allowed in expression');
379
+ });
380
+ test('allow unsupported equality when raw types match', () => {
381
+ const uModel = new test_translator_1.TestTranslator('query: ab->{ where: aweird = b.aweird project: * }');
382
+ expect(uModel).toTranslate();
383
+ });
384
+ test('flag not applied to unsupported', () => {
385
+ const uModel = new test_translator_1.TestTranslator('source: x is a { dimension: notUn is not aun }');
386
+ expect(uModel).translationToFailWith("'not' Can't use type unsupported");
387
+ });
388
+ test('allow unsupported to be cast', () => {
389
+ const uModel = new test_translator_1.TestTranslator('source: x is a { dimension: notUn is aun::string }');
390
+ expect(uModel).toTranslate();
391
+ });
392
+ describe('cast', () => {
393
+ // The "+ 1"s are there to make sure the result is of type 'number'
394
+ test('sql cast', () => {
395
+ expect((0, test_translator_1.expr) `ai::'integer' + 1`).toTranslate();
396
+ expect((0, test_translator_1.expr) `ai::"integer" + 1`).toTranslate();
397
+ expect((0, test_translator_1.expr) `ai::"""integer""" + 1`).toTranslate();
398
+ });
399
+ test('sql safe cast', () => {
400
+ expect((0, test_translator_1.expr) `astr:::'integer' + 1`).toTranslate();
401
+ expect((0, test_translator_1.expr) `astr:::"integer" + 1`).toTranslate();
402
+ expect((0, test_translator_1.expr) `astr:::"""integer""" + 1`).toTranslate();
403
+ });
404
+ test('malloy cast', () => {
405
+ expect((0, test_translator_1.expr) `astr::number + 1`).toTranslate();
406
+ });
407
+ test('malloy safe cast', () => {
408
+ expect((0, test_translator_1.expr) `astr:::number + 1`).toTranslate();
409
+ });
410
+ test('sql cast illegal type name', () => {
411
+ expect((0, test_translator_1.expr) `astr::"stuff 'n' things"`).translationToFailWith("Cast type `stuff 'n' things` is invalid for standardsql dialect");
412
+ });
413
+ });
414
+ });
415
+ //# sourceMappingURL=expressions.spec.js.map
@@ -0,0 +1,274 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright 2023 Google LLC
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining
6
+ * a copy of this software and associated documentation files
7
+ * (the "Software"), to deal in the Software without restriction,
8
+ * including without limitation the rights to use, copy, modify, merge,
9
+ * publish, distribute, sublicense, and/or sell copies of the Software,
10
+ * and to permit persons to whom the Software is furnished to do so,
11
+ * subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be
14
+ * included in all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ const test_translator_1 = require("./test-translator");
26
+ const parse_utils_1 = require("../parse-utils");
27
+ require("./parse-expects");
28
+ const granular_result_1 = require("../ast/types/granular-result");
29
+ describe('literals', () => {
30
+ test('integer', () => {
31
+ expect((0, test_translator_1.expr) `42`).toTranslate();
32
+ });
33
+ test('string', () => {
34
+ const m = new test_translator_1.BetaExpression("'forty two'");
35
+ expect(m).toTranslate();
36
+ const m42 = m.generated().value[0];
37
+ expect(m42).toMatchObject({ literal: 'forty two' });
38
+ });
39
+ test('string with quoted quote', () => {
40
+ const str = "'Isn" + '\\' + "'t this nice'";
41
+ expect(new test_translator_1.BetaExpression(str)).toTranslate();
42
+ });
43
+ test('string with quoted backslash', () => {
44
+ const str = "'Is " + '\\' + '\\' + " nice'";
45
+ expect(new test_translator_1.BetaExpression(str)).toTranslate();
46
+ });
47
+ const literalTimes = [
48
+ ['@1960', 'date', 'year', { literal: '1960-01-01' }],
49
+ ['@1960-Q2', 'date', 'quarter', { literal: '1960-04-01' }],
50
+ ['@1960-06', 'date', 'month', { literal: '1960-06-01' }],
51
+ ['@1960-06-26-WK', 'date', 'week', { literal: '1960-06-26' }],
52
+ ['@1960-06-30', 'date', 'day', { literal: '1960-06-30' }],
53
+ ['@1960-06-30 10', 'timestamp', 'hour', { literal: '1960-06-30 10:00:00' }],
54
+ [
55
+ '@1960-06-30 10:30',
56
+ 'timestamp',
57
+ 'minute',
58
+ { literal: '1960-06-30 10:30:00' },
59
+ ],
60
+ [
61
+ '@1960-06-30 10:30:00',
62
+ 'timestamp',
63
+ undefined,
64
+ { literal: '1960-06-30 10:30:00' },
65
+ ],
66
+ [
67
+ '@1960-06-30 10:30:00.123',
68
+ 'timestamp',
69
+ undefined,
70
+ { literal: '1960-06-30 10:30:00.123' },
71
+ ],
72
+ [
73
+ '@1960-06-30T10:30:00',
74
+ 'timestamp',
75
+ undefined,
76
+ { literal: '1960-06-30 10:30:00' },
77
+ ],
78
+ [
79
+ '@1960-06-30 10:30:00[America/Los_Angeles]',
80
+ 'timestamp',
81
+ undefined,
82
+ {
83
+ literal: '1960-06-30 10:30:00',
84
+ timezone: 'America/Los_Angeles',
85
+ },
86
+ ],
87
+ ];
88
+ test.each(literalTimes)('%s', (expr, timeType, timeframe, result) => {
89
+ const exprModel = new test_translator_1.BetaExpression(expr);
90
+ expect(exprModel).toTranslate();
91
+ const ir = exprModel.generated();
92
+ expect(ir.dataType).toEqual(timeType);
93
+ if (timeframe) {
94
+ expect((0, granular_result_1.isGranularResult)(ir)).toBeTruthy();
95
+ if ((0, granular_result_1.isGranularResult)(ir)) {
96
+ expect(ir.timeframe).toEqual(timeframe);
97
+ }
98
+ }
99
+ else {
100
+ expect((0, granular_result_1.isGranularResult)(ir)).toBeFalsy();
101
+ }
102
+ expect(ir.value[0]).toEqual(expect.objectContaining(result));
103
+ });
104
+ const morphicLiterals = [
105
+ ['@1960', '1960-01-01 00:00:00'],
106
+ ['@1960-Q2', '1960-04-01 00:00:00'],
107
+ ['@1960-06', '1960-06-01 00:00:00'],
108
+ ['@1960-06-26-Wk', '1960-06-26 00:00:00'],
109
+ ['@1960-06-30', '1960-06-30 00:00:00'],
110
+ ['@1960-06-30 00:00', undefined],
111
+ ];
112
+ test.each(morphicLiterals)('morphic value for %s is %s', (expr, morphic) => {
113
+ const exprModel = new test_translator_1.BetaExpression(expr);
114
+ expect(exprModel).toTranslate();
115
+ const ir = exprModel.generated();
116
+ const morphTo = ir.morphic && ir.morphic['timestamp'];
117
+ if (morphic) {
118
+ expect(morphTo).toBeDefined();
119
+ if (morphTo) {
120
+ expect(morphTo[0]).toEqual(expect.objectContaining({ literal: morphic }));
121
+ }
122
+ }
123
+ else {
124
+ expect(morphTo).toBeUndefined();
125
+ }
126
+ });
127
+ test('minute+locale', () => {
128
+ expect((0, test_translator_1.expr) `@1960-06-30 10:30[America/Los_Angeles]`).toTranslate();
129
+ });
130
+ test('second 8601', () => {
131
+ expect((0, test_translator_1.expr) `@1960-06-30T10:30:31`).toTranslate();
132
+ });
133
+ test('null', () => {
134
+ expect((0, test_translator_1.expr) `null`).toTranslate();
135
+ });
136
+ test('now', () => {
137
+ expect((0, test_translator_1.expr) `now`).toTranslate();
138
+ });
139
+ test('true', () => {
140
+ expect((0, test_translator_1.expr) `true`).toTranslate();
141
+ });
142
+ test('false', () => {
143
+ expect((0, test_translator_1.expr) `false`).toTranslate();
144
+ });
145
+ test('regex', () => {
146
+ expect((0, test_translator_1.expr) `r'RegularExpression'`).toTranslate();
147
+ });
148
+ describe('quote comprehension inside strings', () => {
149
+ test('\\b', () => {
150
+ expect((0, parse_utils_1.parseString)('\\b')).toEqual('\b');
151
+ });
152
+ test('\\f', () => {
153
+ expect((0, parse_utils_1.parseString)('\\f')).toEqual('\f');
154
+ });
155
+ test('\\n', () => {
156
+ expect((0, parse_utils_1.parseString)('\\n')).toEqual('\n');
157
+ });
158
+ test('\\r', () => {
159
+ expect((0, parse_utils_1.parseString)('\\r')).toEqual('\r');
160
+ });
161
+ test('\\t', () => {
162
+ expect((0, parse_utils_1.parseString)('\\t')).toEqual('\t');
163
+ });
164
+ test('unicode ?', () => {
165
+ expect((0, parse_utils_1.parseString)('\\u003f')).toEqual('?');
166
+ expect((0, parse_utils_1.parseString)('\\u003F')).toEqual('?');
167
+ });
168
+ test('normal stuff', () => {
169
+ expect((0, parse_utils_1.parseString)('normal stuff')).toEqual('normal stuff');
170
+ });
171
+ test('stuff & nonsense', () => {
172
+ expect((0, parse_utils_1.parseString)('stuff \\u0026 nonsense')).toEqual('stuff & nonsense');
173
+ });
174
+ test('one thing\\nnext thing', () => {
175
+ expect((0, parse_utils_1.parseString)('one thing\\nnext thing')).toEqual('one thing\nnext thing');
176
+ });
177
+ test('quote stripping works', () => {
178
+ expect((0, parse_utils_1.parseString)('|42|', '|')).toEqual('42');
179
+ });
180
+ });
181
+ describe('string parsing in language', () => {
182
+ const tz = 'America/Mexico_City';
183
+ test('multi-line indent increasing', () => {
184
+ const checking = new test_translator_1.BetaExpression(`"""
185
+ left
186
+ mid
187
+ right
188
+ """`);
189
+ expect(checking).toTranslate();
190
+ const v = checking.generated().value[0];
191
+ expect(v).toMatchObject({ literal: '\nleft\n mid\n right\n' });
192
+ });
193
+ test('multi-line indent decreasing', () => {
194
+ const checking = new test_translator_1.BetaExpression(`"""
195
+ right
196
+ mid
197
+ left
198
+ """`);
199
+ expect(checking).toTranslate();
200
+ const v = checking.generated().value[0];
201
+ expect(v).toMatchObject({ literal: '\n right\n mid\nleft\n' });
202
+ });
203
+ test('multi-line indent keep', () => {
204
+ const checking = new test_translator_1.BetaExpression('"""right\n mid\nleft"""');
205
+ expect(checking).toTranslate();
206
+ const v = checking.generated().value[0];
207
+ expect(v).toMatchObject({ literal: 'right\n mid\nleft' });
208
+ });
209
+ test('timezone single quote', () => {
210
+ const m = new test_translator_1.TestTranslator(`run: a-> {timezone: '${tz}'; project: *}`);
211
+ expect(m).toParse();
212
+ });
213
+ test('timezone double quote', () => {
214
+ const m = new test_translator_1.TestTranslator(`run: a-> {timezone: "${tz}"; project: *}`);
215
+ expect(m).toParse();
216
+ });
217
+ test('timezone triple quote', () => {
218
+ const m = new test_translator_1.TestTranslator(`run: a->{timezone: """${tz}"""; project: *}`);
219
+ expect(m).toParse();
220
+ });
221
+ test('timezone with illegal query', () => {
222
+ expect(`run: a->{timezone: """${tz}%{ab->aturtle}"""; project: *}`).translationToFailWith('%{ query } illegal in this string');
223
+ });
224
+ test('table single quote', () => {
225
+ const m = new test_translator_1.TestTranslator("source: n is bigquery.table('n')");
226
+ expect(m).toParse();
227
+ });
228
+ test('table double quote', () => {
229
+ const m = new test_translator_1.TestTranslator('source: n is bigquery.table("n")');
230
+ expect(m).toParse();
231
+ });
232
+ test('table triple quote', () => {
233
+ const m = new test_translator_1.TestTranslator('source: n is bigquery.table("""n""")');
234
+ expect(m).toParse();
235
+ });
236
+ test('sql single quote', () => {
237
+ const m = new test_translator_1.TestTranslator("source: n is bigquery.sql('n')");
238
+ expect(m).toParse();
239
+ });
240
+ test('sql double quote', () => {
241
+ const m = new test_translator_1.TestTranslator('source: n is bigquery.sql("n")');
242
+ expect(m).toParse();
243
+ });
244
+ test('sql triple quote', () => {
245
+ const m = new test_translator_1.TestTranslator('source: n is bigquery.sql("""n""")');
246
+ expect(m).toParse();
247
+ });
248
+ test('import single quote', () => {
249
+ const m = new test_translator_1.TestTranslator("import 'a'");
250
+ expect(m).toParse();
251
+ });
252
+ test('import double quote', () => {
253
+ const m = new test_translator_1.TestTranslator('import "a"');
254
+ expect(m).toParse();
255
+ });
256
+ test('import triple quote', () => {
257
+ const m = new test_translator_1.TestTranslator('import """a"""');
258
+ expect(m).toParse();
259
+ });
260
+ test('literal single quote', () => {
261
+ const x = new test_translator_1.BetaExpression("'x'");
262
+ expect(x).toParse();
263
+ });
264
+ test('literal double quote', () => {
265
+ const x = new test_translator_1.BetaExpression('"x"');
266
+ expect(x).toParse();
267
+ });
268
+ test('literal triple quote', () => {
269
+ const x = new test_translator_1.BetaExpression('"""x"""');
270
+ expect(x).toParse();
271
+ });
272
+ });
273
+ });
274
+ //# sourceMappingURL=literals.spec.js.map