@malloydata/malloy 0.0.390 → 0.0.392

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 (127) hide show
  1. package/dist/api/asynchronous.js +0 -3
  2. package/dist/api/foundation/compile.d.ts +1 -1
  3. package/dist/api/foundation/config.d.ts +80 -8
  4. package/dist/api/foundation/config.js +151 -69
  5. package/dist/api/foundation/config_compile.js +27 -35
  6. package/dist/api/foundation/config_discover.js +5 -9
  7. package/dist/api/foundation/config_overlays.d.ts +6 -0
  8. package/dist/api/foundation/config_overlays.js +12 -0
  9. package/dist/api/foundation/config_resolve.d.ts +4 -1
  10. package/dist/api/foundation/config_resolve.js +64 -4
  11. package/dist/api/foundation/core.d.ts +75 -2
  12. package/dist/api/foundation/core.js +104 -6
  13. package/dist/api/foundation/index.d.ts +2 -0
  14. package/dist/api/foundation/readers.js +1 -1
  15. package/dist/api/foundation/runtime.d.ts +68 -2
  16. package/dist/api/foundation/runtime.js +212 -10
  17. package/dist/api/foundation/types.d.ts +2 -1
  18. package/dist/index.d.ts +3 -1
  19. package/dist/lang/ast/ast-utils.js +0 -1
  20. package/dist/lang/ast/expressions/expr-aggregate-function.d.ts +1 -1
  21. package/dist/lang/ast/expressions/expr-aggregate-function.js +9 -8
  22. package/dist/lang/ast/expressions/expr-coalesce.d.ts +1 -1
  23. package/dist/lang/ast/expressions/expr-coalesce.js +2 -3
  24. package/dist/lang/ast/expressions/expr-count-distinct.js +1 -1
  25. package/dist/lang/ast/expressions/expr-count.js +6 -4
  26. package/dist/lang/ast/expressions/expr-filter-expr.js +0 -1
  27. package/dist/lang/ast/expressions/expr-func.js +9 -4
  28. package/dist/lang/ast/expressions/expr-given.d.ts +18 -0
  29. package/dist/lang/ast/expressions/expr-given.js +69 -0
  30. package/dist/lang/ast/expressions/expr-granular-time.d.ts +1 -1
  31. package/dist/lang/ast/expressions/expr-id-reference.js +3 -2
  32. package/dist/lang/ast/expressions/expr-now.js +0 -1
  33. package/dist/lang/ast/expressions/expr-props.d.ts +132 -132
  34. package/dist/lang/ast/expressions/expr-props.js +2 -2
  35. package/dist/lang/ast/expressions/expr-ungroup.d.ts +1 -1
  36. package/dist/lang/ast/expressions/expr-ungroup.js +4 -4
  37. package/dist/lang/ast/expressions/for-range.d.ts +1 -1
  38. package/dist/lang/ast/expressions/function-ordering.d.ts +1 -1
  39. package/dist/lang/ast/expressions/function-ordering.js +2 -2
  40. package/dist/lang/ast/expressions/time-literal.d.ts +3 -3
  41. package/dist/lang/ast/field-space/include-utils.js +2 -2
  42. package/dist/lang/ast/field-space/index-field-space.js +18 -23
  43. package/dist/lang/ast/field-space/passthrough-space.d.ts +1 -1
  44. package/dist/lang/ast/field-space/query-spaces.d.ts +6 -2
  45. package/dist/lang/ast/field-space/query-spaces.js +29 -19
  46. package/dist/lang/ast/field-space/reference-field.js +1 -1
  47. package/dist/lang/ast/field-space/rename-space-field.d.ts +1 -1
  48. package/dist/lang/ast/field-space/rename-space-field.js +2 -2
  49. package/dist/lang/ast/field-space/struct-space-field-base.js +2 -3
  50. package/dist/lang/ast/index.d.ts +2 -0
  51. package/dist/lang/ast/index.js +2 -0
  52. package/dist/lang/ast/query-builders/index-builder.d.ts +1 -1
  53. package/dist/lang/ast/query-builders/index-builder.js +4 -3
  54. package/dist/lang/ast/query-builders/reduce-builder.d.ts +2 -2
  55. package/dist/lang/ast/query-builders/reduce-builder.js +4 -5
  56. package/dist/lang/ast/query-elements/query-arrow.js +3 -2
  57. package/dist/lang/ast/query-elements/query-base.d.ts +1 -1
  58. package/dist/lang/ast/query-elements/query-base.js +1 -1
  59. package/dist/lang/ast/query-elements/query-refine.js +3 -1
  60. package/dist/lang/ast/query-items/field-declaration.js +2 -2
  61. package/dist/lang/ast/query-properties/drill.js +6 -6
  62. package/dist/lang/ast/query-properties/filters.js +2 -2
  63. package/dist/lang/ast/query-properties/nest.js +3 -3
  64. package/dist/lang/ast/source-elements/composite-source.js +5 -3
  65. package/dist/lang/ast/source-elements/named-source.js +4 -0
  66. package/dist/lang/ast/source-elements/sql-source.js +2 -2
  67. package/dist/lang/ast/source-elements/table-source.js +3 -1
  68. package/dist/lang/ast/source-properties/join.js +4 -4
  69. package/dist/lang/ast/source-query-elements/sq-reference.js +2 -1
  70. package/dist/lang/ast/statements/define-given.d.ts +29 -0
  71. package/dist/lang/ast/statements/define-given.js +163 -0
  72. package/dist/lang/ast/statements/import-statement.js +72 -9
  73. package/dist/lang/ast/typedesc-utils.d.ts +3 -1
  74. package/dist/lang/ast/typedesc-utils.js +4 -47
  75. package/dist/lang/ast/types/expr-value.js +2 -3
  76. package/dist/lang/ast/types/expression-def.d.ts +2 -2
  77. package/dist/lang/ast/types/expression-def.js +2 -2
  78. package/dist/lang/ast/types/malloy-element.d.ts +5 -15
  79. package/dist/lang/ast/types/malloy-element.js +113 -1
  80. package/dist/lang/ast/types/space-field.js +7 -9
  81. package/dist/lang/ast/view-elements/reference-view.js +6 -5
  82. package/dist/lang/ast/view-elements/refine-utils.js +1 -1
  83. package/dist/lang/composite-source-utils.d.ts +30 -15
  84. package/dist/lang/composite-source-utils.js +234 -64
  85. package/dist/lang/lib/Malloy/MalloyLexer.d.ts +171 -169
  86. package/dist/lang/lib/Malloy/MalloyLexer.js +1194 -1178
  87. package/dist/lang/lib/Malloy/MalloyParser.d.ts +408 -334
  88. package/dist/lang/lib/Malloy/MalloyParser.js +3062 -2561
  89. package/dist/lang/lib/Malloy/MalloyParserListener.d.ts +68 -0
  90. package/dist/lang/lib/Malloy/MalloyParserVisitor.d.ts +43 -0
  91. package/dist/lang/malloy-to-ast.d.ts +13 -1
  92. package/dist/lang/malloy-to-ast.js +90 -11
  93. package/dist/lang/parse-log.d.ts +8 -0
  94. package/dist/lang/prettify/filter-type.d.ts +3 -0
  95. package/dist/lang/prettify/filter-type.js +38 -0
  96. package/dist/lang/prettify/formatter.js +6 -0
  97. package/dist/lang/prettify/inline-renderer.js +20 -0
  98. package/dist/lang/prettify/rules.d.ts +1 -1
  99. package/dist/lang/prettify/rules.js +1 -0
  100. package/dist/lang/prettify/sections.js +2 -0
  101. package/dist/lang/prettify/tokens.js +2 -0
  102. package/dist/lang/test/expr-to-str.js +2 -0
  103. package/dist/lang/test/parse-expects.d.ts +1 -0
  104. package/dist/lang/test/parse-expects.js +27 -10
  105. package/dist/model/constant_expression_compiler.js +1 -0
  106. package/dist/model/expression_compiler.d.ts +2 -1
  107. package/dist/model/expression_compiler.js +41 -1
  108. package/dist/model/given_binding.d.ts +2 -0
  109. package/dist/model/given_binding.js +204 -0
  110. package/dist/model/index.d.ts +1 -1
  111. package/dist/model/index.js +2 -1
  112. package/dist/model/malloy_types.d.ts +163 -36
  113. package/dist/model/malloy_types.js +97 -0
  114. package/dist/model/query_model_contract.d.ts +2 -1
  115. package/dist/model/query_model_impl.d.ts +2 -1
  116. package/dist/model/query_model_impl.js +7 -0
  117. package/dist/model/query_node.d.ts +2 -1
  118. package/dist/model/source_def_utils.d.ts +2 -1
  119. package/dist/model/source_def_utils.js +4 -0
  120. package/dist/model/utils.d.ts +14 -1
  121. package/dist/model/utils.js +41 -0
  122. package/dist/to_stable.js +1 -1
  123. package/dist/util/closest_match.d.ts +9 -0
  124. package/dist/util/closest_match.js +47 -0
  125. package/dist/version.d.ts +1 -1
  126. package/dist/version.js +1 -1
  127. package/package.json +4 -4
