@malloydata/malloy 0.0.237-dev250225015031 → 0.0.237-dev250225213433

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.
Files changed (38) hide show
  1. package/dist/annotation.d.ts +15 -0
  2. package/dist/annotation.js +86 -0
  3. package/dist/index.d.ts +2 -1
  4. package/dist/index.js +5 -3
  5. package/dist/lang/ast/types/annotation-elements.d.ts +1 -1
  6. package/dist/lang/ast/types/annotation-elements.js +2 -2
  7. package/dist/lang/ast/types/malloy-element.d.ts +1 -1
  8. package/dist/lang/ast/types/malloy-element.js +2 -2
  9. package/dist/lang/malloy-to-ast.d.ts +1 -1
  10. package/dist/lang/malloy-to-ast.js +2 -2
  11. package/dist/lang/parse-malloy.d.ts +1 -1
  12. package/dist/lang/parse-malloy.js +4 -3
  13. package/dist/lang/parse-utils.d.ts +0 -11
  14. package/dist/lang/parse-utils.js +4 -82
  15. package/dist/lang/syntax-errors/custom-error-messages.d.ts +4 -2
  16. package/dist/lang/syntax-errors/custom-error-messages.js +75 -16
  17. package/dist/lang/syntax-errors/malloy-error-strategy.d.ts +4 -6
  18. package/dist/lang/syntax-errors/malloy-error-strategy.js +3 -22
  19. package/dist/lang/syntax-errors/malloy-parser-error-listener.d.ts +1 -1
  20. package/dist/lang/syntax-errors/malloy-parser-error-listener.js +107 -10
  21. package/dist/lang/test/literals.spec.js +0 -34
  22. package/dist/lang/test/syntax-errors.spec.js +113 -59
  23. package/dist/malloy.d.ts +13 -8
  24. package/dist/malloy.js +23 -23
  25. package/dist/model/malloy_query.d.ts +3 -1
  26. package/dist/model/malloy_query.js +18 -3
  27. package/dist/model/materialization/utils.js +2 -2
  28. package/dist/to_stable.d.ts +3 -0
  29. package/dist/to_stable.js +170 -0
  30. package/package.json +3 -1
  31. package/dist/lang/lib/Malloy/MalloyTagLexer.d.ts +0 -42
  32. package/dist/lang/lib/Malloy/MalloyTagLexer.js +0 -385
  33. package/dist/lang/lib/Malloy/MalloyTagParser.d.ts +0 -180
  34. package/dist/lang/lib/Malloy/MalloyTagParser.js +0 -1051
  35. package/dist/lang/lib/Malloy/MalloyTagVisitor.d.ts +0 -120
  36. package/dist/lang/lib/Malloy/MalloyTagVisitor.js +0 -4
  37. package/dist/tags.d.ts +0 -72
  38. package/dist/tags.js +0 -512
