@malloydata/malloy 0.0.236-dev250213234155 → 0.0.236-dev250219154103

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.
@@ -236,7 +236,6 @@ import { ConnectionNameContext } from "./MalloyParser";
236
236
  import { DebugExprContext } from "./MalloyParser";
237
237
  import { DebugPartialContext } from "./MalloyParser";
238
238
  import { ExperimentalStatementForTestingContext } from "./MalloyParser";
239
- import { CloseCurlyContext } from "./MalloyParser";
240
239
  /**
241
240
  * This interface defines a complete listener for a parse tree produced by
242
241
  * `MalloyParser`.
@@ -2784,14 +2783,4 @@ export interface MalloyParserListener extends ParseTreeListener {
2784
2783
  * @param ctx the parse tree
2785
2784
  */
2786
2785
  exitExperimentalStatementForTesting?: (ctx: ExperimentalStatementForTestingContext) => void;
2787
- /**
2788
- * Enter a parse tree produced by `MalloyParser.closeCurly`.
2789
- * @param ctx the parse tree
2790
- */
2791
- enterCloseCurly?: (ctx: CloseCurlyContext) => void;
2792
- /**
2793
- * Exit a parse tree produced by `MalloyParser.closeCurly`.
2794
- * @param ctx the parse tree
2795
- */
2796
- exitCloseCurly?: (ctx: CloseCurlyContext) => void;
2797
2786
  }
@@ -236,7 +236,6 @@ import { ConnectionNameContext } from "./MalloyParser";
236
236
  import { DebugExprContext } from "./MalloyParser";
237
237
  import { DebugPartialContext } from "./MalloyParser";
238
238
  import { ExperimentalStatementForTestingContext } from "./MalloyParser";
239
- import { CloseCurlyContext } from "./MalloyParser";
240
239
  /**
241
240
  * This interface defines a complete generic visitor for a parse tree produced
242
241
  * by `MalloyParser`.
@@ -1753,10 +1752,4 @@ export interface MalloyParserVisitor<Result> extends ParseTreeVisitor<Result> {
1753
1752
  * @return the visitor result
1754
1753
  */
1755
1754
  visitExperimentalStatementForTesting?: (ctx: ExperimentalStatementForTestingContext) => Result;
1756
- /**
1757
- * Visit a parse tree produced by `MalloyParser.closeCurly`.
1758
- * @param ctx the parse tree
1759
- * @return the visitor result
1760
- */
1761
- visitCloseCurly?: (ctx: CloseCurlyContext) => Result;
1762
1755
  }
@@ -1,6 +1,6 @@
1
- import { ANTLRErrorListener, ParserRuleContext, Token } from 'antlr4ts';
1
+ import { ParserRuleContext, Token } from 'antlr4ts';
2
2
  import { DocumentLocation, DocumentPosition, DocumentRange, DocumentReference, ImportLocation, ModelDef, NamedModelObject, SourceDef, SQLSourceDef, DependencyTree } from '../model/malloy_types';
3
- import { BaseMessageLogger, LogMessage, LogMessageOptions, MessageCode, MessageLogger, MessageParameterType } from './parse-log';
3
+ import { BaseMessageLogger, LogMessage, LogMessageOptions, MessageCode, MessageParameterType } from './parse-log';
4
4
  import { Zone, ZoneData } from './zone';
5
5
  import { ReferenceList } from './reference-list';
6
6
  import { ASTResponse, CompletionsResponse, DataRequestResponse, ProblemResponse, FatalResponse, FinalResponse, HelpContextResponse, MetadataResponse, ModelDataRequest, NeedURLData, TranslateResponse, ModelAnnotationResponse, TablePathResponse } from './translate-response';
@@ -228,11 +228,4 @@ export interface UpdateData extends URLData, SchemaData, SQLSources, ModelData {
228
228
  errors: Partial<ErrorData>;
229
229
  }
