@malloydata/malloy 0.0.3 → 0.0.4
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/lang/ast/ast-expr.d.ts +0 -1
- package/dist/lang/ast/ast-main.d.ts +2 -2
- package/package.json +1 -1
- package/dist/connection_utils.js +0 -56
- package/dist/dialect/dialect.js +0 -77
- package/dist/dialect/dialect_map.js +0 -35
- package/dist/dialect/duckdb.js +0 -347
- package/dist/dialect/index.js +0 -27
- package/dist/dialect/postgres.js +0 -315
- package/dist/dialect/standardsql.js +0 -362
- package/dist/index.js +0 -47
- package/dist/lang/ast/apply-expr.js +0 -231
- package/dist/lang/ast/ast-expr.js +0 -1041
- package/dist/lang/ast/ast-main.js +0 -2087
- package/dist/lang/ast/ast-time-expr.js +0 -549
- package/dist/lang/ast/ast-types.js +0 -215
- package/dist/lang/ast/index.js +0 -34
- package/dist/lang/ast/time-utils.js +0 -94
- package/dist/lang/field-space.js +0 -631
- package/dist/lang/field-utils.js +0 -33
- package/dist/lang/index.js +0 -22
- package/dist/lang/parse-log.js +0 -41
- package/dist/lang/reference-list.js +0 -80
- package/dist/lang/space-field.js +0 -346
- package/dist/lang/test/document-help-context-walker.spec.js +0 -45
- package/dist/lang/test/document-symbol-walker.spec.js +0 -100
- package/dist/lang/test/field-symbols.spec.js +0 -172
- package/dist/lang/test/parse.spec.js +0 -1951
- package/dist/lang/test/test-translator.js +0 -334
- package/dist/lang/zone.js +0 -97
- package/dist/malloy.js +0 -2360
- package/dist/model/index.js +0 -34
- package/dist/model/malloy_query.js +0 -2994
- package/dist/model/malloy_types.js +0 -283
- package/dist/model/sql_block.js +0 -41
- package/dist/model/utils.js +0 -84
- package/dist/runtime_types.js +0 -15
|
@@ -1,1041 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
* Copyright 2021 Google LLC
|
|
4
|
-
*
|
|
5
|
-
* This program is free software; you can redistribute it and/or
|
|
6
|
-
* modify it under the terms of the GNU General Public License
|
|
7
|
-
* version 2 as published by the Free Software Foundation.
|
|
8
|
-
*
|
|
9
|
-
* This program is distributed in the hope that it will be useful,
|
|
10
|
-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
-
* GNU General Public License for more details.
|
|
13
|
-
*/
|
|
14
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.Pick = exports.PickWhen = exports.Range = exports.TopBy = exports.ExprCast = exports.isCastType = exports.ExprFilter = exports.ExprCase = exports.WhenClause = exports.ExprUngroup = exports.ExprSum = exports.ExprAvg = exports.ExprCount = exports.ExprCountDistinct = exports.ExprMax = exports.ExprMin = exports.ExprAlternationTree = exports.ExprMulDiv = exports.ExprAddSub = exports.BinaryNumeric = exports.ExprMinus = exports.ExprParens = exports.ExprNULL = exports.ExprIdReference = exports.ExprLogicalOp = exports.BinaryBoolean = exports.Boolean = exports.ExprNot = exports.ExprTime = exports.ExprRegEx = exports.ExprNumber = exports.ExprString = exports.TypeMistmatch = exports.FieldDeclaration = exports.ConstantSubExpression = exports.ExpressionDef = void 0;
|
|
16
|
-
/*
|
|
17
|
-
** This file is the general framework for expression nodes and expression
|
|
18
|
-
** evaluation.
|
|
19
|
-
*/
|
|
20
|
-
const malloy_types_1 = require("../../model/malloy_types");
|
|
21
|
-
const field_space_1 = require("../field-space");
|
|
22
|
-
const index_1 = require("./index");
|
|
23
|
-
const apply_expr_1 = require("./apply-expr");
|
|
24
|
-
const space_field_1 = require("../space-field");
|
|
25
|
-
const time_utils_1 = require("./time-utils");
|
|
26
|
-
/**
|
|
27
|
-
* Root node for any element in an expression. These essentially
|
|
28
|
-
* create a sub-tree in the larger AST. Expression nodes know
|
|
29
|
-
* how to write themselves as SQL (or rather, generate the
|
|
30
|
-
* template for SQL required by the query writer)
|
|
31
|
-
*/
|
|
32
|
-
class ExpressionDef extends index_1.MalloyElement {
|
|
33
|
-
constructor() {
|
|
34
|
-
super(...arguments);
|
|
35
|
-
this.legalChildTypes = index_1.FT.anyAtomicT;
|
|
36
|
-
}
|
|
37
|
-
granular() {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Some operators want to give the right hand value a chance to
|
|
42
|
-
* rewrite itself. This requests a translation for a rewrite,
|
|
43
|
-
* or returns undefined if that request should be denied.
|
|
44
|
-
* @param fs FieldSpace
|
|
45
|
-
* @returns Translated expression or undefined
|
|
46
|
-
*/
|
|
47
|
-
requestExpression(fs) {
|
|
48
|
-
return this.getExpression(fs);
|
|
49
|
-
}
|
|
50
|
-
defaultFieldName() {
|
|
51
|
-
return undefined;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Check an expression for type compatibility
|
|
55
|
-
* @param _eNode currently unused, will be used to get error location
|
|
56
|
-
* @param eVal ...list of expressions that must match legalChildTypes
|
|
57
|
-
*/
|
|
58
|
-
typeCheck(eNode, eVal) {
|
|
59
|
-
if (!index_1.FT.in(eVal, this.legalChildTypes)) {
|
|
60
|
-
eNode.log(`'${this.elementType}' Can't use type ${index_1.FT.inspect(eVal)}`);
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* This is the operation which makes partial comparison and value trees work
|
|
67
|
-
* The default implemention merely constructs LEFT OP RIGHT, but specialized
|
|
68
|
-
* nodes like alternation trees or or partial comparison can control how
|
|
69
|
-
* the appplication gets generated
|
|
70
|
-
* @param fs The symbol table
|
|
71
|
-
* @param op The operator being applied
|
|
72
|
-
* @param expr The "other" (besdies 'this') value
|
|
73
|
-
* @returns The translated expression
|
|
74
|
-
*/
|
|
75
|
-
apply(fs, op, left) {
|
|
76
|
-
return (0, apply_expr_1.applyBinary)(fs, left, op, this);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
exports.ExpressionDef = ExpressionDef;
|
|
80
|
-
class DollarReference extends ExpressionDef {
|
|
81
|
-
constructor(refType) {
|
|
82
|
-
super();
|
|
83
|
-
this.refType = refType;
|
|
84
|
-
this.elementType = "$";
|
|
85
|
-
}
|
|
86
|
-
getExpression(_fs) {
|
|
87
|
-
return {
|
|
88
|
-
dataType: this.refType,
|
|
89
|
-
value: [{ type: "applyVal" }],
|
|
90
|
-
aggregate: false,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
class ConstantFieldSpace {
|
|
95
|
-
constructor() {
|
|
96
|
-
this.type = "fieldSpace";
|
|
97
|
-
}
|
|
98
|
-
structDef() {
|
|
99
|
-
throw new Error("ConstantFieldSpace cannot generate a structDef");
|
|
100
|
-
}
|
|
101
|
-
emptyStructDef() {
|
|
102
|
-
throw new Error("ConstantFieldSpace cannot generate a structDef");
|
|
103
|
-
}
|
|
104
|
-
lookup(_name) {
|
|
105
|
-
return {
|
|
106
|
-
error: "Only constants allowed in parameter expressions",
|
|
107
|
-
found: undefined,
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
dialectObj() {
|
|
111
|
-
return undefined;
|
|
112
|
-
}
|
|
113
|
-
whenComplete(step) {
|
|
114
|
-
step();
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
class ConstantSubExpression extends ExpressionDef {
|
|
118
|
-
constructor(expr) {
|
|
119
|
-
super({ expr });
|
|
120
|
-
this.expr = expr;
|
|
121
|
-
this.elementType = "constantExpression";
|
|
122
|
-
}
|
|
123
|
-
getExpression(_fs) {
|
|
124
|
-
return this.constantValue();
|
|
125
|
-
}
|
|
126
|
-
get constantFs() {
|
|
127
|
-
if (!this.cfs) {
|
|
128
|
-
this.cfs = new ConstantFieldSpace();
|
|
129
|
-
}
|
|
130
|
-
return this.cfs;
|
|
131
|
-
}
|
|
132
|
-
constantValue() {
|
|
133
|
-
return this.expr.getExpression(this.constantFs);
|
|
134
|
-
}
|
|
135
|
-
constantCondition(type) {
|
|
136
|
-
const compareAndContrast = new index_1.ExprCompare(new DollarReference(type), "=", this.expr);
|
|
137
|
-
const application = compareAndContrast.getExpression(this.constantFs);
|
|
138
|
-
return { ...application, value: (0, index_1.compressExpr)(application.value) };
|
|
139
|
-
}
|
|
140
|
-
apply(fs, op, expr) {
|
|
141
|
-
return this.expr.apply(fs, op, expr);
|
|
142
|
-
}
|
|
143
|
-
requestExpression(fs) {
|
|
144
|
-
return this.expr.requestExpression(fs);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
exports.ConstantSubExpression = ConstantSubExpression;
|
|
148
|
-
class FieldDeclaration extends index_1.MalloyElement {
|
|
149
|
-
constructor(expr, defineName, exprSrc) {
|
|
150
|
-
super({ expr });
|
|
151
|
-
this.expr = expr;
|
|
152
|
-
this.defineName = defineName;
|
|
153
|
-
this.exprSrc = exprSrc;
|
|
154
|
-
this.elementType = "fieldDeclaration";
|
|
155
|
-
}
|
|
156
|
-
fieldDef(fs, exprName) {
|
|
157
|
-
/*
|
|
158
|
-
* In an explore we cannot reference the thing we are defining, you need
|
|
159
|
-
* to use rename. In a query, the output space is a new thing, and expressions
|
|
160
|
-
* can reference the outer value in order to make a value with the new name,
|
|
161
|
-
* and it feels wrong that this is HERE and not somehow in the QueryOperation.
|
|
162
|
-
* For now, this stops the stack overflow, and passes all tests, but I think
|
|
163
|
-
* a refactor of QueryFieldSpace might someday be the place where this should
|
|
164
|
-
* happen.
|
|
165
|
-
*/
|
|
166
|
-
return this.queryFieldDef(new field_space_1.DefSpace(fs, this), exprName);
|
|
167
|
-
}
|
|
168
|
-
queryFieldDef(exprFS, exprName) {
|
|
169
|
-
let exprValue;
|
|
170
|
-
try {
|
|
171
|
-
exprValue = this.expr.getExpression(exprFS);
|
|
172
|
-
}
|
|
173
|
-
catch (error) {
|
|
174
|
-
this.log(`Cannot define '${exprName}', ${error.message}`);
|
|
175
|
-
return {
|
|
176
|
-
name: `error_defining_${exprName}`,
|
|
177
|
-
type: "string",
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
const compressValue = (0, index_1.compressExpr)(exprValue.value);
|
|
181
|
-
const retType = exprValue.dataType;
|
|
182
|
-
if ((0, malloy_types_1.isAtomicFieldType)(retType)) {
|
|
183
|
-
const template = {
|
|
184
|
-
name: exprName,
|
|
185
|
-
type: retType,
|
|
186
|
-
location: this.location,
|
|
187
|
-
};
|
|
188
|
-
if (compressValue.length > 0) {
|
|
189
|
-
template.e = compressValue;
|
|
190
|
-
}
|
|
191
|
-
if (exprValue.aggregate) {
|
|
192
|
-
template.aggregate = true;
|
|
193
|
-
}
|
|
194
|
-
if (this.exprSrc) {
|
|
195
|
-
template.code = this.exprSrc;
|
|
196
|
-
}
|
|
197
|
-
// TODO this should work for dates too
|
|
198
|
-
if ((0, index_1.isGranularResult)(exprValue) && template.type === "timestamp") {
|
|
199
|
-
template.timeframe = exprValue.timeframe;
|
|
200
|
-
}
|
|
201
|
-
return template;
|
|
202
|
-
}
|
|
203
|
-
const circularDef = exprFS instanceof field_space_1.DefSpace && exprFS.foundCircle;
|
|
204
|
-
if (!circularDef) {
|
|
205
|
-
if (exprValue.dataType == "unknown") {
|
|
206
|
-
this.log(`Cannot define '${exprName}', value has unknown type`);
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
const badType = index_1.FT.inspect(exprValue);
|
|
210
|
-
this.log(`Cannot define '${exprName}', unexpected type: ${badType}`);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
return {
|
|
214
|
-
name: `error_defining_${exprName}`,
|
|
215
|
-
type: "string",
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
exports.FieldDeclaration = FieldDeclaration;
|
|
220
|
-
class TypeMistmatch extends Error {
|
|
221
|
-
}
|
|
222
|
-
exports.TypeMistmatch = TypeMistmatch;
|
|
223
|
-
class ExprString extends ExpressionDef {
|
|
224
|
-
constructor(value) {
|
|
225
|
-
super();
|
|
226
|
-
this.value = value;
|
|
227
|
-
this.elementType = "string literal";
|
|
228
|
-
}
|
|
229
|
-
getExpression(_fs) {
|
|
230
|
-
return { ...index_1.FT.stringT, value: [this.value] };
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
exports.ExprString = ExprString;
|
|
234
|
-
class ExprNumber extends ExpressionDef {
|
|
235
|
-
constructor(n) {
|
|
236
|
-
super();
|
|
237
|
-
this.n = n;
|
|
238
|
-
this.elementType = "numeric literal";
|
|
239
|
-
}
|
|
240
|
-
getExpression(_fs) {
|
|
241
|
-
return this.constantExpression();
|
|
242
|
-
}
|
|
243
|
-
constantExpression() {
|
|
244
|
-
return { ...index_1.FT.numberT, value: [this.n] };
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
exports.ExprNumber = ExprNumber;
|
|
248
|
-
class ExprRegEx extends ExpressionDef {
|
|
249
|
-
constructor(regex) {
|
|
250
|
-
super();
|
|
251
|
-
this.regex = regex;
|
|
252
|
-
this.elementType = "regular expression literal";
|
|
253
|
-
}
|
|
254
|
-
getExpression() {
|
|
255
|
-
return {
|
|
256
|
-
dataType: "regular expression",
|
|
257
|
-
aggregate: false,
|
|
258
|
-
value: [`r'${this.regex}'`],
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
exports.ExprRegEx = ExprRegEx;
|
|
263
|
-
class ExprTime extends ExpressionDef {
|
|
264
|
-
constructor(timeType, value, aggregate = false) {
|
|
265
|
-
super();
|
|
266
|
-
this.elementType = "timestampOrDate";
|
|
267
|
-
this.elementType = timeType;
|
|
268
|
-
this.translationValue = {
|
|
269
|
-
dataType: timeType,
|
|
270
|
-
aggregate: aggregate,
|
|
271
|
-
value: typeof value === "string" ? [value] : value,
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
getExpression(_fs) {
|
|
275
|
-
return this.translationValue;
|
|
276
|
-
}
|
|
277
|
-
static fromValue(timeType, expr) {
|
|
278
|
-
let value = expr.value;
|
|
279
|
-
if (timeType != expr.dataType) {
|
|
280
|
-
const toTs = {
|
|
281
|
-
type: "dialect",
|
|
282
|
-
function: "cast",
|
|
283
|
-
safe: false,
|
|
284
|
-
dstType: timeType,
|
|
285
|
-
expr: expr.value,
|
|
286
|
-
};
|
|
287
|
-
if ((0, malloy_types_1.isTimeFieldType)(expr.dataType)) {
|
|
288
|
-
toTs.srcType = expr.dataType;
|
|
289
|
-
}
|
|
290
|
-
value = (0, index_1.compressExpr)([toTs]);
|
|
291
|
-
}
|
|
292
|
-
return new ExprTime(timeType, value, expr.aggregate);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
exports.ExprTime = ExprTime;
|
|
296
|
-
class Unary extends ExpressionDef {
|
|
297
|
-
constructor(expr) {
|
|
298
|
-
super({ expr });
|
|
299
|
-
this.expr = expr;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
class ExprNot extends Unary {
|
|
303
|
-
constructor(expr) {
|
|
304
|
-
super(expr);
|
|
305
|
-
this.elementType = "not";
|
|
306
|
-
this.legalChildTypes = [index_1.FT.boolT, index_1.FT.nullT];
|
|
307
|
-
}
|
|
308
|
-
getExpression(fs) {
|
|
309
|
-
const notThis = this.expr.getExpression(fs);
|
|
310
|
-
if (this.typeCheck(this.expr, notThis)) {
|
|
311
|
-
return {
|
|
312
|
-
...notThis,
|
|
313
|
-
dataType: "boolean",
|
|
314
|
-
value: (0, apply_expr_1.nullsafeNot)(notThis.value),
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
return (0, index_1.errorFor)("not requires boolean");
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
exports.ExprNot = ExprNot;
|
|
321
|
-
class Boolean extends ExpressionDef {
|
|
322
|
-
constructor(value) {
|
|
323
|
-
super();
|
|
324
|
-
this.value = value;
|
|
325
|
-
this.elementType = "boolean literal";
|
|
326
|
-
}
|
|
327
|
-
getExpression() {
|
|
328
|
-
return { ...index_1.FT.boolT, value: [this.value] };
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
exports.Boolean = Boolean;
|
|
332
|
-
class BinaryBoolean extends ExpressionDef {
|
|
333
|
-
constructor(left, op, right) {
|
|
334
|
-
super({ left, right });
|
|
335
|
-
this.left = left;
|
|
336
|
-
this.op = op;
|
|
337
|
-
this.right = right;
|
|
338
|
-
this.elementType = "abstract boolean binary";
|
|
339
|
-
this.legalChildTypes = [index_1.FT.boolT];
|
|
340
|
-
}
|
|
341
|
-
getExpression(fs) {
|
|
342
|
-
const left = this.left.getExpression(fs);
|
|
343
|
-
const right = this.right.getExpression(fs);
|
|
344
|
-
if (this.typeCheck(this.left, left) && this.typeCheck(this.right, right)) {
|
|
345
|
-
return {
|
|
346
|
-
dataType: "boolean",
|
|
347
|
-
aggregate: left.aggregate || right.aggregate,
|
|
348
|
-
value: (0, index_1.compose)(left.value, this.op, right.value),
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
return (0, index_1.errorFor)("logial required boolean");
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
exports.BinaryBoolean = BinaryBoolean;
|
|
355
|
-
class ExprLogicalOp extends BinaryBoolean {
|
|
356
|
-
constructor() {
|
|
357
|
-
super(...arguments);
|
|
358
|
-
this.elementType = "logical operator";
|
|
359
|
-
this.legalChildTypes = [index_1.FT.boolT, { ...index_1.FT.boolT, aggregate: true }];
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
exports.ExprLogicalOp = ExprLogicalOp;
|
|
363
|
-
class ExprIdReference extends ExpressionDef {
|
|
364
|
-
constructor(fieldReference) {
|
|
365
|
-
super();
|
|
366
|
-
this.fieldReference = fieldReference;
|
|
367
|
-
this.elementType = "ExpressionIdReference";
|
|
368
|
-
this.has({ fieldPath: fieldReference });
|
|
369
|
-
}
|
|
370
|
-
get refString() {
|
|
371
|
-
return this.fieldReference.refString;
|
|
372
|
-
}
|
|
373
|
-
getExpression(fs) {
|
|
374
|
-
const def = this.fieldReference.getField(fs);
|
|
375
|
-
if (def.found) {
|
|
376
|
-
// TODO if type is a query or a struct this should fail nicely
|
|
377
|
-
const typeMixin = def.found.type();
|
|
378
|
-
const dataType = typeMixin.type;
|
|
379
|
-
const aggregate = !!typeMixin.aggregate;
|
|
380
|
-
const value = [{ type: def.found.refType, path: this.refString }];
|
|
381
|
-
return { dataType, aggregate, value };
|
|
382
|
-
}
|
|
383
|
-
this.log(def.error);
|
|
384
|
-
return (0, index_1.errorFor)(def.error);
|
|
385
|
-
}
|
|
386
|
-
apply(fs, op, expr) {
|
|
387
|
-
const entry = this.fieldReference.getField(fs).found;
|
|
388
|
-
if (entry instanceof space_field_1.SpaceParam) {
|
|
389
|
-
const cParam = entry.parameter();
|
|
390
|
-
if ((0, malloy_types_1.isConditionParameter)(cParam)) {
|
|
391
|
-
const lval = expr.getExpression(fs);
|
|
392
|
-
return {
|
|
393
|
-
dataType: "boolean",
|
|
394
|
-
aggregate: lval.aggregate,
|
|
395
|
-
value: [
|
|
396
|
-
{
|
|
397
|
-
type: "apply",
|
|
398
|
-
value: lval.value,
|
|
399
|
-
to: [{ type: "parameter", path: this.refString }],
|
|
400
|
-
},
|
|
401
|
-
],
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
return super.apply(fs, op, expr);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
exports.ExprIdReference = ExprIdReference;
|
|
409
|
-
class ExprNULL extends ExpressionDef {
|
|
410
|
-
constructor() {
|
|
411
|
-
super(...arguments);
|
|
412
|
-
this.elementType = "NULL";
|
|
413
|
-
}
|
|
414
|
-
getExpression() {
|
|
415
|
-
return {
|
|
416
|
-
dataType: "null",
|
|
417
|
-
value: ["NULL"],
|
|
418
|
-
aggregate: false,
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
exports.ExprNULL = ExprNULL;
|
|
423
|
-
class ExprParens extends ExpressionDef {
|
|
424
|
-
constructor(expr) {
|
|
425
|
-
super({ expr });
|
|
426
|
-
this.expr = expr;
|
|
427
|
-
this.elementType = "(expression)";
|
|
428
|
-
}
|
|
429
|
-
apply(fs, op, expr) {
|
|
430
|
-
return this.expr.apply(fs, op, expr);
|
|
431
|
-
}
|
|
432
|
-
requestExpression(fs) {
|
|
433
|
-
return this.expr.requestExpression(fs);
|
|
434
|
-
}
|
|
435
|
-
getExpression(fs) {
|
|
436
|
-
const subExpr = this.expr.getExpression(fs);
|
|
437
|
-
return { ...subExpr, value: ["(", ...subExpr.value, ")"] };
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
exports.ExprParens = ExprParens;
|
|
441
|
-
class ExprMinus extends ExpressionDef {
|
|
442
|
-
constructor(expr) {
|
|
443
|
-
super({ expr });
|
|
444
|
-
this.expr = expr;
|
|
445
|
-
this.elementType = "unary minus";
|
|
446
|
-
this.legalChildTypes = [index_1.FT.numberT];
|
|
447
|
-
}
|
|
448
|
-
getExpression(fs) {
|
|
449
|
-
const expr = this.expr.getExpression(fs);
|
|
450
|
-
if (this.typeCheck(this.expr, expr)) {
|
|
451
|
-
if (expr.value.length > 1) {
|
|
452
|
-
return { ...expr, value: ["-(", ...expr.value, ")"] };
|
|
453
|
-
}
|
|
454
|
-
return { ...expr, value: ["-", ...expr.value] };
|
|
455
|
-
}
|
|
456
|
-
return (0, index_1.errorFor)("negate requires number");
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
exports.ExprMinus = ExprMinus;
|
|
460
|
-
class BinaryNumeric extends ExpressionDef {
|
|
461
|
-
constructor(left, op, right) {
|
|
462
|
-
super({ left, right });
|
|
463
|
-
this.left = left;
|
|
464
|
-
this.op = op;
|
|
465
|
-
this.right = right;
|
|
466
|
-
this.elementType = "numeric binary abstract";
|
|
467
|
-
this.legalChildTypes = [index_1.FT.numberT];
|
|
468
|
-
}
|
|
469
|
-
getExpression(fs) {
|
|
470
|
-
return this.right.apply(fs, this.op, this.left);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
exports.BinaryNumeric = BinaryNumeric;
|
|
474
|
-
class ExprAddSub extends BinaryNumeric {
|
|
475
|
-
constructor() {
|
|
476
|
-
super(...arguments);
|
|
477
|
-
this.elementType = "+-";
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
exports.ExprAddSub = ExprAddSub;
|
|
481
|
-
class ExprMulDiv extends BinaryNumeric {
|
|
482
|
-
constructor() {
|
|
483
|
-
super(...arguments);
|
|
484
|
-
this.elementType = "*/";
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
exports.ExprMulDiv = ExprMulDiv;
|
|
488
|
-
class ExprAlternationTree extends BinaryBoolean {
|
|
489
|
-
constructor(left, op, right) {
|
|
490
|
-
super(left, op, right);
|
|
491
|
-
this.elementType = "alternation";
|
|
492
|
-
this.elementType = `${op}alternation${op}`;
|
|
493
|
-
}
|
|
494
|
-
apply(fs, applyOp, expr) {
|
|
495
|
-
const choice1 = this.left.apply(fs, applyOp, expr);
|
|
496
|
-
const choice2 = this.right.apply(fs, applyOp, expr);
|
|
497
|
-
return {
|
|
498
|
-
dataType: "boolean",
|
|
499
|
-
aggregate: choice1.aggregate || choice2.aggregate,
|
|
500
|
-
value: (0, index_1.compose)(choice1.value, this.op === "&" ? "and" : "or", choice2.value),
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
requestExpression(_fs) {
|
|
504
|
-
return undefined;
|
|
505
|
-
}
|
|
506
|
-
getExpression(_fs) {
|
|
507
|
-
this.log(`Alternation tree has no value`);
|
|
508
|
-
return (0, index_1.errorFor)("no value from alternation tree");
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
exports.ExprAlternationTree = ExprAlternationTree;
|
|
512
|
-
class ExprAggregateFunction extends ExpressionDef {
|
|
513
|
-
constructor(func, expr) {
|
|
514
|
-
super();
|
|
515
|
-
this.func = func;
|
|
516
|
-
this.legalChildTypes = [index_1.FT.numberT];
|
|
517
|
-
this.elementType = func;
|
|
518
|
-
if (expr) {
|
|
519
|
-
this.expr = expr;
|
|
520
|
-
this.has({ expr });
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
returns(_forExpression) {
|
|
524
|
-
return "number";
|
|
525
|
-
}
|
|
526
|
-
getExpression(fs) {
|
|
527
|
-
var _a, _b;
|
|
528
|
-
let exprVal = (_a = this.expr) === null || _a === void 0 ? void 0 : _a.getExpression(fs);
|
|
529
|
-
let structPath = (_b = this.source) === null || _b === void 0 ? void 0 : _b.refString;
|
|
530
|
-
if (this.source) {
|
|
531
|
-
const sourceFoot = this.source.getField(fs).found;
|
|
532
|
-
if (sourceFoot) {
|
|
533
|
-
const footType = sourceFoot.type();
|
|
534
|
-
if ((0, malloy_types_1.isAtomicFieldType)(footType.type)) {
|
|
535
|
-
exprVal = {
|
|
536
|
-
dataType: footType.type,
|
|
537
|
-
aggregate: !!footType.aggregate,
|
|
538
|
-
value: [{ type: "field", path: this.source.refString }],
|
|
539
|
-
};
|
|
540
|
-
structPath = this.source.sourceString;
|
|
541
|
-
}
|
|
542
|
-
else {
|
|
543
|
-
if (!(sourceFoot instanceof space_field_1.StructSpaceField)) {
|
|
544
|
-
this.log(`Aggregate source cannot be a ${footType.type}`);
|
|
545
|
-
return (0, index_1.errorFor)(`Aggregate source cannot be a ${footType.type}`);
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
else {
|
|
550
|
-
this.log(`Reference to undefined value ${this.source.refString}`);
|
|
551
|
-
return (0, index_1.errorFor)(`Reference to undefined value ${this.source.refString}`);
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
if (exprVal === undefined) {
|
|
555
|
-
this.log("Missing expression for aggregate function");
|
|
556
|
-
return (0, index_1.errorFor)("agggregate without expression");
|
|
557
|
-
}
|
|
558
|
-
if (this.typeCheck(this.expr || this, { ...exprVal, aggregate: false })) {
|
|
559
|
-
const f = {
|
|
560
|
-
type: "aggregate",
|
|
561
|
-
function: this.func,
|
|
562
|
-
e: exprVal.value,
|
|
563
|
-
};
|
|
564
|
-
if (structPath) {
|
|
565
|
-
f.structPath = structPath;
|
|
566
|
-
}
|
|
567
|
-
return {
|
|
568
|
-
dataType: this.returns(exprVal),
|
|
569
|
-
aggregate: true,
|
|
570
|
-
value: [f],
|
|
571
|
-
};
|
|
572
|
-
}
|
|
573
|
-
return (0, index_1.errorFor)("aggregate type check");
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
class ExprMin extends ExprAggregateFunction {
|
|
577
|
-
constructor(expr) {
|
|
578
|
-
super("min", expr);
|
|
579
|
-
this.legalChildTypes = [index_1.FT.numberT, index_1.FT.stringT, index_1.FT.dateT, index_1.FT.timestampT];
|
|
580
|
-
}
|
|
581
|
-
returns(forExpression) {
|
|
582
|
-
return forExpression.dataType;
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
exports.ExprMin = ExprMin;
|
|
586
|
-
class ExprMax extends ExprAggregateFunction {
|
|
587
|
-
constructor(expr) {
|
|
588
|
-
super("max", expr);
|
|
589
|
-
this.legalChildTypes = [index_1.FT.numberT, index_1.FT.stringT, index_1.FT.dateT, index_1.FT.timestampT];
|
|
590
|
-
}
|
|
591
|
-
returns(forExpression) {
|
|
592
|
-
return forExpression.dataType;
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
exports.ExprMax = ExprMax;
|
|
596
|
-
class ExprCountDistinct extends ExprAggregateFunction {
|
|
597
|
-
constructor(expr) {
|
|
598
|
-
super("count_distinct", expr);
|
|
599
|
-
this.legalChildTypes = [index_1.FT.numberT, index_1.FT.stringT, index_1.FT.dateT, index_1.FT.timestampT];
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
exports.ExprCountDistinct = ExprCountDistinct;
|
|
603
|
-
class ExprAsymmetric extends ExprAggregateFunction {
|
|
604
|
-
constructor(func, expr, source) {
|
|
605
|
-
super(func, expr);
|
|
606
|
-
this.func = func;
|
|
607
|
-
this.expr = expr;
|
|
608
|
-
this.source = source;
|
|
609
|
-
this.has({ source });
|
|
610
|
-
}
|
|
611
|
-
defaultFieldName() {
|
|
612
|
-
if (this.source && this.expr === undefined) {
|
|
613
|
-
const tag = this.source.nameString;
|
|
614
|
-
switch (this.func) {
|
|
615
|
-
case "sum":
|
|
616
|
-
return `total_${tag}`;
|
|
617
|
-
case "avg":
|
|
618
|
-
return `avg_${tag}`;
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
return undefined;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
class ExprCount extends ExprAggregateFunction {
|
|
625
|
-
constructor(source) {
|
|
626
|
-
super("count");
|
|
627
|
-
this.source = source;
|
|
628
|
-
this.elementType = "count";
|
|
629
|
-
this.has({ source });
|
|
630
|
-
}
|
|
631
|
-
defaultFieldName() {
|
|
632
|
-
if (this.source) {
|
|
633
|
-
return "count_" + this.source.nameString;
|
|
634
|
-
}
|
|
635
|
-
return undefined;
|
|
636
|
-
}
|
|
637
|
-
getExpression(_fs) {
|
|
638
|
-
const ret = {
|
|
639
|
-
type: "aggregate",
|
|
640
|
-
function: "count",
|
|
641
|
-
e: [],
|
|
642
|
-
};
|
|
643
|
-
if (this.source) {
|
|
644
|
-
ret.structPath = this.source.refString;
|
|
645
|
-
}
|
|
646
|
-
return {
|
|
647
|
-
dataType: "number",
|
|
648
|
-
aggregate: true,
|
|
649
|
-
value: [ret],
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
exports.ExprCount = ExprCount;
|
|
654
|
-
class ExprAvg extends ExprAsymmetric {
|
|
655
|
-
constructor(expr, source) {
|
|
656
|
-
super("avg", expr, source);
|
|
657
|
-
this.has({ source });
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
exports.ExprAvg = ExprAvg;
|
|
661
|
-
class ExprSum extends ExprAsymmetric {
|
|
662
|
-
constructor(expr, source) {
|
|
663
|
-
super("sum", expr, source);
|
|
664
|
-
this.has({ source });
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
exports.ExprSum = ExprSum;
|
|
668
|
-
class ExprUngroup extends ExpressionDef {
|
|
669
|
-
constructor(control, expr, fields) {
|
|
670
|
-
super({ expr, fields });
|
|
671
|
-
this.control = control;
|
|
672
|
-
this.expr = expr;
|
|
673
|
-
this.fields = fields;
|
|
674
|
-
this.legalChildTypes = index_1.FT.anyAtomicT;
|
|
675
|
-
this.elementType = "ungroup";
|
|
676
|
-
}
|
|
677
|
-
returns(_forExpression) {
|
|
678
|
-
return "number";
|
|
679
|
-
}
|
|
680
|
-
getExpression(fs) {
|
|
681
|
-
const exprVal = this.expr.getExpression(fs);
|
|
682
|
-
if (!exprVal.aggregate) {
|
|
683
|
-
this.expr.log(`${this.control}() expression must be an aggregate`);
|
|
684
|
-
return (0, index_1.errorFor)("ungrouped scalar");
|
|
685
|
-
}
|
|
686
|
-
const ungroup = { type: this.control, e: exprVal.value };
|
|
687
|
-
if (this.typeCheck(this.expr, { ...exprVal, aggregate: false })) {
|
|
688
|
-
if (this.fields.length > 0) {
|
|
689
|
-
let qs = fs;
|
|
690
|
-
if (fs instanceof field_space_1.DefSpace) {
|
|
691
|
-
qs = fs.realFS;
|
|
692
|
-
}
|
|
693
|
-
if (!(qs instanceof field_space_1.QuerySpace)) {
|
|
694
|
-
this.log(`${this.control}() must be in a query -- weird internal error`);
|
|
695
|
-
return (0, index_1.errorFor)("ungroup query check");
|
|
696
|
-
}
|
|
697
|
-
const output = qs.result;
|
|
698
|
-
const dstFields = [];
|
|
699
|
-
const isExclude = this.control == "exclude";
|
|
700
|
-
for (const mustBeInOutput of this.fields) {
|
|
701
|
-
output.whenComplete(() => {
|
|
702
|
-
output.checkUngroup(mustBeInOutput, isExclude);
|
|
703
|
-
});
|
|
704
|
-
dstFields.push(mustBeInOutput.refString);
|
|
705
|
-
}
|
|
706
|
-
ungroup.fields = dstFields;
|
|
707
|
-
}
|
|
708
|
-
return {
|
|
709
|
-
dataType: this.returns(exprVal),
|
|
710
|
-
aggregate: true,
|
|
711
|
-
value: [ungroup],
|
|
712
|
-
};
|
|
713
|
-
}
|
|
714
|
-
this.log(`${this.control}() incompatible type`);
|
|
715
|
-
return (0, index_1.errorFor)("ungrouped type check");
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
exports.ExprUngroup = ExprUngroup;
|
|
719
|
-
class WhenClause extends ExpressionDef {
|
|
720
|
-
constructor(whenThis, thenThis) {
|
|
721
|
-
super({ whenThis, thenThis });
|
|
722
|
-
this.whenThis = whenThis;
|
|
723
|
-
this.thenThis = thenThis;
|
|
724
|
-
this.elementType = "when clause";
|
|
725
|
-
}
|
|
726
|
-
getExpression(_fs) {
|
|
727
|
-
throw new Error("expression did something unxpected with 'WHEN'");
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
exports.WhenClause = WhenClause;
|
|
731
|
-
class ExprCase extends ExpressionDef {
|
|
732
|
-
constructor(when, elseClause) {
|
|
733
|
-
super({ when });
|
|
734
|
-
this.when = when;
|
|
735
|
-
this.elseClause = elseClause;
|
|
736
|
-
this.elementType = "case statement";
|
|
737
|
-
this.has({ elseClause });
|
|
738
|
-
}
|
|
739
|
-
getExpression(fs) {
|
|
740
|
-
let retType;
|
|
741
|
-
let aggregate = false;
|
|
742
|
-
const caseExpr = ["CASE "];
|
|
743
|
-
for (const clause of this.when) {
|
|
744
|
-
const whenExpr = clause.whenThis.getExpression(fs);
|
|
745
|
-
const thenExpr = clause.thenThis.getExpression(fs);
|
|
746
|
-
aggregate || (aggregate = whenExpr.aggregate || thenExpr.aggregate);
|
|
747
|
-
if (thenExpr.dataType !== "null") {
|
|
748
|
-
if (retType && !index_1.FT.typeEq(retType, thenExpr)) {
|
|
749
|
-
this.log(`Mismatched THEN clause types, ${index_1.FT.inspect(retType, thenExpr)}`);
|
|
750
|
-
return (0, index_1.errorFor)("then typecheck");
|
|
751
|
-
}
|
|
752
|
-
else {
|
|
753
|
-
retType = thenExpr;
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
caseExpr.push("WHEN ", ...whenExpr.value, " THEN ", ...thenExpr.value);
|
|
757
|
-
}
|
|
758
|
-
if (this.elseClause) {
|
|
759
|
-
const elseExpr = this.elseClause.getExpression(fs);
|
|
760
|
-
aggregate || (aggregate = elseExpr.aggregate);
|
|
761
|
-
caseExpr.push(" ELSE ", ...elseExpr.value);
|
|
762
|
-
if (elseExpr.dataType !== "null") {
|
|
763
|
-
if (retType && !index_1.FT.typeEq(retType, elseExpr)) {
|
|
764
|
-
this.log(`Mismatched ELSE clause type, ${index_1.FT.inspect(retType, elseExpr)}`);
|
|
765
|
-
return (0, index_1.errorFor)("else typecheck");
|
|
766
|
-
}
|
|
767
|
-
else {
|
|
768
|
-
retType = elseExpr;
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
if (retType === undefined) {
|
|
773
|
-
this.log("case statement type not computable");
|
|
774
|
-
return (0, index_1.errorFor)("typeless case");
|
|
775
|
-
}
|
|
776
|
-
caseExpr.push(" END");
|
|
777
|
-
return {
|
|
778
|
-
dataType: retType.dataType,
|
|
779
|
-
aggregate: aggregate,
|
|
780
|
-
value: caseExpr,
|
|
781
|
-
};
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
exports.ExprCase = ExprCase;
|
|
785
|
-
class ExprFilter extends ExpressionDef {
|
|
786
|
-
constructor(expr, filter) {
|
|
787
|
-
super({ expr, filter });
|
|
788
|
-
this.expr = expr;
|
|
789
|
-
this.filter = filter;
|
|
790
|
-
this.elementType = "filtered expression";
|
|
791
|
-
this.legalChildTypes = index_1.FT.anyAtomicT;
|
|
792
|
-
}
|
|
793
|
-
getExpression(fs) {
|
|
794
|
-
const testList = this.filter.getFilterList(fs);
|
|
795
|
-
const resultExpr = this.expr.getExpression(fs);
|
|
796
|
-
for (const cond of testList) {
|
|
797
|
-
if (cond.aggregate) {
|
|
798
|
-
this.filter.log("Cannot filter a field with an aggregate computation");
|
|
799
|
-
return (0, index_1.errorFor)("no filter on aggregate");
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
if (!resultExpr.aggregate) {
|
|
803
|
-
// TODO could log a warning, but I have a problem with the
|
|
804
|
-
// idea of warnings, so for now ...
|
|
805
|
-
return resultExpr;
|
|
806
|
-
}
|
|
807
|
-
if (this.typeCheck(this.expr, { ...resultExpr, aggregate: false })) {
|
|
808
|
-
return {
|
|
809
|
-
...resultExpr,
|
|
810
|
-
value: [
|
|
811
|
-
{
|
|
812
|
-
type: "filterExpression",
|
|
813
|
-
e: resultExpr.value,
|
|
814
|
-
filterList: testList,
|
|
815
|
-
},
|
|
816
|
-
],
|
|
817
|
-
};
|
|
818
|
-
}
|
|
819
|
-
this.expr.log(`Cannot filter '${resultExpr.dataType}' data`);
|
|
820
|
-
return (0, index_1.errorFor)("cannot filter type");
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
exports.ExprFilter = ExprFilter;
|
|
824
|
-
function isCastType(t) {
|
|
825
|
-
return ["string", "number", "boolean", "date", "timestamp"].includes(t);
|
|
826
|
-
}
|
|
827
|
-
exports.isCastType = isCastType;
|
|
828
|
-
class ExprCast extends ExpressionDef {
|
|
829
|
-
constructor(expr, castType, safe = false) {
|
|
830
|
-
super({ expr });
|
|
831
|
-
this.expr = expr;
|
|
832
|
-
this.castType = castType;
|
|
833
|
-
this.safe = safe;
|
|
834
|
-
this.elementType = "cast";
|
|
835
|
-
}
|
|
836
|
-
getExpression(fs) {
|
|
837
|
-
const expr = this.expr.getExpression(fs);
|
|
838
|
-
return {
|
|
839
|
-
dataType: this.castType,
|
|
840
|
-
aggregate: expr.aggregate,
|
|
841
|
-
value: (0, index_1.compressExpr)((0, time_utils_1.castTo)(this.castType, expr.value, this.safe)),
|
|
842
|
-
};
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
exports.ExprCast = ExprCast;
|
|
846
|
-
class TopBy extends index_1.MalloyElement {
|
|
847
|
-
constructor(by) {
|
|
848
|
-
super();
|
|
849
|
-
this.by = by;
|
|
850
|
-
this.elementType = "topBy";
|
|
851
|
-
if (by instanceof ExpressionDef) {
|
|
852
|
-
this.has({ by });
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
getBy(fs) {
|
|
856
|
-
if (this.by instanceof ExpressionDef) {
|
|
857
|
-
const byExpr = this.by.getExpression(fs);
|
|
858
|
-
if (!byExpr.aggregate) {
|
|
859
|
-
this.log("top by expression must be an aggregate");
|
|
860
|
-
}
|
|
861
|
-
return { by: "expression", e: (0, index_1.compressExpr)(byExpr.value) };
|
|
862
|
-
}
|
|
863
|
-
return { by: "name", name: this.by };
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
exports.TopBy = TopBy;
|
|
867
|
-
class Range extends ExpressionDef {
|
|
868
|
-
constructor(first, last) {
|
|
869
|
-
super({ first, last });
|
|
870
|
-
this.first = first;
|
|
871
|
-
this.last = last;
|
|
872
|
-
this.elementType = "range";
|
|
873
|
-
}
|
|
874
|
-
apply(fs, op, expr) {
|
|
875
|
-
switch (op) {
|
|
876
|
-
case "=":
|
|
877
|
-
case "!=": {
|
|
878
|
-
const op1 = op === "=" ? ">=" : "<";
|
|
879
|
-
const op2 = op === "=" ? "and" : "or";
|
|
880
|
-
const op3 = op === "=" ? "<" : ">=";
|
|
881
|
-
const fromValue = this.first.apply(fs, op1, expr);
|
|
882
|
-
const toValue = this.last.apply(fs, op3, expr);
|
|
883
|
-
return {
|
|
884
|
-
dataType: "boolean",
|
|
885
|
-
aggregate: fromValue.aggregate || toValue.aggregate,
|
|
886
|
-
value: (0, index_1.compose)(fromValue.value, op2, toValue.value),
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
/**
|
|
890
|
-
* This is a little surprising, but is actually how you comapre a
|
|
891
|
-
* value to a range ...
|
|
892
|
-
*
|
|
893
|
-
* val > begin to end val >= end
|
|
894
|
-
* val >= begin to end val >= begin
|
|
895
|
-
* val < begin to end val < begin
|
|
896
|
-
* val <= begin to end val < end
|
|
897
|
-
*/
|
|
898
|
-
case ">":
|
|
899
|
-
return this.last.apply(fs, ">=", expr);
|
|
900
|
-
case ">=":
|
|
901
|
-
return this.first.apply(fs, ">=", expr);
|
|
902
|
-
case "<":
|
|
903
|
-
return this.first.apply(fs, "<", expr);
|
|
904
|
-
case "<=":
|
|
905
|
-
return this.last.apply(fs, "<", expr);
|
|
906
|
-
}
|
|
907
|
-
throw new Error("mysterious error in range computation");
|
|
908
|
-
}
|
|
909
|
-
requestExpression(_fs) {
|
|
910
|
-
return undefined;
|
|
911
|
-
}
|
|
912
|
-
getExpression(_fs) {
|
|
913
|
-
return (0, index_1.errorFor)("a range is not a value");
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
exports.Range = Range;
|
|
917
|
-
class PickWhen extends index_1.MalloyElement {
|
|
918
|
-
constructor(pick, when) {
|
|
919
|
-
super({ when });
|
|
920
|
-
this.pick = pick;
|
|
921
|
-
this.when = when;
|
|
922
|
-
this.elementType = "pickWhen";
|
|
923
|
-
this.has({ pick });
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
exports.PickWhen = PickWhen;
|
|
927
|
-
class Pick extends ExpressionDef {
|
|
928
|
-
constructor(choices, elsePick) {
|
|
929
|
-
super({ choices });
|
|
930
|
-
this.choices = choices;
|
|
931
|
-
this.elsePick = elsePick;
|
|
932
|
-
this.elementType = "pick";
|
|
933
|
-
this.has({ elsePick });
|
|
934
|
-
}
|
|
935
|
-
requestExpression(fs) {
|
|
936
|
-
// pick statements are sometimes partials which must be applied
|
|
937
|
-
// and sometimes have a value.
|
|
938
|
-
if (this.elsePick === undefined) {
|
|
939
|
-
return undefined;
|
|
940
|
-
}
|
|
941
|
-
for (const c of this.choices) {
|
|
942
|
-
if (c.pick == undefined) {
|
|
943
|
-
return undefined;
|
|
944
|
-
}
|
|
945
|
-
const whenResp = c.when.requestExpression(fs);
|
|
946
|
-
if (whenResp == undefined || whenResp.dataType != "boolean") {
|
|
947
|
-
// If when is not a boolean, we'll treat it like a partial compare
|
|
948
|
-
return undefined;
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
return this.getExpression(fs);
|
|
952
|
-
}
|
|
953
|
-
apply(fs, op, expr) {
|
|
954
|
-
const caseValue = ["CASE"];
|
|
955
|
-
let returnType;
|
|
956
|
-
let anyAggregate = false;
|
|
957
|
-
for (const choice of this.choices) {
|
|
958
|
-
const whenExpr = choice.when.apply(fs, "=", expr);
|
|
959
|
-
const thenExpr = choice.pick
|
|
960
|
-
? choice.pick.getExpression(fs)
|
|
961
|
-
: expr.getExpression(fs);
|
|
962
|
-
anyAggregate || (anyAggregate = whenExpr.aggregate || thenExpr.aggregate);
|
|
963
|
-
if (returnType) {
|
|
964
|
-
if (!index_1.FT.typeEq(returnType, thenExpr, true)) {
|
|
965
|
-
const whenType = index_1.FT.inspect(thenExpr);
|
|
966
|
-
this.log(`pick type '${whenType}', expected '${returnType.dataType}'`);
|
|
967
|
-
return (0, index_1.errorFor)("pick when type");
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
else {
|
|
971
|
-
returnType = thenExpr;
|
|
972
|
-
}
|
|
973
|
-
caseValue.push(" WHEN ", ...whenExpr.value, " THEN ", ...thenExpr.value);
|
|
974
|
-
}
|
|
975
|
-
const elsePart = this.elsePick || expr;
|
|
976
|
-
const elseVal = elsePart.getExpression(fs);
|
|
977
|
-
returnType || (returnType = elseVal);
|
|
978
|
-
if (!index_1.FT.typeEq(returnType, elseVal, true)) {
|
|
979
|
-
const errSrc = this.elsePick ? "else" : "pick default";
|
|
980
|
-
this.log(`${errSrc} type '${index_1.FT.inspect(elseVal)}', expected '${returnType.dataType}'`);
|
|
981
|
-
return (0, index_1.errorFor)("pick else type");
|
|
982
|
-
}
|
|
983
|
-
return {
|
|
984
|
-
dataType: returnType.dataType,
|
|
985
|
-
aggregate: anyAggregate || elseVal.aggregate,
|
|
986
|
-
value: (0, index_1.compressExpr)([...caseValue, " ELSE ", ...elseVal.value, " END"]),
|
|
987
|
-
};
|
|
988
|
-
}
|
|
989
|
-
getExpression(fs) {
|
|
990
|
-
if (this.elsePick === undefined) {
|
|
991
|
-
this.log("pick incomplete, missing 'else'");
|
|
992
|
-
return (0, index_1.errorFor)("no value for partial pick");
|
|
993
|
-
}
|
|
994
|
-
const choiceValues = [];
|
|
995
|
-
for (const c of this.choices) {
|
|
996
|
-
if (c.pick === undefined) {
|
|
997
|
-
this.log("pick with no value can only be used with apply");
|
|
998
|
-
return (0, index_1.errorFor)("no value for partial pick");
|
|
999
|
-
}
|
|
1000
|
-
const pickWhen = c.when.requestExpression(fs);
|
|
1001
|
-
if (pickWhen === undefined) {
|
|
1002
|
-
this.log("pick with partial when can only be used with apply");
|
|
1003
|
-
return (0, index_1.errorFor)("partial when");
|
|
1004
|
-
}
|
|
1005
|
-
choiceValues.push({
|
|
1006
|
-
pick: c.pick.getExpression(fs),
|
|
1007
|
-
when: c.when.getExpression(fs),
|
|
1008
|
-
});
|
|
1009
|
-
}
|
|
1010
|
-
const returnType = choiceValues[0].pick;
|
|
1011
|
-
const caseValue = ["CASE"];
|
|
1012
|
-
let anyAggregate = returnType.aggregate;
|
|
1013
|
-
for (const aChoice of choiceValues) {
|
|
1014
|
-
if (!index_1.FT.typeEq(aChoice.when, index_1.FT.boolT)) {
|
|
1015
|
-
this.log(`when expression must be boolean, not '${index_1.FT.inspect(aChoice.when)}`);
|
|
1016
|
-
return (0, index_1.errorFor)("pick when type");
|
|
1017
|
-
}
|
|
1018
|
-
if (!index_1.FT.typeEq(returnType, aChoice.pick, true)) {
|
|
1019
|
-
const whenType = index_1.FT.inspect(aChoice.pick);
|
|
1020
|
-
this.log(`pick type '${whenType}', expected '${returnType.dataType}'`);
|
|
1021
|
-
return (0, index_1.errorFor)("pick value type");
|
|
1022
|
-
}
|
|
1023
|
-
anyAggregate || (anyAggregate = aChoice.pick.aggregate || aChoice.when.aggregate);
|
|
1024
|
-
caseValue.push(" WHEN ", ...aChoice.when.value, " THEN ", ...aChoice.pick.value);
|
|
1025
|
-
}
|
|
1026
|
-
const defVal = this.elsePick.getExpression(fs);
|
|
1027
|
-
anyAggregate || (anyAggregate = defVal.aggregate);
|
|
1028
|
-
if (!index_1.FT.typeEq(returnType, defVal, true)) {
|
|
1029
|
-
this.elsePick.log(`else type '${index_1.FT.inspect(defVal)}', expected '${returnType.dataType}'`);
|
|
1030
|
-
return (0, index_1.errorFor)("pick value type mismatch");
|
|
1031
|
-
}
|
|
1032
|
-
caseValue.push(" ELSE ", ...defVal.value, " END");
|
|
1033
|
-
return {
|
|
1034
|
-
dataType: returnType.dataType,
|
|
1035
|
-
aggregate: !!anyAggregate,
|
|
1036
|
-
value: (0, index_1.compressExpr)(caseValue),
|
|
1037
|
-
};
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
exports.Pick = Pick;
|
|
1041
|
-
//# sourceMappingURL=ast-expr.js.map
|