@malloydata/malloy 0.0.123-dev240205152128 → 0.0.123-dev240205203117

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.
@@ -19,7 +19,6 @@ import { ExprRegexContext } from "./MalloyParser";
19
19
  import { ExprNowContext } from "./MalloyParser";
20
20
  import { TableFunctionContext } from "./MalloyParser";
21
21
  import { TableMethodContext } from "./MalloyParser";
22
- import { NestExistingContext } from "./MalloyParser";
23
22
  import { NestDefContext } from "./MalloyParser";
24
23
  import { ExprFieldPathContext } from "./MalloyParser";
25
24
  import { ExprFieldPropsContext } from "./MalloyParser";
@@ -453,18 +452,6 @@ export interface MalloyParserListener extends ParseTreeListener {
453
452
  * @param ctx the parse tree
454
453
  */
455
454
  exitTableMethod?: (ctx: TableMethodContext) => void;
456
- /**
457
- * Enter a parse tree produced by the `nestExisting`
458
- * labeled alternative in `MalloyParser.nestEntry`.
459
- * @param ctx the parse tree
460
- */
461
- enterNestExisting?: (ctx: NestExistingContext) => void;
462
- /**
463
- * Exit a parse tree produced by the `nestExisting`
464
- * labeled alternative in `MalloyParser.nestEntry`.
465
- * @param ctx the parse tree
466
- */
467
- exitNestExisting?: (ctx: NestExistingContext) => void;
468
455
  /**
469
456
  * Enter a parse tree produced by the `nestDef`
470
457
  * labeled alternative in `MalloyParser.nestEntry`.
@@ -19,7 +19,6 @@ import { ExprRegexContext } from "./MalloyParser";
19
19
  import { ExprNowContext } from "./MalloyParser";
20
20
  import { TableFunctionContext } from "./MalloyParser";
21
21
  import { TableMethodContext } from "./MalloyParser";
22
- import { NestExistingContext } from "./MalloyParser";
23
22
  import { NestDefContext } from "./MalloyParser";
24
23
  import { ExprFieldPathContext } from "./MalloyParser";
25
24
  import { ExprFieldPropsContext } from "./MalloyParser";
@@ -356,13 +355,6 @@ export interface MalloyParserVisitor<Result> extends ParseTreeVisitor<Result> {
356
355
  * @return the visitor result
357
356
  */
358
357
  visitTableMethod?: (ctx: TableMethodContext) => Result;
359
- /**
360
- * Visit a parse tree produced by the `nestExisting`
361
- * labeled alternative in `MalloyParser.nestEntry`.
362
- * @param ctx the parse tree
363
- * @return the visitor result
364
- */
365
- visitNestExisting?: (ctx: NestExistingContext) => Result;
366
358
  /**
367
359
  * Visit a parse tree produced by the `nestDef`
368
360
  * labeled alternative in `MalloyParser.nestEntry`.
@@ -144,7 +144,6 @@ export declare class MalloyToAST extends AbstractParseTreeVisitor<ast.MalloyElem
144
144
  visitRunStatement(pcx: parse.RunStatementContext): ast.AnonymousQuery;
145
145
  visitNestStatement(pcx: parse.NestStatementContext): ast.Nests;
146
146
  visitNestedQueryList(pcx: parse.NestedQueryListContext): ast.Nests;
147
- visitNestExisting(pcx: parse.NestExistingContext): ast.NestFieldDeclaration;
148
147
  visitNestDef(pcx: parse.NestDefContext): ast.NestFieldDeclaration;
149
148
  visitExploreQueryDef(pcx: parse.ExploreQueryDefContext): ast.ViewFieldDeclaration;
150
149
  visitExprNot(pcx: parse.ExprNotContext): ast.ExprNot;
@@ -606,7 +606,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
606
606
  expr = new ast.ExprSum(undefined, ref);
607
607
  }
608
608
  else {
609
- this.contextError(agg, "'${aggFunc}' is not legal in a reference-only aggregation");
609
+ this.contextError(agg, `\`${aggFunc}\` is not legal in a reference-only aggregation`);
610
610
  return ref;
611
611
  }
612
612
  }
@@ -796,27 +796,24 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
796
796
  visitNestedQueryList(pcx) {
797
797
  return new ast.Nests(this.only(pcx.nestEntry().map(cx => this.visit(cx)), x => x instanceof ast.NestFieldDeclaration && x, 'query'));
798
798
  }
799
- visitNestExisting(pcx) {
800
- const nameCx = pcx.fieldPath();
801
- const name = this.getFieldPath(nameCx, ast.ViewOrScalarFieldReference);
802
- const referenceView = this.astAt(new ast.ReferenceView(name), nameCx);
803
- const refineCx = pcx.vExpr();
804
- const notes = this.getNotes(pcx.tags());
805
- if (refineCx) {
806
- const nestRefine = new ast.NestFieldDeclaration(name.nameString, new ast.ViewRefine(referenceView, this.getVExpr(refineCx)));
807
- nestRefine.extendNote({ notes });
808
- return this.astAt(nestRefine, pcx);
809
- }
810
- const nestReference = new ast.NestFieldDeclaration(name.nameString, referenceView);
811
- nestReference.extendNote({ notes });
812
- return this.astAt(nestReference, pcx);
813
- }
814
799
  visitNestDef(pcx) {
815
- const name = (0, parse_utils_1.getId)(pcx.queryName());
800
+ const nameCx = pcx.queryName();
801
+ let name;
816
802
  const vExpr = this.getVExpr(pcx.vExpr());
803
+ if (nameCx) {
804
+ name = (0, parse_utils_1.getId)(nameCx);
805
+ }
806
+ else {
807
+ const implicitName = vExpr.getImplicitName();
808
+ if (implicitName === undefined) {
809
+ this.contextError(pcx, '`nest:` view requires a name (add `nest_name is ...`)');
810
+ }
811
+ name = implicitName !== null && implicitName !== void 0 ? implicitName : '__unnamed__';
812
+ }
817
813
  const nestDef = new ast.NestFieldDeclaration(name, vExpr);
814
+ const isDefineCx = pcx.isDefine();
818
815
  nestDef.extendNote({
819
- notes: this.getNotes(pcx.tags()).concat(this.getIsNotes(pcx.isDefine())),
816
+ notes: this.getNotes(pcx.tags()).concat(isDefineCx ? this.getIsNotes(isDefineCx) : []),
820
817
  });
821
818
  return this.astAt(nestDef, pcx);
822
819
  }
@@ -21,33 +21,9 @@
21
21
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
22
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
  */
24
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
25
- if (k2 === undefined) k2 = k;
26
- var desc = Object.getOwnPropertyDescriptor(m, k);
27
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
28
- desc = { enumerable: true, get: function() { return m[k]; } };
29
- }
30
- Object.defineProperty(o, k2, desc);
31
- }) : (function(o, m, k, k2) {
32
- if (k2 === undefined) k2 = k;
33
- o[k2] = m[k];
34
- }));
35
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
36
- Object.defineProperty(o, "default", { enumerable: true, value: v });
37
- }) : function(o, v) {
38
- o["default"] = v;
39
- });
40
- var __importStar = (this && this.__importStar) || function (mod) {
41
- if (mod && mod.__esModule) return mod;
42
- var result = {};
43
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
44
- __setModuleDefault(result, mod);
45
- return result;
46
- };
47
24
  Object.defineProperty(exports, "__esModule", { value: true });
