@malloydata/malloy 0.0.43-dev230621212545 → 0.0.43-dev230622180905
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-utils.js +1 -1
- package/dist/lang/ast/expressions/expr-coalesce.js +4 -0
- package/dist/lang/ast/expressions/expr-func.js +9 -34
- package/dist/lang/ast/expressions/expr-granular-time.js +14 -2
- package/dist/lang/ast/expressions/expr-minus.js +2 -2
- package/dist/lang/ast/expressions/expr-time-extract.js +22 -4
- package/dist/lang/ast/expressions/for-range.d.ts +1 -1
- package/dist/lang/ast/expressions/for-range.js +10 -2
- package/dist/lang/ast/expressions/pick-when.js +5 -1
- package/dist/lang/ast/field-space/query-spaces.js +3 -6
- package/dist/lang/ast/field-space/reference-field.js +1 -1
- package/dist/lang/ast/fragtype-utils.d.ts +1 -1
- package/dist/lang/ast/fragtype-utils.js +3 -2
- package/dist/lang/ast/parameters/constant-parameter.js +1 -1
- package/dist/lang/ast/query-items/field-declaration.js +5 -8
- package/dist/lang/ast/types/expression-def.js +29 -3
- package/dist/lang/ast/types/malloy-element.d.ts +2 -1
- package/dist/lang/ast/types/malloy-element.js +2 -10
- package/dist/lang/ast/types/space-param.js +1 -1
- package/dist/lang/parse-error-handler.js +1 -0
- package/dist/lang/parse-log.d.ts +1 -1
- package/dist/lang/parse-malloy.d.ts +13 -7
- package/dist/lang/parse-malloy.js +28 -17
- package/dist/lang/test/parse-expects.d.ts +21 -2
- package/dist/lang/test/parse-expects.js +97 -73
- package/dist/lang/test/parse.spec.js +269 -20
- package/dist/lang/test/sql-block.spec.js +1 -1
- package/dist/lang/test/test-translator.js +3 -0
- package/dist/lang/translate-response.d.ts +8 -8
- package/dist/malloy.d.ts +6 -4
- package/dist/malloy.js +14 -7
- package/dist/model/malloy_types.d.ts +6 -3
- package/package.json +1 -1
|
@@ -48,8 +48,12 @@ class ExprCoalesce extends expression_def_1.ExpressionDef {
|
|
|
48
48
|
* SQL, but I decided that is will happen when the "expressions are true
|
|
49
49
|
* trees" rewrite happens.
|
|
50
50
|
*/
|
|
51
|
+
if (!fragtype_utils_1.FT.typeEq(maybeNull, whenNull)) {
|
|
52
|
+
this.log(`Mismatched types for coalesce (${maybeNull.dataType}, ${whenNull.dataType})`);
|
|
53
|
+
}
|
|
51
54
|
return {
|
|
52
55
|
...whenNull,
|
|
56
|
+
dataType: maybeNull.dataType === 'error' ? whenNull.dataType : maybeNull.dataType,
|
|
53
57
|
expressionType: (0, model_1.maxExpressionType)(maybeNull.expressionType, whenNull.expressionType),
|
|
54
58
|
value: (0, model_1.mkExpr) `COALESCE(${maybeNull.value},${whenNull.value})`,
|
|
55
59
|
evalSpace: (0, model_1.mergeEvalSpaces)(maybeNull.evalSpace, whenNull.evalSpace),
|
|
@@ -70,21 +70,14 @@ class ExprFunc extends expression_def_1.ExpressionDef {
|
|
|
70
70
|
const func = (_c = this.modelEntry(this.name.toLowerCase())) === null || _c === void 0 ? void 0 : _c.entry;
|
|
71
71
|
if (func === undefined) {
|
|
72
72
|
this.log(`Unknown function '${this.name}'. Use '${this.name}!(...)' to call a SQL function directly.`);
|
|
73
|
-
return
|
|
74
|
-
dataType: 'unknown',
|
|
75
|
-
expressionType: 'scalar',
|
|
76
|
-
value: [],
|
|
77
|
-
evalSpace: 'constant',
|
|
78
|
-
};
|
|
73
|
+
return (0, ast_utils_1.errorFor)('unknown function');
|
|
79
74
|
}
|
|
80
75
|
else if (func.type !== 'function') {
|
|
81
76
|
this.log(`Cannot call '${this.name}', which is of type ${func.type}`);
|
|
82
|
-
return
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
evalSpace: 'constant',
|
|
87
|
-
};
|
|
77
|
+
return (0, ast_utils_1.errorFor)('called non function');
|
|
78
|
+
}
|
|
79
|
+
if (func.name !== this.name) {
|
|
80
|
+
this.log(`Case insensitivity for function names is deprecated, use '${func.name}' instead`, 'warn');
|
|
88
81
|
}
|
|
89
82
|
// Find the 'implicit argument' for aggregate functions called like `some_join.some_field.agg(...args)`
|
|
90
83
|
// where the full arg list is `(some_field, ...args)`.
|
|
@@ -127,12 +120,7 @@ class ExprFunc extends expression_def_1.ExpressionDef {
|
|
|
127
120
|
this.log(`No matching overload for function ${this.name}(${argExprs
|
|
128
121
|
.map(e => e.dataType)
|
|
129
122
|
.join(', ')})`);
|
|
130
|
-
return
|
|
131
|
-
dataType: 'unknown',
|
|
132
|
-
expressionType: 'scalar',
|
|
133
|
-
value: [],
|
|
134
|
-
evalSpace: 'constant',
|
|
135
|
-
};
|
|
123
|
+
return (0, ast_utils_1.errorFor)('no matching overload');
|
|
136
124
|
}
|
|
137
125
|
const { overload, expressionTypeErrors, evalSpaceErrors, nullabilityErrors } = result;
|
|
138
126
|
// Report errors for expression type mismatch
|
|
@@ -171,12 +159,7 @@ class ExprFunc extends expression_def_1.ExpressionDef {
|
|
|
171
159
|
this.log(`Cannot call function ${this.name}(${argExprs
|
|
172
160
|
.map(e => e.dataType)
|
|
173
161
|
.join(', ')}) with source`);
|
|
174
|
-
return
|
|
175
|
-
dataType: 'unknown',
|
|
176
|
-
expressionType,
|
|
177
|
-
value: [],
|
|
178
|
-
evalSpace: 'constant',
|
|
179
|
-
};
|
|
162
|
+
return (0, ast_utils_1.errorFor)('cannot call with source');
|
|
180
163
|
}
|
|
181
164
|
const funcCall = [
|
|
182
165
|
{
|
|
@@ -189,12 +172,7 @@ class ExprFunc extends expression_def_1.ExpressionDef {
|
|
|
189
172
|
];
|
|
190
173
|
if (type.dataType === 'any') {
|
|
191
174
|
this.log(`Invalid return type ${type.dataType} for function '${this.name}'`);
|
|
192
|
-
return
|
|
193
|
-
dataType: 'unknown',
|
|
194
|
-
expressionType,
|
|
195
|
-
value: [],
|
|
196
|
-
evalSpace: 'constant',
|
|
197
|
-
};
|
|
175
|
+
return (0, ast_utils_1.errorFor)('invalid return type');
|
|
198
176
|
}
|
|
199
177
|
const maxEvalSpace = (0, malloy_types_1.mergeEvalSpaces)(...argExprs.map(e => e.evalSpace));
|
|
200
178
|
// If the merged eval space of all args is constant, the result is constant.
|
|
@@ -240,10 +218,7 @@ function findOverload(func, args) {
|
|
|
240
218
|
// does not make sense to limit function calls to not allow nulls, since have
|
|
241
219
|
// so little control over nullability.
|
|
242
220
|
arg.dataType === 'null' ||
|
|
243
|
-
|
|
244
|
-
// I think we may want to add an `error` type for nodes generated from errors,
|
|
245
|
-
// then make `error` propagate without generating more errors.
|
|
246
|
-
arg.dataType === 'unknown';
|
|
221
|
+
arg.dataType === 'error';
|
|
247
222
|
// Check expression type errors
|
|
248
223
|
if (paramT.expressionType) {
|
|
249
224
|
const expressionTypeMatch = (0, malloy_types_1.isExpressionTypeLEQ)(arg.expressionType, paramT.expressionType);
|
|
@@ -70,8 +70,20 @@ class ExprGranularTime extends expression_def_1.ExpressionDef {
|
|
|
70
70
|
}
|
|
71
71
|
return tsVal;
|
|
72
72
|
}
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
if (exprVal.dataType !== 'error') {
|
|
74
|
+
this.log(`Cannot do time truncation on type '${exprVal.dataType}'`);
|
|
75
|
+
}
|
|
76
|
+
const returnType = exprVal.dataType === 'error'
|
|
77
|
+
? (0, malloy_types_1.isDateUnit)(timeframe)
|
|
78
|
+
? 'date'
|
|
79
|
+
: 'timestamp'
|
|
80
|
+
: exprVal.dataType;
|
|
81
|
+
return {
|
|
82
|
+
...exprVal,
|
|
83
|
+
dataType: returnType,
|
|
84
|
+
value: (0, ast_utils_1.errorFor)('granularity typecheck').value,
|
|
85
|
+
evalSpace: 'constant',
|
|
86
|
+
};
|
|
75
87
|
}
|
|
76
88
|
apply(fs, op, left) {
|
|
77
89
|
return this.getRange(fs).apply(fs, op, left);
|
|
@@ -37,9 +37,9 @@ class ExprMinus extends expression_def_1.ExpressionDef {
|
|
|
37
37
|
const expr = this.expr.getExpression(fs);
|
|
38
38
|
if (this.typeCheck(this.expr, expr)) {
|
|
39
39
|
if (expr.value.length > 1) {
|
|
40
|
-
return { ...expr, value: ['-(', ...expr.value, ')'] };
|
|
40
|
+
return { ...expr, dataType: 'number', value: ['-(', ...expr.value, ')'] };
|
|
41
41
|
}
|
|
42
|
-
return { ...expr, value: ['-', ...expr.value] };
|
|
42
|
+
return { ...expr, dataType: 'number', value: ['-', ...expr.value] };
|
|
43
43
|
}
|
|
44
44
|
return (0, ast_utils_1.errorFor)('negate requires number');
|
|
45
45
|
}
|
|
@@ -54,6 +54,16 @@ class ExprTimeExtract extends expression_def_1.ExpressionDef {
|
|
|
54
54
|
if (from instanceof range_1.Range) {
|
|
55
55
|
let first = from.first.getExpression(fs);
|
|
56
56
|
let last = from.last.getExpression(fs);
|
|
57
|
+
const expressionType = (0, malloy_types_1.maxExpressionType)(first.expressionType, last.expressionType);
|
|
58
|
+
const evalSpace = (0, malloy_types_1.mergeEvalSpaces)(first.evalSpace, last.evalSpace);
|
|
59
|
+
if (first.dataType === 'error' || last.dataType === 'error') {
|
|
60
|
+
return {
|
|
61
|
+
dataType: 'number',
|
|
62
|
+
expressionType,
|
|
63
|
+
evalSpace,
|
|
64
|
+
value: (0, ast_utils_1.errorFor)('extract from error').value,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
57
67
|
if (!(0, malloy_types_1.isTimeFieldType)(first.dataType)) {
|
|
58
68
|
from.first.log(`Can't extract ${extractTo} from '${first.dataType}'`);
|
|
59
69
|
return (0, ast_utils_1.errorFor)(`${extractTo} bad type ${first.dataType}`);
|
|
@@ -95,8 +105,8 @@ class ExprTimeExtract extends expression_def_1.ExpressionDef {
|
|
|
95
105
|
}
|
|
96
106
|
return {
|
|
97
107
|
dataType: 'number',
|
|
98
|
-
expressionType
|
|
99
|
-
evalSpace
|
|
108
|
+
expressionType,
|
|
109
|
+
evalSpace,
|
|
100
110
|
value: [
|
|
101
111
|
{
|
|
102
112
|
type: 'dialect',
|
|
@@ -125,8 +135,16 @@ class ExprTimeExtract extends expression_def_1.ExpressionDef {
|
|
|
125
135
|
],
|
|
126
136
|
};
|
|
127
137
|
}
|
|
128
|
-
|
|
129
|
-
|
|
138
|
+
if (argV.dataType !== 'error') {
|
|
139
|
+
this.log(`${this.extractText}() requires time type, not '${argV.dataType}'`);
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
dataType: 'number',
|
|
143
|
+
expressionType: argV.expressionType,
|
|
144
|
+
evalSpace: argV.evalSpace,
|
|
145
|
+
value: (0, ast_utils_1.errorFor)(`${this.extractText} bad type ${argV.dataType}`)
|
|
146
|
+
.value,
|
|
147
|
+
};
|
|
130
148
|
}
|
|
131
149
|
}
|
|
132
150
|
throw this.internalError(`Illegal extraction unit '${this.extractText}'`);
|
|
@@ -7,7 +7,7 @@ export declare class ForRange extends ExpressionDef {
|
|
|
7
7
|
readonly duration: ExpressionDef;
|
|
8
8
|
readonly timeframe: Timeframe;
|
|
9
9
|
elementType: string;
|
|
10
|
-
legalChildTypes: import("
|
|
10
|
+
legalChildTypes: import("../../../model").TypeDesc[];
|
|
11
11
|
constructor(from: ExpressionDef, duration: ExpressionDef, timeframe: Timeframe);
|
|
12
12
|
apply(fs: FieldSpace, op: string, expr: ExpressionDef): ExprValue;
|
|
13
13
|
requestExpression(_fs: FieldSpace): undefined;
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
25
|
exports.ForRange = void 0;
|
|
26
|
+
const model_1 = require("../../../model");
|
|
26
27
|
const ast_utils_1 = require("../ast-utils");
|
|
27
28
|
const fragtype_utils_1 = require("../fragtype-utils");
|
|
28
29
|
const time_utils_1 = require("../time-utils");
|
|
@@ -46,8 +47,15 @@ class ForRange extends expression_def_1.ExpressionDef {
|
|
|
46
47
|
}
|
|
47
48
|
const nV = this.duration.getExpression(fs);
|
|
48
49
|
if (nV.dataType !== 'number') {
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
if (nV.dataType !== 'error') {
|
|
51
|
+
this.log(`FOR duration count must be a number, not '${nV.dataType}'`);
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
dataType: 'boolean',
|
|
55
|
+
evalSpace: (0, model_1.mergeEvalSpaces)(startV.evalSpace, checkV.evalSpace),
|
|
56
|
+
expressionType: (0, model_1.maxExpressionType)(startV.expressionType, checkV.expressionType),
|
|
57
|
+
value: (0, ast_utils_1.errorFor)('for not number').value,
|
|
58
|
+
};
|
|
51
59
|
}
|
|
52
60
|
const units = this.timeframe.text;
|
|
53
61
|
// If the duration resolution is smaller than date, we have
|
|
@@ -30,7 +30,11 @@ const expression_def_1 = require("../types/expression-def");
|
|
|
30
30
|
const malloy_element_1 = require("../types/malloy-element");
|
|
31
31
|
const utils_1 = require("./utils");
|
|
32
32
|
function typeCoalesce(ev1, ev2) {
|
|
33
|
-
return ev1 === undefined ||
|
|
33
|
+
return ev1 === undefined ||
|
|
34
|
+
ev1.dataType === 'null' ||
|
|
35
|
+
ev1.dataType === 'error'
|
|
36
|
+
? ev2
|
|
37
|
+
: ev1;
|
|
34
38
|
}
|
|
35
39
|
class Pick extends expression_def_1.ExpressionDef {
|
|
36
40
|
constructor(choices, elsePick) {
|
|
@@ -292,14 +292,11 @@ class QuerySpace extends refined_space_1.RefinedSpace {
|
|
|
292
292
|
}
|
|
293
293
|
else {
|
|
294
294
|
const typeDesc = field.typeDesc();
|
|
295
|
-
// Filter out fields whose type is
|
|
296
|
-
// isn't sent to the compiler, where it will wig out.
|
|
297
|
-
// subsequent stages of a query, the field is not in the input struct. But since
|
|
298
|
-
// the error message says "Cannot define x which has unknown type", it makes sense
|
|
299
|
-
// that it wouldn't be "defined" in later stages.
|
|
295
|
+
// Filter out fields whose type is 'error', which means that a totally bad field
|
|
296
|
+
// isn't sent to the compiler, where it will wig out.
|
|
300
297
|
// TODO Figure out how to make errors generated by `canContain` go in the right place,
|
|
301
298
|
// maybe by adding a logable element to SpaceFields.
|
|
302
|
-
if (typeDesc.dataType !== '
|
|
299
|
+
if (typeDesc.dataType !== 'error' && this.canContain(typeDesc)) {
|
|
303
300
|
fields.push(fieldQueryDef);
|
|
304
301
|
}
|
|
305
302
|
}
|
|
@@ -45,7 +45,7 @@ class ReferenceField extends space_field_1.SpaceField {
|
|
|
45
45
|
if (this.res !== undefined && this.res.found) {
|
|
46
46
|
return this.res.found.typeDesc();
|
|
47
47
|
}
|
|
48
|
-
return { dataType: '
|
|
48
|
+
return { dataType: 'error', expressionType: 'scalar', evalSpace: 'input' };
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
exports.ReferenceField = ReferenceField;
|
|
@@ -21,7 +21,7 @@ export declare class FT {
|
|
|
21
21
|
* @param right Right type
|
|
22
22
|
* @param nullOk True if a NULL is an acceptable match
|
|
23
23
|
*/
|
|
24
|
-
static typeEq(left: TypeDesc, right: TypeDesc, nullOk?: boolean): boolean;
|
|
24
|
+
static typeEq(left: TypeDesc, right: TypeDesc, nullOk?: boolean, errorOk?: boolean): boolean;
|
|
25
25
|
/**
|
|
26
26
|
*
|
|
27
27
|
* For error messages, returns a comma seperated list of readable names
|
|
@@ -59,10 +59,11 @@ class FT {
|
|
|
59
59
|
* @param right Right type
|
|
60
60
|
* @param nullOk True if a NULL is an acceptable match
|
|
61
61
|
*/
|
|
62
|
-
static typeEq(left, right, nullOk = false) {
|
|
62
|
+
static typeEq(left, right, nullOk = false, errorOk = true) {
|
|
63
63
|
const maybeEq = left.dataType === right.dataType;
|
|
64
64
|
const nullEq = nullOk && (left.dataType === 'null' || right.dataType === 'null');
|
|
65
|
-
|
|
65
|
+
const errorEq = errorOk && (left.dataType === 'error' || right.dataType === 'error');
|
|
66
|
+
return maybeEq || nullEq || errorEq;
|
|
66
67
|
}
|
|
67
68
|
/**
|
|
68
69
|
*
|
|
@@ -37,7 +37,7 @@ class ConstantParameter extends has_parameter_1.HasParameter {
|
|
|
37
37
|
this.log(`Unexpected expression type '${cVal.dataType}'`);
|
|
38
38
|
return {
|
|
39
39
|
value: ['XXX-type-mismatch-error-XXX'],
|
|
40
|
-
type: '
|
|
40
|
+
type: 'error',
|
|
41
41
|
name: this.name,
|
|
42
42
|
constant: true,
|
|
43
43
|
};
|
|
@@ -66,8 +66,8 @@ class FieldDeclaration extends malloy_element_1.MalloyElement {
|
|
|
66
66
|
catch (error) {
|
|
67
67
|
this.log(`Cannot define '${exprName}', ${error.message}`);
|
|
68
68
|
return {
|
|
69
|
-
name:
|
|
70
|
-
type: '
|
|
69
|
+
name: exprName,
|
|
70
|
+
type: 'error',
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
73
|
const compressValue = (0, utils_1.compressExpr)(exprValue.value);
|
|
@@ -96,17 +96,14 @@ class FieldDeclaration extends malloy_element_1.MalloyElement {
|
|
|
96
96
|
}
|
|
97
97
|
const circularDef = exprFS instanceof DefSpace && exprFS.foundCircle;
|
|
98
98
|
if (!circularDef) {
|
|
99
|
-
if (exprValue.dataType
|
|
100
|
-
this.log(`Cannot define '${exprName}', value has unknown type`);
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
99
|
+
if (exprValue.dataType !== 'error') {
|
|
103
100
|
const badType = fragtype_utils_1.FT.inspect(exprValue);
|
|
104
101
|
this.log(`Cannot define '${exprName}', unexpected type: ${badType}`);
|
|
105
102
|
}
|
|
106
103
|
}
|
|
107
104
|
return {
|
|
108
|
-
name:
|
|
109
|
-
type: '
|
|
105
|
+
name: exprName,
|
|
106
|
+
type: 'error',
|
|
110
107
|
};
|
|
111
108
|
}
|
|
112
109
|
}
|
|
@@ -67,7 +67,7 @@ class ExpressionDef extends malloy_element_1.MalloyElement {
|
|
|
67
67
|
* @param eVal ...list of expressions that must match legalChildTypes
|
|
68
68
|
*/
|
|
69
69
|
typeCheck(eNode, eVal) {
|
|
70
|
-
if (!fragtype_utils_1.FT.in(eVal, this.legalChildTypes)) {
|
|
70
|
+
if (eVal.dataType !== 'error' && !fragtype_utils_1.FT.in(eVal, this.legalChildTypes)) {
|
|
71
71
|
eNode.log(`'${this.elementType}' Can't use type ${fragtype_utils_1.FT.inspect(eVal)}`);
|
|
72
72
|
return false;
|
|
73
73
|
}
|
|
@@ -232,6 +232,9 @@ function nullCompare(left, op, right) {
|
|
|
232
232
|
function equality(fs, left, op, right) {
|
|
233
233
|
const lhs = left.getExpression(fs);
|
|
234
234
|
const rhs = right.getExpression(fs);
|
|
235
|
+
const err = errorCascade('boolean', lhs, rhs);
|
|
236
|
+
if (err)
|
|
237
|
+
return err;
|
|
235
238
|
// Unsupported types can be compare with null
|
|
236
239
|
const checkUnsupport = lhs.dataType === 'unsupported' || rhs.dataType === 'unsupported';
|
|
237
240
|
if (checkUnsupport) {
|
|
@@ -245,7 +248,7 @@ function equality(fs, left, op, right) {
|
|
|
245
248
|
}
|
|
246
249
|
}
|
|
247
250
|
let value = timeCompare(left, lhs, op, rhs) || (0, utils_1.compose)(lhs.value, op, rhs.value);
|
|
248
|
-
if (lhs.dataType !== '
|
|
251
|
+
if (lhs.dataType !== 'error' && rhs.dataType !== 'error') {
|
|
249
252
|
switch (op) {
|
|
250
253
|
case '~':
|
|
251
254
|
case '!~': {
|
|
@@ -285,6 +288,9 @@ function equality(fs, left, op, right) {
|
|
|
285
288
|
function compare(fs, left, op, right) {
|
|
286
289
|
const lhs = left.getExpression(fs);
|
|
287
290
|
const rhs = right.getExpression(fs);
|
|
291
|
+
const err = errorCascade('boolean', lhs, rhs);
|
|
292
|
+
if (err)
|
|
293
|
+
return err;
|
|
288
294
|
const expressionType = (0, malloy_types_1.maxExpressionType)(lhs.expressionType, rhs.expressionType);
|
|
289
295
|
const noCompare = unsupportError(left, lhs, right, rhs);
|
|
290
296
|
if (noCompare) {
|
|
@@ -312,6 +318,9 @@ function allAre(oneType, ...values) {
|
|
|
312
318
|
function numeric(fs, left, op, right) {
|
|
313
319
|
const lhs = left.getExpression(fs);
|
|
314
320
|
const rhs = right.getExpression(fs);
|
|
321
|
+
const err = errorCascade('number', lhs, rhs);
|
|
322
|
+
if (err)
|
|
323
|
+
return err;
|
|
315
324
|
const noGo = unsupportError(left, lhs, right, rhs);
|
|
316
325
|
if (noGo) {
|
|
317
326
|
return noGo;
|
|
@@ -335,7 +344,11 @@ function delta(fs, left, op, right) {
|
|
|
335
344
|
if (noGo) {
|
|
336
345
|
return noGo;
|
|
337
346
|
}
|
|
338
|
-
|
|
347
|
+
const timeLHS = (0, malloy_types_1.isTimeFieldType)(lhs.dataType);
|
|
348
|
+
const err = errorCascade(timeLHS ? 'error' : 'number', lhs, rhs);
|
|
349
|
+
if (err)
|
|
350
|
+
return err;
|
|
351
|
+
if (timeLHS) {
|
|
339
352
|
let duration = right;
|
|
340
353
|
if (rhs.dataType !== 'duration') {
|
|
341
354
|
if ((0, granular_result_1.isGranularResult)(lhs)) {
|
|
@@ -385,6 +398,9 @@ function applyBinary(fs, left, op, right) {
|
|
|
385
398
|
left.log('Cannot operate with unsupported type');
|
|
386
399
|
return noGo;
|
|
387
400
|
}
|
|
401
|
+
const err = errorCascade('number', num, denom);
|
|
402
|
+
if (err)
|
|
403
|
+
return err;
|
|
388
404
|
if (num.dataType !== 'number') {
|
|
389
405
|
left.log('Numerator for division must be a number');
|
|
390
406
|
}
|
|
@@ -411,6 +427,16 @@ function applyBinary(fs, left, op, right) {
|
|
|
411
427
|
return (0, ast_utils_1.errorFor)('applybinary bad operator');
|
|
412
428
|
}
|
|
413
429
|
exports.applyBinary = applyBinary;
|
|
430
|
+
function errorCascade(dataType, ...es) {
|
|
431
|
+
if (es.some(e => e.dataType === 'error')) {
|
|
432
|
+
return {
|
|
433
|
+
dataType,
|
|
434
|
+
expressionType: (0, malloy_types_1.maxOfExpressionTypes)(es.map(e => e.expressionType)),
|
|
435
|
+
value: ["'cascading error'"],
|
|
436
|
+
evalSpace: (0, malloy_types_1.mergeEvalSpaces)(...es.map(e => e.evalSpace)),
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
}
|
|
414
440
|
/**
|
|
415
441
|
* Return an error if a binary operation includes unsupported types.
|
|
416
442
|
*/
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DocumentLocation, DocumentReference, ModelDef, Query, SQLBlockStructDef } from '../../../model/malloy_types';
|
|
2
|
+
import { LogSeverity } from '../../parse-log';
|
|
2
3
|
import { MalloyTranslation } from '../../parse-malloy';
|
|
3
4
|
import { ModelDataRequest } from '../../translate-response';
|
|
4
5
|
import { DocumentCompileResult } from './document-compile-result';
|
|
@@ -31,7 +32,7 @@ export declare abstract class MalloyElement {
|
|
|
31
32
|
private get sourceURL();
|
|
32
33
|
errorsExist(): boolean;
|
|
33
34
|
private readonly logged;
|
|
34
|
-
log(message: string): void;
|
|
35
|
+
log(message: string, severity?: LogSeverity): void;
|
|
35
36
|
/**
|
|
36
37
|
* Mostly for debugging / testing. A string-y version of this object which
|
|
37
38
|
* is used to ask "are these two AST segments equal". Formatted so that
|
|
@@ -151,7 +151,7 @@ class MalloyElement {
|
|
|
151
151
|
}
|
|
152
152
|
return true;
|
|
153
153
|
}
|
|
154
|
-
log(message) {
|
|
154
|
+
log(message, severity = 'error') {
|
|
155
155
|
if (this.codeLocation) {
|
|
156
156
|
/*
|
|
157
157
|
* If this element has a location, then don't report the same
|
|
@@ -163,7 +163,7 @@ class MalloyElement {
|
|
|
163
163
|
this.logged.add(message);
|
|
164
164
|
}
|
|
165
165
|
const trans = this.translator();
|
|
166
|
-
const msg = { at: this.location, message };
|
|
166
|
+
const msg = { at: this.location, message, severity };
|
|
167
167
|
const logTo = trans === null || trans === void 0 ? void 0 : trans.root.logger;
|
|
168
168
|
if (logTo) {
|
|
169
169
|
logTo.log(msg);
|
|
@@ -297,14 +297,6 @@ class RunList extends ListOf {
|
|
|
297
297
|
}
|
|
298
298
|
executeList(doc) {
|
|
299
299
|
while (this.execCursor < this.elements.length) {
|
|
300
|
-
if (doc.errorsExist()) {
|
|
301
|
-
// TODO make a better way to stop cascading errors -- this way means that if there are
|
|
302
|
-
// actual different errors in multiple places, we only see the first one. A better way
|
|
303
|
-
// might be to make things that have error pass all kinds of typechecks, which prevents
|
|
304
|
-
// the real cascade.
|
|
305
|
-
// This stops cascading errors
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
300
|
const el = this.elements[this.execCursor];
|
|
309
301
|
if (isDocStatement(el)) {
|
|
310
302
|
const resp = el.execute(doc);
|
|
@@ -40,7 +40,7 @@ class AbstractParameter extends SpaceParam {
|
|
|
40
40
|
return this.astParam.parameter();
|
|
41
41
|
}
|
|
42
42
|
typeDesc() {
|
|
43
|
-
const type = this.astParam.type || '
|
|
43
|
+
const type = this.astParam.type || 'error';
|
|
44
44
|
// TODO Not sure whether params are considered "input space". It seems like they
|
|
45
45
|
// could be input or constant, depending on usage.
|
|
46
46
|
return { dataType: type, expressionType: 'scalar', evalSpace: 'input' };
|
package/dist/lang/parse-log.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { CodePointCharStream, CommonTokenStream, ParserRuleContext, Token } from 'antlr4ts';
|
|
2
2
|
import { ParseTree } from 'antlr4ts/tree';
|
|
3
3
|
import { DocumentLocation, DocumentPosition, DocumentRange, DocumentReference, ModelDef, NamedModelObject, Query, SQLBlockStructDef, StructDef } from '../model/malloy_types';
|
|
4
|
-
import { MessageLog } from './parse-log';
|
|
4
|
+
import { LogMessage, MessageLog } from './parse-log';
|
|
5
5
|
import { Zone, ZoneData } from './zone';
|
|
6
6
|
import { ReferenceList } from './reference-list';
|
|
7
|
-
import { ASTResponse, CompletionsResponse, DataRequestResponse,
|
|
7
|
+
import { ASTResponse, CompletionsResponse, DataRequestResponse, ProblemResponse, FatalResponse, FinalResponse, HelpContextResponse, MetadataResponse, ModelDataRequest, NeedURLData, TranslateResponse } from './translate-response';
|
|
8
8
|
export declare type StepResponses = DataRequestResponse | ASTResponse | TranslateResponse | ParseResponse | MetadataResponse;
|
|
9
9
|
/**
|
|
10
10
|
* A Translation is a series of translation steps. Each step can depend
|
|
@@ -28,7 +28,7 @@ export interface MalloyParseRoot {
|
|
|
28
28
|
subTranslator: MalloyTranslation;
|
|
29
29
|
malloyVersion: string;
|
|
30
30
|
}
|
|
31
|
-
interface ParseData extends
|
|
31
|
+
interface ParseData extends ProblemResponse, NeedURLData, FinalResponse {
|
|
32
32
|
parse: MalloyParseRoot;
|
|
33
33
|
}
|
|
34
34
|
export declare type ParseResponse = Partial<ParseData>;
|
|
@@ -116,12 +116,18 @@ export declare abstract class MalloyTranslation {
|
|
|
116
116
|
addChild(url: string): void;
|
|
117
117
|
addReference(reference: DocumentReference): void;
|
|
118
118
|
referenceAt(position: DocumentPosition): DocumentReference | undefined;
|
|
119
|
-
fatalErrors(): FatalResponse;
|
|
120
119
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
120
|
+
* This returns a *final* response containing all problems, for when there are
|
|
121
|
+
* errors and the translation needs to stop and report errors. When doing so,
|
|
122
|
+
* it also reports warnings.
|
|
123
123
|
*/
|
|
124
|
-
|
|
124
|
+
fatalResponse(): FatalResponse;
|
|
125
|
+
/**
|
|
126
|
+
* The problem log can grow as progressively deeper questions are asked.
|
|
127
|
+
* When returning "problems so far", make a snapshot.
|
|
128
|
+
*/
|
|
129
|
+
problemResponse(): ProblemResponse;
|
|
130
|
+
problems(): LogMessage[];
|
|
125
131
|
getLineMap(url: string): string[] | undefined;
|
|
126
132
|
prettyErrors(): string;
|
|
127
133
|
childRequest(importURL: string): ModelDataRequest;
|