230
230
  export type ParseUpdate = Partial<UpdateData>;
231
- export declare class MalloyParserErrorHandler implements ANTLRErrorListener<Token> {
232
- readonly translator: MalloyTranslation;
233
- readonly messages: MessageLogger;
234
- constructor(translator: MalloyTranslation, messages: MessageLogger);
235
- logError<T extends MessageCode>(code: T, parameters: MessageParameterType<T>, options?: Omit<LogMessageOptions, 'severity'>): T;
236
- syntaxError(recognizer: unknown, offendingSymbol: Token | undefined, line: number, charPositionInLine: number, message: string, _e: unknown): void;
237
- }
238
231
  export {};
@@ -45,7 +45,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
45
45
  return result;
46
46
  };
47
47
  Object.defineProperty(exports, "__esModule", { value: true });
48
- exports.MalloyParserErrorHandler = exports.MalloyTranslator = exports.MalloyChildTranslator = exports.MalloyTranslation = void 0;
48
+ exports.MalloyTranslator = exports.MalloyChildTranslator = exports.MalloyTranslation = void 0;
49
49
  const antlr4ts_1 = require("antlr4ts");
50
50
  const malloy_types_1 = require("../model/malloy_types");
51
51
  const MalloyLexer_1 = require("./lib/Malloy/MalloyLexer");
@@ -64,6 +64,8 @@ const utils_1 = require("./utils");
64
64
  const tags_1 = require("../tags");
65
65
  const model_annotation_walker_1 = require("./parse-tree-walkers/model-annotation-walker");
66
66
  const find_table_path_walker_1 = require("./parse-tree-walkers/find-table-path-walker");
