@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.
- package/dist/dialect/dialect.d.ts +3 -1
- package/dist/dialect/dialect.js +18 -0
- package/dist/dialect/standardsql/standardsql.d.ts +1 -0
- package/dist/dialect/standardsql/standardsql.js +1 -0
- package/dist/lang/lib/Malloy/MalloyLexer.d.ts +61 -60
- package/dist/lang/lib/Malloy/MalloyLexer.js +969 -937
- package/dist/lang/lib/Malloy/MalloyParser.d.ts +116 -97
- package/dist/lang/lib/Malloy/MalloyParser.js +1947 -1819
- package/dist/lang/lib/Malloy/MalloyParserListener.d.ts +24 -0
- package/dist/lang/lib/Malloy/MalloyParserVisitor.d.ts +15 -0
- package/dist/lang/malloy-to-ast.d.ts +1 -0
- package/dist/lang/malloy-to-ast.js +9 -0
- package/dist/lang/parse-log.d.ts +1 -0
- package/dist/lang/test/expressions.spec.js +11 -11
- package/dist/lang/test/literals.spec.js +14 -2
- package/dist/lang/test/parse-expects.js +1 -1
- package/dist/model/malloy_query.js +3 -3
- package/package.json +1 -1
|
@@ -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));
|
package/dist/lang/parse-log.d.ts
CHANGED
|
@@ -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
|
});
|
|
@@ -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 =
|
|
847
|
+
const notLike = this.parent.dialect.likeExpr(expr);
|
|
848
848
|
return `COALESCE(${notLike},true)`;
|
|
849
849
|
}
|
|
850
850
|
case '()':
|