@malloydata/malloy 0.0.225-dev250113200903 → 0.0.225-dev250113203344

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.
@@ -40,7 +40,7 @@ import { ExprAndTreeContext } from "./MalloyParser";
40
40
  import { ExprOrTreeContext } from "./MalloyParser";
41
41
  import { ExprCompareContext } from "./MalloyParser";
42
42
  import { ExprWarnLikeContext } from "./MalloyParser";
43
- import { ExprWarnNullCmpContext } from "./MalloyParser";
43
+ import { ExprNullCheckContext } from "./MalloyParser";
44
44
  import { ExprWarnInContext } from "./MalloyParser";
45
45
  import { ExprApplyContext } from "./MalloyParser";
46
46
  import { ExprNotContext } from "./MalloyParser";
@@ -209,6 +209,8 @@ import { TimeframeContext } from "./MalloyParser";
209
209
  import { UngroupContext } from "./MalloyParser";
210
210
  import { MalloyOrSQLTypeContext } from "./MalloyParser";
211
211
  import { FieldExprContext } from "./MalloyParser";
212
+ import { PartialCompareContext } from "./MalloyParser";
213
+ import { PartialTestContext } from "./MalloyParser";
212
214
  import { PartialAllowedFieldExprContext } from "./MalloyParser";
213
215
  import { FieldExprListContext } from "./MalloyParser";
214
216
  import { PickStatementContext } from "./MalloyParser";