67
+ const malloy_error_strategy_1 = require("./syntax-errors/malloy-error-strategy");
68
+ const malloy_parser_error_listener_1 = require("./syntax-errors/malloy-parser-error-listener");
67
69
  /**
68
70
  * This ignores a -> popMode when the mode stack is empty, which is a hack,
69
71
  * but it let's us parse }%
@@ -159,7 +161,8 @@ class ParseStep {
159
161
  const tokenStream = new antlr4ts_1.CommonTokenStream(lexer);
160
162
  const malloyParser = new MalloyParser_1.MalloyParser(tokenStream);
161
163
  malloyParser.removeErrorListeners();
162
- malloyParser.addErrorListener(new MalloyParserErrorHandler(that, that.root.logger));
164
+ malloyParser.addErrorListener(new malloy_parser_error_listener_1.MalloyParserErrorListener(that, that.root.logger));
165
+ malloyParser.errorHandler = new malloy_error_strategy_1.MalloyErrorStrategy();
163
166
  // Admitted code smell here, testing likes to parse from an arbitrary
164
167
  // node and this is the simplest way to allow that.
165
168
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -883,26 +886,6 @@ class MalloyTranslator extends MalloyTranslation {
883
886
  }
884
887
  }
885
888
  exports.MalloyTranslator = MalloyTranslator;
886
- class MalloyParserErrorHandler {
887
- constructor(translator, messages) {
888
- this.translator = translator;
889
- this.messages = messages;
890
- }
891
- logError(code, parameters, options) {
892
- this.messages.log((0, parse_log_1.makeLogMessage)(code, parameters, { severity: 'error', ...options }));
893
- return code;
894
- }
895
- syntaxError(recognizer, offendingSymbol, line, charPositionInLine, message, _e) {
896
- const errAt = { line: line - 1, character: charPositionInLine };
897
- const range = offendingSymbol
898
- ? this.translator.rangeFromToken(offendingSymbol)
899
- : { start: errAt, end: errAt };
900
- this.logError('syntax-error', { message }, {
901
- at: { url: this.translator.sourceURL, range },
902
- });
903
- }
904
- }
905
- exports.MalloyParserErrorHandler = MalloyParserErrorHandler;
906
889
  function flattenDependencyTree(dependencies) {
907
890
  return [
908
891
  ...Object.keys(dependencies),
@@ -0,0 +1,10 @@
1
+ import { Parser, Token } from 'antlr4ts';
2
+ export interface ErrorCase {
3
+ ruleContextOptions?: string[];
4
+ offendingSymbol?: number;
5
+ currentToken?: number;
6
+ precedingTokenOptions?: number[][];
7
+ lookAheadOptions?: number[][];
8
+ errorMessage: string;
9
+ }
10
+ export declare const checkCustomErrorMessage: (parser: Parser, offendingSymbol: Token | undefined, errorCases: ErrorCase[]) => string;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.checkCustomErrorMessage = void 0;
10
+ const checkCustomErrorMessage = (parser, offendingSymbol, errorCases) => {
11
+ const currentRuleName = parser.getRuleInvocationStack()[0];
12
+ const currentToken = parser.currentToken;
13
+ for (const errorCase of errorCases) {
14
+ // Check to see if the initial conditions match
15
+ const isCurrentTokenMatch = !errorCase.currentToken || currentToken.type === errorCase.currentToken;
16
+ const isOffendingSymbolMatch = !errorCase.offendingSymbol ||
17
+ (offendingSymbol === null || offendingSymbol === void 0 ? void 0 : offendingSymbol.type) === errorCase.offendingSymbol;
18
+ const isRuleContextMatch = !errorCase.ruleContextOptions ||
19
+ errorCase.ruleContextOptions.includes(currentRuleName);
20
+ if (isCurrentTokenMatch && isOffendingSymbolMatch && isRuleContextMatch) {
21
+ // If so, try to check the preceding tokens.
22
+ if (errorCase.precedingTokenOptions) {
23
+ const hasPrecedingTokenMatch = errorCase.precedingTokenOptions.some(sequence => checkTokenSequenceMatch(parser, sequence, 'lookback'));
24
+ if (!hasPrecedingTokenMatch) {
25
+ continue; // Continue to check a different error case
26
+ }
27
+ }
28
+ if (errorCase.lookAheadOptions) {
29
+ const hasLookaheadTokenMatch = errorCase.lookAheadOptions.some(sequence => checkTokenSequenceMatch(parser, sequence, 'lookahead'));
30
+ if (!hasLookaheadTokenMatch) {
31
+ continue; // Continue to check a different error case
32
+ }
33
+ }
34
+ // If all cases match, return the custom error message
35
+ const message = errorCase.errorMessage.replace('${currentToken}', (offendingSymbol === null || offendingSymbol === void 0 ? void 0 : offendingSymbol.text) || currentToken.text || '');
36
+ return message;
37
+ }
38
+ }
39
+ return '';
40
+ };
41
+ exports.checkCustomErrorMessage = checkCustomErrorMessage;
42
+ const checkTokenSequenceMatch = (parser, sequence, direction) => {
43
+ try {
44
+ for (let i = 0; i < sequence.length; i++) {
45
+ // Note: positive lookahead starts at '2' because '1' is the current token.
46
+ const tokenIndex = direction === 'lookahead' ? i + 2 : -1 * (i + 1);
47
+ const streamToken = parser.inputStream.LA(tokenIndex);
48
+ // Note: negative checking is < -1 becuase Token.EOF is -1, but below
49
+ // that we use negatives to indicate "does-not-match" rules.
50
+ if (sequence[i] >= -1 && streamToken !== sequence[i]) {
51
+ return false;
52
+ }
53
+ if (sequence[i] < -1 && streamToken === -1 * sequence[i]) {
54
+ return false;
55
+ }
56
+ }
57
+ return true;
58
+ }
59
+ catch (ex) {
60
+ // There may not be enough lookback tokens. If so, the case doesn't match.
61
+ return false;
62
+ }
63
+ };
64
+ //# sourceMappingURL=custom-error-messages.js.map
@@ -0,0 +1,14 @@
1
+ import { DefaultErrorStrategy, Parser } from 'antlr4ts';
2
+ /**
3
+ * Custom error strategy for the Malloy Parser. This strategy attempts to
4
+ * detect known cases where the default error strategy results in an unhelpful
5
+ * parse tree or misleading messages. In any cases not explicitly handled, this
6
+ * custom error strategy will fall back to the default error strategy.
7
+ *
8
+ * For more details, read the documentation in DefaultErrorStrategy.d.ts
9
+ * or reference the superclass at:
10
+ * https://github.com/tunnelvisionlabs/antlr4ts/blob/master/src/DefaultErrorStrategy.ts
11
+ */
12
+ export declare class MalloyErrorStrategy extends DefaultErrorStrategy {
13
+ sync(parser: Parser): void;
14
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.MalloyErrorStrategy = void 0;
10
+ const antlr4ts_1 = require("antlr4ts");
11
+ const MalloyParser_1 = require("../lib/Malloy/MalloyParser");
12
+ const custom_error_messages_1 = require("./custom-error-messages");
13
+ const customErrorCases = [
14
+ {
15
+ errorMessage: "Missing '{' after 'extend'",
16
+ currentToken: MalloyParser_1.MalloyParser.EXTEND,
17
+ ruleContextOptions: ['sqExpr'],
18
+ lookAheadOptions: [[-MalloyParser_1.MalloyParser.OCURLY]],
19
+ },
20
+ ];
21
+ /**
22
+ * Custom error strategy for the Malloy Parser. This strategy attempts to
23
+ * detect known cases where the default error strategy results in an unhelpful
24
+ * parse tree or misleading messages. In any cases not explicitly handled, this
25
+ * custom error strategy will fall back to the default error strategy.
26
+ *
27
+ * For more details, read the documentation in DefaultErrorStrategy.d.ts
28
+ * or reference the superclass at:
29
+ * https://github.com/tunnelvisionlabs/antlr4ts/blob/master/src/DefaultErrorStrategy.ts
30
+ */
31
+ class MalloyErrorStrategy extends antlr4ts_1.DefaultErrorStrategy {
32
+ sync(parser) {
33
+ const interceptedErrorMessage = (0, custom_error_messages_1.checkCustomErrorMessage)(parser, undefined, customErrorCases);
34
+ if (interceptedErrorMessage) {
35
+ parser.notifyErrorListeners(interceptedErrorMessage, parser.currentToken, undefined);
36
+ return;
37
+ }
38
+ super.sync(parser);
39
+ }
40
+ }
41
+ exports.MalloyErrorStrategy = MalloyErrorStrategy;
42
+ //# sourceMappingURL=malloy-error-strategy.js.map
@@ -0,0 +1,12 @@
1
+ import { ANTLRErrorListener, Token } from 'antlr4ts';
2
+ import { ErrorCase } from './custom-error-messages';
3
+ import { MessageLogger, MessageCode, MessageParameterType, LogMessageOptions } from '../parse-log';
4
+ import { MalloyTranslation } from '../parse-malloy';
5
+ export declare const commonErrorCases: ErrorCase[];
6
+ export declare class MalloyParserErrorListener implements ANTLRErrorListener<Token> {
7
+ readonly translator: MalloyTranslation;
8
+ readonly messages: MessageLogger;
9
+ constructor(translator: MalloyTranslation, messages: MessageLogger);
10
+ logError<T extends MessageCode>(code: T, parameters: MessageParameterType<T>, options?: Omit<LogMessageOptions, 'severity'>): T;
11
+ syntaxError(recognizer: unknown, offendingSymbol: Token | undefined, line: number, charPositionInLine: number, message: string, _e: unknown): void;
12
+ }
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.MalloyParserErrorListener = exports.commonErrorCases = void 0;
10
+ const MalloyParser_1 = require("../lib/Malloy/MalloyParser");
11
+ const custom_error_messages_1 = require("./custom-error-messages");
12
+ const parse_log_1 = require("../parse-log");
13
+ exports.commonErrorCases = [
14
+ {
15
+ errorMessage: "'view:' must be followed by '<identifier> is {'",
16
+ ruleContextOptions: ['exploreQueryDef'],
17
+ offendingSymbol: MalloyParser_1.MalloyParser.OCURLY,
18
+ precedingTokenOptions: [[MalloyParser_1.MalloyParser.VIEW], [MalloyParser_1.MalloyParser.COLON]],
19
+ },
20
+ {
21
+ errorMessage: "Missing '}' at '${currentToken}'",
22
+ ruleContextOptions: ['vExpr'],
23
+ offendingSymbol: MalloyParser_1.MalloyParser.VIEW,
24
+ currentToken: MalloyParser_1.MalloyParser.OCURLY,
25
+ },
26
+ {
27
+ errorMessage: "Missing '}' at '${currentToken}'",
28
+ ruleContextOptions: [
29
+ 'exploreProperties',
30
+ 'queryProperties',
31
+ 'exploreStatement',
32
+ ],
33
+ lookAheadOptions: [
34
+ [MalloyParser_1.MalloyParser.EOF],
35
+ [MalloyParser_1.MalloyParser.RUN],
36
+ [MalloyParser_1.MalloyParser.SOURCE],
37
+ ],
38
+ },
39
+ ];
40
+ class MalloyParserErrorListener {
41
+ constructor(translator, messages) {
42
+ this.translator = translator;
43
+ this.messages = messages;
44
+ }
45
+ logError(code, parameters, options) {
46
+ this.messages.log((0, parse_log_1.makeLogMessage)(code, parameters, { severity: 'error', ...options }));
47
+ return code;
48
+ }
49
+ syntaxError(recognizer, offendingSymbol, line, charPositionInLine, message, _e) {
50
+ const errAt = { line: line - 1, character: charPositionInLine };
51
+ const range = offendingSymbol
52
+ ? this.translator.rangeFromToken(offendingSymbol)
53
+ : { start: errAt, end: errAt };
54
+ const overrideMessage = (0, custom_error_messages_1.checkCustomErrorMessage)(recognizer, offendingSymbol, exports.commonErrorCases);
55
+ if (overrideMessage) {
56
+ message = overrideMessage;
57
+ }
58
+ this.logError('syntax-error', { message }, {
59
+ at: { url: this.translator.sourceURL, range },
60
+ });
61
+ }
62
+ }
63
+ exports.MalloyParserErrorListener = MalloyParserErrorListener;
64
+ //# sourceMappingURL=malloy-parser-error-listener.js.map
@@ -33,6 +33,7 @@ declare global {
33
33
  toTranslate(): R;
34
34
  toReturnType(tp: string): R;
35
35
  toLog(...expectedErrors: ProblemSpec[]): R;
36
+ toLogAtLeast(...expectedErrors: ProblemSpec[]): R;
36
37
  isLocationIn(at: DocumentLocation, txt: string): R;
37
38
  /**
38
39
  * expect(X).compilesTo('expression-string')
@@ -236,7 +236,11 @@ expect.extend({
236
236
  },
237
237
  toLog: function (s, ...msgs) {
238
238
  const expectCompiles = !msgs.some(m => m.severity === 'error');
239
- return checkForProblems(this, expectCompiles, s, ...msgs);
239
+ return checkForProblems(this, expectCompiles, s, false, ...msgs);
240
+ },
241
+ toLogAtLeast: function (s, ...msgs) {
242
+ const expectCompiles = !msgs.some(m => m.severity === 'error');
243
+ return checkForProblems(this, expectCompiles, s, true, ...msgs);
240
244
  },
241
245
  isLocationIn: function (checkAt, at, text) {
242
246
  if (this.equals(at, checkAt)) {
@@ -327,7 +331,7 @@ expect.extend({
327
331
  function problemSpecSummary(s) {
328
332
  return `${s.severity} '${'message' in s ? s.message : s.code}' ${s.data !== undefined ? (0, test_translator_1.pretty)(s.data) : ''}`;
329
333
  }
330
- function checkForProblems(context, expectCompiles, s, ...msgs) {
334
+ function checkForProblems(context, expectCompiles, s, allowAdditionalErrors, ...msgs) {
331
335
  var _a;
332
336
  let emsg = `Expected ${expectCompiles ? 'to' : 'to not'} compile with: `;
333
337
  const mSrc = isMarkedSource(s) ? s : undefined;
@@ -404,7 +408,7 @@ function checkForProblems(context, expectCompiles, s, ...msgs) {
404
408
  if (i !== msgs.length) {
405
409
  explain.push(...msgs.slice(i).map(m => `Missing: ${m}`));
406
410
  }
407
- if (i !== errList.length) {
411
+ if (!allowAdditionalErrors && i !== errList.length) {
408
412
  explain.push(...errList.slice(i).map(m => `Unexpected Error: ${m.message}`));
409
413
  }
410
414
  if (explain.length === 0) {
@@ -89,7 +89,7 @@ describe('model statements', () => {
89
89
  });
90
90
  describe('error handling', () => {
91
91
  test('no close brace', () => {
92
- expect('run: a -> { group_by: ai').toLog((0, test_translator_1.errorMessage)("'{' missing a '}'"));
92
+ expect('run: a -> { group_by: ai').toLog((0, test_translator_1.errorMessage)("Missing '}' at '<EOF>'"));
93
93
  });
94
94
  test('field and query with same name does not overflow', () => {
95
95
  expect(`
@@ -0,0 +1 @@
1
+ import './parse-expects';
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ const test_translator_1 = require("./test-translator");
10
+ require("./parse-expects");
11
+ describe('errors', () => {
12
+ test('source missing closing curly: EOF', () => {
13
+ expect('source: x is a extend {').toLogAtLeast((0, test_translator_1.errorMessage)("Missing '}' at '<EOF>'"));
14
+ });
15
+ test('view is missing name', () => {
16
+ expect(`
17
+ source: x is a extend {
18
+ view: {
19
+ group_by: b
20
+ }
21
+ }
22
+ `).toLogAtLeast((0, test_translator_1.errorMessage)("'view:' must be followed by '<identifier> is {'"));
23
+ });
24
+ test('missing closing curly, source>view', () => {
25
+ expect(`
26
+ source: x is a extend {
27
+ view: y is {
28
+ group_by: b
29
+
30
+ view: z is {
31
+ group_by: c
32
+ }
33
+ }
34
+ `).toLogAtLeast((0, test_translator_1.errorMessage)("Missing '}' at 'view:'"));
35
+ });
36
+ test('incorrect opening curly after dimension', () => {
37
+ expect(`
38
+ source: x is a extend {
39
+ dimension: {
40
+ test is best
41
+ }
42
+ }
43
+ `).toLogAtLeast((0, test_translator_1.errorMessage)("extraneous input '{' expecting {BQ_STRING, IDENTIFIER}"));
44
+ });
45
+ test('misspelled primarykey', () => {
46
+ expect(`
47
+ source: x is a extend {
48
+ primarykey: id
49
+ }
50
+ `).toLogAtLeast((0, test_translator_1.errorMessage)("no viable alternative at input 'primarykey'"));
51
+ });
52
+ test('missing opening curly after source extend keyword', () => {
53
+ expect(`
54
+ source: x is a extend
55
+ primary_key: id
56
+ `).toLogAtLeast((0, test_translator_1.errorMessage)("Missing '{' after 'extend'"));
57
+ });
58
+ });
59
+ //# sourceMappingURL=syntax-errors.spec.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy",
3
- "version": "0.0.236-dev250213234155",
3
+ "version": "0.0.236-dev250219154103",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./dist/index.js",