dt-sql-parser 4.0.0-beta.4.8-no-sourcemap → 4.0.0-beta.4.9

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/README.md CHANGED
@@ -47,6 +47,9 @@ Additionally, it provides auxiliary functions such as **SQL splitting** and **co
47
47
 
48
48
  We have provided [monaco-sql-languages](https://github.com/DTStack/monaco-sql-languages), it is easily to integrate with `monaco-editor`.
49
49
 
50
+ >Tips: If you want to run `dt-sql-parser` in browser, don't forget to install the `assert` and `util` polyfills, and define the global variable `process.env`.
51
+ None of this is needed in the node environment, because node has them built-in.
52
+
50
53
  <br/>
51
54
 
52
55
  ## Installation
package/dist/index.d.ts CHANGED
@@ -17,7 +17,7 @@ export * from './lib/impala/ImpalaSqlParserListener';
17
17
  export * from './lib/impala/ImpalaSqlParserVisitor';
18
18
  export { SyntaxContextType } from './parser/common/basic-parser-types';
19
19
  export type * from './parser/common/basic-parser-types';
20
- export type { SyntaxError, ParseError, ErrorHandler } from './parser/common/parseErrorListener';
20
+ export type { SyntaxError, ParseError, ErrorListener } from './parser/common/parseErrorListener';
21
21
  /**
22
22
  * @deprecated legacy, will be removed.
23
23
  */
@@ -4,9 +4,9 @@
4
4
  */
5
5
  export interface CaretPosition {
6
6
  /** start at 1 */
7
- lineNumber: number;
7
+ readonly lineNumber: number;
8
8
  /** start at 1 */
9
- column: number;
9
+ readonly column: number;
10
10
  }
11
11
  /**
12
12
  * Syntax context type at caret position
@@ -43,22 +43,22 @@ export declare enum SyntaxContextType {
43
43
  }
44
44
  export interface WordRange {
45
45
  /** content of word */
46
- text: string;
46
+ readonly text: string;
47
47
  /** start at 0 */
48
- startIndex: number;
49
- stopIndex: number;
48
+ readonly startIndex: number;
49
+ readonly stopIndex: number;
50
50
  /** start at 1 */
51
- line: number;
51
+ readonly line: number;
52
52
  /** start at 1 */
53
- startColumn: number;
54
- stopColumn: number;
53
+ readonly startColumn: number;
54
+ readonly stopColumn: number;
55
55
  }
56
56
  /**
57
57
  * Suggested information analyzed from the input
58
58
  */
59
59
  export interface SyntaxSuggestion<T = WordRange> {
60
- syntaxContextType: SyntaxContextType;
61
- wordRanges: T[];
60
+ readonly syntaxContextType: SyntaxContextType;
61
+ readonly wordRanges: T[];
62
62
  }
63
63
  /**
64
64
  * Suggested information analyzed from the input
@@ -67,21 +67,21 @@ export interface Suggestions<T = WordRange> {
67
67
  /**
68
68
  * Suggestions about syntax
69
69
  */
70
- syntax: SyntaxSuggestion<T>[];
70
+ readonly syntax: SyntaxSuggestion<T>[];
71
71
  /**
72
72
  * Suggestions about keywords
73
73
  */
74
- keywords: string[];
74
+ readonly keywords: string[];
75
75
  }
76
76
  export interface TextSlice {
77
77
  /** start at 0 */
78
- startIndex: number;
79
- endIndex: number;
78
+ readonly startIndex: number;
79
+ readonly endIndex: number;
80
80
  /** start at 1 */
81
- startLine: number;
82
- endLine: number;
81
+ readonly startLine: number;
82
+ readonly endLine: number;
83
83
  /** start at 1 */
84
- startColumn: number;
85
- endColumn: number;
86
- text: string;
84
+ readonly startColumn: number;
85
+ readonly endColumn: number;
86
+ readonly text: string;
87
87
  }