@@ -733,17 +735,17 @@ export interface MalloyParserListener extends ParseTreeListener {
733
735
  */
734
736
  exitExprWarnLike?: (ctx: ExprWarnLikeContext) => void;
735
737
  /**
736
- * Enter a parse tree produced by the `exprWarnNullCmp`
738
+ * Enter a parse tree produced by the `exprNullCheck`
737
739
  * labeled alternative in `MalloyParser.fieldExpr`.
738
740
  * @param ctx the parse tree
739
741
  */
740
- enterExprWarnNullCmp?: (ctx: ExprWarnNullCmpContext) => void;
742
+ enterExprNullCheck?: (ctx: ExprNullCheckContext) => void;
741
743
  /**
742
- * Exit a parse tree produced by the `exprWarnNullCmp`
744
+ * Exit a parse tree produced by the `exprNullCheck`
743
745
  * labeled alternative in `MalloyParser.fieldExpr`.
744
746
  * @param ctx the parse tree
745
747
  */
746
- exitExprWarnNullCmp?: (ctx: ExprWarnNullCmpContext) => void;
748
+ exitExprNullCheck?: (ctx: ExprNullCheckContext) => void;
747
749
  /**
748
750
  * Enter a parse tree produced by the `exprWarnIn`
749
751
  * labeled alternative in `MalloyParser.fieldExpr`.
@@ -2512,6 +2514,26 @@ export interface MalloyParserListener extends ParseTreeListener {
2512
2514
  * @param ctx the parse tree
2513
2515
  */
2514
2516
  exitFieldExpr?: (ctx: FieldExprContext) => void;
2517
+ /**
2518
+ * Enter a parse tree produced by `MalloyParser.partialCompare`.
2519
+ * @param ctx the parse tree
2520
+ */
2521
+ enterPartialCompare?: (ctx: PartialCompareContext) => void;
2522
+ /**
2523
+ * Exit a parse tree produced by `MalloyParser.partialCompare`.
2524
+ * @param ctx the parse tree
2525
+ */
2526
+ exitPartialCompare?: (ctx: PartialCompareContext) => void;
2527
+ /**
2528
+ * Enter a parse tree produced by `MalloyParser.partialTest`.
2529
+ * @param ctx the parse tree
2530
+ */
2531
+ enterPartialTest?: (ctx: PartialTestContext) => void;
2532
+ /**
2533
+ * Exit a parse tree produced by `MalloyParser.partialTest`.
2534
+ * @param ctx the parse tree
2535
+ */
2536
+ exitPartialTest?: (ctx: PartialTestContext) => void;
2515
2537
  /**
2516
2538
  * Enter a parse tree produced by `MalloyParser.partialAllowedFieldExpr`.
2517
2539
  * @param ctx the parse tree
@@ -40,7 +40,7 @@ import { ExprAndTreeContext } from "./MalloyParser";
40
40
  import { ExprOrTreeContext } from "./MalloyParser";
41
41
  import { ExprCompareContext } from "./MalloyParser";
42
42
  import { ExprWarnLikeContext } from "./MalloyParser";
43
- import { ExprWarnNullCmpContext } from "./MalloyParser";
43
+ import { ExprNullCheckContext } from "./MalloyParser";
44
44
  import { ExprWarnInContext } from "./MalloyParser";
45
45
  import { ExprApplyContext } from "./MalloyParser";
46
46
  import { ExprNotContext } from "./MalloyParser";
@@ -209,6 +209,8 @@ import { TimeframeContext } from "./MalloyParser";
209
209
  import { UngroupContext } from "./MalloyParser";
210
210
  import { MalloyOrSQLTypeContext } from "./MalloyParser";
211
211
  import { FieldExprContext } from "./MalloyParser";
212
+ import { PartialCompareContext } from "./MalloyParser";
213
+ import { PartialTestContext } from "./MalloyParser";
212
214
  import { PartialAllowedFieldExprContext } from "./MalloyParser";
213
215
  import { FieldExprListContext } from "./MalloyParser";
214
216
  import { PickStatementContext } from "./MalloyParser";
@@ -531,12 +533,12 @@ export interface MalloyParserVisitor<Result> extends ParseTreeVisitor<Result> {
531
533
  */
532
534
  visitExprWarnLike?: (ctx: ExprWarnLikeContext) => Result;
533
535
  /**
534
- * Visit a parse tree produced by the `exprWarnNullCmp`
536
+ * Visit a parse tree produced by the `exprNullCheck`
535
537
  * labeled alternative in `MalloyParser.fieldExpr`.
536
538
  * @param ctx the parse tree
537
539
  * @return the visitor result
538
540
  */
539
- visitExprWarnNullCmp?: (ctx: ExprWarnNullCmpContext) => Result;
541
+ visitExprNullCheck?: (ctx: ExprNullCheckContext) => Result;
540
542
  /**
541
543
  * Visit a parse tree produced by the `exprWarnIn`
542
544
  * labeled alternative in `MalloyParser.fieldExpr`.
@@ -1589,6 +1591,18 @@ export interface MalloyParserVisitor<Result> extends ParseTreeVisitor<Result> {
1589
1591
  * @return the visitor result
1590
1592
  */
1591
1593
  visitFieldExpr?: (ctx: FieldExprContext) => Result;
1594
+ /**
1595
+ * Visit a parse tree produced by `MalloyParser.partialCompare`.
1596
+ * @param ctx the parse tree
1597
+ * @return the visitor result
1598
+ */
1599
+ visitPartialCompare?: (ctx: PartialCompareContext) => Result;
1600
+ /**
1601
+ * Visit a parse tree produced by `MalloyParser.partialTest`.
1602
+ * @param ctx the parse tree
1603
+ * @return the visitor result
1604
+ */
1605
+ visitPartialTest?: (ctx: PartialTestContext) => Result;
1592
1606
  /**
1593
1607
  * Visit a parse tree produced by `MalloyParser.partialAllowedFieldExpr`.
1594
1608
  * @param ctx the parse tree
@@ -158,6 +158,8 @@ export declare class MalloyToAST extends AbstractParseTreeVisitor<ast.MalloyElem
158
158
  visitExprOrTree(pcx: parse.ExprOrTreeContext): ast.ExprAlternationTree;
159
159
  visitExprAndTree(pcx: parse.ExprAndTreeContext): ast.ExprAlternationTree;
160
160
  visitExprCoalesce(pcx: parse.ExprCoalesceContext): ast.ExprCoalesce;
161
+ visitPartialCompare(pcx: parse.PartialCompareContext): ast.PartialCompare;
162
+ visitPartialTest(pcx: parse.PartialTestContext): ast.ExpressionDef;
161
163
  visitPartialAllowedFieldExpr(pcx: parse.PartialAllowedFieldExprContext): ast.ExpressionDef;
162
164
  visitExprString(pcx: parse.ExprStringContext): ast.ExprString;
163
165
  visitExprRegex(pcx: parse.ExprRegexContext): ast.ExprRegEx;
@@ -236,7 +238,7 @@ export declare class MalloyToAST extends AbstractParseTreeVisitor<ast.MalloyElem
236
238
  visitExprLiteralRecord(pcx: parse.ExprLiteralRecordContext): ast.RecordLiteral;
237
239
  visitExprArrayLiteral(pcx: parse.ExprArrayLiteralContext): ast.ArrayLiteral;
238
240
  visitExprWarnLike(pcx: parse.ExprWarnLikeContext): ast.ExprCompare;
239
- visitExprWarnNullCmp(pcx: parse.ExprWarnNullCmpContext): ast.ExprCompare;
241
+ visitExprNullCheck(pcx: parse.ExprNullCheckContext): ast.ExprIsNull;
240
242
  visitExprWarnIn(pcx: parse.ExprWarnInContext): ast.ExprLegacyIn;
241
243
  }
242
244
  export {};
@@ -881,17 +881,30 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
881
881
  const right = this.getFieldExpr(pcx.fieldExpr()[1]);
882
882
  return this.astAt(new ast.ExprCoalesce(left, right), pcx);
883
883
  }
884
+ visitPartialCompare(pcx) {
885
+ const partialOp = pcx.compareOp().text;
886
+ if (ast.isComparison(partialOp)) {
887
+ return this.astAt(new ast.PartialCompare(partialOp, this.getFieldExpr(pcx.fieldExpr())), pcx);
888
+ }
889
+ throw this.internalError(pcx, `partial comparison '${partialOp}' not recognized`);
890
+ }
891
+ visitPartialTest(pcx) {
892
+ const cmp = pcx.partialCompare();
893
+ if (cmp) {
894
+ return this.visitPartialCompare(cmp);
895
+ }
896
+ return this.astAt(new ast.PartialIsNull(pcx.NOT() ? '!=' : '='), pcx);
897
+ }
884
898
  visitPartialAllowedFieldExpr(pcx) {
885
- var _a;
886
- const fieldExpr = this.getFieldExpr(pcx.fieldExpr());
887
- const partialOp = (_a = pcx.compareOp()) === null || _a === void 0 ? void 0 : _a.text;
888
- if (partialOp) {
889
- if (ast.isComparison(partialOp)) {
890
- return this.astAt(new ast.PartialCompare(partialOp, fieldExpr), pcx);
891
- }
892
- throw this.internalError(pcx, `partial comparison '${partialOp}' not recognized`);
899
+ const exprCx = pcx.fieldExpr();
900
+ if (exprCx) {
901
+ return this.getFieldExpr(exprCx);
893
902
  }
894
- return fieldExpr;
903
+ const partialCx = pcx.partialTest();
904
+ if (partialCx) {
905
+ return this.visitPartialTest(partialCx);
906
+ }
907
+ throw this.internalError(pcx, 'impossible partial');
895
908
  }
896
909
  visitExprString(pcx) {
897
910
  const str = this.getPlainStringFrom(pcx);
@@ -935,6 +948,15 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
935
948
  const left = this.getFieldExpr(pcx.fieldExpr(0));
936
949
  const right = this.getFieldExpr(pcx.fieldExpr(1));
937
950
  if (ast.isEquality(op)) {
951
+ const wholeRange = this.parseInfo.rangeFromContext(pcx);
952
+ if (right instanceof ast.ExprNULL) {
953
+ if (op === '=') {
954
+ this.warnWithReplacement('sql-is-null', "Use 'is null' to check for NULL instead of '= null'", wholeRange, `${this.getSourceCode(pcx.fieldExpr(0))} is null`);
955
+ }
956
+ else if (op === '!=') {
957
+ this.warnWithReplacement('sql-is-not-null', "Use 'is not null' to check for NULL instead of '!= null'", wholeRange, `${this.getSourceCode(pcx.fieldExpr(0))} is not null`);
958
+ }
959
+ }
938
960
  return this.astAt(new ast.ExprEquality(left, op, right), pcx);
939
961
  }
940
962
  else if (ast.isComparison(op)) {
@@ -1465,19 +1487,9 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
1465
1487
  }
1466
1488
  return this.astAt(new ast.ExprCompare(this.getFieldExpr(left), op, this.getFieldExpr(right)), pcx);
1467
1489
  }
1468
- visitExprWarnNullCmp(pcx) {
1469
- let op = '=';
1490
+ visitExprNullCheck(pcx) {
1470
1491
  const expr = pcx.fieldExpr();
1471
- const wholeRange = this.parseInfo.rangeFromContext(pcx);
1472
- if (pcx.NOT()) {
1473
- op = '!=';
1474
- this.warnWithReplacement('sql-is-not-null', "Use '!= NULL' to check for NULL instead of 'IS NOT NULL'", wholeRange, `${this.getSourceCode(expr)} != null`);
1475
- }
1476
- else {
1477
- this.warnWithReplacement('sql-is-null', "Use '= NULL' to check for NULL instead of 'IS NULL'", wholeRange, `${this.getSourceCode(expr)} = null`);
1478
- }
1479
- const nullExpr = new ast.ExprNULL();
1480
- return this.astAt(new ast.ExprCompare(this.getFieldExpr(expr), op, nullExpr), pcx);
1492
+ return this.astAt(new ast.ExprIsNull(this.getFieldExpr(expr), pcx.NOT() ? '!=' : '='), pcx);
1481
1493
  }
1482
1494
  visitExprWarnIn(pcx) {
1483
1495
  const expr = this.getFieldExpr(pcx.fieldExpr());
@@ -136,6 +136,18 @@ describe('expressions', () => {
136
136
  test('null-check (??)', () => {
137
137
  expect('ai ?? 7').compilesTo('{ai coalesce 7}');
138
138
  });
139
+ test('normal is-null', () => {
140
+ expect('ai is null').compilesTo('{is-null ai}');
141
+ });
142
+ test('normal is-not-null', () => {
143
+ expect('ai is not null').compilesTo('{is-not-null ai}');
144
+ });
145
+ test('apply is-null', () => {
146
+ expect('ai ? is null').compilesTo('{is-null ai}');
147
+ });
148
+ test('apply is-not-null', () => {
149
+ expect('ai ? is not null').compilesTo('{is-not-null ai}');
150
+ });
139
151
  test('coalesce type mismatch', () => {
140
152
  expect(new test_translator_1.BetaExpression('ai ?? @2003')).toLog((0, test_translator_1.errorMessage)('Mismatched types for coalesce (number, date)'));
141
153
  });
@@ -177,19 +189,19 @@ describe('expressions', () => {
177
189
  expect((0, test_translator_1.expr) `ai ? (> 1 & < 100)`).toTranslate();
178
190
  });
179
191
  describe('sql friendly warnings', () => {
180
- test('is null with warning', () => {
181
- const warnSrc = (0, test_translator_1.expr) `ai is null`;
182
- expect(warnSrc).toLog((0, test_translator_1.warningMessage)("Use '= NULL' to check for NULL instead of 'IS NULL'"));
192
+ test('= null with warning', () => {
193
+ const warnSrc = (0, test_translator_1.expr) `${'ai = null'}`;
194
+ expect(warnSrc).toLog((0, test_translator_1.warningMessage)("Use 'is null' to check for NULL instead of '= null'"));
183
195
  expect(warnSrc).compilesTo('{is-null ai}');
184
196
  const warning = warnSrc.translator.problems()[0];
185
- expect(warning.replacement).toEqual('ai = null');
197
+ expect(warning.replacement).toEqual('ai is null');
186
198
  });
187
199
  test('is not null with warning', () => {
188
- const warnSrc = (0, test_translator_1.expr) `ai is not null`;
189
- expect(warnSrc).toLog((0, test_translator_1.warningMessage)("Use '!= NULL' to check for NULL instead of 'IS NOT NULL'"));
200
+ const warnSrc = (0, test_translator_1.expr) `${'ai != null'}`;
201
+ expect(warnSrc).toLog((0, test_translator_1.warningMessage)("Use 'is not null' to check for NULL instead of '!= null'"));
190
202
  expect(warnSrc).compilesTo('{is-not-null ai}');
191
203
  const warning = warnSrc.translator.problems()[0];
192
- expect(warning.replacement).toEqual('ai != null');
204
+ expect(warning.replacement).toEqual('ai is not null');
193
205
  });
194
206
  test('like with warning', () => {
195
207
  const warnSrc = (0, test_translator_1.expr) `astr like 'a'`;
@@ -205,20 +217,6 @@ describe('expressions', () => {
205
217
  const warning = warnSrc.translator.problems()[0];
206
218
  expect(warning.replacement).toEqual("astr !~ 'a'");
207
219
  });
208
- test('is is-null in a model', () => {
209
- const isNullSrc = (0, test_translator_1.model) `source: xa is a extend { dimension: x1 is astr is null }`;
210
- expect(isNullSrc).toLog((0, test_translator_1.warningMessage)("Use '= NULL' to check for NULL instead of 'IS NULL'"));
211
- });
212
- test('is not-null in a model', () => {
213
- const isNullSrc = (0, test_translator_1.model) `source: xa is a extend { dimension: x1 is not null }`;
214
- expect(isNullSrc).toTranslate();
215
- });
216
- test('is not-null is in a model', () => {
217
- const isNullSrc = (0, test_translator_1.model) `source: xa is a extend { dimension: x1 is not null is null }`;
218
- expect(isNullSrc).toLog((0, test_translator_1.warningMessage)("Use '= NULL' to check for NULL instead of 'IS NULL'"));
219
- const warning = isNullSrc.translator.problems()[0];
220
- expect(warning.replacement).toEqual('null = null');
221
- });
222
220
  test('x is expr y is not null', () => {
223
221
  const isNullSrc = (0, test_translator_1.model) `source: xa is a extend { dimension: x is 1 y is not null }`;
224
222
  expect(isNullSrc).toTranslate();
@@ -1042,7 +1040,7 @@ describe('expressions', () => {
1042
1040
  ['astr', 'string'],
1043
1041
  ['abool', 'boolean'],
1044
1042
  ])('Can compare field %s (type %s) to NULL', (name, _datatype) => {
1045
- expect((0, test_translator_1.expr) `${name} = NULL`).toTranslate();
1043
+ expect((0, test_translator_1.expr) `${name} IS NULL`).toTranslate();
1046
1044
  });
1047
1045
  });
1048
1046
  describe('alternations as in', () => {
@@ -1098,7 +1096,7 @@ describe('sql native fields in schema', () => {
1098
1096
  expect(uModel).toTranslate();
1099
1097
  });
1100
1098
  test('sql native reference can be compared to NULL', () => {
1101
- const uModel = new test_translator_1.TestTranslator('run: a->{ where: aun != NULL; select: * }');
1099
+ const uModel = new test_translator_1.TestTranslator('run: a->{ where: aun is not null; select: * }');
1102
1100
  expect(uModel).toTranslate();
1103
1101
  });
1104
1102
  test('flag unsupported equality', () => {
@@ -61,7 +61,7 @@ describe('parameters', () => {
61
61
  expect(`
62
62
  ##! experimental.parameters
63
63
  source: ab_new(param is null::string) is ab extend {
64
- where: param = null
64
+ where: param is null
65
65
  }
66
66
  run: ab_new(param is "foo") -> { select: * } -> { select: * }
67
67
  `).toTranslate();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy",
3
- "version": "0.0.225-dev250113200903",
3
+ "version": "0.0.225-dev250113203344",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./dist/index.js",