@@ -0,0 +1,15 @@
1
+ import { Tag } from '@malloydata/malloy-tag';
2
+ import { Annotation } from './model';
3
+ import { LogMessage } from './lang';
4
+ export interface TagParseSpec {
5
+ prefix?: RegExp;
6
+ extending?: Tag;
7
+ scopes?: Tag[];
8
+ }
9
+ export declare function addModelScope(spec: TagParseSpec | undefined, modelScope: Tag): TagParseSpec;
10
+ export declare function annotationToTaglines(annote: Annotation | undefined, prefix?: RegExp): string[];
11
+ export interface MalloyTagParse {
12
+ tag: Tag;
13
+ log: LogMessage[];
14
+ }
15
+ export declare function annotationToTag(annote: Annotation | undefined, spec?: TagParseSpec): MalloyTagParse;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.annotationToTag = exports.annotationToTaglines = exports.addModelScope = void 0;
4
+ const malloy_tag_1 = require("@malloydata/malloy-tag");
5
+ function addModelScope(spec, modelScope) {
6
+ const useSpec = spec ? { ...spec } : {};
7
+ if (useSpec.scopes) {
8
+ useSpec.scopes = useSpec.scopes.concat(modelScope);
9
+ }
10
+ else {
11
+ useSpec.scopes = [modelScope];
12
+ }
13
+ return useSpec;
14
+ }
15
+ exports.addModelScope = addModelScope;
16
+ function annotationToTaglines(annote, prefix) {
17
+ annote || (annote = {});
18
+ const tagLines = annote.inherits
19
+ ? annotationToTaglines(annote.inherits, prefix)
20
+ : [];
21
+ function prefixed(na) {
22
+ const ret = [];
23
+ for (const n of na || []) {
24
+ if (prefix === undefined || n.text.match(prefix)) {
25
+ ret.push(n.text);
26
+ }
27
+ }
28
+ return ret;
29
+ }
30
+ return tagLines.concat(prefixed(annote.blockNotes), prefixed(annote.notes));
31
+ }
32
+ exports.annotationToTaglines = annotationToTaglines;
33
+ function annotationToTag(annote, spec = {}) {
34
+ var _a;
35
+ let extending = spec.extending || new malloy_tag_1.Tag();
36
+ const prefix = spec.prefix || /^##? /;
37
+ annote || (annote = {});
38
+ const allErrs = [];
39
+ if (annote.inherits) {
40
+ const inherits = annotationToTag(annote.inherits, spec);
41
+ allErrs.push(...inherits.log);
42
+ extending = inherits.tag;
43
+ }
44
+ const allNotes = [];
45
+ if (annote.blockNotes) {
46
+ allNotes.push(...annote.blockNotes);
47
+ }
48
+ if (annote.notes) {
49
+ allNotes.push(...annote.notes);
50
+ }
51
+ const matchingNotes = [];
52
+ for (const note of allNotes) {
53
+ if (note.text.match(prefix)) {
54
+ matchingNotes.push(note);
55
+ }
56
+ }
57
+ for (let i = 0; i < matchingNotes.length; i++) {
58
+ const note = matchingNotes[i];
59
+ if (note.text.match(prefix)) {
60
+ const noteParse = malloy_tag_1.Tag.fromTagLine(note.text, i, extending, ...((_a = spec.scopes) !== null && _a !== void 0 ? _a : []));
61
+ extending = noteParse.tag;
62
+ allErrs.push(...noteParse.log.map((e) => mapMalloyError(e, note)));
63
+ }
64
+ }
65
+ return { tag: extending, log: allErrs };
66
+ }
67
+ exports.annotationToTag = annotationToTag;
68
+ function mapMalloyError(e, note) {
69
+ const loc = {
70
+ line: note.at.range.start.line,
71
+ character: note.at.range.start.character + e.offset,
72
+ };
73
+ return {
74
+ code: 'tag-parse-error',
75
+ severity: 'error',
76
+ message: e.message,
77
+ at: {
78
+ url: note.at.url,
79
+ range: {
80
+ start: loc,
81
+ end: loc,
82
+ },
83
+ },
84
+ };
85
+ }
86
+ //# sourceMappingURL=annotation.js.map
package/dist/index.d.ts CHANGED
@@ -10,4 +10,5 @@ export type { QueryOptionsReader, RunSQLOptions } from './run_sql_options';
10
10
  export type { EventStream, ModelString, ModelURL, QueryString, QueryURL, URLReader, InvalidationKey, } from './runtime_types';
11
11
  export type { Connection, ConnectionConfig, ConnectionFactory, ConnectionParameter, ConnectionParameterValue, ConnectionConfigSchema, FetchSchemaOptions, InfoConnection, LookupConnection, PersistSQLResults, PooledConnection, TestableConnection, StreamingConnection, } from './connection/types';
12
12
  export { toAsyncGenerator } from './connection_utils';
