@malloydata/malloy 0.0.236-dev250219154103 → 0.0.236-dev250221173808
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/syntax-errors/custom-error-messages.d.ts +1 -0
- package/dist/lang/syntax-errors/custom-error-messages.js +17 -6
- package/dist/lang/syntax-errors/malloy-parser-error-listener.js +6 -0
- package/dist/lang/test/syntax-errors.spec.js +29 -0
- package/dist/model/composite_source_utils.js +1 -0
- package/package.json +1 -1
|
@@ -6,5 +6,6 @@ export interface ErrorCase {
|
|
|
6
6
|
precedingTokenOptions?: number[][];
|
|
7
7
|
lookAheadOptions?: number[][];
|
|
8
8
|
errorMessage: string;
|
|
9
|
+
lookbackFromOffendingSymbol?: boolean;
|
|
9
10
|
}
|
|
10
11
|
export declare const checkCustomErrorMessage: (parser: Parser, offendingSymbol: Token | undefined, errorCases: ErrorCase[]) => string;
|
|
@@ -20,13 +20,17 @@ const checkCustomErrorMessage = (parser, offendingSymbol, errorCases) => {
|
|
|
20
20
|
if (isCurrentTokenMatch && isOffendingSymbolMatch && isRuleContextMatch) {
|
|
21
21
|
// If so, try to check the preceding tokens.
|
|
22
22
|
if (errorCase.precedingTokenOptions) {
|
|
23
|
-
const hasPrecedingTokenMatch = errorCase.precedingTokenOptions.some(sequence => checkTokenSequenceMatch(parser, sequence, 'lookback'
|
|
23
|
+
const hasPrecedingTokenMatch = errorCase.precedingTokenOptions.some(sequence => checkTokenSequenceMatch(parser, sequence, 'lookback', errorCase.lookbackFromOffendingSymbol
|
|
24
|
+
? offendingSymbol === null || offendingSymbol === void 0 ? void 0 : offendingSymbol.tokenIndex
|
|
25
|
+
: undefined));
|
|
24
26
|
if (!hasPrecedingTokenMatch) {
|
|
25
27
|
continue; // Continue to check a different error case
|
|
26
28
|
}
|
|
27
29
|
}
|
|
28
30
|
if (errorCase.lookAheadOptions) {
|
|
29
|
-
const hasLookaheadTokenMatch = errorCase.lookAheadOptions.some(sequence => checkTokenSequenceMatch(parser, sequence, 'lookahead'
|
|
31
|
+
const hasLookaheadTokenMatch = errorCase.lookAheadOptions.some(sequence => checkTokenSequenceMatch(parser, sequence, 'lookahead', errorCase.lookbackFromOffendingSymbol
|
|
32
|
+
? offendingSymbol === null || offendingSymbol === void 0 ? void 0 : offendingSymbol.tokenIndex
|
|
33
|
+
: undefined));
|
|
30
34
|
if (!hasLookaheadTokenMatch) {
|
|
31
35
|
continue; // Continue to check a different error case
|
|
32
36
|
}
|
|
@@ -39,12 +43,19 @@ const checkCustomErrorMessage = (parser, offendingSymbol, errorCases) => {
|
|
|
39
43
|
return '';
|
|
40
44
|
};
|
|
41
45
|
exports.checkCustomErrorMessage = checkCustomErrorMessage;
|
|
42
|
-
const checkTokenSequenceMatch = (parser, sequence, direction) => {
|
|
46
|
+
const checkTokenSequenceMatch = (parser, sequence, direction, anchorTokenPosition) => {
|
|
43
47
|
try {
|
|
44
48
|
for (let i = 0; i < sequence.length; i++) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
let streamToken = undefined;
|
|
50
|
+
if (typeof anchorTokenPosition === 'number') {
|
|
51
|
+
const tokenOffset = direction === 'lookahead' ? i + 1 : -1 * (i + 1);
|
|
52
|
+
streamToken = parser.inputStream.get(anchorTokenPosition + tokenOffset).type;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// Note: positive lookahead starts at '2' because '1' is the current token.
|
|
56
|
+
const tokenOffset = direction === 'lookahead' ? i + 2 : -1 * (i + 1);
|
|
57
|
+
streamToken = parser.inputStream.LA(tokenOffset);
|
|
58
|
+
}
|
|
48
59
|
// Note: negative checking is < -1 becuase Token.EOF is -1, but below
|
|
49
60
|
// that we use negatives to indicate "does-not-match" rules.
|
|
50
61
|
if (sequence[i] >= -1 && streamToken !== sequence[i]) {
|
|
@@ -36,6 +36,12 @@ exports.commonErrorCases = [
|
|
|
36
36
|
[MalloyParser_1.MalloyParser.SOURCE],
|
|
37
37
|
],
|
|
38
38
|
},
|
|
39
|
+
{
|
|
40
|
+
errorMessage: "'aggregate:' entries must include a name (ex: `some_name is count()`)",
|
|
41
|
+
precedingTokenOptions: [[MalloyParser_1.MalloyParser.AGGREGATE]],
|
|
42
|
+
lookAheadOptions: [[-MalloyParser_1.MalloyParser.IS]],
|
|
43
|
+
lookbackFromOffendingSymbol: true,
|
|
44
|
+
},
|
|
39
45
|
];
|
|
40
46
|
class MalloyParserErrorListener {
|
|
41
47
|
constructor(translator, messages) {
|
|
@@ -55,5 +55,34 @@ describe('errors', () => {
|
|
|
55
55
|
primary_key: id
|
|
56
56
|
`).toLogAtLeast((0, test_translator_1.errorMessage)("Missing '{' after 'extend'"));
|
|
57
57
|
});
|
|
58
|
+
test('missing alias for aggregate entry', () => {
|
|
59
|
+
expect(`
|
|
60
|
+
run: x -> {
|
|
61
|
+
aggregate: count()
|
|
62
|
+
}
|
|
63
|
+
`).toLogAtLeast((0, test_translator_1.errorMessage)("'aggregate:' entries must include a name (ex: `some_name is count()`)"));
|
|
64
|
+
});
|
|
65
|
+
test('missing alias for aggregate inside source>view', () => {
|
|
66
|
+
expect(`
|
|
67
|
+
source: x is aa extend {
|
|
68
|
+
measure: airport_count is count()
|
|
69
|
+
|
|
70
|
+
view: by_state is {
|
|
71
|
+
where: state is not null
|
|
72
|
+
aggregate: count()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
`).toLogAtLeast((0, test_translator_1.errorMessage)("'aggregate:' entries must include a name (ex: `some_name is count()`)"));
|
|
76
|
+
});
|
|
77
|
+
test('run opening curly to EOF', () => {
|
|
78
|
+
expect(`
|
|
79
|
+
run: x -> {
|
|
80
|
+
`).toLogAtLeast((0, test_translator_1.errorMessage)("Missing '}' at '<EOF>'"));
|
|
81
|
+
});
|
|
82
|
+
test('source opening curly to EOF', () => {
|
|
83
|
+
expect(`
|
|
84
|
+
source: y is x extend {
|
|
85
|
+
`).toLogAtLeast((0, test_translator_1.errorMessage)("Missing '}' at '<EOF>'"));
|
|
86
|
+
});
|
|
58
87
|
});
|
|
59
88
|
//# sourceMappingURL=syntax-errors.spec.js.map
|
|
@@ -88,6 +88,7 @@ function _resolveCompositeSources(path, source, compositeFieldUsage, narrowedCom
|
|
|
88
88
|
return { error: { code: 'not_a_composite_source', data: { path } } };
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
+
base.arguments = source.arguments;
|
|
91
92
|
const fieldsByName = {};
|
|
92
93
|
const narrowedJoinedSources = (_e = narrowedCompositeFieldResolution === null || narrowedCompositeFieldResolution === void 0 ? void 0 : narrowedCompositeFieldResolution.joined) !== null && _e !== void 0 ? _e : {};
|
|
93
94
|
for (const field of base.fields) {
|