@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
|
@@ -88,11 +88,12 @@ class ParseStep {
|
|
|
88
88
|
that.urlIsFullPath = false;
|
|
89
89
|
that.root.logger.log({
|
|
90
90
|
message: `Could not compute full path URL: ${msg}`,
|
|
91
|
+
severity: 'error',
|
|
91
92
|
});
|
|
92
93
|
}
|
|
93
94
|
}
|
|
94
95
|
if (!that.urlIsFullPath) {
|
|
95
|
-
return that.
|
|
96
|
+
return that.fatalResponse();
|
|
96
97
|
}
|
|
97
98
|
const srcEnt = that.root.importZone.getEntry(that.sourceURL);
|
|
98
99
|
if (srcEnt.status !== 'present') {
|
|
@@ -101,8 +102,8 @@ class ParseStep {
|
|
|
101
102
|
? `import error: ${srcEnt.message}`
|
|
102
103
|
: `import '${that.sourceURL}' error: ${srcEnt.message}`;
|
|
103
104
|
const at = srcEnt.firstReference || that.defaultLocation();
|
|
104
|
-
that.root.logger.log({ message, at });
|
|
105
|
-
this.response = that.
|
|
105
|
+
that.root.logger.log({ message, at, severity: 'error' });
|
|
106
|
+
this.response = that.fatalResponse();
|
|
106
107
|
return this.response;
|
|
107
108
|
}
|
|
108
109
|
return { urls: [that.sourceURL] };
|
|
@@ -116,13 +117,14 @@ class ParseStep {
|
|
|
116
117
|
catch (parseException) {
|
|
117
118
|
that.root.logger.log({
|
|
118
119
|
message: `Malloy internal parser exception [${parseException.message}]`,
|
|
120
|
+
severity: 'error',
|
|
119
121
|
});
|
|
120
122
|
parse = undefined;
|
|
121
123
|
}
|
|
122
124
|
if (that.root.logger.hasErrors()) {
|
|
123
125
|
this.response = {
|
|
124
126
|
parse,
|
|
125
|
-
...that.
|
|
127
|
+
...that.fatalResponse(),
|
|
126
128
|
};
|
|
127
129
|
}
|
|
128
130
|
else {
|
|
@@ -228,6 +230,7 @@ class ImportsAndTablesStep {
|
|
|
228
230
|
that.root.logger.log({
|
|
229
231
|
message: `Malformed URL '${relativeRef}'"`,
|
|
230
232
|
at: { url: that.sourceURL, range: firstRef },
|
|
233
|
+
severity: 'error',
|
|
231
234
|
});
|
|
232
235
|
}
|
|
233
236
|
}
|
|
@@ -275,7 +278,7 @@ class ASTStep {
|
|
|
275
278
|
const parseResponse = that.parseStep.response;
|
|
276
279
|
// Errors in self or children will show up here ..
|
|
277
280
|
if (that.root.logger.hasErrors()) {
|
|
278
|
-
this.response = that.
|
|
281
|
+
this.response = that.fatalResponse();
|
|
279
282
|
return this.response;
|
|
280
283
|
}
|
|
281
284
|
const parse = parseResponse === null || parseResponse === void 0 ? void 0 : parseResponse.parse;
|
|
@@ -285,7 +288,7 @@ class ASTStep {
|
|
|
285
288
|
const secondPass = new malloy_to_ast_1.MalloyToAST(parse, that.root.logger);
|
|
286
289
|
const newAst = secondPass.visit(parse.root);
|
|
287
290
|
if (that.root.logger.hasErrors()) {
|
|
288
|
-
this.response = that.
|
|
291
|
+
this.response = that.fatalResponse();
|
|
289
292
|
return this.response;
|
|
290
293
|
}
|
|
291
294
|
if (newAst.elementType === 'unimplemented') {
|
|
@@ -316,7 +319,7 @@ class ASTStep {
|
|
|
316
319
|
}
|
|
317
320
|
// If there is a partial ast ...
|
|
318
321
|
if (that.root.logger.hasErrors()) {
|
|
319
|
-
this.response = that.
|
|
322
|
+
this.response = that.fatalResponse();
|
|
320
323
|
return this.response;
|
|
321
324
|
}
|
|
322
325
|
/*
|
|
@@ -336,7 +339,7 @@ class ASTStep {
|
|
|
336
339
|
}
|
|
337
340
|
newAst.setTranslator(that);
|
|
338
341
|
this.response = {
|
|
339
|
-
...that.
|
|
342
|
+
...that.problemResponse(),
|
|
340
343
|
ast: newAst,
|
|
341
344
|
final: true,
|
|
342
345
|
};
|
|
@@ -474,11 +477,12 @@ class TranslateStep {
|
|
|
474
477
|
that.root.logger.log({
|
|
475
478
|
message: `'${that.sourceURL}' did not parse to malloy document`,
|
|
476
479
|
at: that.defaultLocation(),
|
|
480
|
+
severity: 'error',
|
|
477
481
|
});
|
|
478
482
|
}
|
|
479
483
|
}
|
|
480
484
|
if (that.root.logger.hasErrors()) {
|
|
481
|
-
this.response = that.
|
|
485
|
+
this.response = that.fatalResponse();
|
|
482
486
|
}
|
|
483
487
|
else {
|
|
484
488
|
this.response = {
|
|
@@ -487,7 +491,7 @@ class TranslateStep {
|
|
|
487
491
|
queryList: that.queryList,
|
|
488
492
|
sqlBlocks: that.sqlBlocks,
|
|
489
493
|
},
|
|
490
|
-
...that.
|
|
494
|
+
...that.problemResponse(),
|
|
491
495
|
final: true,
|
|
492
496
|
};
|
|
493
497
|
}
|
|
@@ -533,19 +537,26 @@ class MalloyTranslation {
|
|
|
533
537
|
referenceAt(position) {
|
|
534
538
|
return this.references.find(position);
|
|
535
539
|
}
|
|
536
|
-
|
|
540
|
+
/**
|
|
541
|
+
* This returns a *final* response containing all problems, for when there are
|
|
542
|
+
* errors and the translation needs to stop and report errors. When doing so,
|
|
543
|
+
* it also reports warnings.
|
|
544
|
+
*/
|
|
545
|
+
fatalResponse() {
|
|
537
546
|
return {
|
|
538
547
|
final: true,
|
|
539
|
-
|
|
548
|
+
...this.problemResponse(),
|
|
540
549
|
};
|
|
541
550
|
}
|
|
542
551
|
/**
|
|
543
|
-
* The
|
|
544
|
-
* When returning "
|
|
552
|
+
* The problem log can grow as progressively deeper questions are asked.
|
|
553
|
+
* When returning "problems so far", make a snapshot.
|
|
545
554
|
*/
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
555
|
+
problemResponse() {
|
|
556
|
+
return { problems: this.problems() };
|
|
557
|
+
}
|
|
558
|
+
problems() {
|
|
559
|
+
return [...this.root.logger.getLog()];
|
|
549
560
|
}
|
|
550
561
|
getLineMap(url) {
|
|
551
562
|
var _a, _b;
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { DocumentLocation } from '../../model';
|
|
2
|
-
|
|
2
|
+
import { LogSeverity } from '../parse-log';
|
|
3
|
+
declare type SimpleProblemSpec = string | RegExp;
|
|
4
|
+
declare type ComplexProblemSpec = {
|
|
5
|
+
severity: LogSeverity;
|
|
6
|
+
message: SimpleProblemSpec;
|
|
7
|
+
};
|
|
8
|
+
declare type ProblemSpec = SimpleProblemSpec | ComplexProblemSpec;
|
|
3
9
|
declare global {
|
|
4
10
|
namespace jest {
|
|
5
11
|
interface Matchers<R> {
|
|
@@ -24,6 +30,19 @@ declare global {
|
|
|
24
30
|
* the markings.
|
|
25
31
|
*/
|
|
26
32
|
toTranslate(): R;
|
|
33
|
+
/**
|
|
34
|
+
* expect(X).toTranslateWithWarnings(expectedWarnings)
|
|
35
|
+
*
|
|
36
|
+
* Passes if the source compiles to code which could be used to
|
|
37
|
+
* generate SQL, and the specified warnings appear. If X is a marked
|
|
38
|
+
* source, the warnings which are found must match the locations of
|
|
39
|
+
* the markings.
|
|
40
|
+
*
|
|
41
|
+
* X can be a MarkedSource, a string, or a model. If it is a marked
|
|
42
|
+
* source, the errors which are found must match the locations of
|
|
43
|
+
* the markings.
|
|
44
|
+
*/
|
|
45
|
+
toTranslateWithWarnings(...expectedWarnings: SimpleProblemSpec[]): R;
|
|
27
46
|
toReturnType(tp: string): R;
|
|
28
47
|
/**
|
|
29
48
|
* expect(X).translateToFailWith(expectedErrors)
|
|
@@ -35,7 +54,7 @@ declare global {
|
|
|
35
54
|
* @param expectedErrors varargs list of strings which must match
|
|
36
55
|
* exactly, or regular expressions.
|
|
37
56
|
*/
|
|
38
|
-
translationToFailWith(...expectedErrors:
|
|
57
|
+
translationToFailWith(...expectedErrors: ProblemSpec[]): R;
|
|
39
58
|
isLocationIn(at: DocumentLocation, txt: string): R;
|
|
40
59
|
}
|
|
41
60
|
}
|
|
@@ -25,13 +25,13 @@
|
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
const test_translator_1 = require("./test-translator");
|
|
27
27
|
const util_1 = require("util");
|
|
28
|
-
function
|
|
28
|
+
function ensureNoProblems(trans) {
|
|
29
29
|
if (trans.logger === undefined) {
|
|
30
30
|
throw new Error('JESTERY BROKEN, CANT FIND ERORR LOG');
|
|
31
31
|
}
|
|
32
|
-
if (trans.logger.
|
|
32
|
+
if (!trans.logger.empty()) {
|
|
33
33
|
return {
|
|
34
|
-
message: () => `Translation
|
|
34
|
+
message: () => `Translation problems:\n${trans.prettyErrors()}`,
|
|
35
35
|
pass: false,
|
|
36
36
|
};
|
|
37
37
|
}
|
|
@@ -92,6 +92,19 @@ function highlightError(dl, txt) {
|
|
|
92
92
|
}
|
|
93
93
|
return output.join('\n');
|
|
94
94
|
}
|
|
95
|
+
function normalizeProblemSpec(defaultSeverity) {
|
|
96
|
+
return function (spec) {
|
|
97
|
+
if (typeof spec === 'string') {
|
|
98
|
+
return { severity: defaultSeverity, message: spec };
|
|
99
|
+
}
|
|
100
|
+
else if (spec instanceof RegExp) {
|
|
101
|
+
return { severity: defaultSeverity, message: spec };
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
return spec;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
95
108
|
function isMarkedSource(ts) {
|
|
96
109
|
return typeof ts !== 'string' && !(ts instanceof test_translator_1.TestTranslator);
|
|
97
110
|
}
|
|
@@ -105,7 +118,7 @@ function xlator(ts) {
|
|
|
105
118
|
return ts.translator || new test_translator_1.TestTranslator(ts.code);
|
|
106
119
|
}
|
|
107
120
|
function xlated(tt) {
|
|
108
|
-
const errorCheck =
|
|
121
|
+
const errorCheck = ensureNoProblems(tt);
|
|
109
122
|
if (!errorCheck.pass) {
|
|
110
123
|
return errorCheck;
|
|
111
124
|
}
|
|
@@ -116,7 +129,7 @@ expect.extend({
|
|
|
116
129
|
toParse: function (tx) {
|
|
117
130
|
const x = xlator(tx);
|
|
118
131
|
x.compile();
|
|
119
|
-
return
|
|
132
|
+
return ensureNoProblems(x);
|
|
120
133
|
},
|
|
121
134
|
toTranslate: function (tx) {
|
|
122
135
|
const x = xlator(tx);
|
|
@@ -136,74 +149,10 @@ expect.extend({
|
|
|
136
149
|
return { pass, message: () => msg };
|
|
137
150
|
},
|
|
138
151
|
translationToFailWith: function (s, ...msgs) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (msgs.length === 1) {
|
|
144
|
-
emsg += ` ${qmsgs[0]}`;
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
emsg += `s [\n${qmsgs.join('\n')}\n]`;
|
|
148
|
-
}
|
|
149
|
-
const m = xlator(s);
|
|
150
|
-
const src = m.testSrc;
|
|
151
|
-
emsg += `\nSource:\n${src}`;
|
|
152
|
-
m.compile();
|
|
153
|
-
const t = m.translate();
|
|
154
|
-
if (t.translated) {
|
|
155
|
-
return { pass: false, message: () => emsg };
|
|
156
|
-
}
|
|
157
|
-
else if (t.errors === undefined) {
|
|
158
|
-
return {
|
|
159
|
-
pass: false,
|
|
160
|
-
message: () => 'TEST ERROR, not all objects resolved in source\n' +
|
|
161
|
-
(0, test_translator_1.pretty)(t) +
|
|
162
|
-
'\n' +
|
|
163
|
-
emsg,
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
const explain = [];
|
|
168
|
-
const errList = m.errors().errors;
|
|
169
|
-
let i;
|
|
170
|
-
for (i = 0; i < msgs.length && errList[i]; i += 1) {
|
|
171
|
-
const msg = msgs[i];
|
|
172
|
-
const err = errList[i];
|
|
173
|
-
const matched = typeof msg === 'string'
|
|
174
|
-
? msg === err.message
|
|
175
|
-
: err.message.match(msg);
|
|
176
|
-
if (!matched) {
|
|
177
|
-
explain.push(`Expected: ${msg}\nGot: ${err.message}`);
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
if (mSrc === null || mSrc === void 0 ? void 0 : mSrc.locations[i]) {
|
|
181
|
-
const have = (_a = err.at) === null || _a === void 0 ? void 0 : _a.range;
|
|
182
|
-
const want = mSrc.locations[i].range;
|
|
183
|
-
if (!this.equals(have, want)) {
|
|
184
|
-
explain.push(`Expected '${msg}' at location: ${(0, util_1.inspect)(want)}\n` +
|
|
185
|
-
`Actual location: ${(0, util_1.inspect)(have)}`);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
if (i !== msgs.length) {
|
|
191
|
-
explain.push(...msgs.slice(i).map(m => `Missing: ${m}`));
|
|
192
|
-
}
|
|
193
|
-
if (i !== errList.length) {
|
|
194
|
-
explain.push(...errList.slice(i).map(m => `Unexpected Error: ${m.message}`));
|
|
195
|
-
}
|
|
196
|
-
if (explain.length === 0) {
|
|
197
|
-
return {
|
|
198
|
-
pass: true,
|
|
199
|
-
message: () => `All expected errors found: ${(0, test_translator_1.pretty)(msgs)}`,
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
return {
|
|
203
|
-
pass: false,
|
|
204
|
-
message: () => `Compiler did not generated expected errors\n${explain.join('\n')}`,
|
|
205
|
-
};
|
|
206
|
-
}
|
|
152
|
+
return checkForProblems(this, false, s, 'error', ...msgs);
|
|
153
|
+
},
|
|
154
|
+
toTranslateWithWarnings: function (s, ...msgs) {
|
|
155
|
+
return checkForProblems(this, true, s, 'warn', ...msgs);
|
|
207
156
|
},
|
|
208
157
|
isLocationIn: function (checkAt, at, text) {
|
|
209
158
|
if (this.equals(at, checkAt)) {
|
|
@@ -221,4 +170,79 @@ expect.extend({
|
|
|
221
170
|
};
|
|
222
171
|
},
|
|
223
172
|
});
|
|
173
|
+
function checkForProblems(context, expectCompiles, s, defaultSeverity, ...msgs) {
|
|
174
|
+
var _a;
|
|
175
|
+
let emsg = `Expected ${expectCompiles ? 'to' : 'to not'} compile with: `;
|
|
176
|
+
const mSrc = isMarkedSource(s) ? s : undefined;
|
|
177
|
+
const normalize = normalizeProblemSpec(defaultSeverity);
|
|
178
|
+
const normMsgs = msgs.map(normalize);
|
|
179
|
+
const qmsgs = normMsgs.map(s => `${s.severity} '${s.message}'`);
|
|
180
|
+
if (msgs.length === 1) {
|
|
181
|
+
emsg += ` ${qmsgs[0]}`;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
emsg += `s [\n${qmsgs.join('\n')}\n]`;
|
|
185
|
+
}
|
|
186
|
+
const m = xlator(s);
|
|
187
|
+
const src = m.testSrc;
|
|
188
|
+
emsg += `\nSource:\n${src}`;
|
|
189
|
+
m.compile();
|
|
190
|
+
const t = m.translate();
|
|
191
|
+
if (t.translated && !expectCompiles) {
|
|
192
|
+
return { pass: false, message: () => emsg };
|
|
193
|
+
}
|
|
194
|
+
else if (t.problems === undefined) {
|
|
195
|
+
return {
|
|
196
|
+
pass: false,
|
|
197
|
+
message: () => 'TEST ERROR, not all objects resolved in source\n' +
|
|
198
|
+
(0, test_translator_1.pretty)(t) +
|
|
199
|
+
'\n' +
|
|
200
|
+
emsg,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
const explain = [];
|
|
205
|
+
const errList = m.problemResponse().problems;
|
|
206
|
+
let i;
|
|
207
|
+
for (i = 0; i < normMsgs.length && errList[i]; i += 1) {
|
|
208
|
+
const msg = normMsgs[i];
|
|
209
|
+
const err = errList[i];
|
|
210
|
+
const matched = typeof msg.message === 'string'
|
|
211
|
+
? msg.message === err.message
|
|
212
|
+
: err.message.match(msg.message);
|
|
213
|
+
if (err.severity !== msg.severity) {
|
|
214
|
+
explain.push(`Expected ${msg.severity}, got ${err.severity} ${err.message}`);
|
|
215
|
+
}
|
|
216
|
+
if (!matched) {
|
|
217
|
+
explain.push(`Expected: ${msg.message}\nGot: ${err.message}`);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
if (mSrc === null || mSrc === void 0 ? void 0 : mSrc.locations[i]) {
|
|
221
|
+
const have = (_a = err.at) === null || _a === void 0 ? void 0 : _a.range;
|
|
222
|
+
const want = mSrc.locations[i].range;
|
|
223
|
+
if (!context.equals(have, want)) {
|
|
224
|
+
explain.push(`Expected '${msg.message}' at location: ${(0, util_1.inspect)(want)}\n` +
|
|
225
|
+
`Actual location: ${(0, util_1.inspect)(have)}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (i !== msgs.length) {
|
|
231
|
+
explain.push(...msgs.slice(i).map(m => `Missing: ${m}`));
|
|
232
|
+
}
|
|
233
|
+
if (i !== errList.length) {
|
|
234
|
+
explain.push(...errList.slice(i).map(m => `Unexpected Error: ${m.message}`));
|
|
235
|
+
}
|
|
236
|
+
if (explain.length === 0) {
|
|
237
|
+
return {
|
|
238
|
+
pass: true,
|
|
239
|
+
message: () => `All expected errors found: ${(0, test_translator_1.pretty)(msgs)}`,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
pass: false,
|
|
244
|
+
message: () => `Compiler did not generated expected errors\n${explain.join('\n')}`,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
224
248
|
//# sourceMappingURL=parse-expects.js.map
|