13
- export { type TagParse, Tag, type TagDict } from './tags';
13
+ export { modelDefToModelInfo } from './to_stable';
14
+ export { annotationToTag } from './annotation';
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.InMemoryURLReader = exports.EmptyURLReader = exports.SingleConnectionRuntime = exports.ConnectionRuntime = exports.AtomicFieldType = exports.Runtime = exports.Malloy = exports.Model = exports.MalloyTranslator = exports.composeSQLExpr = exports.indent = exports.expressionIsUngroupedAggregate = exports.expressionIsScalar = exports.expressionIsCalculation = exports.expressionIsAnalytic = exports.expressionIsAggregate = exports.mkFieldDef = exports.mkArrayDef = exports.isScalarArray = exports.isRepeatedRecord = exports.isSamplingRows = exports.isSamplingPercent = exports.isSamplingEnable = exports.isJoinedSource = exports.isJoined = exports.isLeafAtomic = exports.Segment = exports.isSourceDef = exports.TinyParser = exports.Dialect = exports.spread = exports.literal = exports.variadicParam = exports.param = exports.makeParam = exports.sql = exports.maxScalar = exports.minAggregate = exports.anyExprType = exports.minScalar = exports.overload = exports.qtz = exports.arg = exports.registerDialect = exports.MySQLDialect = exports.SnowflakeDialect = exports.PostgresDialect = exports.TrinoDialect = exports.StandardSQLDialect = exports.DuckDBDialect = void 0;
4
- exports.Tag = exports.toAsyncGenerator = exports.CacheManager = exports.InMemoryModelCache = exports.Explore = exports.DataWriter = exports.Parse = exports.JSONWriter = exports.CSVWriter = exports.QueryMaterializer = exports.Result = exports.PreparedResult = exports.TimestampTimeframe = exports.DateTimeframe = exports.SourceRelationship = exports.JoinRelationship = exports.MalloyError = exports.FixedConnectionMap = void 0;
4
+ exports.annotationToTag = exports.modelDefToModelInfo = exports.toAsyncGenerator = exports.CacheManager = exports.InMemoryModelCache = exports.Explore = exports.DataWriter = exports.Parse = exports.JSONWriter = exports.CSVWriter = exports.QueryMaterializer = exports.Result = exports.PreparedResult = exports.TimestampTimeframe = exports.DateTimeframe = exports.SourceRelationship = exports.JoinRelationship = exports.MalloyError = exports.FixedConnectionMap = void 0;
5
5
  /*
6
6
  * Copyright 2023 Google LLC
7
7
  *
@@ -98,6 +98,8 @@ Object.defineProperty(exports, "InMemoryModelCache", { enumerable: true, get: fu
98
98
  Object.defineProperty(exports, "CacheManager", { enumerable: true, get: function () { return malloy_1.CacheManager; } });
99
99
  var connection_utils_1 = require("./connection_utils");
100
100
  Object.defineProperty(exports, "toAsyncGenerator", { enumerable: true, get: function () { return connection_utils_1.toAsyncGenerator; } });
101
- var tags_1 = require("./tags");
102
- Object.defineProperty(exports, "Tag", { enumerable: true, get: function () { return tags_1.Tag; } });
101
+ var to_stable_1 = require("./to_stable");
102
+ Object.defineProperty(exports, "modelDefToModelInfo", { enumerable: true, get: function () { return to_stable_1.modelDefToModelInfo; } });
103
+ var annotation_1 = require("./annotation");
104
+ Object.defineProperty(exports, "annotationToTag", { enumerable: true, get: function () { return annotation_1.annotationToTag; } });
103
105
  //# sourceMappingURL=index.js.map
@@ -1,5 +1,5 @@
1
1
  import { Note } from '../../../model/malloy_types';
2
- import { Tag } from '../../../tags';
2
+ import { Tag } from '@malloydata/malloy-tag';
3
3
  import { MessageLogger } from '../../parse-log';
4
4
  import { Document, DocStatement, MalloyElement } from './malloy-element';
5
5
  import { QueryPropertyInterface } from './query-property-interface';
@@ -23,8 +23,8 @@
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.ModelAnnotation = exports.ObjectAnnotation = void 0;
26
- const tags_1 = require("../../../tags");
27
26
  const malloy_element_1 = require("./malloy-element");
27
+ const annotation_1 = require("../../../annotation");
28
28
  class ObjectAnnotation extends malloy_element_1.MalloyElement {
29
29
  constructor(notes) {
30
30
  super();
@@ -42,7 +42,7 @@ class ModelAnnotation extends ObjectAnnotation {
42
42
  this.elementType = 'modelAnnotation';
43
43
  }
44
44
  getCompilerFlags(existing, logTo) {
45
- const tagParse = tags_1.Tag.annotationToTag({ notes: this.notes }, {
45
+ const tagParse = (0, annotation_1.annotationToTag)({ notes: this.notes }, {
46
46
  prefix: /^##! /,
47
47
  extending: existing,
48
48
  });
@@ -1,5 +1,5 @@
1
1
  import { Annotation, DocumentLocation, DocumentReference, ModelDef, ModelAnnotation, NamedModelObject, Query, StructDef } from '../../../model/malloy_types';
2
- import { Tag } from '../../../tags';
2
+ import { Tag } from '@malloydata/malloy-tag';
3
3
  import { LogMessageOptions, MessageLogger, MessageParameterType, MessageCode } from '../../parse-log';
4
4
  import { MalloyTranslation } from '../../parse-malloy';
5
5
  import { ModelDataRequest } from '../../translate-response';
@@ -25,7 +25,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.Document = exports.DocStatementList = exports.ListOf = exports.isDocStatementOrDocStatementList = exports.isDocStatement = exports.ExperimentalExperiment = exports.ModelEntryReference = exports.Unimplemented = exports.MalloyElement = void 0;
26
26
  const dialect_1 = require("../../../dialect");
27
27
  const malloy_types_1 = require("../../../model/malloy_types");
28
- const tags_1 = require("../../../tags");
28
+ const malloy_tag_1 = require("@malloydata/malloy-tag");
29
29
  const parse_log_1 = require("../../parse-log");
30
30
  const ast_utils_1 = require("../ast-utils");
31
31
  const dialect_name_space_1 = require("./dialect-name-space");
@@ -432,7 +432,7 @@ class Document extends MalloyElement {
432
432
  this.queryList = [];
433
433
  this.didInitModel = false;
434
434
  this.annotation = {};
435
- this.experiments = new tags_1.Tag({});
435
+ this.experiments = new malloy_tag_1.Tag({});
436
436
  this.modelAnnotationTodoList = [];
437
437
  this.dialectNameSpaces = new Map();
438
438
  this.statements = new DocStatementList(statements);
@@ -10,7 +10,7 @@ import { FieldDeclarationConstructor } from './ast';
10
10
  import { HasString, HasID } from './parse-utils';
11
11
  import { CastType } from '../model';
12
12
  import { AccessModifierLabel, DocumentLocation, DocumentRange, Note } from '../model/malloy_types';
13
- import { Tag } from '../tags';
13
+ import { Tag } from '@malloydata/malloy-tag';
14
14
  declare class ErrorNode extends ast.SourceQueryElement {
15
15
  elementType: string;
16
16
  }
@@ -55,7 +55,7 @@ const Interval_1 = require("antlr4ts/misc/Interval");
55
55
  const ast_1 = require("./ast");
56
56
  const parse_utils_1 = require("./parse-utils");
57
57
  const malloy_types_1 = require("../model/malloy_types");
58
- const tags_1 = require("../tags");
58
+ const malloy_tag_1 = require("@malloydata/malloy-tag");
59
59
  const constant_expression_1 = require("./ast/expressions/constant-expression");
60
60
  const utils_1 = require("./utils");
61
61
  class ErrorNode extends ast.SourceQueryElement {
@@ -83,7 +83,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
83
83
  this.msgLog = msgLog;
84
84
  this.compilerFlags = compilerFlags;
85
85
  for (const flag of DEFAULT_COMPILER_FLAGS) {
86
- const withNewTag = tags_1.Tag.fromTagline(flag, this.compilerFlags);
86
+ const withNewTag = malloy_tag_1.Tag.fromTagLine(flag, 0, this.compilerFlags);
87
87
  this.compilerFlags = withNewTag.tag;
88
88
  }
89
89
  }
@@ -4,7 +4,7 @@ import { BaseMessageLogger, LogMessage, LogMessageOptions, MessageCode, MessageP
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';
7
- import { Tag } from '../tags';
7
+ import { Tag } from '@malloydata/malloy-tag';
8
8
  import { MalloyParseInfo } from './malloy-parse-info';
9
9
  import { EventStream } from '../runtime_types';
10
10
  export type StepResponses = DataRequestResponse | ASTResponse | TranslateResponse | ParseResponse | MetadataResponse | PretranslatedResponse;
@@ -61,9 +61,10 @@ const document_help_context_walker_1 = require("./parse-tree-walkers/document-he
61
61
  const reference_list_1 = require("./reference-list");
62
62
  const translate_response_1 = require("./translate-response");
63
63
  const utils_1 = require("./utils");
64
- const tags_1 = require("../tags");
64
+ const malloy_tag_1 = require("@malloydata/malloy-tag");
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 annotation_1 = require("../annotation");
67
68
  const malloy_error_strategy_1 = require("./syntax-errors/malloy-error-strategy");
68
69
  const malloy_parser_error_listener_1 = require("./syntax-errors/malloy-parser-error-listener");
69
70
  /**
@@ -470,7 +471,7 @@ class TranslateStep {
470
471
  }
471
472
  // begin with the compiler flags of the model we are extending
472
473
  if (extendingModel && !this.importedAnnotations) {
473
- const tagParse = tags_1.Tag.annotationToTag(extendingModel.annotation, {
474
+ const tagParse = (0, annotation_1.annotationToTag)(extendingModel.annotation, {
474
475
  prefix: /^##! /,
475
476
  });
476
477
  that.compilerFlags = tagParse.tag;
@@ -530,7 +531,7 @@ class MalloyTranslation {
530
531
  this.grammarRule = grammarRule;
531
532
  this.sqlSources = [];
532
533
  this.imports = [];
533
- this.compilerFlags = new tags_1.Tag();
534
+ this.compilerFlags = new malloy_tag_1.Tag();
534
535
  this._urlIsFullPath = undefined;
535
536
  /*
536
537
  Experimental dialect support, not confident this is how this should work.
@@ -14,17 +14,6 @@ export type HasString = {
14
14
  };
15
15
  type StringPart = ParserRuleContext | string;
16
16
  export declare function getStringParts(cx: SqlStringContext): Generator<StringPart>;
17
- /**
18
- * Parses the interior of a string, doing all \ substitutions. In most cases
19
- * a lexical analyzer has already recognized this as a string. As a convenience,
20
- * strip off the quoting outer chartacters if asked, then parse the interior of
21
- * the string. The intention is to be compatible with JSON strings, in terms
22
- * of which \X substitutions are processed.
23
- * @param str is the string to parse
24
- * @param surround is the quoting character, default means quotes already stripped
25
- * @returns a string with the \ processing completed
26
- */
27
- export declare function parseString(str: string, surround?: string): string;
28
17
  export type HasID = ParserRuleContext & {
29
18
  id: () => IdContext;
30
19
  };
