@malloydata/malloy 0.0.88-dev231002231014 → 0.0.88-dev231002235732

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 (50) hide show
  1. package/dist/lang/ast/elements/define-query.d.ts +3 -3
  2. package/dist/lang/ast/elements/define-query.js +9 -4
  3. package/dist/lang/ast/elements/define-source.d.ts +3 -3
  4. package/dist/lang/ast/elements/define-source.js +12 -4
  5. package/dist/lang/ast/elements/pipeline-desc.d.ts +1 -0
  6. package/dist/lang/ast/elements/pipeline-desc.js +3 -0
  7. package/dist/lang/ast/elements/source-query-nodes.d.ts +72 -0
  8. package/dist/lang/ast/elements/source-query-nodes.js +368 -0
  9. package/dist/lang/ast/elements/source-query.d.ts +11 -0
  10. package/dist/lang/ast/{query-elements/run-query.js → elements/source-query.js} +32 -21
  11. package/dist/lang/ast/index.d.ts +2 -2
  12. package/dist/lang/ast/index.js +2 -2
  13. package/dist/lang/ast/query-elements/anonymous-query.d.ts +3 -3
  14. package/dist/lang/ast/query-elements/anonymous-query.js +15 -8
  15. package/dist/lang/ast/query-elements/existing-query.js +1 -1
  16. package/dist/lang/ast/query-items/field-references.d.ts +4 -0
  17. package/dist/lang/ast/query-items/field-references.js +11 -1
  18. package/dist/lang/ast/query-items/typecheck_utils.js +5 -5
  19. package/dist/lang/ast/query-properties/joins.d.ts +7 -5
  20. package/dist/lang/ast/query-properties/joins.js +22 -8
  21. package/dist/lang/ast/sources/sql-source.js +5 -0
  22. package/dist/lang/ast/sql-elements/sql-string.d.ts +2 -2
  23. package/dist/lang/ast/sql-elements/sql-string.js +19 -9
  24. package/dist/lang/ast/types/malloy-element.d.ts +3 -1
  25. package/dist/lang/ast/types/malloy-element.js +16 -5
  26. package/dist/lang/lib/Malloy/MalloyParser.d.ts +179 -184
  27. package/dist/lang/lib/Malloy/MalloyParser.js +3141 -2928
  28. package/dist/lang/lib/Malloy/MalloyParserListener.d.ts +147 -199
  29. package/dist/lang/lib/Malloy/MalloyParserVisitor.d.ts +88 -120
  30. package/dist/lang/malloy-to-ast.d.ts +16 -15
  31. package/dist/lang/malloy-to-ast.js +147 -141
  32. package/dist/lang/parse-tree-walkers/document-symbol-walker.js +1 -1
  33. package/dist/lang/parse-utils.js +2 -2
  34. package/dist/lang/test/annotation.spec.js +35 -46
  35. package/dist/lang/test/document-symbol-walker.spec.js +2 -2
  36. package/dist/lang/test/expressions.spec.js +16 -16
  37. package/dist/lang/test/imports.spec.js +10 -8
  38. package/dist/lang/test/literals.spec.js +4 -4
  39. package/dist/lang/test/locations.spec.js +82 -80
  40. package/dist/lang/test/parse-expects.js +5 -1
  41. package/dist/lang/test/parse.spec.js +173 -117
  42. package/dist/lang/test/query.spec.js +199 -203
  43. package/dist/lang/test/source.spec.js +54 -45
  44. package/dist/lang/test/sql-block.spec.js +19 -12
  45. package/dist/lang/test/test-translator.js +8 -5
  46. package/dist/malloy.js +2 -2
  47. package/package.json +1 -2
  48. package/dist/lang/ast/query-elements/run-query.d.ts +0 -13
  49. package/dist/lang/ast/sources/named-source-or-query.d.ts +0 -10
  50. package/dist/lang/ast/sources/named-source-or-query.js +0 -71
