@malloydata/malloy 0.0.43-dev230622144455 → 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/expressions/expr-func.js +3 -0
- package/dist/lang/ast/types/malloy-element.d.ts +2 -1
- package/dist/lang/ast/types/malloy-element.js +2 -2
- 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 +8 -4
- 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 +4 -3
- package/dist/malloy.js +11 -7
- package/package.json +1 -1
|
@@ -76,6 +76,9 @@ class ExprFunc extends expression_def_1.ExpressionDef {
|
|
|
76
76
|
this.log(`Cannot call '${this.name}', which is of type ${func.type}`);
|
|
77
77
|
return (0, ast_utils_1.errorFor)('called non function');
|
|
78
78
|
}
|
|
79
|
+
if (func.name !== this.name) {
|
|
80
|
+
this.log(`Case insensitivity for function names is deprecated, use '${func.name}' instead`, 'warn');
|
|
81
|
+
}
|
|
79
82
|
// Find the 'implicit argument' for aggregate functions called like `some_join.some_field.agg(...args)`
|
|
80
83
|
// where the full arg list is `(some_field, ...args)`.
|
|
81
84
|
let implicitExpr = undefined;
|
|
@@ -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);
|
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;
|
|
@@ -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
|
|
@@ -85,7 +85,7 @@ describe('model statements', () => {
|
|
|
85
85
|
`).toTranslate();
|
|
86
86
|
});
|
|
87
87
|
test('from(query) refined into query', () => {
|
|
88
|
-
expect('query: from(ab -> {group_by: astr}) { dimension: bigstr is
|
|
88
|
+
expect('query: from(ab -> {group_by: astr}) { dimension: bigstr is upper(astr) } -> { group_by: bigstr }').toTranslate();
|
|
89
89
|
});
|
|
90
90
|
test('query with shortcut filtered turtle', () => {
|
|
91
91
|
expect("query: allA is ab->aturtle {? astr ~ 'a%' }").toTranslate();
|
|
@@ -371,6 +371,11 @@ describe('model statements', () => {
|
|
|
371
371
|
expect(`query: a -> {
|
|
372
372
|
group_by: s is concat('a', 'b')
|
|
373
373
|
}`).toTranslate();
|
|
374
|
+
});
|
|
375
|
+
test('function incorrect case', () => {
|
|
376
|
+
expect(`query: a -> {
|
|
377
|
+
group_by: s is CONCAT('a', 'b')
|
|
378
|
+
}`).toTranslateWithWarnings("Case insensitivity for function names is deprecated, use 'concat' instead");
|
|
374
379
|
});
|
|
375
380
|
test('function no matching overload', () => {
|
|
376
381
|
expect(`query: a -> {
|
|
@@ -1531,7 +1536,7 @@ describe('error handling', () => {
|
|
|
1531
1536
|
expect('source: na is a { rename: astr is astr }').translationToFailWith("Can't rename field to itself");
|
|
1532
1537
|
});
|
|
1533
1538
|
test('reference to field in its definition', () => {
|
|
1534
|
-
expect('source: na is a { dimension: ustr is
|
|
1539
|
+
expect('source: na is a { dimension: ustr is upper(ustr) } ').translationToFailWith("Circular reference to 'ustr' in definition");
|
|
1535
1540
|
});
|
|
1536
1541
|
test('empty model', () => {
|
|
1537
1542
|
expect('').toTranslate();
|
|
@@ -2336,7 +2341,6 @@ describe('translation need error locations', () => {
|
|
|
2336
2341
|
errors: { urls: { [(result.urls || [])[0]]: 'Bad file!' } },
|
|
2337
2342
|
});
|
|
2338
2343
|
expect(source).translationToFailWith(/Bad file!/);
|
|
2339
|
-
return undefined;
|
|
2340
2344
|
});
|
|
2341
2345
|
test('sql struct error location', () => {
|
|
2342
2346
|
const source = (0, test_translator_1.markSource) `
|
|
@@ -2351,7 +2355,7 @@ describe('translation need error locations', () => {
|
|
|
2351
2355
|
m.update({ errors: { compileSQL: { [req.name]: 'Bad SQL!' } } });
|
|
2352
2356
|
}
|
|
2353
2357
|
expect(m).not.toTranslate();
|
|
2354
|
-
const errList = m.
|
|
2358
|
+
const errList = m.problems();
|
|
2355
2359
|
expect(errList[0].at).isLocationIn(source.locations[0], source.code);
|
|
2356
2360
|
});
|
|
2357
2361
|
test('table struct error location', () => {
|
|
@@ -176,7 +176,7 @@ describe('sql:', () => {
|
|
|
176
176
|
extModel.importZone.define('sqlblocktest://main', 'query: malloy_source -> { project: * }');
|
|
177
177
|
const tr = extModel.translate(modelDef);
|
|
178
178
|
// because extModel is not a TestTranslator we can't use the hotness
|
|
179
|
-
expect(tr.
|
|
179
|
+
expect(tr.problems).toEqual([]);
|
|
180
180
|
expect(tr.translated).toBeDefined();
|
|
181
181
|
});
|
|
182
182
|
});
|
|
@@ -284,6 +284,7 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
|
|
|
284
284
|
this.logger.log({
|
|
285
285
|
at: this.defaultLocation(),
|
|
286
286
|
message: `Missing imports: ${whatImports.join(',')}`,
|
|
287
|
+
severity: 'error',
|
|
287
288
|
});
|
|
288
289
|
}
|
|
289
290
|
const needThese = this.schemaZone.getUndefined();
|
|
@@ -292,12 +293,14 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
|
|
|
292
293
|
this.logger.log({
|
|
293
294
|
at: this.defaultLocation(),
|
|
294
295
|
message: `Missing schema: ${needThese.join(',')}`,
|
|
296
|
+
severity: 'error',
|
|
295
297
|
});
|
|
296
298
|
}
|
|
297
299
|
if (mysterious) {
|
|
298
300
|
this.logger.log({
|
|
299
301
|
at: this.defaultLocation(),
|
|
300
302
|
message: 'mysterious translation failure',
|
|
303
|
+
severity: 'error',
|
|
301
304
|
});
|
|
302
305
|
}
|
|
303
306
|
}
|
|
@@ -12,10 +12,10 @@ import { DocumentHelpContext } from './parse-tree-walkers/document-help-context-
|
|
|
12
12
|
export interface FinalResponse {
|
|
13
13
|
final: true;
|
|
14
14
|
}
|
|
15
|
-
export interface
|
|
16
|
-
|
|
15
|
+
export interface ProblemResponse {
|
|
16
|
+
problems: LogMessage[];
|
|
17
17
|
}
|
|
18
|
-
export declare type FatalResponse = FinalResponse &
|
|
18
|
+
export declare type FatalResponse = FinalResponse & ProblemResponse;
|
|
19
19
|
export interface NeedSchemaData {
|
|
20
20
|
tables: string[];
|
|
21
21
|
}
|
|
@@ -31,24 +31,24 @@ interface NeededData extends NeedURLData, NeedSchemaData, NeedCompileSQL {
|
|
|
31
31
|
export declare type DataRequestResponse = Partial<NeededData> | null;
|
|
32
32
|
export declare function isNeedResponse(dr: DataRequestResponse): dr is NeededData;
|
|
33
33
|
export declare type ModelDataRequest = NeedCompileSQL | undefined;
|
|
34
|
-
interface ASTData extends
|
|
34
|
+
interface ASTData extends ProblemResponse, NeededData, FinalResponse {
|
|
35
35
|
ast: MalloyElement;
|
|
36
36
|
}
|
|
37
37
|
export declare type ASTResponse = Partial<ASTData>;
|
|
38
|
-
interface Metadata extends NeededData,
|
|
38
|
+
interface Metadata extends NeededData, ProblemResponse, FinalResponse {
|
|
39
39
|
symbols: DocumentSymbol[];
|
|
40
40
|
highlights: DocumentHighlight[];
|
|
41
41
|
}
|
|
42
42
|
export declare type MetadataResponse = Partial<Metadata>;
|
|
43
|
-
interface Completions extends NeededData,
|
|
43
|
+
interface Completions extends NeededData, ProblemResponse, FinalResponse {
|
|
44
44
|
completions: DocumentCompletion[];
|
|
45
45
|
}
|
|
46
46
|
export declare type CompletionsResponse = Partial<Completions>;
|
|
47
|
-
interface HelpContext extends NeededData,
|
|
47
|
+
interface HelpContext extends NeededData, ProblemResponse, FinalResponse {
|
|
48
48
|
helpContext: DocumentHelpContext | undefined;
|
|
49
49
|
}
|
|
50
50
|
export declare type HelpContextResponse = Partial<HelpContext>;
|
|
51
|
-
interface TranslatedResponseData extends NeededData,
|
|
51
|
+
interface TranslatedResponseData extends NeededData, ProblemResponse, FinalResponse {
|
|
52
52
|
translated: {
|
|
53
53
|
modelDef: ModelDef;
|
|
54
54
|
queryList: Query[];
|
package/dist/malloy.d.ts
CHANGED
|
@@ -134,11 +134,11 @@ export declare class Malloy {
|
|
|
134
134
|
* A Malloy error, which may contain log messages produced during compilation.
|
|
135
135
|
*/
|
|
136
136
|
export declare class MalloyError extends Error {
|
|
137
|
+
readonly problems: LogMessage[];
|
|
137
138
|
/**
|
|
138
139
|
* An array of log messages produced during compilation.
|
|
139
140
|
*/
|
|
140
|
-
|
|
141
|
-
constructor(message: string, log?: LogMessage[]);
|
|
141
|
+
constructor(message: string, problems?: LogMessage[]);
|
|
142
142
|
}
|
|
143
143
|
/**
|
|
144
144
|
* A compiled Malloy document.
|
|
@@ -148,7 +148,8 @@ export declare class Model {
|
|
|
148
148
|
private queryList;
|
|
149
149
|
private sqlBlocks;
|
|
150
150
|
_referenceAt: (location: ModelDocumentPosition) => DocumentReference | undefined;
|
|
151
|
-
|
|
151
|
+
readonly problems: LogMessage[];
|
|
152
|
+
constructor(modelDef: ModelDef, queryList: InternalQuery[], sqlBlocks: SQLBlockStructDef[], problems: LogMessage[], referenceAt?: (location: ModelDocumentPosition) => DocumentReference | undefined);
|
|
152
153
|
/**
|
|
153
154
|
* Retrieve a document reference for the token at the given position within
|
|
154
155
|
* the document that produced this model.
|
package/dist/malloy.js
CHANGED
|
@@ -78,10 +78,10 @@ class Malloy {
|
|
|
78
78
|
const result = translator.translate(model === null || model === void 0 ? void 0 : model._modelDef);
|
|
79
79
|
if (result.final) {
|
|
80
80
|
if (result.translated) {
|
|
81
|
-
return new Model(result.translated.modelDef, result.translated.queryList, result.translated.sqlBlocks, (position) => translator.referenceAt(position));
|
|
81
|
+
return new Model(result.translated.modelDef, result.translated.queryList, result.translated.sqlBlocks, result.problems || [], (position) => translator.referenceAt(position));
|
|
82
82
|
}
|
|
83
83
|
else {
|
|
84
|
-
const errors = result.
|
|
84
|
+
const errors = result.problems || [];
|
|
85
85
|
const errText = translator.prettyErrors();
|
|
86
86
|
throw new MalloyError(`Error(s) compiling model:\n${errText}`, errors);
|
|
87
87
|
}
|
|
@@ -304,9 +304,12 @@ exports.Malloy = Malloy;
|
|
|
304
304
|
* A Malloy error, which may contain log messages produced during compilation.
|
|
305
305
|
*/
|
|
306
306
|
class MalloyError extends Error {
|
|
307
|
-
|
|
307
|
+
/**
|
|
308
|
+
* An array of log messages produced during compilation.
|
|
309
|
+
*/
|
|
310
|
+
constructor(message, problems = []) {
|
|
308
311
|
super(message);
|
|
309
|
-
this.
|
|
312
|
+
this.problems = problems;
|
|
310
313
|
}
|
|
311
314
|
}
|
|
312
315
|
exports.MalloyError = MalloyError;
|
|
@@ -314,11 +317,12 @@ exports.MalloyError = MalloyError;
|
|
|
314
317
|
* A compiled Malloy document.
|
|
315
318
|
*/
|
|
316
319
|
class Model {
|
|
317
|
-
constructor(modelDef, queryList, sqlBlocks, referenceAt = () => undefined) {
|
|
320
|
+
constructor(modelDef, queryList, sqlBlocks, problems, referenceAt = () => undefined) {
|
|
318
321
|
this.modelDef = modelDef;
|
|
319
322
|
this.queryList = queryList;
|
|
320
323
|
this.sqlBlocks = sqlBlocks;
|
|
321
324
|
this._referenceAt = referenceAt;
|
|
325
|
+
this.problems = problems;
|
|
322
326
|
}
|
|
323
327
|
/**
|
|
324
328
|
* Retrieve a document reference for the token at the given position within
|
|
@@ -894,7 +898,7 @@ class Explore extends Entity {
|
|
|
894
898
|
};
|
|
895
899
|
}
|
|
896
900
|
getSingleExploreModel() {
|
|
897
|
-
return new Model(this.modelDef, [], []);
|
|
901
|
+
return new Model(this.modelDef, [], [], []);
|
|
898
902
|
}
|
|
899
903
|
get fieldMap() {
|
|
900
904
|
var _a;
|
|
@@ -1396,7 +1400,7 @@ class Runtime {
|
|
|
1396
1400
|
// be used in tests.
|
|
1397
1401
|
_loadModelFromModelDef(modelDef) {
|
|
1398
1402
|
return new ModelMaterializer(this, async () => {
|
|
1399
|
-
return new Model(modelDef, [], []);
|
|
1403
|
+
return new Model(modelDef, [], [], []);
|
|
1400
1404
|
});
|
|
1401
1405
|
}
|
|
1402
1406
|
/**
|