@@ -22,7 +22,8 @@
22
22
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.getPlainString = exports.unIndent = exports.getOptionalId = exports.idToStr = exports.getId = exports.parseString = exports.getStringParts = exports.getStringIfShort = exports.getShortString = void 0;
25
+ exports.getPlainString = exports.unIndent = exports.getOptionalId = exports.idToStr = exports.getId = exports.getStringParts = exports.getStringIfShort = exports.getShortString = void 0;
26
+ const malloy_tag_1 = require("@malloydata/malloy-tag");
26
27
  /**
27
28
  * Take the text of a matched string, including the matching quote
28
29
  * characters, and return the actual contents of the string after
@@ -34,7 +35,7 @@ function getShortString(scx) {
34
35
  var _a, _b;
35
36
  const str = ((_a = scx.DQ_STRING()) === null || _a === void 0 ? void 0 : _a.text) || ((_b = scx.SQ_STRING()) === null || _b === void 0 ? void 0 : _b.text);
36
37
  if (str) {
37
- return parseString(str, str[0]);
38
+ return malloy_tag_1.ParseUtil.parseString(str, str[0]);
38
39
  }
39
40
  // shortString: DQ_STRING | SQ_STRING; So this will never happen
40
41
  return '';
@@ -66,85 +67,6 @@ function* getStringParts(cx) {
66
67
  }
67
68
  }
68
69
  exports.getStringParts = getStringParts;
69
- var ParseState;
70
- (function (ParseState) {
71
- ParseState[ParseState["Normal"] = 0] = "Normal";
72
- ParseState[ParseState["ReverseVirgule"] = 1] = "ReverseVirgule";
73
- ParseState[ParseState["Unicode"] = 2] = "Unicode";
74
- })(ParseState || (ParseState = {}));
75
- /**
76
- * Parses the interior of a string, doing all \ substitutions. In most cases
77
- * a lexical analyzer has already recognized this as a string. As a convenience,
78
- * strip off the quoting outer chartacters if asked, then parse the interior of
79
- * the string. The intention is to be compatible with JSON strings, in terms
80
- * of which \X substitutions are processed.
81
- * @param str is the string to parse
82
- * @param surround is the quoting character, default means quotes already stripped
83
- * @returns a string with the \ processing completed
84
- */
85
- function parseString(str, surround = '') {
86
- let inner = str.slice(surround.length);
87
- let state = ParseState.Normal;
88
- if (surround.length) {
89
- inner = inner.slice(0, -surround.length);
90
- }
91
- let out = '';
92
- let unicode = '';
93
- for (const c of inner) {
94
- switch (state) {
95
- case ParseState.Normal: {
96
- if (c === '\\') {
97
- state = ParseState.ReverseVirgule;
98
- }
99
- else {
100
- out += c;
101
- }
102
- break;
103
- }
104
- case ParseState.ReverseVirgule: {
105
- let outc = c;
106
- if (c === 'u') {
107
- state = ParseState.Unicode;
108
- unicode = '';
109
- continue;
110
- }
111
- if (c === 'b') {
112
- outc = '\b';
113
- }
114
- else if (c === 'f') {
115
- outc = '\f';
116
- }
117
- else if (c === 'n') {
118
- outc = '\n';
119
- }
120
- else if (c === 'r') {
121
- outc = '\r';
122
- }
123
- else if (c === 't') {
124
- outc = '\t';
125
- }
126
- out += outc;
127
- state = ParseState.Normal;
128
- break;
129
- }
130
- case ParseState.Unicode: {
131
- if ('ABCDEFabcdef0123456789'.includes(c)) {
132
- unicode += c;
133
- if (unicode.length === 4) {
134
- out += String.fromCharCode(parseInt(unicode, 16));
135
- state = ParseState.Normal;
136
- }
137
- }
138
- else {
139
- // Don't think we ever get here ...
140
- state = ParseState.Normal;
141
- }
142
- }
143
- }
144
- }
145
- return out;
146
- }
147
- exports.parseString = parseString;
148
70
  /**
149
71
  * An identifier is either a sequence of id characters or a `quoted`
150
72
  * This parses either to simply the resulting text.
@@ -158,7 +80,7 @@ exports.getId = getId;
158
80
  function idToStr(cx) {
159
81
  const quoted = cx.BQ_STRING();
160
82
  if (quoted) {
161
- return parseString(quoted.text, '`');
83
+ return malloy_tag_1.ParseUtil.parseString(quoted.text, '`');
162
84
  }
163
85
  return cx.text;
164
86
  }
@@ -2,10 +2,12 @@ import { Parser, Token } from 'antlr4ts';
2
2
  export interface ErrorCase {
3
3
  ruleContextOptions?: string[];
4
4
  offendingSymbol?: number;
5
+ offendingSymbolTextOptions?: string[];
5
6
  currentToken?: number;
6
7
  precedingTokenOptions?: number[][];
7
- lookAheadOptions?: number[][];
8
+ lookAheadOptions?: (number | string)[][];
8
9
  errorMessage: string;
9
- lookbackFromOffendingSymbol?: boolean;
10
+ lookbackSiblingRuleOptions?: number[];
11
+ predecessorHasAncestorRule?: number;
10
12
  }
11
13
  export declare const checkCustomErrorMessage: (parser: Parser, offendingSymbol: Token | undefined, errorCases: ErrorCase[]) => string;
@@ -8,6 +8,7 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.checkCustomErrorMessage = void 0;
10
10
  const checkCustomErrorMessage = (parser, offendingSymbol, errorCases) => {
11
+ var _a, _b;
11
12
  const currentRuleName = parser.getRuleInvocationStack()[0];
12
13
  const currentToken = parser.currentToken;
13
14
  for (const errorCase of errorCases) {
@@ -17,52 +18,110 @@ const checkCustomErrorMessage = (parser, offendingSymbol, errorCases) => {
17
18
  (offendingSymbol === null || offendingSymbol === void 0 ? void 0 : offendingSymbol.type) === errorCase.offendingSymbol;
18
19
  const isRuleContextMatch = !errorCase.ruleContextOptions ||
19
20
  errorCase.ruleContextOptions.includes(currentRuleName);
20
- if (isCurrentTokenMatch && isOffendingSymbolMatch && isRuleContextMatch) {
21
+ const isOffendingSymbolTextMatch = !errorCase.offendingSymbolTextOptions ||
22
+ errorCase.offendingSymbolTextOptions.includes(((_a = offendingSymbol === null || offendingSymbol === void 0 ? void 0 : offendingSymbol.text) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || '');
23
+ if (isCurrentTokenMatch &&
24
+ isOffendingSymbolMatch &&
25
+ isRuleContextMatch &&
26
+ isOffendingSymbolTextMatch) {
27
+ if (errorCase.lookbackSiblingRuleOptions) {
28
+ const siblings = parser.ruleContext.children;
29
+ if (!siblings) {
30
+ continue;
31
+ }
32
+ // We use 'any' here because the sibling isn't guaranteed to be a RuleContext,
33
+ // but we only care if it has a ruleIndex. If it doesn't then .includes() won't
34
+ // match anyways.
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
+ const precedingRule = siblings[siblings.length - 1];
37
+ if (!errorCase.lookbackSiblingRuleOptions.includes(precedingRule.ruleIndex)) {
38
+ continue;
39
+ }
40
+ }
21
41
  // If so, try to check the preceding tokens.
22
42
  if (errorCase.precedingTokenOptions) {
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));
43
+ const hasPrecedingTokenMatch = errorCase.precedingTokenOptions.some(sequence => checkTokenSequenceMatch(parser, sequence, 'lookback', offendingSymbol === null || offendingSymbol === void 0 ? void 0 : offendingSymbol.tokenIndex));
26
44
  if (!hasPrecedingTokenMatch) {
27
45
  continue; // Continue to check a different error case
28
46
  }
29
47
  }
30
48
  if (errorCase.lookAheadOptions) {
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));
49
+ const hasLookaheadTokenMatch = errorCase.lookAheadOptions.some(sequence => checkTokenSequenceMatch(parser, sequence, 'lookahead', offendingSymbol === null || offendingSymbol === void 0 ? void 0 : offendingSymbol.tokenIndex));
34
50
  if (!hasLookaheadTokenMatch) {
35
51
  continue; // Continue to check a different error case
36
52
  }
37
53
  }
54
+ if (errorCase.predecessorHasAncestorRule) {
55
+ const precedingSibling = (_b = parser.ruleContext.children) === null || _b === void 0 ? void 0 : _b[0];
56
+ if (!precedingSibling ||
57
+ !doesRightmostBranchContainRule(precedingSibling, errorCase.predecessorHasAncestorRule)) {
58
+ continue;
59
+ }
60
+ }
38
61
  // If all cases match, return the custom error message
39
- const message = errorCase.errorMessage.replace('${currentToken}', (offendingSymbol === null || offendingSymbol === void 0 ? void 0 : offendingSymbol.text) || currentToken.text || '');
62
+ let message = errorCase.errorMessage
63
+ .replace('${currentToken}', currentToken.text || '')
64
+ .replace('${offendingSymbol}', (offendingSymbol === null || offendingSymbol === void 0 ? void 0 : offendingSymbol.text) || '');
65
+ try {
66
+ const previousToken = parser.inputStream.LT(-1);
67
+ message = message.replace('${previousToken}', previousToken.text || '');
68
+ }
69
+ catch (ex) {
70
+ // This shouldn't ever occur, but if it does, just leave the untokenized message.
71
+ }
40
72
  return message;
41
73
  }
42
74
  }
43
75
  return '';
44
76
  };
45
77
  exports.checkCustomErrorMessage = checkCustomErrorMessage;
78
+ // Recursively walk the rightmost branch (the path to the most recent token)
79
+ // to see whether any of those parse tree nodes match the provided rule number.
80
+ const doesRightmostBranchContainRule = (root, ruleNumber, depthLimit = 20) => {
81
+ var _a;
82
+ if (root.ruleIndex === ruleNumber) {
83
+ return true;
84
+ }
85
+ if (depthLimit <= 0) {
86
+ return false;
87
+ }
88
+ const childCount = ((_a = root.children) === null || _a === void 0 ? void 0 : _a.length) || 0;
89
+ if (root.children && childCount > 0) {
90
+ const rightmostChild = root.children[root.children.length - 1];
91
+ return doesRightmostBranchContainRule(rightmostChild, ruleNumber, depthLimit - 1);
92
+ }
93
+ return false;
94
+ };
46
95
  const checkTokenSequenceMatch = (parser, sequence, direction, anchorTokenPosition) => {
96
+ var _a;
47
97
  try {
48
98
  for (let i = 0; i < sequence.length; i++) {
49
99
  let streamToken = undefined;
50
100
  if (typeof anchorTokenPosition === 'number') {
51
101
  const tokenOffset = direction === 'lookahead' ? i + 1 : -1 * (i + 1);
52
- streamToken = parser.inputStream.get(anchorTokenPosition + tokenOffset).type;
102
+ streamToken = parser.inputStream.get(anchorTokenPosition + tokenOffset);
53
103
  }
54
104
  else {
55
105
  // Note: positive lookahead starts at '2' because '1' is the current token.
56
106
  const tokenOffset = direction === 'lookahead' ? i + 2 : -1 * (i + 1);
57
- streamToken = parser.inputStream.LA(tokenOffset);
107
+ streamToken = parser.inputStream.LT(tokenOffset);
58
108
  }
59
- // Note: negative checking is < -1 becuase Token.EOF is -1, but below
60
- // that we use negatives to indicate "does-not-match" rules.
61
- if (sequence[i] >= -1 && streamToken !== sequence[i]) {
62
- return false;
109
+ if (typeof sequence[i] === 'number') {
110
+ const tokenIndex = sequence[i];
111
+ // Note: negative checking is < -1 becuase Token.EOF is -1, but below
112
+ // that we use negatives to indicate "does-not-match" rules.
113
+ if (tokenIndex >= -1 && streamToken.type !== tokenIndex) {
114
+ return false;
115
+ }
116
+ if (tokenIndex < -1 && streamToken.type === -1 * tokenIndex) {
117
+ return false;
118
+ }
63
119
  }
64
- if (sequence[i] < -1 && streamToken === -1 * sequence[i]) {
65
- return false;
120
+ else if (typeof sequence[i] === 'string') {
121
+ const tokenText = sequence[i];
122
+ if (tokenText !== ((_a = streamToken.text) === null || _a === void 0 ? void 0 : _a.toLowerCase())) {
123
+ return false;
124
+ }
66
125
  }
67
126
  }
68
127
  return true;
@@ -1,14 +1,12 @@
1
- import { DefaultErrorStrategy, Parser } from 'antlr4ts';
1
+ import { DefaultErrorStrategy } from 'antlr4ts';
2
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.
3
+ * Custom error strategy for the Malloy Parser.
4
+ *
5
+ * This class does not currently override default ANTLR error handling.
7
6
  *
8
7
  * For more details, read the documentation in DefaultErrorStrategy.d.ts
9
8
  * or reference the superclass at:
10
9
  * https://github.com/tunnelvisionlabs/antlr4ts/blob/master/src/DefaultErrorStrategy.ts
11
10
  */