@@ -28,10 +28,10 @@ require("./parse-expects");
28
28
  describe('model statements', () => {
29
29
  describe('table method', () => {
30
30
  test('table method works', () => {
31
- expect("source: testA is conn.table('aTable')").toTranslate();
31
+ expect("source: testA is _db_.table('aTable')").toTranslate();
32
32
  });
33
33
  test('table method works with quoted connection name', () => {
34
- expect("source: testA is `conn`.table('aTable')").toTranslate();
34
+ expect("source: testA is `_db_`.table('aTable')").toTranslate();
35
35
  });
36
36
  test('table method fails when connection name is wrong', () => {
37
37
  expect("source: testA is bad_conn.table('aTable')").not.toTranslate();
@@ -52,51 +52,51 @@ describe('model statements', () => {
52
52
  });
53
53
  test('table function is deprecated', () => {
54
54
  expect(`##! m4warnings
55
- source: testA is table('conn:aTable')
55
+ source: testA is table('_db_:aTable')
56
56
  `).toTranslateWithWarnings("`table('connection_name:table_path')` is deprecated; use `connection_name.table('table_path')`");
57
57
  });
58
58
  });
59
59
  test('errors on redefinition of query', () => {
60
- expect('query: q1 is a -> { project: * }, q1 is a -> { project: * }').translationToFailWith("'q1' is already defined, cannot redefine");
60
+ expect('query: q1 is a -> { select: * }, q1 is a -> { select: * }').translationToFailWith("'q1' is already defined, cannot redefine");
61
61
  });
62
62
  });
63
63
  describe('error handling', () => {
64
64
  test('field and query with same name does not overflow', () => {
65
65
  expect(`
66
- source: flights is table('malloytest.flights') {
67
- query: carrier is { group_by: carrier }
66
+ source: flights is _db_.table('malloytest.flights') extend {
67
+ view: carrier is { group_by: carrier }
68
68
  }
69
69
  `).translationToFailWith("Cannot redefine 'carrier'");
70
70
  });
71
71
  test('redefine source', () => {
72
72
  expect((0, test_translator_1.markSource) `
73
- source: airports is table('malloytest.airports') + {
73
+ source: airports is _db_.table('malloytest.airports') extend {
74
74
  primary_key: code
75
75
  }
76
- source: airports is table('malloytest.airports') + {
76
+ source: airports is _db_.table('malloytest.airports') extend {
77
77
  primary_key: code
78
78
  }
79
79
  `).translationToFailWith("Cannot redefine 'airports'");
80
80
  });
81
81
  test('query from undefined source', () => {
82
- expect((0, test_translator_1.markSource) `query: ${'x'}->{ project: y }`).translationToFailWith("Undefined query or source 'x'");
82
+ expect((0, test_translator_1.markSource) `run: ${'x'}->{ select: y }`).translationToFailWith("Reference to undefined object 'x'");
83
83
  });
84
84
  test('query with expression from undefined source', () => {
85
85
  // Regression check: Once upon a time this died with an exception even
86
86
  // when "query: x->{ group_by: y}" (above) generated the correct error.
87
- expect((0, test_translator_1.markSource) `query: ${'x'}->{ project: y is z / 2 }`).translationToFailWith("Undefined query or source 'x'");
87
+ expect((0, test_translator_1.markSource) `run: ${'x'}->{ select: y is z / 2 }`).translationToFailWith("Reference to undefined object 'x'");
88
88
  });
89
89
  test('join reference before definition', () => {
90
90
  expect((0, test_translator_1.markSource) `
91
- source: newAB is a { join_one: newB is ${'bb'} on astring }
91
+ source: newAB is a extend { join_one: newB is ${'bb'} on astring }
92
92
  source: newB is b
93
- `).translationToFailWith("Undefined query or source 'bb'");
93
+ `).translationToFailWith("Reference to undefined object 'bb'");
94
94
  });
95
95
  test('non-rename rename', () => {
96
- expect('source: na is a { rename: astr is astr }').translationToFailWith("Can't rename field to itself");
96
+ expect('source: na is a extend { rename: astr is astr }').translationToFailWith("Can't rename field to itself");
97
97
  });
98
98
  test('reference to field in its definition', () => {
99
- expect('source: na is a { dimension: ustr is upper(ustr) } ').translationToFailWith("Circular reference to 'ustr' in definition");
99
+ expect('source: na is a extend { dimension: ustr is upper(ustr) } ').translationToFailWith("Circular reference to 'ustr' in definition");
100
100
  });
101
101
  test('empty model', () => {
102
102
  expect('').toTranslate();
@@ -105,51 +105,53 @@ describe('error handling', () => {
105
105
  expect('\n').toTranslate();
106
106
  });
107
107
  test('query without fields', () => {
108
- expect('query: a -> { top: 5 }').translationToFailWith("Can't determine query type (group_by/aggregate/nest,project,index)");
108
+ expect('run: a -> { top: 5 }').translationToFailWith("Can't determine query type (group_by/aggregate/nest,project,index)");
109
109
  });
110
110
  test('refine cannot change query type', () => {
111
- expect('query: ab -> aturtle { project: astr }').translationToFailWith(/Not legal in grouping query/);
111
+ expect('run: ab -> aturtle + { select: astr }').translationToFailWith(/Not legal in grouping query/);
112
112
  });
113
113
  test('undefined field ref in query', () => {
114
- expect('query: ab -> { aggregate: xyzzy }').translationToFailWith("'xyzzy' is not defined");
114
+ expect('run: ab -> { aggregate: xyzzy }').translationToFailWith("'xyzzy' is not defined");
115
115
  });
116
116
  test('query on source with errors', () => {
117
117
  expect((0, test_translator_1.markSource) `
118
- source: na is a { join_one: ${'n'} on astr }
119
- `).translationToFailWith("Undefined source 'n'");
118
+ source: na is a extend { join_one: ${'n'} on astr }
119
+ `).translationToFailWith("Reference to undefined object 'n'");
120
120
  });
121
121
  test('detect duplicate output field names', () => {
122
- expect((0, test_translator_1.markSource) `query: ab -> { group_by: astr, ${'astr'} }`).translationToFailWith("Output already has a field named 'astr'");
122
+ expect((0, test_translator_1.markSource) `run: ab -> { group_by: astr, ${'astr'} }`).translationToFailWith("Output already has a field named 'astr'");
123
123
  });
124
124
  test('detect join tail overlap existing ref', () => {
125
- expect((0, test_translator_1.markSource) `query: ab -> { group_by: astr, ${'b.astr'} }`).translationToFailWith("Output already has a field named 'astr'");
125
+ expect((0, test_translator_1.markSource) `run: ab -> { group_by: astr, ${'b.astr'} }`).translationToFailWith("Output already has a field named 'astr'");
126
126
  });
127
127
  test('undefined in expression with regex compare', () => {
128
128
  expect(`
129
- source: c is a {
129
+ source: c is a extend {
130
130
  dimension: d is meaning_of_life ~ r'(forty two|fifty four)'
131
131
  }
132
132
  `).translationToFailWith("'meaning_of_life' is not defined");
133
133
  });
134
134
  test('detect output collision on join references', () => {
135
135
  expect(`
136
- query: ab -> {
136
+ run: ab -> {
137
137
  group_by: astr, b.astr
138
138
  }
139
139
  `).translationToFailWith("Output already has a field named 'astr'");
140
140
  });
141
141
  test('rejoin a query is renamed', () => {
142
+ // should not use from()
142
143
  expect(`
144
+ ##! -m4warnings
143
145
  source: querySrc is from(
144
- table('malloytest.flights')->{
146
+ _db_.table('malloytest.flights')->{
145
147
  group_by: origin
146
148
  nest: nested is { group_by: destination }
147
149
  }
148
150
  )
149
151
 
150
- source: refineQuerySrc is querySrc {
152
+ source: refineQuerySrc is querySrc extend {
151
153
  join_one: rejoin is querySrc on 7=8
152
- query: broken is {
154
+ view: broken is {
153
155
  group_by: rejoin.nested.destination
154
156
  }
155
157
  }
@@ -159,7 +161,7 @@ describe('error handling', () => {
159
161
  expect('}%').translationToFailWith(/extraneous input '}%' expecting/);
160
162
  });
161
163
  test('bad sql in sql block', () => {
162
- const badSelect = (0, test_translator_1.model) `sql: someName is { select: """)""" }`;
164
+ const badSelect = (0, test_translator_1.model) `source: someName is _db_.sql(")")`;
163
165
  const badTrans = badSelect.translator;
164
166
  expect(badSelect).toParse();
165
167
  const needSchema = badTrans.translate();
@@ -168,12 +170,12 @@ describe('error handling', () => {
168
170
  badTrans.update({
169
171
  errors: {
170
172
  compileSQL: {
171
- [needSchema.compileSQL.name]: 'ZZZZ',
173
+ [needSchema.compileSQL.name]: 'Nobody ZZZZ',
172
174
  },
173
175
  },
174
176
  });
175
177
  }
176
- expect(badTrans).translationToFailWith('Invalid SQL, ZZZZ');
178
+ expect(badTrans).translationToFailWith('Invalid SQL, Nobody ZZZZ');
177
179
  });
178
180
  });
179
181
  describe('translate imports', () => {
@@ -184,7 +186,7 @@ describe('translate imports', () => {
184
186
  expect(resp1.final).toBeUndefined();
185
187
  m.update({
186
188
  urls: {
187
- 'internal://test/langtests/myfile.malloy': 'query: ab -> { project: * }',
189
+ 'internal://test/langtests/myfile.malloy': 'query: ab -> { select: * }',
188
190
  },
189
191
  });
190
192
  const resp2 = m.translate();
@@ -207,8 +209,8 @@ describe('translation need error locations', () => {
207
209
  });
208
210
  test('sql struct error location', () => {
209
211
  const source = (0, test_translator_1.markSource) `
210
- sql: bad_sql is {select: ${'"""BAD_SQL"""'}}
211
- query: from_sql(bad_sql) -> { project: * }
212
+ source: bad_sql is _db_.sql(${'"""BAD_SQL"""'})
213
+ run: bad_sql -> { select: * }
212
214
  `;
213
215
  const m = new test_translator_1.TestTranslator(source.code);
214
216
  expect(m).toParse();
@@ -223,7 +225,7 @@ describe('translation need error locations', () => {
223
225
  });
224
226
  test('table struct error location', () => {
225
227
  const source = (0, test_translator_1.model) `
226
- source: bad_explore is ${"table('malloy-data.bad.table')"}
228
+ source: bad_explore is ${"_db_.table('malloy-data.bad.table')"}
227
229
  `;
228
230
  const m = source.translator;
229
231
  const result = m.translate();
@@ -238,8 +240,8 @@ describe('translation need error locations', () => {
238
240
  describe('error cascading', () => {
239
241
  test('errors can appear in multiple top level objects', () => {
240
242
  expect((0, test_translator_1.markSource) `
241
- source: a1 is a { dimension: ${'x is count()'} }
242
- source: a2 is a { dimension: ${'x is count()'} }
243
+ source: a1 is a extend { dimension: ${'x is count()'} }
244
+ source: a2 is a extend { dimension: ${'x is count()'} }
243
245
  `).translationToFailWith('Cannot use an aggregate field in a dimension declaration, did you mean to use a measure declaration instead?', 'Cannot use an aggregate field in a dimension declaration, did you mean to use a measure declaration instead?');
244
246
  });
245
247
  const typedScalars = {
@@ -305,13 +307,13 @@ describe('error cascading', () => {
305
307
  const ungroupedAggregates = ['all(measure_err)'];
306
308
  test('dependent errors do not cascade', () => {
307
309
  expect(`
308
- source: a1 is a {
310
+ source: a1 is a extend {
309
311
  join_one: b with astr
310
312
  dimension:
311
313
  ${'err is null'}
312
314
  ${scalars.map((d, i) => `e${i} is ${d}`).join('\n ')}
313
315
  measure:
314
- measure_err is count(distinct foo),
316
+ measure_err is count(foo),
315
317
  ${[...aggregates, ...ungroupedAggregates]
316
318
  .map((m, i) => `e${i + scalars.length} is ${m}`)
317
319
  .join('\n ')}
@@ -321,7 +323,7 @@ describe('error cascading', () => {
321
323
  test('error type inference is good', () => {
322
324
  for (const scalar of scalars) {
323
325
  const source = `
324
- source: a1 is a {
326
+ source: a1 is a extend {
325
327
  dimension:
326
328
  ${'err is null'}
327
329
  dim is length(${scalar}, 1)
@@ -332,14 +334,14 @@ describe('error cascading', () => {
332
334
  });
333
335
  test('eval space of errors is preserved', () => {
334
336
  expect(`
335
- source: a1 is a {
337
+ source: a1 is a extend {
336
338
  join_one: b with astr
337
339
  }
338
- query: a1 -> {
340
+ run: a1 -> {
339
341
  group_by:
340
342
  ${'err is null'}
341
343
  aggregate:
342
- measure_err is count(distinct foo)
344
+ measure_err is count(foo)
343
345
  calculate:
344
346
  ${scalars
345
347
  .map((d, i) => `e${i} is lag(${d})`)
@@ -354,74 +356,74 @@ describe('error cascading', () => {
354
356
  describe('pipeline comprehension', () => {
355
357
  test('second query gets namespace from first', () => {
356
358
  expect(`
357
- source: aq is a {
358
- query: t1 is {
359
+ source: aq is a extend {
360
+ view: t1 is {
359
361
  group_by: t1int is ai, t1str is astr
360
362
  } -> {
361
- project: t1str, t1int
363
+ select: t1str, t1int
362
364
  }
363
365
  }
364
366
  `).toTranslate();
365
367
  });
366
368
  test("second query doesn't have access to original fields", () => {
367
369
  expect((0, test_translator_1.model) `
368
- source: aq is a {
369
- query: t1 is {
370
+ source: aq is a extend {
371
+ view: t1 is {
370
372
  group_by: t1int is ai, t1str is astr
371
373
  } -> {
372
- project: ${'ai'}
374
+ select: ${'ai'}
373
375
  }
374
376
  }
375
377
  `).translationToFailWith("'ai' is not defined");
376
378
  });
377
379
  test('new query can append ops to existing query', () => {
378
380
  expect(`
379
- source: aq is a {
380
- query: t0 is {
381
+ source: aq is a extend {
382
+ view: t0 is {
381
383
  group_by: t1int is ai, t1str is astr
382
384
  }
383
- query: t1 is t0 -> {
384
- project: t1str, t1int
385
+ view: t1 is t0 -> {
386
+ select: t1str, t1int
385
387
  }
386
388
  }
387
389
  `).toTranslate();
388
390
  });
389
391
  test('new query can refine and append to exisiting query', () => {
390
392
  expect(`
391
- source: aq is table('aTable') {
392
- query: by_region is { group_by: astr }
393
- query: by_region2 is by_region {
393
+ source: aq is _db_.table('aTable') extend {
394
+ view: by_region is { group_by: astr }
395
+ view: by_region2 is by_region + {
394
396
  nest: dateNest is { group_by: ad }
395
397
  } -> {
396
- project: astr, dateNest.ad
398
+ select: astr, dateNest.ad
397
399
  }
398
400
  }
399
401
  `).toTranslate();
400
402
  });
401
403
  test('reference to a query can include a refinement', () => {
402
404
  expect(`
403
- query: ab -> {
405
+ run: ab -> {
404
406
  group_by: ai
405
- nest: aturtle { limit: 1 }
407
+ nest: aturtle + { limit: 1 }
406
408
  }
407
409
  `).toTranslate();
408
410
  });
409
411
  test('Querying an sourcebased on a query', () => {
410
412
  expect(`
411
413
  query: q is a -> { group_by: astr; aggregate: strsum is ai.sum() }
412
- source: aq is a {
413
- join_one: aq is from(->q) on astr = aq.astr
414
+ source: aq is a extend {
415
+ join_one: aq is q on astr = aq.astr
414
416
  }
415
- query: aqf is aq -> { project: * }
417
+ run: aq -> { select: * }
416
418
  `).toTranslate();
417
419
  });
418
420
  test('new query appends to existing query', () => {
419
421
  const src = `
420
- query: s1 is table('malloytest.flights') -> {
422
+ query: s1 is _db_.table('malloytest.flights') -> {
421
423
  group_by: origin, destination
422
424
  }
423
- query: s2 is ->s1 ->{
424
- group_by: destination
425
+ query: s2 is s1 -> {
426
+ select: *, extra is 'extra'
425
427
  }
426
428
  `;
427
429
  const m = new test_translator_1.TestTranslator(src);
@@ -480,7 +482,7 @@ describe('sql expressions', () => {
480
482
  });
481
483
  test('reference to sql expression in extended source', () => {
482
484
  const m = (0, test_translator_1.model) `
483
- source: na is bigquery.sql("""SELECT 1 as one""") {
485
+ source: na is bigquery.sql("""SELECT 1 as one""") extend {
484
486
  dimension: two is one + one
485
487
  }
486
488
  `;
@@ -495,7 +497,7 @@ describe('sql expressions', () => {
495
497
  });
496
498
  test('reference to sql expression in named query', () => {
497
499
  const m = new test_translator_1.TestTranslator(`
498
- query: nq is bigquery.sql("""SELECT 1 as one""") -> { project: * }
500
+ query: nq is bigquery.sql("""SELECT 1 as one""") -> { select: * }
499
501
  `);
500
502
  expect(m).toParse();
501
503
  const compileSql = m.translate().compileSQL;
@@ -509,7 +511,7 @@ describe('sql expressions', () => {
509
511
  });
510
512
  test('reference to sql expression in unnamed query', () => {
511
513
  const m = new test_translator_1.TestTranslator(`
512
- query: bigquery.sql("""SELECT 1 as one""") -> { project: * }
514
+ run: bigquery.sql("""SELECT 1 as one""") -> { select: * }
513
515
  `);
514
516
  expect(m).toParse();
515
517
  const compileSql = m.translate().compileSQL;
@@ -523,7 +525,7 @@ describe('sql expressions', () => {
523
525
  });
524
526
  test('reference to sql expression in join', () => {
525
527
  const m = (0, test_translator_1.model) `
526
- source: quux is a {
528
+ source: quux is a extend {
527
529
  join_one: xyzzy is
528
530
  duckdb.sql("""SELECT 1 as one""")
529
531
  on ai = xyzzy.one
@@ -591,7 +593,7 @@ describe('sql expressions', () => {
591
593
  });
592
594
  test('reference to sql expression in anonymous query', () => {
593
595
  const m = new test_translator_1.TestTranslator(`
594
- query: bigquery.sql("""select 1 as one""")
596
+ run: bigquery.sql("""select 1 as one""")
595
597
  `);
596
598
  expect(m).toParse();
597
599
  const compileSql = m.translate().compileSQL;
@@ -605,11 +607,11 @@ describe('sql expressions', () => {
605
607
  });
606
608
  // TODO this is not possible to implement yet unless we
607
609
  // can distinguish between the generated IR of `conn.sql(...)`
608
- // and `conn.sql(...) -> { project: * }`.
610
+ // and `conn.sql(...) -> { select: * }`.
609
611
  test.skip('cannot refine a SQL query', () => {
610
612
  const m = new test_translator_1.TestTranslator(`
611
613
  query: q1 is bigquery.sql("""select 1 as one""")
612
- run: q1 refine { where: 1 = 1 }
614
+ run: q1 + { where: 1 = 1 }
613
615
  `);
614
616
  expect(m).toParse();
615
617
  const compileSql = m.translate().compileSQL;
@@ -623,11 +625,12 @@ describe('sql expressions', () => {
623
625
  });
624
626
  });
625
627
  describe('turtle with leading arrow', () => {
626
- test('optional leading arrow for defining a turtle', () => {
628
+ test('pre m4 optional leading arrow for defining a turtle', () => {
627
629
  expect(`
630
+ ##! -m4warnings
628
631
  source: c is a extend {
629
- query: x is -> { project: * }
630
- query: y is { project: * }
632
+ query: x is -> { select: * }
633
+ query: y is { select: * }
631
634
  }
632
635
  `).toTranslate();
633
636
  });
@@ -637,7 +640,7 @@ describe('extend and refine', () => {
637
640
  test('query name with query refinements', () => {
638
641
  expect(`
639
642
  query: q is a -> { group_by: ai }
640
- run: q refine { group_by: ai2 is ai }
643
+ run: q + { group_by: ai2 is ai }
641
644
  `).toTranslate();
642
645
  });
643
646
  test('query name with source refinements', () => {
@@ -647,7 +650,7 @@ describe('extend and refine', () => {
647
650
  `).toTranslate();
648
651
  });
649
652
  test('source name with query refinements', () => {
650
- expect('run: a refine { group_by: ai }').translationToFailWith("Illegal reference to 'a', query expected");
653
+ expect('run: a + { group_by: ai }').translationToFailWith("Cannot add view refinements to 'a' because it is a source");
651
654
  });
652
655
  test('source name with source refinements', () => {
653
656
  expect('source: s is a extend { dimension: ai_2 is ai + ai }').toTranslate();
@@ -656,96 +659,111 @@ describe('extend and refine', () => {
656
659
  expect('source: s is a -> { group_by: ai } extend { dimension: ai_2 is ai + ai }').toTranslate();
657
660
  });
658
661
  test('query with extension then new stage', () => {
659
- expect('run: a { dimension: x is 1 } -> { group_by: x }').toTranslate();
662
+ expect('run: a extend { dimension: x is 1 } -> { group_by: x }').toTranslate();
660
663
  });
661
664
  });
662
665
  describe('extend and refine, old syntax', () => {
663
666
  test('query name with query refinements', () => {
664
667
  expect(`
665
668
  query: q is a -> { group_by: ai }
666
- run: q { group_by: ai2 is ai }
669
+ run: q + { group_by: ai2 is ai }
667
670
  `).toTranslate();
668
671
  });
669
672
  test('query name with source refinements', () => {
670
673
  expect(`
671
674
  query: q is a -> { group_by: ai }
672
- source: s is q { dimension: ai_2 is ai + ai }
675
+ source: s is q extend { dimension: ai_2 is ai + ai }
673
676
  `).toTranslate();
674
677
  });
675
678
  test('source name with query refinements', () => {
676
- expect('run: a { group_by: one }').translationToFailWith("Illegal reference to 'a', query expected");
679
+ expect('run: a + { group_by: one }').translationToFailWith("Cannot add view refinements to 'a' because it is a source");
677
680
  });
678
681
  test('source name with source refinements', () => {
679
- expect('source: s is a { dimension: ai_2 is ai + ai }').toTranslate();
682
+ expect('source: s is a extend { dimension: ai_2 is ai + ai }').toTranslate();
680
683
  });
681
684
  test('source name with ambiguous refinements', () => {
682
- // Ambiguous refinements are assumed to be source extensions
683
- expect('run: a { join_one: b on b.ai = ai } -> { project: b.* }').toTranslate();
684
- expect('run: a { where: 1 = 1 } -> { project: * }').toTranslate();
685
- expect('run: a { declare: three is 3 } -> { project: * }').toTranslate();
686
- expect('source: s is a { join_one: b on b.ai = ai }').toTranslate();
687
- expect('source: s is a { where: 1 = 1 }').toTranslate();
688
- expect('source: s is a { declare: three is 3 }').toTranslate();
685
+ expect(`##! -m4warnings
686
+ run: a + { join_one: b on b.ai = ai } -> { select: b.* }`).toTranslate();
687
+ expect(`##! -m4warnings
688
+ run: a + { where: 1 = 1 } -> { select: * }`).toTranslate();
689
+ expect(`##! -m4warnings
690
+ run: a + { dimension: three is 3 } -> { select: * }`).toTranslate();
691
+ expect(`##! -m4warnings
692
+ source: s is a { join_one: b on b.ai = ai }`).toTranslate();
693
+ expect(`##! -m4warnings
694
+ source: s is a { where: 1 = 1 }`).toTranslate();
695
+ expect(`##! -m4warnings
696
+ source: s is a { dimension: three is 3 }`).toTranslate();
689
697
  });
690
698
  describe('query name with ambiguous refinements', () => {
691
- test('implicitly convert the query into a source', () => {
699
+ test('adding segment to ambuguously refined query', () => {
692
700
  expect(`
701
+ ##! -m4warnings
693
702
  query: q is a -> { group_by: ai }
694
- run: q { join_one: b on 1 = 1 } -> { project: b.* }
695
- `).toTranslate();
703
+ run: q + { join_one: b on 1 = 1 } -> { select: b.* }
704
+ `).translationToFailWith("No such field as 'b'");
696
705
  expect(`
706
+ ##! -m4warnings
697
707
  query: q is a -> { group_by: ai }
698
- run: q { where: 1 = 1 } -> { project: * }
708
+ run: q + { where: 1 = 1 } -> { select: * }
699
709
  `).toTranslate();
700
710
  expect(`
711
+ ##! -m4warnings
701
712
  query: q is a -> { group_by: ai }
702
- run: q { declare: three is 3 } -> { project: * }
713
+ run: q + { declare: three is 3 } -> { select: * }
703
714
  `).toTranslate();
704
715
  });
705
716
  test('automatically recognize this as a query refinement without new stage', () => {
706
717
  expect(`
718
+ ##! -m4warnings
707
719
  query: q is a -> { group_by: ai }
708
- run: q { join_one: b on 1 = 1 }
720
+ run: q + { join_one: b on 1 = 1 }
709
721
  `).toTranslate();
710
722
  expect(`
723
+ ##! -m4warnings
711
724
  query: q is a -> { group_by: ai }
712
- run: q { where: 1 = 1 }
725
+ run: q + { where: 1 = 1 }
713
726
  `).toTranslate();
714
727
  expect(`
728
+ ##! -m4warnings
715
729
  query: q is a -> { group_by: ai }
716
- run: q { declare: three is 3 }
730
+ run: q + { declare: three is 3 }
717
731
  `).toTranslate();
718
732
  });
719
- test('can also just add from() to use as a source', () => {
720
- // TODO add a warning when you use from() -- "`from()` is deprecated; to apply source extensions to a query, use `extend`"
733
+ test('can extend a query into a source', () => {
721
734
  expect(`
722
735
  query: q is a -> { group_by: ai }
723
- source: s is from(q) { join_one: b on b.ai = ai }
736
+ source: s is q extend { join_one: b on b.ai = ai }
724
737
  `).toTranslate();
725
738
  });
726
739
  test('can also add an arrow to clarify that it is a query', () => {
727
740
  // Of course, number 1 and 3 are actually useless because you're defining
728
741
  // something and then not using it...
729
742
  expect(`
743
+ ##! -m4warnings
730
744
  query: q is a -> { group_by: ai }
731
- run: -> q { join_one: b on b.ai = ai } -> { project: * }
745
+ run: q + { join_one: b on b.ai = ai } -> { select: * }
732
746
  `).toTranslate();
733
747
  expect(`
748
+ ##! -m4warnings
734
749
  query: q is a -> { group_by: ai }
735
- run: -> q { where: 1 = 1 } -> { project: * }
750
+ run: q + { where: 1 = 1 } -> { select: * }
736
751
  `).toTranslate();
737
752
  expect(`
753
+ ##! -m4warnings
738
754
  query: q is a -> { group_by: ai }
739
- run: -> q { declare: three is 3 } -> { project: * }
755
+ run: q + { declare: three is 3 } -> { select: * }
740
756
  `).toTranslate();
741
757
  // Alternatively, fix 1 and 3 by actually using the declared thing
742
758
  expect(`
759
+ ##! -m4warnings
743
760
  query: q is a -> { group_by: ai }
744
- run: q { join_one: b on b.ai = ai; group_by: ai2 is b.ai }
761
+ run: q + { join_one: b on b.ai = ai; group_by: ai2 is b.ai }
745
762
  `).toTranslate();
746
763
  expect(`
764
+ ##! -m4warnings
747
765
  query: q is a -> { group_by: ai }
748
- run: q { declare: three is 3; group_by: three }
766
+ run: q + { extend: {dimension: three is 3} group_by: three }
749
767
  `).toTranslate();
750
768
  });
751
769
  });
@@ -755,41 +773,40 @@ describe('extend and refine', () => {
755
773
  expect('run: a').translationToFailWith("Illegal reference to 'a', query expected");
756
774
  });
757
775
  test('syntactically valid to refine a source, but illegal', () => {
758
- expect('run: a { group_by: ai } ').translationToFailWith("Illegal reference to 'a', query expected");
759
- expect('run: a refine { group_by: ai } ').translationToFailWith("Illegal reference to 'a', query expected");
776
+ expect('run: a + { group_by: ai } ').translationToFailWith("Cannot add view refinements to 'a' because it is a source");
760
777
  });
761
778
  });
762
779
  describe('turtles', () => {
763
780
  test('explicit refine in turtle works', () => {
764
781
  expect(`source: c is a extend {
765
- query: x is { project: * }
766
- query: y is x refine { limit: 1 }
782
+ view: x is { select: * }
783
+ view: y is x + { limit: 1 }
767
784
  }`).toTranslate();
768
785
  });
769
786
  test('implicit refine in turtle works', () => {
770
787
  expect(`source: c is a extend {
771
- query: x is { project: * }
772
- query: y is x { limit: 1 }
788
+ view: x is { select: * }
789
+ view: y is x + { limit: 1 }
773
790
  }`).toTranslate();
774
791
  });
775
792
  });
776
793
  describe('nests', () => {
777
794
  test('explicit refine in nest', () => {
778
795
  expect(`source: c is a extend {
779
- query: x is { project: * }
796
+ view: x is { select: * }
780
797
  }
781
798
 
782
799
  run: c -> {
783
- nest: x refine { limit: 1 }
800
+ nest: x + { limit: 1 }
784
801
  }`).toTranslate();
785
802
  });
786
- test('implicit refine in nest', () => {
803
+ test('refine in nest', () => {
787
804
  expect(`source: c is a extend {
788
- query: x is { project: * }
805
+ view: x is { select: * }
789
806
  }
790
807
 
791
808
  run: c -> {
792
- nest: x { limit: 1 }
809
+ nest: x + { limit: 1 }
793
810
  }`).toTranslate();
794
811
  });
795
812
  });
@@ -802,20 +819,20 @@ describe('extend and refine', () => {
802
819
 
803
820
  run: c -> {
804
821
  nest: x { limit: 1 }
805
- }`).toTranslateWithWarnings('Implicit query refinement is deprecated, use the `refine` operator.');
822
+ }`).toTranslateWithWarnings('Implicit query refinement is deprecated, use the `+` operator');
806
823
  });
807
824
  test('implicit refine in turtle works', () => {
808
825
  expect(`##! m4warnings
809
826
  source: c is a extend {
810
827
  view: x is { select: * }
811
828
  view: y is x { limit: 1 }
812
- }`).toTranslateWithWarnings('Implicit query refinement is deprecated, use the `refine` operator.');
829
+ }`).toTranslateWithWarnings('Implicit query refinement is deprecated, use the `+` operator');
813
830
  });
814
831
  test('implicit query refinement', () => {
815
832
  expect(`##! m4warnings
816
833
  query: q is a -> { group_by: ai }
817
834
  run: q { group_by: three is 3 }
818
- `).toTranslateWithWarnings('Implicit query refinement is deprecated, use the `refine` operator.');
835
+ `).toTranslateWithWarnings('Implicit query refinement is deprecated, use the `+` operator');
819
836
  });
820
837
  test('implicit source extension', () => {
821
838
  expect(`##! m4warnings
@@ -867,4 +884,43 @@ describe('miscellaneous m4 warnings', () => {
867
884
  `).toTranslateWithWarnings('Leading arrow (`->`) when referencing a query is deprecated; remove the arrow');
868
885
  });
869
886
  });
887
+ describe('m3/m4 source query sentences', () => {
888
+ const srcExtend = '{accept:ai}';
889
+ const qryRefine = '{limit:1}';
890
+ const query = '{select:*}';
891
+ // todo MTOY write test to make sure arrow has correct precedence vs +
892
+ // also maybe arrow vs extend
893
+ test('M4 should error on these sq expressions', () => {
894
+ expect(`source: s is a + ${qryRefine}`).translationToFailWith("Cannot add view refinements to 'a' because it is a source");
895
+ expect(`query: q_m4_err is a + ${qryRefine}`).translationToFailWith("Cannot add view refinements to 'a' because it is a source");
896
+ });
897
+ test('legal sqexpressions', () => {
898
+ // some things that are m4 warnings are commented out
899
+ expect(`
900
+ source: s is a
901
+ query: q is s -> ${query}
902
+
903
+ source: s0 is a;
904
+ // source: s0_extbare is s ${srcExtend};
905
+ // source: s0_extplus is s + ${srcExtend};
906
+ source: s0_ext is s extend ${srcExtend};
907
+ source: qs is q;
908
+ source: qs0 is q extend ${srcExtend};
909
+ source: qs1 is q + ${qryRefine};
910
+ source: s1_m4 is q + ${qryRefine};
911
+ source: s2_m4 is q + ${qryRefine} -> ${query} extend ${srcExtend};
912
+ source: s3 is s extend ${srcExtend};
913
+ source: s4 is q extend ${srcExtend};
914
+ // source: s5 is from(s -> ${query})
915
+
916
+ query: q0 is q;
917
+ // query: q0_refbare is q ${qryRefine};
918
+ query: q0_refplus is q + ${qryRefine};
919
+ // query: q1_bare is ab -> aturtle ${qryRefine};
920
+ query: q1_plus is ab -> aturtle + ${qryRefine};
921
+ query: q2 is s -> ${query} extend ${srcExtend} -> ${query};
922
+
923
+ `).toTranslate();
924
+ });
925
+ });
870
926
  //# sourceMappingURL=parse.spec.js.map