@@ -2,7 +2,7 @@ import { Parser, Lexer, Token, CommonTokenStream, CodePointCharStream, ParserRul
2
2
  import { ParseTreeListener } from 'antlr4ts/tree';
3
3
  import { CandidatesCollection } from 'antlr4-c3';
4
4
  import { CaretPosition, Suggestions, TextSlice } from './basic-parser-types';
5
- import { ParseError, ErrorHandler } from './parseErrorListener';
5
+ import { ParseError, ErrorListener } from './parseErrorListener';
6
6
  interface IParser<IParserRuleContext extends ParserRuleContext> extends Parser {
7
7
  program(): IParserRuleContext;
8
8
  }
@@ -22,7 +22,7 @@ export default abstract class BasicParser<L extends Lexer = Lexer, PRC extends P
22
22
  protected _parsedInput: string;
23
23
  protected _parseErrors: ParseError[];
24
24
  /** members for cache end */
25
- private _errorHandler;
25
+ private _errorListener;
26
26
  /**
27
27
  * PreferredRules for antlr4-c3
28
28
  */
@@ -53,19 +53,19 @@ export default abstract class BasicParser<L extends Lexer = Lexer, PRC extends P
53
53
  * Create an antlr4 lexer from input.
54
54
  * @param input string
55
55
  */
56
- createLexer(input: string, errorListener?: ErrorHandler<any>): L;
56
+ createLexer(input: string, errorListener?: ErrorListener<any>): L;
57
57
  /**
58
58
  * Create an antlr4 parser from input.
59
59
  * @param input string
60
60
  */
61
- createParser(input: string, errorListener?: ErrorHandler<any>): P;
61
+ createParser(input: string, errorListener?: ErrorListener<any>): P;
62
62
  /**
63
63
  * Parse input string and return parseTree.
64
64
  * @param input string
65
65
  * @param errorListener listen parse errors and lexer errors.
66
66
  * @returns parseTree
67
67
  */
68
- parse(input: string, errorListener?: ErrorHandler<any>): PRC;
68
+ parse(input: string, errorListener?: ErrorListener<any>): PRC;
69
69
  /**
70
70
  * Create an antlr4 parser from input.
71
71
  * And the instances will be cache.
@@ -1,8 +1,9 @@
1
1
  import { CharStreams, CommonTokenStream, } from 'antlr4ts';
2
2
  import { ParseTreeWalker } from 'antlr4ts/tree';
3
3
  import { CodeCompletionCore } from 'antlr4-c3';
4
- import { findCaretTokenIndex } from '../../utils/findCaretTokenIndex';
4
+ import { findCaretTokenIndex } from './utils/findCaretTokenIndex';
5
5
  import ParseErrorListener from './parseErrorListener';
6
+ import { ErrorStrategy } from './errorStrategy';
6
7
  /**
7
8
  * Custom Parser class, subclass needs extends it.
8
9
  */
@@ -11,7 +12,7 @@ export default class BasicParser {
11
12
  this._parsedInput = null;
12
13
  this._parseErrors = [];
13
14
  /** members for cache end */
14
- this._errorHandler = (error) => {
15
+ this._errorListener = (error) => {
15
16
  this._parseErrors.push(error);
16
17
  };
17
18
  }
@@ -51,6 +52,7 @@ export default class BasicParser {
51
52
  parse(input, errorListener) {
52
53
  const parser = this.createParser(input, errorListener);
53
54
  parser.buildParseTree = true;
55
+ parser.errorHandler = new ErrorStrategy();
54
56
  return parser.program();
55
57
  }
56
58
  /**
@@ -63,7 +65,7 @@ export default class BasicParser {
63
65
  this._charStreams = CharStreams.fromString(input.toUpperCase());
64
66
  this._lexer = this.createLexerFormCharStream(this._charStreams);
65
67
  this._lexer.removeErrorListeners();
66
- this._lexer.addErrorListener(new ParseErrorListener(this._errorHandler));
68
+ this._lexer.addErrorListener(new ParseErrorListener(this._errorListener));
67
69
  this._tokenStream = new CommonTokenStream(this._lexer);
68
70
  /**
69
71
  * All tokens are generated in advance.
@@ -73,6 +75,7 @@ export default class BasicParser {
73
75
  this._tokenStream.fill();
74
76
  this._parser = this.createParserFromTokenStream(this._tokenStream);
75
77
  this._parser.buildParseTree = true;
78
+ this._parser.errorHandler = new ErrorStrategy();
76
79
  return this._parser;
77
80
  }
78
81
  /**
@@ -92,7 +95,7 @@ export default class BasicParser {
92
95
  const parser = this.createParserWithCache(input);
93
96
  this._parsedInput = input;
94
97
  parser.removeErrorListeners();
95
- parser.addErrorListener(new ParseErrorListener(this._errorHandler));
98
+ parser.addErrorListener(new ParseErrorListener(this._errorListener));
96
99
  this._parseTree = parser.program();
97
100
  return this._parseTree;
98
101
  }
@@ -161,6 +164,7 @@ export default class BasicParser {
161
164
  * @returns suggestion
162
165
  */
163
166
  getSuggestionAtCaretPosition(input, caretPosition) {
167
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
164
168
  const splitListener = this.splitListener;
165
169
  // TODO: add splitListener to all sqlParser implements add remove following if
166
170
  if (!splitListener)
@@ -175,48 +179,63 @@ export default class BasicParser {
175
179
  return null;
176
180
  /**
177
181
  * Split sql by statement.
178
- * Try to collect candidates from the caret statement only.
182
+ * Try to collect candidates in as small a range as possible.
179
183
  */
180
184
  this.listen(splitListener, this._parseTree);
185
+ const statementCount = (_a = splitListener.statementsContext) === null || _a === void 0 ? void 0 : _a.length;
186
+ const statementsContext = splitListener.statementsContext;
181
187
  // If there are multiple statements.
182
- if (splitListener.statementsContext.length > 1) {
183
- // find statement rule context where caretPosition is located.
184
- const caretStatementContext = splitListener === null || splitListener === void 0 ? void 0 : splitListener.statementsContext.find((ctx) => {
185
- var _a;
186
- return (caretTokenIndex <= ((_a = ctx.stop) === null || _a === void 0 ? void 0 : _a.tokenIndex) &&
187
- caretTokenIndex >= ctx.start.tokenIndex);
188
- });
189
- if (caretStatementContext) {
190
- c3Context = caretStatementContext;
191
- }
192
- else {
193
- const lastStatementToken = splitListener.statementsContext[(splitListener === null || splitListener === void 0 ? void 0 : splitListener.statementsContext.length) - 1]
194
- .start;
188
+ if (statementCount > 1) {
189
+ /**
190
+ * Find a minimum valid range, reparse the fragment, and provide a new parse tree to C3.
191
+ * The boundaries of this range must be statements with no syntax errors.
192
+ * This can ensure the stable performance of the C3.
193
+ */
194
+ let startStatement;
195
+ let stopStatement;
196
+ for (let index = 0; index < statementCount; index++) {
197
+ const ctx = statementsContext[index];
198
+ const isCurrentCtxValid = !ctx.exception;
199
+ if (!isCurrentCtxValid)
200
+ continue;
195
201
  /**
196
- * If caretStatementContext is not found and it follows all statements.
197
- * Reparses part of the input following the penultimate statement.
198
- * And c3 will collect candidates in the new parseTreeContext.
202
+ * Ensure that the statementContext before the left boundary
203
+ * and the last statementContext on the right boundary are qualified SQL statements.
199
204
  */
200
- if (caretTokenIndex > (lastStatementToken === null || lastStatementToken === void 0 ? void 0 : lastStatementToken.tokenIndex)) {
201
- /**
202
- * Save offset of the tokenIndex in the partInput
203
- * compared to the tokenIndex in the whole input
204
- */
205
- tokenIndexOffset = lastStatementToken === null || lastStatementToken === void 0 ? void 0 : lastStatementToken.tokenIndex;
206
- // Correct caretTokenIndex
207
- caretTokenIndex = caretTokenIndex - tokenIndexOffset;
208
- const inputSlice = input.slice(lastStatementToken.startIndex);
209
- const lexer = this.createLexer(inputSlice);
210
- lexer.removeErrorListeners();
211
- const tokenStream = new CommonTokenStream(lexer);
212
- tokenStream.fill();
213
- const parser = this.createParserFromTokenStream(tokenStream);
214
- parser.removeErrorListeners();
215
- parser.buildParseTree = true;
216
- sqlParserIns = parser;
217
- c3Context = parser.program();
205
+ const isPrevCtxValid = index === 0 || !((_b = statementsContext[index - 1]) === null || _b === void 0 ? void 0 : _b.exception);
206
+ const isNextCtxValid = index === statementCount - 1 || !((_c = statementsContext[index + 1]) === null || _c === void 0 ? void 0 : _c.exception);
207
+ if (ctx.stop.tokenIndex < caretTokenIndex && isPrevCtxValid) {
208
+ startStatement = ctx;
209
+ }
210
+ if (!stopStatement && ctx.start.tokenIndex > caretTokenIndex && isNextCtxValid) {
211
+ stopStatement = ctx;
212
+ break;
218
213
  }
219
214
  }
215
+ // A boundary consisting of the index of the input.
216
+ const startIndex = (_e = (_d = startStatement === null || startStatement === void 0 ? void 0 : startStatement.start) === null || _d === void 0 ? void 0 : _d.startIndex) !== null && _e !== void 0 ? _e : 0;
217
+ const stopIndex = (_g = (_f = stopStatement === null || stopStatement === void 0 ? void 0 : stopStatement.stop) === null || _f === void 0 ? void 0 : _f.stopIndex) !== null && _g !== void 0 ? _g : input.length - 1;
218
+ /**
219
+ * Save offset of the tokenIndex in the range of input
220
+ * compared to the tokenIndex in the whole input
221
+ */
222
+ tokenIndexOffset = (_j = (_h = startStatement === null || startStatement === void 0 ? void 0 : startStatement.start) === null || _h === void 0 ? void 0 : _h.tokenIndex) !== null && _j !== void 0 ? _j : 0;
223
+ caretTokenIndex = caretTokenIndex - tokenIndexOffset;
224
+ /**
225
+ * Reparse the input fragment,
226
+ * and c3 will collect candidates in the newly generated parseTree.
227
+ */
228
+ const inputSlice = input.slice(startIndex, stopIndex);
229
+ const lexer = this.createLexer(inputSlice);
230
+ lexer.removeErrorListeners();
231
+ const tokenStream = new CommonTokenStream(lexer);
232
+ tokenStream.fill();
233
+ const parser = this.createParserFromTokenStream(tokenStream);
234
+ parser.removeErrorListeners();
235
+ parser.buildParseTree = true;
236
+ parser.errorHandler = new ErrorStrategy();
237
+ sqlParserIns = parser;
238
+ c3Context = parser.program();
220
239
  }
221
240
  const core = new CodeCompletionCore(sqlParserIns);
222
241
  core.preferredRules = this.preferredRules;
@@ -0,0 +1,12 @@
1
+ import { DefaultErrorStrategy } from 'antlr4ts/DefaultErrorStrategy';
2
+ import { Parser } from 'antlr4ts/Parser';
3
+ import { RecognitionException } from 'antlr4ts/RecognitionException';
4
+ import { Token } from 'antlr4ts/Token';
5
+ /**
6
+ * Base on DefaultErrorStrategy.
7
+ * The difference is that it assigns exception to the context.exception when it encounters error.
8
+ */
9
+ export declare class ErrorStrategy extends DefaultErrorStrategy {
10
+ recover(recognizer: Parser, e: RecognitionException): void;
11
+ recoverInline(recognizer: Parser): Token;
12
+ }
@@ -0,0 +1,51 @@
1
+ import { DefaultErrorStrategy } from 'antlr4ts/DefaultErrorStrategy';
2
+ import { InputMismatchException } from 'antlr4ts/InputMismatchException';
3
+ import { IntervalSet } from 'antlr4ts/misc/IntervalSet';
4
+ /**
5
+ * Base on DefaultErrorStrategy.
6
+ * The difference is that it assigns exception to the context.exception when it encounters error.
7
+ */
8
+ export class ErrorStrategy extends DefaultErrorStrategy {
9
+ recover(recognizer, e) {
10
+ // Mark the context as an anomaly
11
+ for (let context = recognizer.context; context; context = context.parent) {
12
+ context.exception = e;
13
+ }
14
+ // Error recovery
15
+ if (this.lastErrorIndex === recognizer.inputStream.index &&
16
+ this.lastErrorStates &&
17
+ this.lastErrorStates.contains(recognizer.state)) {
18
+ recognizer.consume();
19
+ }
20
+ this.lastErrorIndex = recognizer.inputStream.index;
21
+ if (!this.lastErrorStates) {
22
+ this.lastErrorStates = new IntervalSet();
23
+ }
24
+ this.lastErrorStates.add(recognizer.state);
25
+ let followSet = this.getErrorRecoverySet(recognizer);
26
+ this.consumeUntil(recognizer, followSet);
27
+ }
28
+ recoverInline(recognizer) {
29
+ let e;
30
+ if (this.nextTokensContext === undefined) {
31
+ e = new InputMismatchException(recognizer);
32
+ }
33
+ else {
34
+ e = new InputMismatchException(recognizer, this.nextTokensState, this.nextTokensContext);
35
+ }
36
+ // Mark the context as an anomaly
37
+ for (let context = recognizer.context; context; context = context.parent) {
38
+ context.exception = e;
39
+ }
40
+ // Error recovery
41
+ let matchedSymbol = this.singleTokenDeletion(recognizer);
42
+ if (matchedSymbol) {
43
+ recognizer.consume();
44
+ return matchedSymbol;
45
+ }
46
+ if (this.singleTokenInsertion(recognizer)) {
47
+ return this.getMissingSymbol(recognizer);
48
+ }
49
+ throw e;
50
+ }
51
+ }
@@ -4,30 +4,30 @@ import { ATNSimulator } from 'antlr4ts/atn/ATNSimulator';
4
4
  * Converted from {@link SyntaxError}.
5
5
  */
6
6
  export interface ParseError {
7
- startLine: number;
8
- endLine: number;
9
- startCol: number;
10
- endCol: number;
11
- message: string;
7
+ readonly startLine: number;
8
+ readonly endLine: number;
9
+ readonly startCol: number;
10
+ readonly endCol: number;
11
+ readonly message: string;
12
12
  }
13
13
  /**
14
14
  * The type of error resulting from lexical parsing and parsing.
15
15
  */
16
16
  export interface SyntaxError<T> {
17
- recognizer: Recognizer<T, ATNSimulator>;
18
- offendingSymbol: Token;
19
- line: number;
20
- charPositionInLine: number;
21
- msg: string;
22
- e: RecognitionException;
17
+ readonly recognizer: Recognizer<T, ATNSimulator>;
18
+ readonly offendingSymbol: Token;
19
+ readonly line: number;
20
+ readonly charPositionInLine: number;
21
+ readonly msg: string;
22
+ readonly e: RecognitionException;
23
23
  }
24
24
  /**
25
- * ErrorHandler will be invoked when it encounters a parsing error.
25
+ * ErrorListener will be invoked when it encounters a parsing error.
26
26
  * Includes lexical errors and parsing errors.
27
27
  */
28
- export type ErrorHandler<T> = (parseError: ParseError, originalError: SyntaxError<T>) => void;
28
+ export type ErrorListener<T> = (parseError: ParseError, originalError: SyntaxError<T>) => void;
29
29
  export default class ParseErrorListener implements ANTLRErrorListener<Token> {
30
- private _errorHandler;
31
- constructor(errorListener: ErrorHandler<Token>);
30
+ private _errorListener;
31
+ constructor(errorListener: ErrorListener<Token>);
32
32
  syntaxError(recognizer: Recognizer<Token, ATNSimulator>, offendingSymbol: any, line: number, charPositionInLine: number, msg: string, e: RecognitionException): void;
33
33
  }
@@ -1,14 +1,14 @@
1
1
  export default class ParseErrorListener {
2
2
  constructor(errorListener) {
3
- this._errorHandler = errorListener;
3
+ this._errorListener = errorListener;
4
4
  }
5
5
  syntaxError(recognizer, offendingSymbol, line, charPositionInLine, msg, e) {
6
6
  let endCol = charPositionInLine + 1;
7
7
  if (offendingSymbol && offendingSymbol.text !== null) {
8
8
  endCol = charPositionInLine + offendingSymbol.text.length;
9
9
  }
10
- if (this._errorHandler) {
11
- this._errorHandler({
10
+ if (this._errorListener) {
11
+ this._errorListener({
12
12
  startLine: line,
13
13
  endLine: line,
14
14
  startCol: charPositionInLine,
@@ -1,5 +1,5 @@
1
1
  import { Token } from 'antlr4ts';
2
- import { CaretPosition } from '../parser/common/basic-parser-types';
2
+ import { CaretPosition } from '../basic-parser-types';
3
3
  /**
4
4
  * find token index via caret position (cursor position)
5
5
  * @param caretPosition
@@ -4,7 +4,7 @@ import { MySqlLexer } from '../lib/mysql/MySqlLexer';
4
4
  import { MySqlParser, ProgramContext, SingleStatementContext } from '../lib/mysql/MySqlParser';
5
5
  import BasicParser from './common/basicParser';
6
6
  import { Suggestions } from './common/basic-parser-types';
7
- import { MySqlParserListener } from 'src/lib/mysql/MySqlParserListener';
7
+ import { MySqlParserListener } from '../lib/mysql/MySqlParserListener';
8
8
  export default class MySQL extends BasicParser<MySqlLexer, ProgramContext, MySqlParser> {
9
9
  protected createLexerFormCharStream(charStreams: any): MySqlLexer;
10
10
  protected createParserFromTokenStream(tokenStream: any): MySqlParser;
@@ -4,7 +4,7 @@ import { SparkSqlLexer } from '../lib/spark/SparkSqlLexer';
4
4
  import { SparkSqlParser, ProgramContext, SingleStatementContext } from '../lib/spark/SparkSqlParser';
5
5
  import BasicParser from './common/basicParser';
6
6
  import { Suggestions } from './common/basic-parser-types';
7
- import { SparkSqlParserListener } from 'src/lib/spark/SparkSqlParserListener';
7
+ import { SparkSqlParserListener } from '../lib/spark/SparkSqlParserListener';
8
8
  export default class SparkSQL extends BasicParser<SparkSqlLexer, ProgramContext, SparkSqlParser> {
9
9
  protected createLexerFormCharStream(charStreams: any): SparkSqlLexer;
10
10
  protected createParserFromTokenStream(tokenStream: any): SparkSqlParser;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dt-sql-parser",
3
- "version": "4.0.0-beta.4.8-no-sourcemap",
3
+ "version": "4.0.0-beta.4.9",
4
4
  "authors": "DTStack Corporation",
5
5
  "description": "SQL Parsers for BigData, built with antlr4",
6
6
  "keywords": [
@@ -28,6 +28,7 @@
28
28
  "release": "node ./scripts/release.js",
29
29
  "lint": "prettier --check '**/*.ts' --config ./.prettierrc",
30
30
  "lint-fix": "prettier --write '**/*.ts' --config ./.prettierrc",
31
+ "format-g4": "antlr-format -c ./antlr.format.json -v ./src/grammar/**/*.g4",
31
32
  "cleanComment": "node ./scripts/cleanCommentCli.js"
32
33
  },
33
34
  "license": "MIT",
@@ -39,6 +40,7 @@
39
40
  "@swc/jest": "^0.2.26",
40
41
  "@types/jest": "^29.5.1",
41
42
  "@types/node": "^18.15.11",
43
+ "antlr-format-cli": "^1.2.1",
42
44
  "antlr4ts-cli": "^0.5.0-alpha.4",
43
45
  "chalk": "4.1.2",
44
46
  "commitizen": "^4.3.0",