12
11
  export declare class MalloyErrorStrategy extends DefaultErrorStrategy {
13
- sync(parser: Parser): void;
14
12
  }
@@ -8,35 +8,16 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.MalloyErrorStrategy = void 0;
10
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
11
  /**
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.
12
+ * Custom error strategy for the Malloy Parser.
13
+ *
14
+ * This class does not currently override default ANTLR error handling.
26
15
  *
27
16
  * For more details, read the documentation in DefaultErrorStrategy.d.ts
28
17
  * or reference the superclass at:
29
18
  * https://github.com/tunnelvisionlabs/antlr4ts/blob/master/src/DefaultErrorStrategy.ts
30
19
  */
31
20
  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
21
  }
41
22
  exports.MalloyErrorStrategy = MalloyErrorStrategy;
42
23
  //# sourceMappingURL=malloy-error-strategy.js.map
@@ -2,7 +2,7 @@ import { ANTLRErrorListener, Token } from 'antlr4ts';
2
2
  import { ErrorCase } from './custom-error-messages';
3
3
  import { MessageLogger, MessageCode, MessageParameterType, LogMessageOptions } from '../parse-log';
4
4
  import { MalloyTranslation } from '../parse-malloy';
5
- export declare const commonErrorCases: ErrorCase[];
5
+ export declare const malloyCustomErrorCases: ErrorCase[];
6
6
  export declare class MalloyParserErrorListener implements ANTLRErrorListener<Token> {
7
7
  readonly translator: MalloyTranslation;
8
8
  readonly messages: MessageLogger;