@malloydata/malloy-interfaces 0.0.237-dev250225015031 → 0.0.237-dev250225213433

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.
@@ -0,0 +1,438 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.queryToMalloy = void 0;
4
+ const util_1 = require("./util");
5
+ function queryToMalloy(query, { tabWidth } = { tabWidth: 2 }) {
6
+ const fragments = queryToFragments(query);
7
+ return codeFromFragments(fragments, { tabWidth });
8
+ }
9
+ exports.queryToMalloy = queryToMalloy;
10
+ const INDENT = Symbol('indent');
11
+ const NEWLINE = Symbol('newline');
12
+ const OUTDENT = Symbol('outdent');
13
+ const OPTIONAL_NEWLINE_INDENT = Symbol('optional_newline_indent');
14
+ function codeFromFragments(fragments, { tabWidth } = { tabWidth: 2 }) {
15
+ let code = '';
16
+ let indent = 0;
17
+ let isStartOfLine = true;
18
+ for (const fragment of fragments) {
19
+ if (fragment === NEWLINE) {
20
+ code += '\n';
21
+ isStartOfLine = true;
22
+ }
23
+ else if (fragment === OUTDENT) {
24
+ indent--;
25
+ }
26
+ else if (fragment === INDENT) {
27
+ indent++;
28
+ }
29
+ else if (fragment === OPTIONAL_NEWLINE_INDENT) {
30
+ continue; // TODO
31
+ }
32
+ else {
33
+ if (isStartOfLine) {
34
+ code += ' '.repeat(indent * tabWidth);
35
+ isStartOfLine = false;
36
+ }
37
+ code += fragment;
38
+ }
39
+ }
40
+ return code;
41
+ }
42
+ function wrap(open, block, close) {
43
+ if (block.includes(NEWLINE)) {
44
+ return [open, NEWLINE, INDENT, ...block, NEWLINE, OUTDENT, close];
45
+ }
46
+ return [open, ' ', ...block, ' ', close];
47
+ }
48
+ function escapeString(str) {
49
+ return { contents: str, quoteCharacter: '"' }; // TODO
50
+ }
51
+ function literalToFragments(literal) {
52
+ var _a, _b;
53
+ switch (literal.kind) {
54
+ case 'boolean_literal':
55
+ return [literal.boolean_value.toString()];
56
+ case 'string_literal': {
57
+ const { contents, quoteCharacter } = escapeString(literal.string_value);
58
+ return [quoteCharacter, contents, quoteCharacter];
59
+ }
60
+ case 'number_literal':
61
+ // TODO big numbers etc?
62
+ return [literal.number_value.toString()];
63
+ case 'null_literal':
64
+ return ['null'];
65
+ case 'date_literal':
66
+ return [
67
+ serializeDateAsLiteral(parseDate(literal.date_value), (_a = literal.granularity) !== null && _a !== void 0 ? _a : 'day'),
68
+ ];
69
+ case 'timestamp_literal':
70
+ return [
71
+ serializeDateAsLiteral(parseDate(literal.timestamp_value), (_b = literal.granularity) !== null && _b !== void 0 ? _b : 'second'),
72
+ ];
73
+ }
74
+ }
75
+ function parseDate(date) {
76
+ return new Date(date);
77
+ }
78
+ function digits(value, digits) {
79
+ return value.toString().padStart(digits, '0');
80
+ }
81
+ function serializeDateAsLiteral(date, granularity) {
82
+ switch (granularity) {
83
+ case 'year': {
84
+ const year = digits(date.getUTCFullYear(), 4);
85
+ return `@${year}`;
86
+ }
87
+ case 'quarter': {
88
+ const year = digits(date.getUTCFullYear(), 4);
89
+ const quarter = Math.floor(date.getUTCMonth() / 3) + 1;
90
+ return `@${year}-Q${quarter}`;
91
+ }
92
+ case 'month': {
93
+ const year = digits(date.getUTCFullYear(), 2);
94
+ const month = digits(date.getUTCMonth() + 1, 2);
95
+ return `@${year}-${month}`;
96
+ }
97
+ case 'week': {
98
+ const year = digits(date.getUTCFullYear(), 2);
99
+ const month = digits(date.getUTCMonth() + 1, 2);
100
+ const day = digits(date.getUTCDate(), 2);
101
+ return `@WK${year}-${month}-${day}`;
102
+ }
103
+ case 'day': {
104
+ const year = digits(date.getUTCFullYear(), 2);
105
+ const month = digits(date.getUTCMonth() + 1, 2);
106
+ const day = digits(date.getUTCDate(), 2);
107
+ return `@${year}-${month}-${day}`;
108
+ }
109
+ case 'hour': {
110
+ const year = digits(date.getUTCFullYear(), 2);
111
+ const month = digits(date.getUTCMonth() + 1, 2);
112
+ const day = digits(date.getUTCDate(), 2);
113
+ const hour = digits(date.getUTCHours(), 2);
114
+ return `@${year}-${month}-${day} ${hour}`;
115
+ }
116
+ case 'minute': {
117
+ const year = digits(date.getUTCFullYear(), 2);
118
+ const month = digits(date.getUTCMonth() + 1, 2);
119
+ const day = digits(date.getUTCDate(), 2);
120
+ const hour = digits(date.getUTCHours(), 2);
121
+ const minute = digits(date.getUTCMinutes(), 2);
122
+ return `@${year}-${month}-${day} ${hour}:${minute}`;
123
+ }
124
+ case 'second': {
125
+ const year = digits(date.getUTCFullYear(), 2);
126
+ const month = digits(date.getUTCMonth() + 1, 2);
127
+ const day = digits(date.getUTCDate(), 2);
128
+ const hour = digits(date.getUTCHours(), 2);
129
+ const minute = digits(date.getUTCMinutes(), 2);
130
+ const second = digits(date.getUTCSeconds(), 2);
131
+ return `@${year}-${month}-${day} ${hour}:${minute}:${second}`;
132
+ }
133
+ default:
134
+ throw new Error('Unknown timeframe.');
135
+ }
136
+ }
137
+ function referenceToFragments(reference) {
138
+ var _a;
139
+ const fragments = [];
140
+ for (const name of (_a = reference.path) !== null && _a !== void 0 ? _a : []) {
141
+ fragments.push((0, util_1.maybeQuoteIdentifier)(name));
142
+ fragments.push('.');
143
+ }
144
+ fragments.push((0, util_1.maybeQuoteIdentifier)(reference.name));
145
+ if (reference.parameters) {
146
+ const parameterFragments = [];
147
+ for (let i = 0; i < reference.parameters.length; i++) {
148
+ const p = reference.parameters[i];
149
+ parameterFragments.push((0, util_1.maybeQuoteIdentifier)(p.name));
150
+ parameterFragments.push(' is ');
151
+ parameterFragments.push(...literalToFragments(p.value));
152
+ if (i < reference.parameters.length - 1) {
153
+ parameterFragments.push(',', NEWLINE);
154
+ }
155
+ }
156
+ fragments.push(...wrap('(', parameterFragments, ')'));
157
+ }
158
+ return fragments;
159
+ }
160
+ function queryToFragments(query) {
161
+ const fragments = [];
162
+ fragments.push(...annotationsToFragments(query.annotations));
163
+ fragments.push('run: ');
164
+ fragments.push(...queryDefinitionToFragments(query.definition));
165
+ return fragments;
166
+ }
167
+ function queryDefinitionToFragments(query) {
168
+ const fragments = [];
169
+ switch (query.kind) {
170
+ case 'arrow': {
171
+ fragments.push(...referenceToFragments(query.source_reference));
172
+ fragments.push(' -> ');
173
+ fragments.push(...viewDefinitionToFragments(query.view));
174
+ break;
175
+ }
176
+ case 'query_reference': {
177
+ fragments.push(...referenceToFragments(query));
178
+ break;
179
+ }
180
+ case 'refinement': {
181
+ fragments.push(...referenceToFragments(query.query_reference));
182
+ fragments.push(' + ');
183
+ fragments.push(...viewDefinitionToFragments(query.refinement));
184
+ break;
185
+ }
186
+ }
187
+ return fragments;
188
+ }
189
+ function viewDefinitionToFragments(view) {
190
+ const fragments = [];
191
+ switch (view.kind) {
192
+ case 'arrow': {
193
+ fragments.push(...viewDefinitionToFragments(view.source));
194
+ fragments.push(' -> ');
195
+ fragments.push(...viewDefinitionToFragments(view.view));
196
+ break;
197
+ }
198
+ case 'view_reference': {
199
+ fragments.push(...referenceToFragments(view));
200
+ break;
201
+ }
202
+ case 'refinement': {
203
+ fragments.push(...viewDefinitionToFragments(view.base));
204
+ fragments.push(' + ');
205
+ fragments.push(...viewDefinitionToFragments(view.refinement));
206
+ break;
207
+ }
208
+ case 'segment': {
209
+ fragments.push(...segmentToFragments(view));
210
+ break;
211
+ }
212
+ }
213
+ return fragments;
214
+ }
215
+ function segmentToFragments(segment) {
216
+ if (segment.operations.length === 0)
217
+ return ['{ }'];
218
+ const onMultipleLines = segment.operations.length > 1;
219
+ const operationFragments = [];
220
+ for (let i = 0; i < segment.operations.length; i++) {
221
+ const operation = segment.operations[i];
222
+ const likeOperations = [operation];
223
+ while (i < segment.operations.length - 1) {
224
+ const nextOperation = segment.operations[i + 1];
225
+ if (nextOperation.kind === operation.kind) {
226
+ likeOperations.push(nextOperation);
227
+ i++;
228
+ }
229
+ else {
230
+ break;
231
+ }
232
+ }
233
+ operationFragments.push(...groupedOperationsToFragments(likeOperations));
234
+ if (onMultipleLines && i < segment.operations.length - 1) {
235
+ operationFragments.push(NEWLINE);
236
+ }
237
+ }
238
+ return wrap('{', operationFragments, '}');
239
+ }
240
+ function groupedOperationsToFragments(operations) {
241
+ switch (operations[0].kind) {
242
+ case 'aggregate':
243
+ return aggregateToFragments(operations);
244
+ case 'group_by':
245
+ return groupByToFragments(operations);
246
+ case 'order_by':
247
+ return orderByToFragments(operations);
248
+ case 'nest':
249
+ return nestToFragments(operations);
250
+ case 'limit':
251
+ return limitToFragments(operations);
252
+ case 'where':
253
+ return whereToFragments(operations);
254
+ }
255
+ }
256
+ function formatBlock(label, items, separator = '') {
257
+ const fragments = [];
258
+ fragments.push(`${label}:`);
259
+ const indented = items.length > 1 || items.some(item => item.includes(NEWLINE));
260
+ if (indented) {
261
+ fragments.push(NEWLINE, INDENT);
262
+ }
263
+ else {
264
+ fragments.push(' ');
265
+ }
266
+ for (let i = 0; i < items.length; i++) {
267
+ const item = items[i];
268
+ fragments.push(...item);
269
+ if (items.length > 1 && i < items.length - 1) {
270
+ fragments.push(separator);
271
+ }
272
+ if (indented && i < items.length - 1) {
273
+ fragments.push(NEWLINE);
274
+ }
275
+ }
276
+ if (indented) {
277
+ fragments.push(OUTDENT);
278
+ }
279
+ return fragments;
280
+ }
281
+ function fieldToFragments(field) {
282
+ const fragments = [];
283
+ // TODO annotations
284
+ fragments.push(...expressionToFragments(field.expression));
285
+ return fragments;
286
+ }
287
+ function timeUnitToFragment(timeUnit) {
288
+ return timeUnit;
289
+ }
290
+ function expressionToFragments(expression) {
291
+ switch (expression.kind) {
292
+ case 'field_reference':
293
+ return referenceToFragments(expression);
294
+ case 'time_truncation':
295
+ return [
296
+ ...referenceToFragments(expression.field_reference),
297
+ '.',
298
+ timeUnitToFragment(expression.truncation),
299
+ ];
300
+ case 'filtered_field':
301
+ return [
302
+ ...referenceToFragments(expression.field_reference),
303
+ ...wrap(' {', whereToFragments(expression.where), '}'),
304
+ ];
305
+ }
306
+ }
307
+ function groupByOrAggregateItemToFragments(item, hideAnnotations = false) {
308
+ const fragments = [];
309
+ if (!hideAnnotations) {
310
+ fragments.push(...annotationsToFragments(item.field.annotations));
311
+ }
312
+ if (item.name) {
313
+ fragments.push((0, util_1.maybeQuoteIdentifier)(item.name));
314
+ fragments.push(' is ');
315
+ }
316
+ fragments.push(...fieldToFragments(item.field));
317
+ return fragments;
318
+ }
319
+ function groupByToFragments(groupBy) {
320
+ const fragments = [];
321
+ const hoistAnnotations = groupBy.length === 1;
322
+ if (hoistAnnotations) {
323
+ fragments.push(...annotationsToFragments(groupBy[0].field.annotations));
324
+ }
325
+ fragments.push(...formatBlock('group_by', groupBy.map(i => groupByOrAggregateItemToFragments(i, hoistAnnotations))));
326
+ return fragments;
327
+ }
328
+ function aggregateToFragments(groupBy) {
329
+ const fragments = [];
330
+ const hoistAnnotations = groupBy.length === 1;
331
+ if (hoistAnnotations) {
332
+ fragments.push(...annotationsToFragments(groupBy[0].field.annotations));
333
+ }
334
+ fragments.push(...formatBlock('aggregate', groupBy.map(i => groupByOrAggregateItemToFragments(i, hoistAnnotations))));
335
+ return fragments;
336
+ }
337
+ function orderByToFragments(orderBy) {
338
+ return formatBlock('order_by', orderBy.map(orderByItemToFragments), ',');
339
+ }
340
+ function orderByItemToFragments(orderByItem) {
341
+ const fragments = [];
342
+ fragments.push(...referenceToFragments(orderByItem.field_reference));
343
+ if (orderByItem.direction) {
344
+ fragments.push(' ');
345
+ fragments.push(orderByItem.direction);
346
+ }
347
+ return fragments;
348
+ }
349
+ function nestToFragments(nest) {
350
+ const fragments = [];
351
+ const hoistAnnotations = nest.length === 1;
352
+ if (hoistAnnotations) {
353
+ fragments.push(...annotationsToFragments(nest[0].view.annotations));
354
+ }
355
+ fragments.push(...formatBlock('nest', nest.map(i => nestItemToFragments(i, hoistAnnotations))));
356
+ return fragments;
357
+ }
358
+ function nestItemToFragments(nestItem, hideAnnotations = false) {
359
+ const fragments = [];
360
+ if (!hideAnnotations) {
361
+ fragments.push(...annotationsToFragments(nestItem.view.annotations));
362
+ }
363
+ if (nestItem.name) {
364
+ fragments.push((0, util_1.maybeQuoteIdentifier)(nestItem.name));
365
+ fragments.push(' is ');
366
+ }
367
+ fragments.push(...viewToFragments(nestItem.view));
368
+ return fragments;
369
+ }
370
+ function viewToFragments(view) {
371
+ // TODO annotations
372
+ return viewDefinitionToFragments(view.definition);
373
+ }
374
+ function limitItemToFragments(limit) {
375
+ return [`limit: ${limit.limit}`];
376
+ }
377
+ function limitToFragments(limits) {
378
+ const fragments = [];
379
+ for (let i = 0; i < limits.length; i++) {
380
+ if (i !== 0) {
381
+ fragments.push(NEWLINE);
382
+ }
383
+ fragments.push(...limitItemToFragments(limits[i]));
384
+ }
385
+ return fragments;
386
+ }
387
+ function whereToFragments(where) {
388
+ return formatBlock('where', where.map(whereItemToFragments));
389
+ }
390
+ const FILTER_QUOTES = ['`', "'", '"']; // technically , '"""', "'''" are valid too, but they're ugly
391
+ function quoteFilter(filter) {
392
+ let bestQuote = undefined;
393
+ let bestEscaped = undefined;
394
+ for (const quote of FILTER_QUOTES) {
395
+ const escaped = escapeFilter(filter, quote);
396
+ if (escaped === filter) {
397
+ return `f${quote}${filter}${quote}`;
398
+ }
399
+ if (bestEscaped === undefined || escaped.length < bestEscaped.length) {
400
+ bestQuote = quote;
401
+ bestEscaped = escaped;
402
+ }
403
+ }
404
+ return `f${bestQuote}${bestEscaped}${bestQuote}`;
405
+ }
406
+ function escapeFilter(filter, quote) {
407
+ let result = '';
408
+ for (let i = 0; i < filter.length; i++) {
409
+ if (filter.slice(i).startsWith(quote)) {
410
+ result += '\\' + quote;
411
+ i += quote.length;
412
+ }
413
+ else {
414
+ result += filter[i];
415
+ if (filter[i] === '\\') {
416
+ result += filter[++i];
417
+ }
418
+ }
419
+ }
420
+ return result;
421
+ }
422
+ function whereItemToFragments(whereItem) {
423
+ switch (whereItem.filter.kind) {
424
+ case 'filter_string':
425
+ return [
426
+ ...referenceToFragments(whereItem.filter.field_reference),
427
+ ' ~ ',
428
+ quoteFilter(whereItem.filter.filter),
429
+ ];
430
+ }
431
+ }
432
+ function annotationsToFragments(annotations) {
433
+ return annotations ? annotations.flatMap(annotationToFragments) : [];
434
+ }
435
+ function annotationToFragments(annotation) {
436
+ return [annotation.value.trim(), NEWLINE];
437
+ }
438
+ //# sourceMappingURL=to_malloy.js.map