@@ -22,6 +22,7 @@ import { FilterString_stubContext } from "./MalloyParser";
22
22
  import { ExprNowContext } from "./MalloyParser";
23
23
  import { NestDefContext } from "./MalloyParser";
24
24
  import { ExprFieldPathContext } from "./MalloyParser";
25
+ import { ExprGivenRefContext } from "./MalloyParser";
25
26
  import { ExprLiteralContext } from "./MalloyParser";
26
27
  import { ExprArrayLiteralContext } from "./MalloyParser";
27
28
  import { ExprLiteralRecordContext } from "./MalloyParser";
@@ -100,6 +101,11 @@ import { UserTypeShapeContext } from "./MalloyParser";
100
101
  import { UserTypeFieldContext } from "./MalloyParser";
101
102
  import { UserTypeFieldTypeContext } from "./MalloyParser";
102
103
  import { DefineQueryContext } from "./MalloyParser";
104
+ import { DefineGivenStatementContext } from "./MalloyParser";
105
+ import { GivenDefListContext } from "./MalloyParser";
106
+ import { GivenDefContext } from "./MalloyParser";
107
+ import { GivenNameDefContext } from "./MalloyParser";
108
+ import { GivenTypeContext } from "./MalloyParser";
103
109
  import { TopLevelAnonQueryDefContext } from "./MalloyParser";
104
110
  import { AnnotationContext } from "./MalloyParser";
105
111
  import { TagsContext } from "./MalloyParser";
