Skip to content

Commit 9b8d7b1

Browse files
committed
feat: improve semanticContextCollector docs
1 parent b29d058 commit 9b8d7b1

8 files changed

+52
-29
lines changed

src/parser/common/semanticContextCollector.ts

+45-22
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export const SQL_SPLIT_SYMBOL_TEXT = ';';
66

77
abstract class SemanticContextCollector {
88
constructor(_input: string, caretPosition: CaretPosition, allTokens: Token[]) {
9-
// If caretPosition is whiteSpace, tokenIndex may be undefined.
9+
// If caretPosition token is whiteSpace, tokenIndex may be undefined.
1010
const tokenIndex = findCaretTokenIndex(caretPosition, allTokens);
1111

1212
if (tokenIndex !== undefined) {
@@ -17,14 +17,15 @@ abstract class SemanticContextCollector {
1717
if (allTokens?.length) {
1818
let i = tokenIndex ? tokenIndex - 1 : allTokens.length - 1;
1919
/**
20-
* Find the previous no-hidden token.
20+
* Link to @case4 and @case5
21+
* Find the previous unhidden token.
2122
* If can't find tokenIndex or current token is whiteSpace at caretPosition,
2223
* prevTokenIndex is useful to help us determine if it is new statement.
2324
*/
2425
while (i >= 0) {
2526
if (
2627
allTokens[i].channel !== Token.HIDDEN_CHANNEL &&
27-
(allTokens[i].line < caretPosition?.lineNumber ||
28+
(allTokens[i].line < caretPosition.lineNumber ||
2829
(allTokens[i].line === caretPosition.lineNumber &&
2930
allTokens[i].column < caretPosition.column))
3031
) {
@@ -34,7 +35,10 @@ abstract class SemanticContextCollector {
3435
i--;
3536
}
3637

37-
// Current token is the first token of tokenStream or the previous token is semicolon
38+
/**
39+
* We can directly conclude newStatement semantics when current token is
40+
* the first token of tokenStream or the previous token is semicolon
41+
*/
3842
if (
3943
tokenIndex === 0 ||
4044
i === -1 ||
@@ -48,10 +52,27 @@ abstract class SemanticContextCollector {
4852

4953
private _tokenIndex: number;
5054
private _allTokens: Token[] = [];
55+
56+
/**
57+
* If current caret position is in a newStatement semantics, it needs to follow some cases:
58+
* @case1 there is no statement node with an error before the current statement in the parse tree;
59+
*
60+
* @case2 if it is an uncomplete keyword, it will be parsed as an `ErrorNode`
61+
* and need be a direct child node of `program`;
62+
*
63+
* @case3 if it is a complete keyword, the parsed TerminalNode or ErrorNode should be
64+
* the first leaf node of current statement rule;
65+
*
66+
* @case4 if it is whiteSpace in caret position, we can't visit it in antlr4 listener,
67+
* so we find the first unhidden token before the whiteSpace token, and the unhidden token
68+
* should be the last leaf node of statement its belongs to;
69+
*
70+
* @case5 if the previous token is split symbol like `;`, ignore case1 and forcefully judged as newStatement.
71+
*/
5172
private _isNewStatement: boolean = false;
5273

5374
/**
54-
* Prev tokenIndex that not white space before current tokenIndex or cart position
75+
* Prev tokenIndex that not white space before current tokenIndex or caret position
5576
*/
5677
private _prevTokenIndex: number;
5778

@@ -65,7 +86,7 @@ abstract class SemanticContextCollector {
6586

6687
abstract getStatementRuleType(): number;
6788

68-
private previousStatementHasError(node: TerminalNode | ErrorNode | ParserRuleContext) {
89+
private prevStatementHasError(node: TerminalNode | ErrorNode | ParserRuleContext) {
6990
let parent = node.parent as ParserRuleContext;
7091
if (!parent) return false;
7192

@@ -85,19 +106,17 @@ abstract class SemanticContextCollector {
85106
}
86107

87108
/**
88-
* Most root rules is program.
109+
* Most root rule is `program`.
89110
*/
90-
private getIsRootRuleNode(node: TerminalNode | ErrorNode | ParserRuleContext) {
111+
private isRootRule(node: TerminalNode | ErrorNode | ParserRuleContext) {
91112
return node instanceof ParserRuleContext && node?.parent === null;
92113
}
93114

94115
/**
95-
* Caret position is white space, so it will not visited as terminal node or error node.
96-
* We can find the first previous no-white-space token,
97-
* and if previous token is the last leaf node of the statement,
98-
* it can be considered as being in the context of new statement
116+
* link to @case4
117+
* It should be called in each language's own `enterStatement`.
99118
*/
100-
protected statementVisitor(ctx: ParserRuleContext) {
119+
protected visitStatement(ctx: ParserRuleContext) {
101120
const isWhiteSpaceToken =
102121
this._tokenIndex === undefined ||
103122
this._allTokens[this._tokenIndex]?.type === this.getWhiteSpaceRuleType() ||
@@ -108,9 +127,7 @@ abstract class SemanticContextCollector {
108127
this._prevTokenIndex && ctx.stop?.tokenIndex === this._prevTokenIndex;
109128

110129
if (isWhiteSpaceToken && isPrevTokenEndOfStatement && ctx.exception === null) {
111-
this._isNewStatement = !this.previousStatementHasError(ctx)
112-
? true
113-
: this._isNewStatement;
130+
this._isNewStatement = !this.prevStatementHasError(ctx) ? true : this._isNewStatement;
114131
}
115132
}
116133

@@ -124,14 +141,16 @@ abstract class SemanticContextCollector {
124141
let currentNode: TerminalNode | ParserRuleContext = node;
125142

126143
/**
144+
* Link to @case2
127145
* The error node is a direct child node of the program node
128146
*/
129-
if (this.getIsRootRuleNode(parent)) {
130-
this._isNewStatement = !this.previousStatementHasError(currentNode);
147+
if (this.isRootRule(parent)) {
148+
this._isNewStatement = !this.prevStatementHasError(currentNode);
131149
return;
132150
}
133151

134152
/**
153+
* Link to @case3
135154
* Error node must be the first leaf node of the statement parse tree.
136155
**/
137156
while (parent !== null && parent.ruleIndex !== this.getStatementRuleType()) {
@@ -147,6 +166,7 @@ abstract class SemanticContextCollector {
147166
let isNewStatement = true;
148167

149168
/**
169+
* Link to @case1
150170
* Previous statement must have no exception
151171
*/
152172
if (parent?.ruleIndex === this.getStatementRuleType()) {
@@ -160,9 +180,7 @@ abstract class SemanticContextCollector {
160180
*/
161181
const isStatementEOF = parent.exception?.offendingToken?.text === '<EOF>';
162182
isNewStatement =
163-
this.previousStatementHasError(parent) && !isStatementEOF
164-
? false
165-
: isNewStatement;
183+
this.prevStatementHasError(parent) && !isStatementEOF ? false : isNewStatement;
166184
}
167185
}
168186

@@ -176,6 +194,7 @@ abstract class SemanticContextCollector {
176194
let parent = node.parent as ParserRuleContext | null;
177195

178196
/**
197+
* Link to @case3
179198
* Current terminal node must be the first leaf node of the statement parse tree.
180199
**/
181200
while (parent !== null && parent.ruleIndex !== this.getStatementRuleType()) {
@@ -190,12 +209,16 @@ abstract class SemanticContextCollector {
190209

191210
let isNewStatement = true;
192211

212+
/**
213+
* Link to @case1
214+
* Previous statement must have no exception
215+
*/
193216
if (parent?.ruleIndex === this.getStatementRuleType()) {
194217
const programRule = parent.parent;
195218
const currentStatementRuleIndex =
196219
programRule?.children?.findIndex((node) => node === parent) || -1;
197220
if (currentStatementRuleIndex > 0) {
198-
isNewStatement = this.previousStatementHasError(parent) ? false : isNewStatement;
221+
isNewStatement = this.prevStatementHasError(parent) ? false : isNewStatement;
199222
}
200223
}
201224

src/parser/flink/flinkSemanticContextCollector.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class FlinkSemanticContextCollector
1313
return FlinkSqlParser.RULE_singleStatement;
1414
}
1515
enterSingleStatement(ctx: SingleStatementContext) {
16-
this.statementVisitor(ctx);
16+
this.visitStatement(ctx);
1717
}
1818
}
1919

src/parser/hive/hiveSemanticContextCollector.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class HiveSemanticContextCollector
1313
return HiveSqlParser.RULE_statement;
1414
}
1515
enterStatement(ctx: StatementContext) {
16-
this.statementVisitor(ctx);
16+
this.visitStatement(ctx);
1717
}
1818
}
1919

src/parser/impala/impalaSemanticContextCollector.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class ImpalaSemanticContextCollector
1313
return ImpalaSqlParser.RULE_singleStatement;
1414
}
1515
enterSingleStatement(ctx: SingleStatementContext) {
16-
this.statementVisitor(ctx);
16+
this.visitStatement(ctx);
1717
}
1818
}
1919

src/parser/mysql/mysqlSemanticContextCollector.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class MySqlSemanticContextCollector
1313
return MySqlParser.RULE_singleStatement;
1414
}
1515
enterSingleStatement(ctx: SingleStatementContext) {
16-
this.statementVisitor(ctx);
16+
this.visitStatement(ctx);
1717
}
1818
}
1919

src/parser/postgresql/postgreSemanticContextCollector.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class PostgreSemanticContextCollector
1414
}
1515

1616
enterSingleStmt(ctx: SingleStmtContext) {
17-
this.statementVisitor(ctx);
17+
this.visitStatement(ctx);
1818
}
1919
}
2020

src/parser/spark/sparkSemanticContextCollector.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class SparkSemanticContextCollector
1313
return SparkSqlParser.RULE_singleStatement;
1414
}
1515
enterSingleStatement(ctx: SingleStatementContext) {
16-
this.statementVisitor(ctx);
16+
this.visitStatement(ctx);
1717
}
1818
}
1919

src/parser/trino/trinoSemanticContextCollector.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class TrinoSemanticContextCollector extends SemanticContextCollector implements
1010
return TrinoSqlParser.RULE_statements;
1111
}
1212
enterStatements(ctx: StatementsContext) {
13-
this.statementVisitor(ctx);
13+
this.visitStatement(ctx);
1414
}
1515
}
1616

0 commit comments

Comments
 (0)