@malloydata/malloy 0.0.237-dev250223010918 → 0.0.237-dev250224215546

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.
@@ -13,6 +13,7 @@ import { LiteralMonthContext } from "./MalloyParser";
13
13
  import { LiteralQuarterContext } from "./MalloyParser";
14
14
  import { LiteralYearContext } from "./MalloyParser";
15
15
  import { ExprStringContext } from "./MalloyParser";
16
+ import { Stub_rawStringContext } from "./MalloyParser";
16
17
  import { ExprNumberContext } from "./MalloyParser";
17
18
  import { ExprTimeContext } from "./MalloyParser";
18
19
  import { ExprNULLContext } from "./MalloyParser";
@@ -199,6 +200,7 @@ import { MalloyTypeContext } from "./MalloyParser";
199
200
  import { CompareOpContext } from "./MalloyParser";
200
201
  import { StringContext } from "./MalloyParser";
201
202
  import { ShortStringContext } from "./MalloyParser";
203
+ import { RawStringContext } from "./MalloyParser";
202
204
  import { NumericLiteralContext } from "./MalloyParser";
203
205
  import { LiteralContext } from "./MalloyParser";
204
206
  import { DateLiteralContext } from "./MalloyParser";
@@ -409,6 +411,18 @@ export interface MalloyParserListener extends ParseTreeListener {
409
411
  * @param ctx the parse tree
410
412
  */
411
413
  exitExprString?: (ctx: ExprStringContext) => void;
414
+ /**
415
+ * Enter a parse tree produced by the `stub_rawString`
416
+ * labeled alternative in `MalloyParser.literal`.
417
+ * @param ctx the parse tree
418
+ */
419
+ enterStub_rawString?: (ctx: Stub_rawStringContext) => void;
420
+ /**
421
+ * Exit a parse tree produced by the `stub_rawString`
422
+ * labeled alternative in `MalloyParser.literal`.
423
+ * @param ctx the parse tree
424
+ */
425
+ exitStub_rawString?: (ctx: Stub_rawStringContext) => void;
412
426
  /**
413
427
  * Enter a parse tree produced by the `exprNumber`
414
428
  * labeled alternative in `MalloyParser.literal`.
@@ -2413,6 +2427,16 @@ export interface MalloyParserListener extends ParseTreeListener {
2413
2427
  * @param ctx the parse tree
2414
2428
  */
2415
2429
  exitShortString?: (ctx: ShortStringContext) => void;
2430
+ /**
2431
+ * Enter a parse tree produced by `MalloyParser.rawString`.
2432
+ * @param ctx the parse tree
2433
+ */
2434
+ enterRawString?: (ctx: RawStringContext) => void;
2435
+ /**
2436
+ * Exit a parse tree produced by `MalloyParser.rawString`.
2437
+ * @param ctx the parse tree
2438
+ */
2439
+ exitRawString?: (ctx: RawStringContext) => void;
2416
2440
  /**
2417
2441
  * Enter a parse tree produced by `MalloyParser.numericLiteral`.
2418
2442
  * @param ctx the parse tree
@@ -13,6 +13,7 @@ import { LiteralMonthContext } from "./MalloyParser";
13
13
  import { LiteralQuarterContext } from "./MalloyParser";
14
14
  import { LiteralYearContext } from "./MalloyParser";
15
15
  import { ExprStringContext } from "./MalloyParser";
16
+ import { Stub_rawStringContext } from "./MalloyParser";
16
17
  import { ExprNumberContext } from "./MalloyParser";
17
18
  import { ExprTimeContext } from "./MalloyParser";
18
19
  import { ExprNULLContext } from "./MalloyParser";
@@ -199,6 +200,7 @@ import { MalloyTypeContext } from "./MalloyParser";
199
200
  import { CompareOpContext } from "./MalloyParser";
200
201
  import { StringContext } from "./MalloyParser";
201
202
  import { ShortStringContext } from "./MalloyParser";
203
+ import { RawStringContext } from "./MalloyParser";
202
204
  import { NumericLiteralContext } from "./MalloyParser";
203
205
  import { LiteralContext } from "./MalloyParser";
204
206
  import { DateLiteralContext } from "./MalloyParser";
@@ -342,6 +344,13 @@ export interface MalloyParserVisitor<Result> extends ParseTreeVisitor<Result> {
342
344
  * @return the visitor result
343
345
  */
344
346
  visitExprString?: (ctx: ExprStringContext) => Result;
347
+ /**
348
+ * Visit a parse tree produced by the `stub_rawString`
349
+ * labeled alternative in `MalloyParser.literal`.
350
+ * @param ctx the parse tree
351
+ * @return the visitor result
352
+ */
353
+ visitStub_rawString?: (ctx: Stub_rawStringContext) => Result;
345
354
  /**
346
355
  * Visit a parse tree produced by the `exprNumber`
347
356
  * labeled alternative in `MalloyParser.literal`.
@@ -1530,6 +1539,12 @@ export interface MalloyParserVisitor<Result> extends ParseTreeVisitor<Result> {
1530
1539
  * @return the visitor result
1531
1540
  */
1532
1541
  visitShortString?: (ctx: ShortStringContext) => Result;
1542
+ /**
1543
+ * Visit a parse tree produced by `MalloyParser.rawString`.
1544
+ * @param ctx the parse tree
1545
+ * @return the visitor result
1546
+ */
1547
+ visitRawString?: (ctx: RawStringContext) => Result;
1533
1548
  /**
1534
1549
  * Visit a parse tree produced by `MalloyParser.numericLiteral`.
1535
1550
  * @param ctx the parse tree
@@ -162,6 +162,7 @@ export declare class MalloyToAST extends AbstractParseTreeVisitor<ast.MalloyElem
162
162
  visitPartialTest(pcx: parse.PartialTestContext): ast.ExpressionDef;
163
163
  visitPartialAllowedFieldExpr(pcx: parse.PartialAllowedFieldExprContext): ast.ExpressionDef;
164
164
  visitExprString(pcx: parse.ExprStringContext): ast.ExprString;
165
+ visitRawString(pcx: parse.RawStringContext): ast.ExprString;
165
166
  visitExprRegex(pcx: parse.ExprRegexContext): ast.ExprRegEx;
166
167
  visitExprNow(_pcx: parse.ExprNowContext): ast.ExprNow;
167
168
  visitExprNumber(pcx: parse.ExprNumberContext): ast.ExprNumber;
@@ -910,6 +910,15 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
910
910
  const str = this.getPlainStringFrom(pcx);
911
911
  return new ast.ExprString(str);
912
912
  }
913
+ visitRawString(pcx) {
914
+ const str = pcx.text.slice(1).trimStart();
915
+ const lastChar = str[str.length - 1];
916
+ if (lastChar === '\n') {
917
+ this.contextError(pcx, 'literal-string-newline', 'String cannot contain a new-line character');
918
+ }
919
+ const astStr = new ast.ExprString(str.slice(1, -1));
920
+ return this.astAt(astStr, pcx);
921
+ }
913
922
  visitExprRegex(pcx) {
914
923
  const malloyRegex = pcx.HACKY_REGEX().text;
915
924
  return new ast.ExprRegEx(malloyRegex.slice(2, -1));
@@ -361,6 +361,7 @@ type MessageParameterTypes = {
361
361
  'cannot-tag-include-except': string;
362
362
  'unsupported-path-in-include': string;
363
363
  'wildcard-include-rename': string;
364
+ 'literal-string-newline': string;
364
365
  };
365
366
  export declare const MESSAGE_FORMATTERS: PartialErrorCodeMessageMap;
366
367
  export type MessageCode = keyof MessageParameterTypes;
@@ -62,7 +62,7 @@ describe('expressions', () => {
62
62
  expect((0, test_translator_1.expr) `concat('foo')`).toTranslate();
63
63
  });
64
64
  test('raw function call codegen', () => {
65
- expect((0, test_translator_1.expr) `special_function!(aweird, 'foo')`).compilesTo('special_function({aweird},{"foo"})');
65
+ expect((0, test_translator_1.expr) `special_function!(aweird, 'foo')`).compilesTo('special_function({aweird},{{"foo"}})');
66
66
  });
67
67
  describe('operators', () => {
68
68
  test('addition', () => {
@@ -110,19 +110,19 @@ describe('expressions', () => {
110
110
  expect('42 < 7').compilesTo('{42 < 7}');
111
111
  });
112
112
  test('match', () => {
113
- expect("'forty-two' ~ 'fifty-four'").compilesTo('{"forty-two" like "fifty-four"}');
113
+ expect("'forty-two' ~ 'fifty-four'").compilesTo('{{"forty-two"} like {"fifty-four"}}');
114
114
  });
115
115
  test('not match', () => {
116
- expect("'forty-two' !~ 'fifty-four'").compilesTo('{"forty-two" !like "fifty-four"}');
116
+ expect("'forty-two' !~ 'fifty-four'").compilesTo('{{"forty-two"} !like {"fifty-four"}}');
117
117
  });
118
118
  test('regexp-match', () => {
119
- expect("'forty-two' ~ r'fifty-four'").compilesTo('{"forty-two" regex-match /fifty-four/}');
119
+ expect("'forty-two' ~ r'fifty-four'").compilesTo('{{"forty-two"} regex-match /fifty-four/}');
120
120
  });
121
121
  test('not regexp-match', () => {
122
- expect("'forty-two' !~ r'fifty-four'").compilesTo('{not {"forty-two" regex-match /fifty-four/}}');
122
+ expect("'forty-two' !~ r'fifty-four'").compilesTo('{not {{"forty-two"} regex-match /fifty-four/}}');
123
123
  });
124
124
  test('apply as equality', () => {
125
- expect("'forty-two' ? 'fifty-four'").compilesTo('{"forty-two" = "fifty-four"}');
125
+ expect("'forty-two' ? 'fifty-four'").compilesTo('{{"forty-two"} = {"fifty-four"}}');
126
126
  });
127
127
  test('not', () => {
128
128
  expect('not true').compilesTo('{not true}');
@@ -212,14 +212,14 @@ describe('expressions', () => {
212
212
  test('like with warning', () => {
213
213
  const warnSrc = (0, test_translator_1.expr) `astr like 'a'`;
214
214
  expect(warnSrc).toLog((0, test_translator_1.warningMessage)("Use Malloy operator '~' instead of 'LIKE'"));
215
- expect(warnSrc).compilesTo('{astr like "a"}');
215
+ expect(warnSrc).compilesTo('{astr like {"a"}}');
216
216
  const warning = warnSrc.translator.problems()[0];
217
217
  expect(warning.replacement).toEqual("astr ~ 'a'");
218
218
  });
219
219
  test('NOT LIKE with warning', () => {
220
220
  const warnSrc = (0, test_translator_1.expr) `astr not like 'a'`;
221
221
  expect(warnSrc).toLog((0, test_translator_1.warningMessage)("Use Malloy operator '!~' instead of 'NOT LIKE'"));
222
- expect(warnSrc).compilesTo('{astr !like "a"}');
222
+ expect(warnSrc).compilesTo('{astr !like {"a"}}');
223
223
  const warning = warnSrc.translator.problems()[0];
224
224
  expect(warning.replacement).toEqual("astr !~ 'a'");
225
225
  });
@@ -767,7 +767,7 @@ describe('expressions', () => {
767
767
  end
768
768
  `;
769
769
  expect(e).toLog((0, test_translator_1.warning)('sql-case'));
770
- expect(e).compilesTo('{case when {ai = 42} then "the answer" when {ai = 54} then "the questionable answer" else "random"}');
770
+ expect(e).compilesTo('{case when {ai = 42} then {"the answer"} when {ai = 54} then {"the questionable answer"} else {"random"}}');
771
771
  });
772
772
  test('with value', () => {
773
773
  const e = (0, test_translator_1.expr) `
@@ -778,7 +778,7 @@ describe('expressions', () => {
778
778
  end
779
779
  `;
780
780
  expect(e).toLog((0, test_translator_1.warning)('sql-case'));
781
- expect(e).compilesTo('{case ai when 42 then "the answer" when 54 then "the questionable answer" else "random"}');
781
+ expect(e).compilesTo('{case ai when 42 then {"the answer"} when 54 then {"the questionable answer"} else {"random"}}');
782
782
  });
783
783
  test('no else', () => {
784
784
  const e = (0, test_translator_1.expr) `
@@ -788,7 +788,7 @@ describe('expressions', () => {
788
788
  end
789
789
  `;
790
790
  expect(e).toLog((0, test_translator_1.warning)('sql-case'));
791
- expect(e).compilesTo('{case when {ai = 42} then "the answer" when {ai = 54} then "the questionable answer"}');
791
+ expect(e).compilesTo('{case when {ai = 42} then {"the answer"} when {ai = 54} then {"the questionable answer"}}');
792
792
  });
793
793
  test('wrong then type', () => {
794
794
  expect((0, test_translator_1.expr) `
@@ -44,6 +44,15 @@ describe('literals', () => {
44
44
  const str = "'Is " + '\\' + '\\' + " nice'";
45
45
  expect(new test_translator_1.BetaExpression(str)).toTranslate();
46
46
  });
47
+ test("raw s'string", () => {
48
+ expect("s'a'").compilesTo('{"a"}');
49
+ });
50
+ test('raw s"string', () => {
51
+ expect('s "a"').compilesTo('{"a"}');
52
+ });
53
+ test('raw string with quoted end char', () => {
54
+ expect("s'a\\'b'").compilesTo('{"a\\\'b"}');
55
+ });
47
56
  const literalTimes = [
48
57
  ['@1960', 'date', 'year', { literal: '1960-01-01' }],
49
58
  ['@1960-Q2', 'date', 'quarter', { literal: '1960-04-01' }],
@@ -269,6 +278,9 @@ describe('literals', () => {
269
278
  const x = new test_translator_1.BetaExpression('"""x"""');
270
279
  expect(x).toParse();
271
280
  });
281
+ test('Error for missing close in raw string', () => {
282
+ expect((0, test_translator_1.expr) `s'hello\n`).toLog((0, test_translator_1.errorMessage)('String cannot contain a new-line character'));
283
+ });
272
284
  test('a string containing a tab', () => expect((0, test_translator_1.expr) `'\t'`).toParse());
273
285
  });
274
286
  describe('compound literals', () => {
@@ -279,10 +291,10 @@ describe('literals', () => {
279
291
  expect('{aninline.column}').compilesTo('{column:aninline.column}');
280
292
  });
281
293
  test('array of records with same schema', () => {
282
- expect('[{name is "one", val is 1},{name is "two", val is 2}]').compilesTo('[{name:"one", val:1}, {name:"two", val:2}]');
294
+ expect('[{name is "one", val is 1},{name is "two", val is 2}]').compilesTo('[{name:{"one"}, val:1}, {name:{"two"}, val:2}]');
283
295
  });
284
296
  test('array of records with head schema', () => {
285
- expect('[{name is "one", val is 1},{"two", 2}]').compilesTo('[{name:"one", val:1}, {name:"two", val:2}]');
297
+ expect('[{name is "one", val is 1},{"two", 2}]').compilesTo('[{name:{"one"}, val:1}, {name:{"two"}, val:2}]');
286
298
  });
287
299
  });
288
300
  });
@@ -145,7 +145,7 @@ function eToStr(e, symbols) {
145
145
  case 'numberLiteral':
146
146
  return `${e.literal}`;
147
147
  case 'stringLiteral':
148
- return `"${e.literal}"`;
148
+ return `{"${e.literal}"}`;
149
149
  case 'timeLiteral':
150
150
  return `@${e.literal}`;
151
151
  case 'recordLiteral': {
@@ -836,15 +836,15 @@ class QueryField extends QueryNode {
836
836
  return `${expr.kids.left.sql} ${expr.node} ${expr.kids.right.sql}`;
837
837
  case 'coalesce':
838
838
  return `COALESCE(${expr.kids.left.sql},${expr.kids.right.sql})`;
839
- case 'like':
840
- return `${expr.kids.left.sql} LIKE ${expr.kids.right.sql}`;
841
839
  case 'in': {
842
840
  const oneOf = expr.kids.oneOf.map(o => o.sql).join(',');
843
841
  return `${expr.kids.e.sql} ${expr.not ? 'NOT IN' : 'IN'} (${oneOf})`;
844
842
  }
843
+ case 'like':
844
+ return this.parent.dialect.likeExpr(expr);
845
845
  // Malloy inequality comparisons always return a boolean
846
846
  case '!like': {
847
- const notLike = `${expr.kids.left.sql} NOT LIKE ${expr.kids.right.sql}`;
847
+ const notLike = this.parent.dialect.likeExpr(expr);
848
848
  return `COALESCE(${notLike},true)`;
849
849
  }
850
850
  case '()':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy",
3
- "version": "0.0.237-dev250223010918",
3
+ "version": "0.0.237-dev250224215546",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./dist/index.js",