48
25
  exports.walkForDocumentSymbols = void 0;
49
26
  const ParseTreeWalker_1 = require("antlr4ts/tree/ParseTreeWalker");
50
- const parser = __importStar(require("../lib/Malloy/MalloyParser"));
51
27
  const parse_utils_1 = require("../parse-utils");
52
28
  class DocumentSymbolWalker {
53
29
  constructor(translator, tokens, scopes, symbols) {
@@ -147,12 +123,48 @@ class DocumentSymbolWalker {
147
123
  exitExploreQueryDef(_pcx) {
148
124
  this.popScope();
149
125
  }
126
+ getNestDefName(pcx) {
127
+ const nameCx = pcx.queryName();
128
+ if (nameCx) {
129
+ return nameCx.id().text;
130
+ }
131
+ let result = undefined;
132
+ let done = false;
133
+ const vExprListen = {
134
+ enterVArrow(pcx) {
135
+ pcx.vExpr().enterRule(vExprListen);
136
+ },
137
+ enterVSeg(pcx) {
138
+ pcx.segExpr().enterRule(segExprListen);
139
+ },
140
+ };
141
+ const segExprListen = {
142
+ enterSegField(pcx) {
143
+ const names = pcx.fieldPath().fieldName();
144
+ if (!done)
145
+ result !== null && result !== void 0 ? result : (result = names[names.length - 1].id().text);
146
+ },
147
+ enterSegParen(pcx) {
148
+ pcx.vExpr().enterRule(vExprListen);
149
+ },
150
+ enterSegRefine(pcx) {
151
+ pcx._lhs.enterRule(segExprListen);
152
+ },
153
+ enterSegOps() {
154
+ result = undefined;
155
+ done = true;
156
+ },
157
+ };
158
+ pcx.vExpr().enterRule(vExprListen);
159
+ return result;
160
+ }
150
161
  handleNestEntry(pcx) {
162
+ const name = this.getNestDefName(pcx);
163
+ if (name === undefined)
164
+ return;
151
165
  const symbol = {
152
166
  range: this.translator.rangeFromContext(pcx),
153
- name: pcx instanceof parser.NestExistingContext
154
- ? pcx.fieldPath().text
155
- : pcx.queryName().id().text,
167
+ name,
156
168
  type: 'query',
157
169
  children: [],
158
170
  };
@@ -162,12 +174,11 @@ class DocumentSymbolWalker {
162
174
  }
163
175
  return symbol;
164
176
  }
165
- enterNestExisting(pcx) {
166
- this.handleNestEntry(pcx);
167
- }
168
177
  enterNestDef(pcx) {
169
178
  const symbol = this.handleNestEntry(pcx);
170
- this.scopes.push(symbol);
179
+ if (symbol) {
180
+ this.scopes.push(symbol);
181
+ }
171
182
  }
172
183
  exitNestDef(_pcx) {
173
184
  this.popScope();
@@ -115,6 +115,20 @@ test('turtle children turtles are included', () => {
115
115
  }
116
116
  `, 'inner_turtle', 'query', [0, 0, 0]);
117
117
  });
118
+ test('refinement chain gets name correctly', () => {
119
+ testSymbol((0, test_translator_1.markSource) `
120
+ source: flights is DB.table('my.table.flights') extend {
121
+ query: my_turtle is { nest: ${'something + something_else'} }
122
+ }
123
+ `, 'something', 'query', [0, 0, 0]);
124
+ });
125
+ test('arrow in nest infers name correctly', () => {
126
+ testSymbol((0, test_translator_1.markSource) `
127
+ source: flights is DB.table('my.table.flights') extend {
128
+ query: my_turtle is { nest: ${'thingy -> something + something_else'} }
129
+ }
130
+ `, 'something', 'query', [0, 0, 0]);
131
+ });
118
132
  test('join withs are included', () => {
119
133
  testSymbol((0, test_translator_1.markSource) `
120
134
  source: flights is DB.table('my.table.flights') extend {
@@ -475,6 +475,18 @@ describe('expressions', () => {
475
475
  test('source.sum(nested.column)', () => {
476
476
  expect(modelX `source.sum(nested.column)`).translationToFailWith('Cannot compute `sum` across repeated relationship `nested`; use `nested.column.sum()`');
477
477
  });
478
+ test('can aggregate field defined with no join usage', () => {
479
+ expect((0, test_translator_1.markSource) `
480
+ ##! experimental { sql_functions }
481
+ source: s is a extend {
482
+ measure: c is count()
483
+ dimension: f is 1
484
+ }
485
+ run: s -> {
486
+ aggregate: v is f.sum()
487
+ }
488
+ `).toTranslate();
489
+ });
478
490
  test('sum(inline.column)', () => {
479
491
  expect(modelX `sum(inline.column)`).toTranslateWithWarnings('Join path is required for this calculation; use `inline.column.sum()` or `source.sum(inline.column)` to get a result weighted with respect to `source`');
480
492
  });
@@ -799,6 +811,11 @@ describe('unspported fields in schema', () => {
799
811
  test('negative numbers are not tokens', () => {
800
812
  expect((0, test_translator_1.expr) `ai-1`).toTranslate();
801
813
  });
814
+ describe('sql functions', () => {
815
+ test('can aggregate a sql_ function', () => {
816
+ expect((0, test_translator_1.expr) `sum(sql_number("\${a} * 2"))`).toTranslate();
817
+ });
818
+ });
802
819
  describe('cast', () => {
803
820
  // The "+ 1"s are there to make sure the result is of type 'number'
804
821
  test('sql cast', () => {
@@ -318,6 +318,32 @@ describe('partial views', () => {
318
318
  }
319
319
  `).toTranslate();
320
320
  });
321
+ test('order by tacked on the end should work', () => {
322
+ expect((0, test_translator_1.markSource) `
323
+ run: a -> {
324
+ nest: astr + ai + { order_by: astr }
325
+ }
326
+ run: a -> {
327
+ nest: astr + { order_by: astr }
328
+ }
329
+ run: a -> astr + { order_by: astr }
330
+ run: a -> astr + ai + { order_by: astr }
331
+ `).toTranslate();
332
+ });
333
+ test('name can be inferred with arrow', () => {
334
+ expect((0, test_translator_1.markSource) `
335
+ run: a extend { view: foo is { group_by: astr } } -> {
336
+ nest: foo -> astr + { order_by: astr }
337
+ }
338
+ `).toTranslate();
339
+ });
340
+ test('nice error when nest has no name', () => {
341
+ expect((0, test_translator_1.markSource) `
342
+ run: a -> {
343
+ nest: { group_by: astr }
344
+ }
345
+ `).translationToFailWith('`nest:` view requires a name (add `nest_name is ...`)');
346
+ });
321
347
  test.skip('partial with index', () => {
322
348
  expect((0, test_translator_1.markSource) `
323
349
  source: x is a extend {
@@ -875,5 +875,19 @@ describe('m3/m4 source query sentences', () => {
875
875
  `).toTranslate();
876
876
  });
877
877
  });
878
+ describe('sql_functions', () => {
879
+ test('can aggregate sql function', () => {
880
+ expect((0, test_translator_1.markSource) `
881
+ ##! experimental { sql_functions }
882
+ source: s is a extend {
883
+ measure: c is count()
884
+ dimension: sql_expr is sql_number("LENGTH(\${TABLE}.astr)")
885
+ }
886
+ run: s -> {
887
+ aggregate: v is sql_expr.sum()
888
+ }
889
+ `).toTranslate();
890
+ });
891
+ });
878
892
  test('non breaking space in source', () => expect('source:\u00a0z\u00a0is\u00a0a').toParse());
879
893
  //# sourceMappingURL=parse.spec.js.map
@@ -125,6 +125,9 @@ function exprMap(expr, func) {
125
125
  case 'function_parameter':
126
126
  case 'parameter':
127
127
  case 'outputField':
128
+ case 'source-reference':
129
+ case 'aggregate_limit':
130
+ case 'aggregate_order_by':
128
131
  return fragment;
129
132
  case 'sql-string':
130
133
  return {
@@ -231,6 +234,9 @@ function exprWalk(expr, func) {
231
234
  case 'function_parameter':
232
235
  case 'parameter':
233
236
  case 'outputField':
237
+ case 'source-reference':
238
+ case 'aggregate_limit':
239
+ case 'aggregate_order_by':
234
240
  return fragment;
235
241
  case 'function_call':
236
242
  return {
@@ -304,7 +310,7 @@ function exprWalk(expr, func) {
304
310
  }
305
311
  }
306
312
  default:
307
- throw new Error('unexpected');
313
+ throw new Error(`unexpected fragment type ${fragment.type}`);
308
314
  }
309
315
  });
310
316
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy",
3
- "version": "0.0.123-dev240205152128",
3
+ "version": "0.0.123-dev240205203117",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",