@@ -546,6 +552,18 @@ export interface MalloyParserListener extends ParseTreeListener {
546
552
  * @param ctx the parse tree
547
553
  */
548
554
  exitExprFieldPath?: (ctx: ExprFieldPathContext) => void;
555
+ /**
556
+ * Enter a parse tree produced by the `exprGivenRef`
557
+ * labeled alternative in `MalloyParser.fieldExpr`.
558
+ * @param ctx the parse tree
559
+ */
560
+ enterExprGivenRef?: (ctx: ExprGivenRefContext) => void;
561
+ /**
562
+ * Exit a parse tree produced by the `exprGivenRef`
563
+ * labeled alternative in `MalloyParser.fieldExpr`.
564
+ * @param ctx the parse tree
565
+ */
566
+ exitExprGivenRef?: (ctx: ExprGivenRefContext) => void;
549
567
  /**
550
568
  * Enter a parse tree produced by the `exprLiteral`
551
569
  * labeled alternative in `MalloyParser.fieldExpr`.
@@ -1458,6 +1476,56 @@ export interface MalloyParserListener extends ParseTreeListener {
1458
1476
  * @param ctx the parse tree
1459
1477
  */
1460
1478
  exitDefineQuery?: (ctx: DefineQueryContext) => void;
1479
+ /**
1480
+ * Enter a parse tree produced by `MalloyParser.defineGivenStatement`.
1481
+ * @param ctx the parse tree
1482
+ */
1483
+ enterDefineGivenStatement?: (ctx: DefineGivenStatementContext) => void;
1484
+ /**
1485
+ * Exit a parse tree produced by `MalloyParser.defineGivenStatement`.
1486
+ * @param ctx the parse tree
1487
+ */
1488
+ exitDefineGivenStatement?: (ctx: DefineGivenStatementContext) => void;
1489
+ /**
1490
+ * Enter a parse tree produced by `MalloyParser.givenDefList`.
1491
+ * @param ctx the parse tree
1492
+ */
1493
+ enterGivenDefList?: (ctx: GivenDefListContext) => void;
1494
+ /**
1495
+ * Exit a parse tree produced by `MalloyParser.givenDefList`.
1496
+ * @param ctx the parse tree
1497
+ */
1498
+ exitGivenDefList?: (ctx: GivenDefListContext) => void;
1499
+ /**
1500
+ * Enter a parse tree produced by `MalloyParser.givenDef`.
1501
+ * @param ctx the parse tree
1502
+ */
1503
+ enterGivenDef?: (ctx: GivenDefContext) => void;
1504
+ /**
1505
+ * Exit a parse tree produced by `MalloyParser.givenDef`.
1506
+ * @param ctx the parse tree
1507
+ */
1508
+ exitGivenDef?: (ctx: GivenDefContext) => void;
1509
+ /**
1510
+ * Enter a parse tree produced by `MalloyParser.givenNameDef`.
1511
+ * @param ctx the parse tree
1512
+ */
1513
+ enterGivenNameDef?: (ctx: GivenNameDefContext) => void;
1514
+ /**
1515
+ * Exit a parse tree produced by `MalloyParser.givenNameDef`.
1516
+ * @param ctx the parse tree
1517
+ */
1518
+ exitGivenNameDef?: (ctx: GivenNameDefContext) => void;
1519
+ /**
1520
+ * Enter a parse tree produced by `MalloyParser.givenType`.
1521
+ * @param ctx the parse tree
1522
+ */
1523
+ enterGivenType?: (ctx: GivenTypeContext) => void;
1524
+ /**
1525
+ * Exit a parse tree produced by `MalloyParser.givenType`.
1526
+ * @param ctx the parse tree
1527
+ */
1528
+ exitGivenType?: (ctx: GivenTypeContext) => void;
1461
1529
  /**
1462
1530
  * Enter a parse tree produced by `MalloyParser.topLevelAnonQueryDef`.
1463
1531
  * @param ctx the parse tree
@@ -22,6 +22,7 @@ import { FilterString_stubContext } from "./MalloyParser";
22
22
  import { ExprNowContext } from "./MalloyParser";
23
23
  import { NestDefContext } from "./MalloyParser";
24
24
  import { ExprFieldPathContext } from "./MalloyParser";
25
+ import { ExprGivenRefContext } from "./MalloyParser";
25
26
  import { ExprLiteralContext } from "./MalloyParser";
26
27
  import { ExprArrayLiteralContext } from "./MalloyParser";
27
28
  import { ExprLiteralRecordContext } from "./MalloyParser";
@@ -100,6 +101,11 @@ import { UserTypeShapeContext } from "./MalloyParser";
100
101
  import { UserTypeFieldContext } from "./MalloyParser";
101
102
  import { UserTypeFieldTypeContext } from "./MalloyParser";
102
103
  import { DefineQueryContext } from "./MalloyParser";
104
+ import { DefineGivenStatementContext } from "./MalloyParser";
105
+ import { GivenDefListContext } from "./MalloyParser";
106
+ import { GivenDefContext } from "./MalloyParser";
107
+ import { GivenNameDefContext } from "./MalloyParser";
108
+ import { GivenTypeContext } from "./MalloyParser";
103
109
  import { TopLevelAnonQueryDefContext } from "./MalloyParser";
104
110
  import { AnnotationContext } from "./MalloyParser";
105
111
  import { TagsContext } from "./MalloyParser";
@@ -434,6 +440,13 @@ export interface MalloyParserVisitor<Result> extends ParseTreeVisitor<Result> {
434
440
  * @return the visitor result
435
441
  */
436
442
  visitExprFieldPath?: (ctx: ExprFieldPathContext) => Result;
443
+ /**
444
+ * Visit a parse tree produced by the `exprGivenRef`
445
+ * labeled alternative in `MalloyParser.fieldExpr`.
446
+ * @param ctx the parse tree
447
+ * @return the visitor result
448
+ */
449
+ visitExprGivenRef?: (ctx: ExprGivenRefContext) => Result;
437
450
  /**
438
451
  * Visit a parse tree produced by the `exprLiteral`
439
452
  * labeled alternative in `MalloyParser.fieldExpr`.
@@ -968,6 +981,36 @@ export interface MalloyParserVisitor<Result> extends ParseTreeVisitor<Result> {
968
981
  * @return the visitor result
969
982
  */
970
983
  visitDefineQuery?: (ctx: DefineQueryContext) => Result;
984
+ /**
985
+ * Visit a parse tree produced by `MalloyParser.defineGivenStatement`.
986
+ * @param ctx the parse tree
987
+ * @return the visitor result
988
+ */
989
+ visitDefineGivenStatement?: (ctx: DefineGivenStatementContext) => Result;
990
+ /**
991
+ * Visit a parse tree produced by `MalloyParser.givenDefList`.
992
+ * @param ctx the parse tree
993
+ * @return the visitor result
994
+ */
995
+ visitGivenDefList?: (ctx: GivenDefListContext) => Result;
996
+ /**
997
+ * Visit a parse tree produced by `MalloyParser.givenDef`.
998
+ * @param ctx the parse tree
999
+ * @return the visitor result
1000
+ */
1001
+ visitGivenDef?: (ctx: GivenDefContext) => Result;
1002
+ /**
1003
+ * Visit a parse tree produced by `MalloyParser.givenNameDef`.
1004
+ * @param ctx the parse tree
1005
+ * @return the visitor result
1006
+ */
1007
+ visitGivenNameDef?: (ctx: GivenNameDefContext) => Result;
1008
+ /**
1009
+ * Visit a parse tree produced by `MalloyParser.givenType`.
1010
+ * @param ctx the parse tree
1011
+ * @return the visitor result
1012
+ */
1013
+ visitGivenType?: (ctx: GivenTypeContext) => Result;
971
1014
  /**
972
1015
  * Visit a parse tree produced by `MalloyParser.topLevelAnonQueryDef`.
973
1016
  * @param ctx the parse tree
@@ -8,7 +8,7 @@ import type { LogMessageOptions, MessageCode, MessageLogger, MessageParameterTyp
8
8
  import type { MalloyParseInfo } from './malloy-parse-info';
9
9
  import type { FieldDeclarationConstructor } from './ast';
10
10
  import type { HasString, HasID } from './parse-utils';
11
- import type { AccessModifierLabel, AtomicTypeDef, BasicAtomicTypeDef, DocumentLocation, DocumentRange, Note } from '../model/malloy_types';
11
+ import type { AccessModifierLabel, AtomicTypeDef, BasicAtomicTypeDef, DocumentLocation, DocumentRange, FilterExprType, GivenTypeDef, Note } from '../model/malloy_types';
12
12
  import type { Tag } from '@malloydata/malloy-tag';
13
13
  import type * as Malloy from '@malloydata/malloy-interfaces';
14
14
  import { Timer } from '../timing';
@@ -89,6 +89,17 @@ export declare class MalloyToAST extends AbstractParseTreeVisitor<ast.MalloyElem
89
89
  protected getIsNotes(cx: parse.IsDefineContext): Note[];
90
90
  visitMalloyDocument(pcx: parse.MalloyDocumentContext): ast.Document;
91
91
  visitDefineSourceStatement(pcx: parse.DefineSourceStatementContext): ast.DefineSourceList;
92
+ visitDefineGivenStatement(pcx: parse.DefineGivenStatementContext): ast.DefineGivens;
93
+ visitGivenDef(pcx: parse.GivenDefContext): ast.GivenDeclaration;
94
+ /**
95
+ * Parse the TYPE inside `filter<TYPE>`. Returns the filter type on
96
+ * success; logs `illegal-filter-type` and returns undefined when TYPE
97
+ * is not a filterable type. `getBasicMalloyType` already logs for
98
+ * invalid basic types, so on its `'error'` path we stay silent and
99
+ * return undefined.
100
+ */
101
+ protected getFilterType(basicCx: parse.MalloyBasicTypeContext): FilterExprType | undefined;
102
+ getGivenType(pcx: parse.GivenTypeContext): GivenTypeDef;
92
103
  visitDefineUserTypeStatement(pcx: parse.DefineUserTypeStatementContext): ast.DefineUserTypeList;
93
104
  visitUserTypeDefinition(pcx: parse.UserTypeDefinitionContext): ast.DefineUserType;
94
105
  getUserTypeShapeExpr(cx: parse.UserTypeExprContext): ast.UserTypeShape;
@@ -185,6 +196,7 @@ export declare class MalloyToAST extends AbstractParseTreeVisitor<ast.MalloyElem
185
196
  visitExprNow(_pcx: parse.ExprNowContext): ast.ExprNow;
186
197
  visitExprNumber(pcx: parse.ExprNumberContext): ast.ExprNumber;
187
198
  visitExprFieldPath(pcx: parse.ExprFieldPathContext): ast.ExprIdReference;
199
+ visitExprGivenRef(pcx: parse.ExprGivenRefContext): ast.GivenReference;
188
200
  visitExprNULL(_pcx: parse.ExprNULLContext): ast.ExprNULL;
189
201
  visitExprExpr(pcx: parse.ExprExprContext): ast.ExprParens;
190
202
  visitExprMinus(pcx: parse.ExprMinusContext): ast.ExprMinus;
@@ -86,6 +86,7 @@ function isIndirectUserType(t) {
86
86
  return 'namedUserType' in t;
87
87
  }
88
88
  const DEFAULT_COMPILER_FLAGS = [];
89
+ const LEGAL_FILTER_TYPES = 'string, number, boolean, date, timestamp, timestamptz';
89
90
  /**
90
91
  * ANTLR visitor pattern parse tree traversal. Generates a Malloy
91
92
  * AST from an ANTLR parse tree.
@@ -286,6 +287,79 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
286
287
  defList.extendNote({ blockNotes });
287
288
  return defList;
288
289
  }
290
+ visitDefineGivenStatement(pcx) {
291
+ this.inExperiment('givens', pcx);
292
+ const defsCx = pcx.givenDefList().givenDef();
293
+ const givens = defsCx.map(dcx => this.visitGivenDef(dcx));
294
+ const blockNotes = this.getNotes(pcx.tags());
295
+ const block = new ast.DefineGivens(givens);
296
+ for (const g of givens) {
297
+ g.extendNote({ blockNotes });
298
+ }
299
+ return this.astAt(block, pcx);
300
+ }
301
+ visitGivenDef(pcx) {
302
+ const name = (0, parse_utils_1.getId)(pcx.givenNameDef());
303
+ const typeDef = this.getGivenType(pcx.givenType());
304
+ const defaultCx = pcx.fieldExpr();
305
+ let defVal;
306
+ if (defaultCx) {
307
+ defVal = this.astAt(new ast.ConstantExpression(this.getFieldExpr(defaultCx)), defaultCx);
308
+ }
309
+ const isCx = pcx.isDefine();
310
+ if (isCx) {
311
+ // We accept the `tags IS tags` shape syntactically so we can give a
312
+ // targeted error here instead of a confusing parse-level one. Tags
313
+ // belong above the declaration; nothing should sit between `is` and
314
+ // the default expression.
315
+ const afterIs = isCx._afterIs;
316
+ if (afterIs && afterIs.annotation().length > 0) {
317
+ this.contextError(afterIs, 'given-no-tags-after-is', 'Annotations are not allowed between `is` and the default value of a given; place them above the declaration');
318
+ }
319
+ }
320
+ const decl = new ast.GivenDeclaration(name, typeDef, defVal);
321
+ decl.extendNote({ notes: this.getNotes(pcx.tags()) });
322
+ return this.astAt(decl, pcx);
323
+ }
324
+ /**
325
+ * Parse the TYPE inside `filter<TYPE>`. Returns the filter type on
326
+ * success; logs `illegal-filter-type` and returns undefined when TYPE
327
+ * is not a filterable type. `getBasicMalloyType` already logs for
328
+ * invalid basic types, so on its `'error'` path we stay silent and
329
+ * return undefined.
330
+ */
331
+ getFilterType(basicCx) {
332
+ const inner = this.getBasicMalloyType(basicCx);
333
+ if (inner.type === 'error')
334
+ return undefined;
335
+ if ((0, malloy_filter_1.isFilterable)(inner.type)) {
336
+ return inner.type;
337
+ }
338
+ this.contextError(basicCx, 'illegal-filter-type', `\`filter<${basicCx.text}>\` is not allowed; filtering is only supported for ${LEGAL_FILTER_TYPES}`);
339
+ return undefined;
340
+ }
341
+ getGivenType(pcx) {
342
+ if (pcx.FILTER()) {
343
+ const basicCx = pcx.malloyBasicType();
344
+ if (basicCx) {
345
+ const filterType = this.getFilterType(basicCx);
346
+ if (filterType)
347
+ return { type: 'filter expression', filterType };
348
+ }
349
+ return { type: 'error' };
350
+ }
351
+ const mtcx = pcx.malloyType();
352
+ if (mtcx) {
353
+ return this.getMalloyType(mtcx);
354
+ }
355
+ // Unreachable from valid grammar input: `givenType` is exactly
356
+ // `malloyType | FILTER LT malloyBasicType GT`, so one of the branches
357
+ // above must have matched. Surfacing a real error here (rather than
358
+ // throwing) keeps the translator running and gives any AI or human
359
+ // reading the log a hint that the parser and AST builder have drifted.
360
+ this.contextError(pcx, 'unexpected-malloy-type', `\`${pcx.text}\` was not recognized as a legal type. This is likely a compiler bug — the grammar admits cases the translator doesn't handle.`);
361
+ return { type: 'error' };
362
+ }
289
363
  visitDefineUserTypeStatement(pcx) {
290
364
  this.inExperiment('virtual_source', pcx);
291
365
  const defsCx = pcx.userTypePropertyList().userTypeDefinition();
@@ -385,21 +459,20 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
385
459
  let pType;
386
460
  const typeCx = pcx.legalParamType();
387
461
  if (typeCx) {
388
- const typeDef = this.getBasicMalloyType(typeCx.malloyBasicType());
389
- const t = typeDef.type;
390
462
  if (typeCx.FILTER()) {
391
- if ((0, malloy_filter_1.isFilterable)(t)) {
392
- pType = { type: 'filter expression', filterType: t };
393
- }
394
- else {
395
- this.contextError(typeCx, 'parameter-illegal-default-type', `Unknown filter type ${t}`);
463
+ const filterType = this.getFilterType(typeCx.malloyBasicType());
464
+ if (filterType) {
465
+ pType = { type: 'filter expression', filterType };
396
466
  }
397
467
  }
398
- else if (!(0, malloy_types_1.isParameterType)(t)) {
399
- this.contextError(typeCx, 'parameter-illegal-default-type', `Unknown parameter type ${t}`);
400
- }
401
468
  else {
402
- pType = { type: t };
469
+ const t = this.getBasicMalloyType(typeCx.malloyBasicType()).type;
470
+ if (!(0, malloy_types_1.isParameterType)(t)) {
471
+ this.contextError(typeCx, 'parameter-illegal-default-type', `Unknown parameter type ${t}`);
472
+ }
473
+ else {
474
+ pType = { type: t };
475
+ }
403
476
  }
404
477
  }
405
478
  const defaultCx = pcx.fieldExpr();
@@ -1048,6 +1121,12 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
1048
1121
  const idRef = new ast.ExprIdReference(this.getFieldPath(pcx.fieldPath(), ast.ExpressionFieldReference));
1049
1122
  return this.astAt(idRef, pcx);
1050
1123
  }
1124
+ visitExprGivenRef(pcx) {
1125
+ this.inExperiment('givens', pcx);
1126
+ // GIVEN_REF token text is `$NAME`; strip the leading `$`.
1127
+ const name = pcx.GIVEN_REF().text.slice(1);
1128
+ return this.astAt(new ast.GivenReference(name), pcx);
1129
+ }
1051
1130
  visitExprNULL(_pcx) {
1052
1131
  return new ast.ExprNULL();
1053
1132
  }
@@ -265,6 +265,7 @@ type MessageParameterTypes = {
265
265
  'query-definition-from-non-query': string;
266
266
  'source-definition-name-conflict': string;
267
267
  'user-type-definition-name-conflict': string;
268
+ 'given-definition-name-conflict': string;
268
269
  'virtual-source-not-yet-implemented': string;
269
270
  'virtual-source-missing-user-type-refs': string;
270
271
  'virtual-source-unknown-dialect': string;
@@ -279,6 +280,8 @@ type MessageParameterTypes = {
279
280
  'selective-import-not-found': string;
280
281
  'name-conflict-on-indiscriminate-import': string;
281
282
  'failed-import': string;
283
+ 'unsatisfied-given-in-query': string;
284
+ 'given-alias-collision': string;
282
285
  'failed-to-compute-output-schema': string;
283
286
  'invalid-timeframe-for-time-offset': string;
284
287
  'time-comparison-type-mismatch': string;
@@ -286,6 +289,11 @@ type MessageParameterTypes = {
286
289
  'time-offset-type-mismatch': string;
287
290
  'unexpected-binary-operator': string;
288
291
  'illegal-reference-in-parameter-default': string;
292
+ 'given-reference-not-implemented': string;
293
+ 'given-not-found': string;
294
+ 'given-no-tags-after-is': string;
295
+ 'illegal-filter-type': string;
296
+ 'invalid-source-from-given': string;
289
297
  'aggregate-analytic-in-select': string;
290
298
  'refinement-of-raw-query': string;
291
299
  'illegal-multistage-refinement-operation': string;
@@ -0,0 +1,3 @@
1
+ import type * as parser from '../lib/Malloy/MalloyParser';
2
+ import type { Formatter } from './formatter';
3
+ export declare function formatFilterTypeOrFallback(f: Formatter, ctx: parser.GivenTypeContext | parser.LegalParamTypeContext): void;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Contributors to the Malloy project
4
+ * SPDX-License-Identifier: MIT
5
+ *
6
+ * RULE: FILTER TYPE — `filter<T>`
7
+ *
8
+ * `givenType` and `legalParamType` both have a `FILTER LT malloyBasicType GT`
9
+ * alternative. The default leaf walker treats LT/GT as binary operators
10
+ * (spaces both sides), which renders this as `filter < string >`. This rule
11
+ * intercepts the two contexts and emits the four-token sequence as a glued
12
+ * unit.
13
+ *
14
+ * Falls through to default child recursion when the context is the non-FILTER
15
+ * alternative (a plain malloy type), so the type's own formatting still runs.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.formatFilterTypeOrFallback = formatFilterTypeOrFallback;
19
+ const tokens_1 = require("./tokens");
20
+ const leaf_1 = require("./leaf");
21
+ function formatFilterTypeOrFallback(f, ctx) {
22
+ const filterTok = ctx.FILTER();
23
+ if (!filterTok) {
24
+ for (let i = 0; i < ctx.childCount; i++)
25
+ f.format(ctx.getChild(i));
26
+ return;
27
+ }
28
+ const action = (0, tokens_1.leadingAction)(f.lastEmittedType, tokens_1.L.FILTER);
29
+ if (action === 'glue')
30
+ f.o.trimTrailingSpace();
31
+ else if (action === 'space')
32
+ f.o.space();
33
+ const typeCtx = ctx.malloyBasicType();
34
+ f.o.text(`filter<${typeCtx.text}>`);
35
+ const gtTok = ctx.GT().symbol;
36
+ (0, leaf_1.note)(f, tokens_1.L.GT, gtTok.tokenIndex, gtTok);
37
+ }
38
+ //# sourceMappingURL=filter-type.js.map
@@ -61,6 +61,7 @@ const field_properties_1 = require("./field-properties");
61
61
  const pick_case_1 = require("./pick-case");
62
62
  const binary_chain_1 = require("./binary-chain");
63
63
  const import_select_1 = require("./import-select");
64
+ const filter_type_1 = require("./filter-type");
64
65
  class Formatter {
65
66
  constructor(src, tokens) {
66
67
  this.src = src;
@@ -128,6 +129,11 @@ class Formatter {
128
129
  if (node instanceof parser.ImportSelectContext) {
129
130
  return (0, import_select_1.formatImportSelect)(this, node);
130
131
  }
132
+ // RULE: FILTER TYPE — `filter<T>` rendered glued, not as `filter < T >`.
133
+ if (node instanceof parser.GivenTypeContext ||
134
+ node instanceof parser.LegalParamTypeContext) {
135
+ return (0, filter_type_1.formatFilterTypeOrFallback)(this, node);
136
+ }
131
137
  // RULE: PICK / CASE / BINARY CHAIN.
132
138
  if (node instanceof parser.PickStatementContext)
133
139
  return (0, pick_case_1.formatPickStatement)(this, node);
@@ -30,6 +30,10 @@ function renderItemInline(f, ctx) {
30
30
  var _a;
31
31
  let buf = '';
32
32
  let lastType = null;
33
+ // True between the LT and matching GT of a `filter<T>` window (LT
34
+ // immediately follows FILTER). All four tokens render glued; mirrors
35
+ // formatFilterTypeOrFallback in the leaf walker.
36
+ let inFilterType = false;
33
37
  // Coalescing space: skip if buffer is empty or last char already provides
34
38
  // separation. Mirrors Out.space() (./out) minus the newline check, since
35
39
  // this renderer never emits a newline.
@@ -83,6 +87,22 @@ function renderItemInline(f, ctx) {
83
87
  lastType = t.type;
84
88
  continue;
85
89
  }
90
+ // filter<T>: glue all four tokens.
91
+ if (t.type === tokens_1.L.LT && lastType === tokens_1.L.FILTER) {
92
+ trim();
93
+ buf += '<';
94
+ inFilterType = true;
95
+ lastType = t.type;
96
+ continue;
97
+ }
98
+ if (inFilterType) {
99
+ trim();
100
+ buf += text;
101
+ if (t.type === tokens_1.L.GT)
102
+ inFilterType = false;
103
+ lastType = t.type;
104
+ continue;
105
+ }
86
106
  // Everything else: classifier-driven leading separator + text + (if a
87
107
  // binary op) trailing space. Same shape as the leaf walker's default.
88
108
  const action = (0, tokens_1.leadingAction)(lastType, t.type);
@@ -1,5 +1,5 @@
1
1
  import type { ParserRuleContext } from 'antlr4ts';
2
- export type ItemKind = 'fieldEntry' | 'nestEntry' | 'fieldDef' | 'fieldName' | 'collectionMember' | 'orderBySpec' | 'fieldExpr' | 'joinDef' | 'includeField' | 'indexElement';
2
+ export type ItemKind = 'fieldEntry' | 'nestEntry' | 'fieldDef' | 'fieldName' | 'collectionMember' | 'orderBySpec' | 'fieldExpr' | 'joinDef' | 'includeField' | 'indexElement' | 'givenDef';
3
3
  export interface SectionRule {
4
4
  ctxClass: new (...args: never[]) => ParserRuleContext;
5
5
  keywordTypes: number[];
@@ -87,6 +87,7 @@ exports.SECTION_STATEMENT_RULES = [
87
87
  // findKeyword in ./sections handles that nested case.
88
88
  rule(parser.IncludeItemContext, [tokens_1.L.PUBLIC, tokens_1.L.PRIVATE, tokens_1.L.INTERNAL], c => c.includeList(), 'includeField'),
89
89
  rule(parser.IndexStatementContext, [tokens_1.L.INDEX], c => c.indexFields(), 'indexElement'),
90
+ rule(parser.DefineGivenStatementContext, [tokens_1.L.GIVEN], c => c.givenDefList(), 'givenDef'),
90
91
  ];
91
92
  // Coarse statement-kind labels for the same-kind-no-blank rule in block
92
93
  // bodies. Different kinds preserve a single user-supplied blank line.
@@ -156,6 +156,8 @@ function listItems(listCtx, itemKind) {
156
156
  return c instanceof parser.IncludeFieldContext;
157
157
  case 'indexElement':
158
158
  return c instanceof parser.IndexElementContext;
159
+ case 'givenDef':
160
+ return c instanceof parser.GivenDefContext;
159
161
  }
160
162
  };
161
163
  const out = [];
@@ -67,6 +67,8 @@ exports.TOP_LEVEL_STARTERS = new Set([
67
67
  exports.L.QUERY,
68
68
  exports.L.RUN,
69
69
  exports.L.IMPORT,
70
+ exports.L.GIVEN,
71
+ exports.L.TYPE,
70
72
  ]);
71
73
  // Token types after which an immediately following `(` or `[` is a call /
72
74
  // subscript and should hug (no leading space). Anything else (binary ops,
@@ -28,6 +28,8 @@ function exprToStr(e, symbols) {
28
28
  }
29
29
  case 'parameter':
30
30
  return `{parameter ${e.path}}`;
31
+ case 'given':
32
+ return `{$${e.refName}}`;
31
33
  case '()':
32
34
  return `(${subExpr(e.e)})`;
33
35
  case 'numberLiteral':
@@ -48,6 +48,7 @@ declare global {
48
48
  compilesTo(exprString: string): R;
49
49
  toBeExpr(exprString: string): R;
50
50
  hasFieldUsage(paths: string[][]): R;
51
+ hasExpandedFieldUsage(paths: string[][]): R;
51
52
  }
52
53
  }
53
54
  }
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- /* eslint-disable no-console */
3
2
  /*
4
3
  * Copyright 2023 Google LLC
5
4
  *
@@ -241,18 +240,36 @@ expect.extend({
241
240
  if (!badRefs.pass) {
242
241
  return badRefs;
243
242
  }
244
- const actual = bx.generated().fieldUsage;
243
+ const actual = (0, model_1.fieldUsageFrom)(bx.generated().refSummary);
245
244
  const actualPaths = actual.filter(u => (0, model_1.bareFieldUsage)(u)).map(u => u.path);
246
- // there is no guarantee of the order of field usage data, so we sort the two lists
247
- // so i need to compare sorted versions of the two lists... we can sort on path.join('.)
248
- // maybe make a lambda for that and pass it to sort
249
- const pass = this.equals(actualPaths.sort((a, b) => a.join('.').localeCompare(b.join('.'))), paths.sort((a, b) => a.join('.').localeCompare(b.join('.'))));
250
- const msg = pass
251
- ? `Matched: ${actual}`
252
- : this.utils.diff(paths, actualPaths);
253
- return { pass, message: () => `${msg}` };
245
+ return comparePathSets(this, actualPaths, paths);
246
+ },
247
+ hasExpandedFieldUsage: function (segment, paths) {
248
+ var _a;
249
+ if (!(0, model_1.isQuerySegment)(segment) && !(0, model_1.isIndexSegment)(segment)) {
250
+ return {
251
+ pass: false,
252
+ message: () => 'hasExpandedFieldUsage: receiver is not a query or index segment',
253
+ };
254
+ }
255
+ const actualPaths = ((_a = segment.expandedFieldUsage) !== null && _a !== void 0 ? _a : [])
256
+ .filter(u => (0, model_1.bareFieldUsage)(u))
257
+ .map(u => u.path);
258
+ return comparePathSets(this, actualPaths, paths);
254
259
  },
255
260
  });
261
+ // Field-usage entries have no order or duplication guarantee. Compare as
262
+ // sets of `path.join('.')` strings so equality is purely set membership.
263
+ function comparePathSets(ctx, actualPaths, expectedPaths) {
264
+ const toKey = (p) => p.join('.');
265
+ const actualSet = new Set(actualPaths.map(toKey));
266
+ const expectedSet = new Set(expectedPaths.map(toKey));
267
+ const pass = ctx.equals(actualSet, expectedSet);
268
+ const msg = pass
269
+ ? `Matched: ${[...actualSet].sort().join(', ')}`
270
+ : ctx.utils.diff([...expectedSet].sort(), [...actualSet].sort());
271
+ return { pass, message: () => `${msg}` };
272
+ }
256
273
  function problemSpecSummary(s) {
257
274
  return `${s.severity} '${'message' in s ? s.message : s.code}' ${s.data !== undefined ? (0, test_translator_1.pretty)(s.data) : ''}`;
258
275
  }
@@ -87,6 +87,7 @@ class ConstantQueryStruct extends query_node_1.QueryStruct {
87
87
  // Create minimal model with empty structs map
88
88
  const minimalModel = {
89
89
  structs: new Map(),
90
+ givens: {},
90
91
  };
91
92
  // Create minimal prepare result options with eventStream
92
93
  const minimalPrepareOptions = { eventStream };
@@ -1,4 +1,4 @@
1
- import type { Expr, FunctionCallNode, FunctionOverloadDef, FunctionOrderBy, FieldnameNode, OutputFieldNode, GenericSQLExpr, FilteredExpr, UngroupNode, ParameterNode, AggregateExpr, SourceReferenceNode, CaseExpr } from './malloy_types';
1
+ import type { Expr, FunctionCallNode, FunctionOverloadDef, FunctionOrderBy, GivenRefNode, FieldnameNode, OutputFieldNode, GenericSQLExpr, FilteredExpr, UngroupNode, ParameterNode, AggregateExpr, SourceReferenceNode, CaseExpr } from './malloy_types';
2
2
  import { type FieldInstanceResult } from './field_instance';
3
3
  import { GenerateState } from './utils';
4
4
  import type { QueryStruct } from './query_node';
@@ -11,6 +11,7 @@ export declare function generateFunctionCallExpression(resultSet: FieldInstanceR
11
11
  export declare function generateFieldFragment(resultSet: FieldInstanceResult, context: QueryStruct, expr: FieldnameNode, state: GenerateState): string;
12
12
  export declare function generateOutputFieldFragment(resultSet: FieldInstanceResult, _context: QueryStruct, frag: OutputFieldNode, _state: GenerateState): string;
13
13
  export declare function generateParameterFragment(resultSet: FieldInstanceResult, context: QueryStruct, expr: ParameterNode, state: GenerateState): string;
14
+ export declare function generateGivenFragment(resultSet: FieldInstanceResult, context: QueryStruct, expr: GivenRefNode, state: GenerateState): string;
14
15
  export declare function generateFilterFragment(resultSet: FieldInstanceResult, context: QueryStruct, expr: FilteredExpr, state: GenerateState): string;
15
16
  export declare function generateDimFragment(resultSet: FieldInstanceResult, context: QueryStruct, expr: Expr, state: GenerateState): string;
16
17
  export declare function generateUngroupedFragment(resultSet: FieldInstanceResult, context: QueryStruct, expr: UngroupNode, state: GenerateState): string;