chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,288 @@
|
||||
lexer grammar TSQLLexer;
|
||||
|
||||
// NB! We cat TSQLLexter.typescript.g4 when generating the grammar.
|
||||
|
||||
// NOTE: don't forget to add new keywords to the parser rule "keyword"!
|
||||
|
||||
// Keywords
|
||||
|
||||
ALL: A L L;
|
||||
AND: A N D;
|
||||
ANTI: A N T I;
|
||||
ANY: A N Y;
|
||||
ARRAY: A R R A Y;
|
||||
AS: A S;
|
||||
ASCENDING: A S C | A S C E N D I N G;
|
||||
ASOF: A S O F;
|
||||
BETWEEN: B E T W E E N;
|
||||
BOTH: B O T H;
|
||||
BY: B Y;
|
||||
CASE: C A S E;
|
||||
CAST: C A S T;
|
||||
CATCH: C A T C H;
|
||||
COHORT: C O H O R T;
|
||||
COLLATE: C O L L A T E;
|
||||
CROSS: C R O S S;
|
||||
CUBE: C U B E;
|
||||
CURRENT: C U R R E N T;
|
||||
DATE: D A T E;
|
||||
DAY: D A Y;
|
||||
DESC: D E S C;
|
||||
DESCENDING: D E S C E N D I N G;
|
||||
DISTINCT: D I S T I N C T;
|
||||
ELSE: E L S E;
|
||||
END: E N D;
|
||||
EXCEPT: E X C E P T;
|
||||
EXTRACT: E X T R A C T;
|
||||
FINAL: F I N A L;
|
||||
FINALLY: F I N A L L Y;
|
||||
FIRST: F I R S T;
|
||||
FN: F N;
|
||||
FOLLOWING: F O L L O W I N G;
|
||||
FOR: F O R;
|
||||
FROM: F R O M;
|
||||
FULL: F U L L;
|
||||
FUN: F U N;
|
||||
GROUP: G R O U P;
|
||||
HAVING: H A V I N G;
|
||||
HOUR: H O U R;
|
||||
ID: I D;
|
||||
IF: I F;
|
||||
ILIKE: I L I K E;
|
||||
IN: I N;
|
||||
INF: I N F | I N F I N I T Y;
|
||||
INNER: I N N E R;
|
||||
INTERSECT: I N T E R S E C T;
|
||||
INTERVAL: I N T E R V A L;
|
||||
IS: I S;
|
||||
JOIN: J O I N;
|
||||
KEY: K E Y;
|
||||
LAST: L A S T;
|
||||
LEADING: L E A D I N G;
|
||||
LEFT: L E F T;
|
||||
LET: L E T;
|
||||
LIKE: L I K E;
|
||||
LIMIT: L I M I T;
|
||||
MINUTE: M I N U T E;
|
||||
MONTH: M O N T H;
|
||||
NAN_SQL: N A N; // conflicts with macro NAN
|
||||
NOT: N O T;
|
||||
NULL_SQL: N U L L; // conflicts with macro NULL
|
||||
NULLS: N U L L S;
|
||||
OFFSET: O F F S E T;
|
||||
ON: O N;
|
||||
OR: O R;
|
||||
ORDER: O R D E R;
|
||||
OUTER: O U T E R;
|
||||
OVER: O V E R;
|
||||
PARTITION: P A R T I T I O N;
|
||||
PRECEDING: P R E C E D I N G;
|
||||
PREWHERE: P R E W H E R E;
|
||||
QUARTER: Q U A R T E R;
|
||||
RANGE: R A N G E;
|
||||
RETURN: R E T U R N;
|
||||
RIGHT: R I G H T;
|
||||
ROLLUP: R O L L U P;
|
||||
ROW: R O W;
|
||||
ROWS: R O W S;
|
||||
SAMPLE: S A M P L E;
|
||||
SECOND: S E C O N D;
|
||||
SELECT: S E L E C T;
|
||||
SEMI: S E M I;
|
||||
SETTINGS: S E T T I N G S;
|
||||
SUBSTRING: S U B S T R I N G;
|
||||
THEN: T H E N;
|
||||
THROW: T H R O W;
|
||||
TIES: T I E S;
|
||||
TIMESTAMP: T I M E S T A M P;
|
||||
TO: T O;
|
||||
TOP: T O P;
|
||||
TOTALS: T O T A L S;
|
||||
TRAILING: T R A I L I N G;
|
||||
TRIM: T R I M;
|
||||
TRUNCATE: T R U N C A T E;
|
||||
TRY: T R Y;
|
||||
UNBOUNDED: U N B O U N D E D;
|
||||
UNION: U N I O N;
|
||||
USING: U S I N G;
|
||||
WEEK: W E E K;
|
||||
WHEN: W H E N;
|
||||
WHERE: W H E R E;
|
||||
WHILE: W H I L E;
|
||||
WINDOW: W I N D O W;
|
||||
WITH: W I T H;
|
||||
YEAR: Y E A R | Y Y Y Y;
|
||||
|
||||
// Tokens
|
||||
|
||||
// copied from clickhouse_driver/util/escape.py
|
||||
ESCAPE_CHAR_COMMON
|
||||
: BACKSLASH B
|
||||
| BACKSLASH F
|
||||
| BACKSLASH R
|
||||
| BACKSLASH N
|
||||
| BACKSLASH T
|
||||
| BACKSLASH '0'
|
||||
| BACKSLASH A
|
||||
| BACKSLASH V
|
||||
| BACKSLASH BACKSLASH;
|
||||
|
||||
IDENTIFIER
|
||||
: (LETTER | UNDERSCORE | DOLLAR) (LETTER | UNDERSCORE | DEC_DIGIT | DOLLAR)*
|
||||
| BACKQUOTE ( ~([\\`]) | ESCAPE_CHAR_COMMON | BACKSLASH QUOTE_SINGLE | (BACKQUOTE BACKQUOTE) )* BACKQUOTE
|
||||
| QUOTE_DOUBLE ( ~([\\"]) | ESCAPE_CHAR_COMMON | BACKSLASH QUOTE_DOUBLE | (QUOTE_DOUBLE QUOTE_DOUBLE) )* QUOTE_DOUBLE
|
||||
;
|
||||
FLOATING_LITERAL
|
||||
: HEXADECIMAL_LITERAL DOT HEX_DIGIT* (P | E) (PLUS | DASH)? DEC_DIGIT+
|
||||
| HEXADECIMAL_LITERAL (P | E) (PLUS | DASH)? DEC_DIGIT+
|
||||
| DECIMAL_LITERAL DOT DEC_DIGIT* E (PLUS | DASH)? DEC_DIGIT+
|
||||
| DOT DECIMAL_LITERAL E (PLUS | DASH)? DEC_DIGIT+
|
||||
| DECIMAL_LITERAL E (PLUS | DASH)? DEC_DIGIT+
|
||||
;
|
||||
OCTAL_LITERAL: '0' OCT_DIGIT+;
|
||||
DECIMAL_LITERAL: DEC_DIGIT+;
|
||||
HEXADECIMAL_LITERAL: '0' X HEX_DIGIT+;
|
||||
|
||||
// It's important that quote-symbol is a single character.
|
||||
STRING_LITERAL: QUOTE_SINGLE ( ~([\\']) | ESCAPE_CHAR_COMMON | BACKSLASH QUOTE_SINGLE | (QUOTE_SINGLE QUOTE_SINGLE) )* QUOTE_SINGLE;
|
||||
|
||||
|
||||
// Alphabet and allowed symbols
|
||||
|
||||
fragment A: [aA];
|
||||
fragment B: [bB];
|
||||
fragment C: [cC];
|
||||
fragment D: [dD];
|
||||
fragment E: [eE];
|
||||
fragment F: [fF];
|
||||
fragment G: [gG];
|
||||
fragment H: [hH];
|
||||
fragment I: [iI];
|
||||
fragment J: [jJ];
|
||||
fragment K: [kK];
|
||||
fragment L: [lL];
|
||||
fragment M: [mM];
|
||||
fragment N: [nN];
|
||||
fragment O: [oO];
|
||||
fragment P: [pP];
|
||||
fragment Q: [qQ];
|
||||
fragment R: [rR];
|
||||
fragment S: [sS];
|
||||
fragment T: [tT];
|
||||
fragment U: [uU];
|
||||
fragment V: [vV];
|
||||
fragment W: [wW];
|
||||
fragment X: [xX];
|
||||
fragment Y: [yY];
|
||||
fragment Z: [zZ];
|
||||
|
||||
fragment LETTER: [a-zA-Z];
|
||||
fragment OCT_DIGIT: [0-7];
|
||||
fragment DEC_DIGIT: [0-9];
|
||||
fragment HEX_DIGIT: [0-9a-fA-F];
|
||||
|
||||
ARROW: '->';
|
||||
ASTERISK: '*';
|
||||
BACKQUOTE: '`';
|
||||
BACKSLASH: '\\';
|
||||
COLON: ':';
|
||||
COMMA: ',';
|
||||
CONCAT: '||';
|
||||
DASH: '-';
|
||||
DOLLAR: '$';
|
||||
DOT: '.';
|
||||
EQ_DOUBLE: '==';
|
||||
EQ_SINGLE: '=';
|
||||
GT_EQ: '>=';
|
||||
GT: '>';
|
||||
HASH: '#';
|
||||
IREGEX_SINGLE: '~*';
|
||||
IREGEX_DOUBLE: '=~*';
|
||||
LBRACE: '{' -> pushMode(DEFAULT_MODE);
|
||||
LBRACKET: '[';
|
||||
LPAREN: '(';
|
||||
LT_EQ: '<=';
|
||||
TAG_LT_SLASH: '</' -> type(LT_SLASH), pushMode(TSQLX_TAG_CLOSE);
|
||||
TAG_LT_OPEN: '<' {isOpeningTag()}? -> type(LT), pushMode(TSQLX_TAG_OPEN);
|
||||
LT: '<';
|
||||
LT_SLASH: '</';
|
||||
NOT_EQ: '!=' | '<>';
|
||||
NOT_IREGEX: '!~*';
|
||||
NOT_REGEX: '!~';
|
||||
NULL_PROPERTY: '?.';
|
||||
NULLISH: '??';
|
||||
PERCENT: '%';
|
||||
PLUS: '+';
|
||||
QUERY: '?';
|
||||
QUOTE_DOUBLE: '"';
|
||||
QUOTE_SINGLE_TEMPLATE: 'f\'' -> pushMode(IN_TEMPLATE_STRING); // start of regular f'' template strings
|
||||
QUOTE_SINGLE_TEMPLATE_FULL: 'F\'' -> pushMode(IN_FULL_TEMPLATE_STRING); // magic F' symbol used to parse "full text" templates
|
||||
QUOTE_SINGLE: '\'';
|
||||
REGEX_SINGLE: '~';
|
||||
REGEX_DOUBLE: '=~';
|
||||
RBRACE: '}' -> popMode;
|
||||
RBRACKET: ']';
|
||||
RPAREN: ')';
|
||||
SEMICOLON: ';';
|
||||
SLASH: '/';
|
||||
SLASH_GT: '/>';
|
||||
UNDERSCORE: '_';
|
||||
|
||||
// Comments and whitespace
|
||||
MULTI_LINE_COMMENT: '/*' .*? '*/' -> skip;
|
||||
SINGLE_LINE_COMMENT: ('--' | '//') ~('\n'|'\r')* ('\n' | '\r' | EOF) -> skip;
|
||||
// whitespace is hidden and not skipped so that it's preserved in ANTLR errors like "no viable alternative"
|
||||
WHITESPACE: [ \u000B\u000C\t\r\n] -> channel(HIDDEN);
|
||||
|
||||
// ───────── f' TEMPLATE STRING MODE ─────────
|
||||
mode IN_TEMPLATE_STRING;
|
||||
STRING_TEXT: ((~([\\'{])) | ESCAPE_CHAR_COMMON | BACKSLASH QUOTE_SINGLE | (BACKSLASH LBRACE) | (QUOTE_SINGLE QUOTE_SINGLE))+;
|
||||
STRING_ESCAPE_TRIGGER: LBRACE -> pushMode(DEFAULT_MODE);
|
||||
STRING_QUOTE_SINGLE: QUOTE_SINGLE -> type(QUOTE_SINGLE), popMode;
|
||||
|
||||
// ───────── F' FULL TEMPLATE STRING MODE ─────────
|
||||
// a magic F' takes us to "full template strings" mode, where we don't need to escape single quotes and parse until EOF
|
||||
// this can't be used within a normal columnExpr, but has to be parsed for separately
|
||||
mode IN_FULL_TEMPLATE_STRING;
|
||||
FULL_STRING_TEXT: ((~([{])) | ESCAPE_CHAR_COMMON | (BACKSLASH LBRACE))+;
|
||||
FULL_STRING_ESCAPE_TRIGGER: LBRACE -> pushMode(DEFAULT_MODE);
|
||||
|
||||
// ───────── TSQLX TAG MODE for opening/self-closing tags ─────────
|
||||
mode TSQLX_TAG_OPEN;
|
||||
|
||||
TAG_SELF_CLOSE_GT : '/>' -> type(SLASH_GT), popMode; // <tag …/>
|
||||
TAG_OPEN_GT : '>' -> type(GT), popMode, pushMode(TSQLX_TEXT); // <tag …>
|
||||
|
||||
// minimal token set; map everything back to the default token types
|
||||
TAG_IDENT : [a-zA-Z_][a-zA-Z0-9_-]* -> type(IDENTIFIER);
|
||||
TAG_EQ : '=' -> type(EQ_SINGLE);
|
||||
TAG_STRING : STRING_LITERAL -> type(STRING_LITERAL);
|
||||
TAG_WS : [ \t\r\n]+ -> channel(HIDDEN);
|
||||
TAG_LBRACE : '{' -> type(LBRACE), pushMode(DEFAULT_MODE);
|
||||
|
||||
|
||||
// ───────── TSQLX TAG MODE for closing tags ─────────
|
||||
mode TSQLX_TAG_CLOSE;
|
||||
|
||||
TAGC_GT : '>' -> type(GT), popMode; // *** no TEXT push ***
|
||||
TAGC_IDENT : [a-zA-Z_][a-zA-Z0-9_-]* -> type(IDENTIFIER);
|
||||
TAGC_WS : [ \t\r\n]+ -> channel(HIDDEN);
|
||||
|
||||
|
||||
// ───────── TSQLX TEXT MODE ─────────
|
||||
mode TSQLX_TEXT;
|
||||
|
||||
TSQLX_TEXT_TEXT
|
||||
: ~[<{]+ ; // everything except “{” or “<”
|
||||
|
||||
TSQLX_TEXT_LBRACE
|
||||
: '{' -> type(LBRACE), pushMode(DEFAULT_MODE);
|
||||
|
||||
TSQLX_TEXT_LT_SLASH
|
||||
: '</' -> type(LT_SLASH), popMode, pushMode(TSQLX_TAG_CLOSE);
|
||||
|
||||
TSQLX_TEXT_LT
|
||||
: '<' -> type(LT), pushMode(TSQLX_TAG_OPEN);
|
||||
|
||||
TSQLX_TEXT_WS
|
||||
: [ \t\r\n]+ -> channel(HIDDEN);
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,208 @@
|
||||
ALL=1
|
||||
AND=2
|
||||
ANTI=3
|
||||
ANY=4
|
||||
ARRAY=5
|
||||
AS=6
|
||||
ASCENDING=7
|
||||
ASOF=8
|
||||
BETWEEN=9
|
||||
BOTH=10
|
||||
BY=11
|
||||
CASE=12
|
||||
CAST=13
|
||||
CATCH=14
|
||||
COHORT=15
|
||||
COLLATE=16
|
||||
CROSS=17
|
||||
CUBE=18
|
||||
CURRENT=19
|
||||
DATE=20
|
||||
DAY=21
|
||||
DESC=22
|
||||
DESCENDING=23
|
||||
DISTINCT=24
|
||||
ELSE=25
|
||||
END=26
|
||||
EXCEPT=27
|
||||
EXTRACT=28
|
||||
FINAL=29
|
||||
FINALLY=30
|
||||
FIRST=31
|
||||
FN=32
|
||||
FOLLOWING=33
|
||||
FOR=34
|
||||
FROM=35
|
||||
FULL=36
|
||||
FUN=37
|
||||
GROUP=38
|
||||
HAVING=39
|
||||
HOUR=40
|
||||
ID=41
|
||||
IF=42
|
||||
ILIKE=43
|
||||
IN=44
|
||||
INF=45
|
||||
INNER=46
|
||||
INTERSECT=47
|
||||
INTERVAL=48
|
||||
IS=49
|
||||
JOIN=50
|
||||
KEY=51
|
||||
LAST=52
|
||||
LEADING=53
|
||||
LEFT=54
|
||||
LET=55
|
||||
LIKE=56
|
||||
LIMIT=57
|
||||
MINUTE=58
|
||||
MONTH=59
|
||||
NAN_SQL=60
|
||||
NOT=61
|
||||
NULL_SQL=62
|
||||
NULLS=63
|
||||
OFFSET=64
|
||||
ON=65
|
||||
OR=66
|
||||
ORDER=67
|
||||
OUTER=68
|
||||
OVER=69
|
||||
PARTITION=70
|
||||
PRECEDING=71
|
||||
PREWHERE=72
|
||||
QUARTER=73
|
||||
RANGE=74
|
||||
RETURN=75
|
||||
RIGHT=76
|
||||
ROLLUP=77
|
||||
ROW=78
|
||||
ROWS=79
|
||||
SAMPLE=80
|
||||
SECOND=81
|
||||
SELECT=82
|
||||
SEMI=83
|
||||
SETTINGS=84
|
||||
SUBSTRING=85
|
||||
THEN=86
|
||||
THROW=87
|
||||
TIES=88
|
||||
TIMESTAMP=89
|
||||
TO=90
|
||||
TOP=91
|
||||
TOTALS=92
|
||||
TRAILING=93
|
||||
TRIM=94
|
||||
TRUNCATE=95
|
||||
TRY=96
|
||||
UNBOUNDED=97
|
||||
UNION=98
|
||||
USING=99
|
||||
WEEK=100
|
||||
WHEN=101
|
||||
WHERE=102
|
||||
WHILE=103
|
||||
WINDOW=104
|
||||
WITH=105
|
||||
YEAR=106
|
||||
ESCAPE_CHAR_COMMON=107
|
||||
IDENTIFIER=108
|
||||
FLOATING_LITERAL=109
|
||||
OCTAL_LITERAL=110
|
||||
DECIMAL_LITERAL=111
|
||||
HEXADECIMAL_LITERAL=112
|
||||
STRING_LITERAL=113
|
||||
ARROW=114
|
||||
ASTERISK=115
|
||||
BACKQUOTE=116
|
||||
BACKSLASH=117
|
||||
COLON=118
|
||||
COMMA=119
|
||||
CONCAT=120
|
||||
DASH=121
|
||||
DOLLAR=122
|
||||
DOT=123
|
||||
EQ_DOUBLE=124
|
||||
EQ_SINGLE=125
|
||||
GT_EQ=126
|
||||
GT=127
|
||||
HASH=128
|
||||
IREGEX_SINGLE=129
|
||||
IREGEX_DOUBLE=130
|
||||
LBRACE=131
|
||||
LBRACKET=132
|
||||
LPAREN=133
|
||||
LT_EQ=134
|
||||
LT=135
|
||||
LT_SLASH=136
|
||||
NOT_EQ=137
|
||||
NOT_IREGEX=138
|
||||
NOT_REGEX=139
|
||||
NULL_PROPERTY=140
|
||||
NULLISH=141
|
||||
PERCENT=142
|
||||
PLUS=143
|
||||
QUERY=144
|
||||
QUOTE_DOUBLE=145
|
||||
QUOTE_SINGLE_TEMPLATE=146
|
||||
QUOTE_SINGLE_TEMPLATE_FULL=147
|
||||
QUOTE_SINGLE=148
|
||||
REGEX_SINGLE=149
|
||||
REGEX_DOUBLE=150
|
||||
RBRACE=151
|
||||
RBRACKET=152
|
||||
RPAREN=153
|
||||
SEMICOLON=154
|
||||
SLASH=155
|
||||
SLASH_GT=156
|
||||
UNDERSCORE=157
|
||||
MULTI_LINE_COMMENT=158
|
||||
SINGLE_LINE_COMMENT=159
|
||||
WHITESPACE=160
|
||||
STRING_TEXT=161
|
||||
STRING_ESCAPE_TRIGGER=162
|
||||
FULL_STRING_TEXT=163
|
||||
FULL_STRING_ESCAPE_TRIGGER=164
|
||||
TAG_WS=165
|
||||
TAGC_WS=166
|
||||
TSQLX_TEXT_TEXT=167
|
||||
TSQLX_TEXT_WS=168
|
||||
'->'=114
|
||||
'*'=115
|
||||
'`'=116
|
||||
'\\'=117
|
||||
':'=118
|
||||
','=119
|
||||
'||'=120
|
||||
'-'=121
|
||||
'$'=122
|
||||
'.'=123
|
||||
'=='=124
|
||||
'>='=126
|
||||
'#'=128
|
||||
'~*'=129
|
||||
'=~*'=130
|
||||
'{'=131
|
||||
'['=132
|
||||
'('=133
|
||||
'<='=134
|
||||
'<'=135
|
||||
'</'=136
|
||||
'!~*'=138
|
||||
'!~'=139
|
||||
'?.'=140
|
||||
'??'=141
|
||||
'%'=142
|
||||
'+'=143
|
||||
'?'=144
|
||||
'"'=145
|
||||
'f\''=146
|
||||
'F\''=147
|
||||
'\''=148
|
||||
'~'=149
|
||||
'=~'=150
|
||||
'}'=151
|
||||
']'=152
|
||||
')'=153
|
||||
';'=154
|
||||
'/'=155
|
||||
'_'=157
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,81 @@
|
||||
lexer grammar TSQLLexer;
|
||||
|
||||
@header {
|
||||
// put any global imports you need here
|
||||
}
|
||||
|
||||
@members {
|
||||
|
||||
private _peekChar(k: number): string {
|
||||
// Return the k-th look-ahead as a *single-char string* or '\0' at EOF.
|
||||
const c = this._input.LA(k); // int code point or IntStream.EOF (-1)
|
||||
if (c < 0 || c > 0x10FFFF) { // EOF or out-of-range → sentinel
|
||||
return '\0';
|
||||
}
|
||||
return String.fromCharCode(c);
|
||||
}
|
||||
|
||||
private _skipWsAndComments(idx: number): number {
|
||||
// Return the first index ≥ idx that is *not* whitespace / single-line comment.
|
||||
while (true) {
|
||||
const ch = this._peekChar(idx);
|
||||
if (/\s/.test(ch)) { // spaces, newlines, tabs …
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// single-line comments
|
||||
if (ch === '/' && this._peekChar(idx + 1) === '/') { // //
|
||||
idx += 2;
|
||||
} else if (ch === '-' && this._peekChar(idx + 1) === '-') { // --
|
||||
idx += 2;
|
||||
} else if (ch === '#') { // #
|
||||
idx++;
|
||||
} else {
|
||||
break; // no ws / comment
|
||||
}
|
||||
// consume until EOL / EOF
|
||||
while (!['\0', '\n', '\r'].includes(this._peekChar(idx))) {
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
// ───── opening tag test ─────
|
||||
isOpeningTag(): boolean {
|
||||
const ch1 = this._peekChar(1);
|
||||
if (!(/[a-zA-Z]/.test(ch1) || ch1 === '_')) {
|
||||
return false; // not a tag name start
|
||||
}
|
||||
|
||||
// skip tag name
|
||||
let i = 2;
|
||||
while (true) {
|
||||
const ch = this._peekChar(i);
|
||||
if (/[a-zA-Z0-9]/.test(ch) || ch === '_' || ch === '-') {
|
||||
i++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let ch = this._peekChar(i);
|
||||
|
||||
// immediate delimiter → tag
|
||||
if (ch === '>' || ch === '/') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// need to look beyond whitespace
|
||||
if (/\s/.test(ch)) {
|
||||
i = this._skipWsAndComments(i + 1);
|
||||
ch = this._peekChar(i);
|
||||
return ch === '>' || ch === '/' || /[a-zA-Z0-9]/.test(ch) || ch === '_';
|
||||
}
|
||||
|
||||
// anything else → not a tag
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
parser grammar TSQLParser;
|
||||
|
||||
options {
|
||||
tokenVocab = TSQLLexer;
|
||||
}
|
||||
|
||||
|
||||
program: declaration* EOF;
|
||||
|
||||
declaration: varDecl | statement ;
|
||||
|
||||
expression: columnExpr;
|
||||
|
||||
varDecl: LET identifier ( COLON EQ_SINGLE expression )? ;
|
||||
identifierList: identifier (COMMA identifier)* COMMA?;
|
||||
|
||||
statement : returnStmt
|
||||
| throwStmt
|
||||
| tryCatchStmt
|
||||
| ifStmt
|
||||
| whileStmt
|
||||
| forInStmt
|
||||
| forStmt
|
||||
| funcStmt
|
||||
| varAssignment
|
||||
| block
|
||||
| exprStmt
|
||||
| emptyStmt
|
||||
;
|
||||
|
||||
returnStmt : RETURN expression? SEMICOLON?;
|
||||
throwStmt : THROW expression? SEMICOLON?;
|
||||
catchBlock : CATCH (LPAREN catchVar=identifier (COLON catchType=identifier)? RPAREN)? catchStmt=block;
|
||||
tryCatchStmt : TRY tryStmt=block catchBlock* (FINALLY finallyStmt=block)?;
|
||||
ifStmt : IF LPAREN expression RPAREN statement ( ELSE statement )? ;
|
||||
whileStmt : WHILE LPAREN expression RPAREN statement SEMICOLON?;
|
||||
forStmt : FOR LPAREN
|
||||
(initializerVarDeclr=varDecl | initializerVarAssignment=varAssignment | initializerExpression=expression)? SEMICOLON
|
||||
condition=expression? SEMICOLON
|
||||
(incrementVarDeclr=varDecl | incrementVarAssignment=varAssignment | incrementExpression=expression)?
|
||||
RPAREN statement SEMICOLON?;
|
||||
forInStmt : FOR LPAREN LET identifier (COMMA identifier)? IN expression RPAREN statement SEMICOLON?;
|
||||
funcStmt : (FN | FUN) identifier LPAREN identifierList? RPAREN block;
|
||||
varAssignment : expression COLON EQ_SINGLE expression ;
|
||||
exprStmt : expression SEMICOLON?;
|
||||
emptyStmt : SEMICOLON ;
|
||||
block : LBRACE declaration* RBRACE ;
|
||||
|
||||
kvPair: expression ':' expression ;
|
||||
kvPairList: kvPair (COMMA kvPair)* COMMA?;
|
||||
|
||||
|
||||
// SELECT statement
|
||||
select: (selectSetStmt | selectStmt | tSQLxTagElement) SEMICOLON? EOF;
|
||||
|
||||
selectStmtWithParens: selectStmt | LPAREN selectSetStmt RPAREN | placeholder;
|
||||
|
||||
subsequentSelectSetClause: (EXCEPT | UNION ALL | UNION DISTINCT | INTERSECT | INTERSECT DISTINCT) selectStmtWithParens;
|
||||
selectSetStmt: selectStmtWithParens (subsequentSelectSetClause)*;
|
||||
|
||||
selectStmt:
|
||||
with=withClause?
|
||||
SELECT DISTINCT? topClause?
|
||||
columns=columnExprList
|
||||
from=fromClause?
|
||||
arrayJoinClause?
|
||||
prewhereClause?
|
||||
where=whereClause?
|
||||
groupByClause? (WITH (CUBE | ROLLUP))? (WITH TOTALS)?
|
||||
havingClause?
|
||||
windowClause?
|
||||
orderByClause?
|
||||
limitByClause?
|
||||
(limitAndOffsetClause | offsetOnlyClause)?
|
||||
settingsClause?
|
||||
;
|
||||
|
||||
withClause: WITH withExprList;
|
||||
topClause: TOP DECIMAL_LITERAL (WITH TIES)?;
|
||||
fromClause: FROM joinExpr;
|
||||
arrayJoinClause: (LEFT | INNER)? ARRAY JOIN columnExprList;
|
||||
windowClause: WINDOW identifier AS LPAREN windowExpr RPAREN (COMMA identifier AS LPAREN windowExpr RPAREN)*;
|
||||
prewhereClause: PREWHERE columnExpr;
|
||||
whereClause: WHERE columnExpr;
|
||||
groupByClause: GROUP BY ((CUBE | ROLLUP) LPAREN columnExprList RPAREN | columnExprList);
|
||||
havingClause: HAVING columnExpr;
|
||||
orderByClause: ORDER BY orderExprList;
|
||||
projectionOrderByClause: ORDER BY columnExprList;
|
||||
limitByClause: LIMIT limitExpr BY columnExprList;
|
||||
limitAndOffsetClause
|
||||
: LIMIT columnExpr (COMMA columnExpr)? (WITH TIES)? // compact OFFSET-optional form
|
||||
| LIMIT columnExpr (WITH TIES)? OFFSET columnExpr // verbose OFFSET-included form with WITH TIES
|
||||
;
|
||||
offsetOnlyClause: OFFSET columnExpr;
|
||||
settingsClause: SETTINGS settingExprList;
|
||||
|
||||
joinExpr
|
||||
: joinExpr joinOp? JOIN joinExpr joinConstraintClause # JoinExprOp
|
||||
| joinExpr joinOpCross joinExpr # JoinExprCrossOp
|
||||
| tableExpr FINAL? sampleClause? # JoinExprTable
|
||||
| LPAREN joinExpr RPAREN # JoinExprParens
|
||||
;
|
||||
joinOp
|
||||
: ((ALL | ANY | ASOF)? INNER | INNER (ALL | ANY | ASOF)? | (ALL | ANY | ASOF)) # JoinOpInner
|
||||
| ( (SEMI | ALL | ANTI | ANY | ASOF)? (LEFT | RIGHT) OUTER?
|
||||
| (LEFT | RIGHT) OUTER? (SEMI | ALL | ANTI | ANY | ASOF)?
|
||||
) # JoinOpLeftRight
|
||||
| ((ALL | ANY)? FULL OUTER? | FULL OUTER? (ALL | ANY)?) # JoinOpFull
|
||||
;
|
||||
joinOpCross
|
||||
: CROSS JOIN
|
||||
| COMMA
|
||||
;
|
||||
joinConstraintClause
|
||||
: ON columnExprList
|
||||
| USING LPAREN columnExprList RPAREN
|
||||
| USING columnExprList
|
||||
;
|
||||
|
||||
sampleClause: SAMPLE ratioExpr (OFFSET ratioExpr)?;
|
||||
limitExpr: columnExpr ((COMMA | OFFSET) columnExpr)?;
|
||||
orderExprList: orderExpr (COMMA orderExpr)*;
|
||||
orderExpr: columnExpr (ASCENDING | DESCENDING | DESC)? (NULLS (FIRST | LAST))? (COLLATE STRING_LITERAL)?;
|
||||
ratioExpr: placeholder | numberLiteral (SLASH numberLiteral)?;
|
||||
settingExprList: settingExpr (COMMA settingExpr)*;
|
||||
settingExpr: identifier EQ_SINGLE literal;
|
||||
|
||||
windowExpr: winPartitionByClause? winOrderByClause? winFrameClause?;
|
||||
winPartitionByClause: PARTITION BY columnExprList;
|
||||
winOrderByClause: ORDER BY orderExprList;
|
||||
winFrameClause: (ROWS | RANGE) winFrameExtend;
|
||||
winFrameExtend
|
||||
: winFrameBound # frameStart
|
||||
| BETWEEN winFrameBound AND winFrameBound # frameBetween
|
||||
;
|
||||
winFrameBound: (CURRENT ROW | UNBOUNDED PRECEDING | UNBOUNDED FOLLOWING | numberLiteral PRECEDING | numberLiteral FOLLOWING);
|
||||
//rangeClause: RANGE LPAREN (MIN identifier MAX identifier | MAX identifier MIN identifier) RPAREN;
|
||||
|
||||
// Columns
|
||||
expr: columnExpr EOF;
|
||||
columnTypeExpr
|
||||
: identifier # ColumnTypeExprSimple // UInt64
|
||||
| identifier LPAREN identifier columnTypeExpr (COMMA identifier columnTypeExpr)* COMMA? RPAREN # ColumnTypeExprNested // Nested
|
||||
| identifier LPAREN enumValue (COMMA enumValue)* COMMA? RPAREN # ColumnTypeExprEnum // Enum
|
||||
| identifier LPAREN columnTypeExpr (COMMA columnTypeExpr)* COMMA? RPAREN # ColumnTypeExprComplex // Array, Tuple
|
||||
| identifier LPAREN columnExprList? RPAREN # ColumnTypeExprParam // FixedString(N)
|
||||
;
|
||||
columnExprList: columnExpr (COMMA columnExpr)* COMMA?;
|
||||
columnExpr
|
||||
: CASE caseExpr=columnExpr? (WHEN whenExpr=columnExpr THEN thenExpr=columnExpr)+ (ELSE elseExpr=columnExpr)? END # ColumnExprCase
|
||||
| CAST LPAREN columnExpr AS columnTypeExpr RPAREN # ColumnExprCast
|
||||
| DATE STRING_LITERAL # ColumnExprDate
|
||||
// | EXTRACT LPAREN interval FROM columnExpr RPAREN # ColumnExprExtract // Interferes with a function call
|
||||
| INTERVAL STRING_LITERAL # ColumnExprIntervalString
|
||||
| INTERVAL columnExpr interval # ColumnExprInterval
|
||||
| SUBSTRING LPAREN columnExpr FROM columnExpr (FOR columnExpr)? RPAREN # ColumnExprSubstring
|
||||
| TIMESTAMP STRING_LITERAL # ColumnExprTimestamp
|
||||
| TRIM LPAREN (BOTH | LEADING | TRAILING) string FROM columnExpr RPAREN # ColumnExprTrim
|
||||
| identifier (LPAREN columnExprs=columnExprList? RPAREN) (LPAREN DISTINCT? columnArgList=columnExprList? RPAREN)? OVER LPAREN windowExpr RPAREN # ColumnExprWinFunction
|
||||
| identifier (LPAREN columnExprs=columnExprList? RPAREN) (LPAREN DISTINCT? columnArgList=columnExprList? RPAREN)? OVER identifier # ColumnExprWinFunctionTarget
|
||||
| identifier (LPAREN columnExprs=columnExprList? RPAREN)? LPAREN DISTINCT? columnArgList=columnExprList? RPAREN # ColumnExprFunction
|
||||
| columnExpr LPAREN selectSetStmt RPAREN # ColumnExprCallSelect
|
||||
| columnExpr LPAREN columnExprList? RPAREN # ColumnExprCall
|
||||
| tSQLxTagElement # ColumnExprTagElement
|
||||
| templateString # ColumnExprTemplateString
|
||||
| literal # ColumnExprLiteral
|
||||
|
||||
// FIXME(ilezhankin): this part looks very ugly, maybe there is another way to express it
|
||||
| columnExpr LBRACKET columnExpr RBRACKET # ColumnExprArrayAccess
|
||||
| columnExpr DOT DECIMAL_LITERAL # ColumnExprTupleAccess
|
||||
| columnExpr DOT identifier # ColumnExprPropertyAccess
|
||||
| columnExpr NULL_PROPERTY LBRACKET columnExpr RBRACKET # ColumnExprNullArrayAccess
|
||||
| columnExpr NULL_PROPERTY DECIMAL_LITERAL # ColumnExprNullTupleAccess
|
||||
| columnExpr NULL_PROPERTY identifier # ColumnExprNullPropertyAccess
|
||||
| DASH columnExpr # ColumnExprNegate
|
||||
| left=columnExpr ( operator=ASTERISK // *
|
||||
| operator=SLASH // /
|
||||
| operator=PERCENT // %
|
||||
) right=columnExpr # ColumnExprPrecedence1
|
||||
| left=columnExpr ( operator=PLUS // +
|
||||
| operator=DASH // -
|
||||
| operator=CONCAT // ||
|
||||
) right=columnExpr # ColumnExprPrecedence2
|
||||
| left=columnExpr ( operator=EQ_DOUBLE // =
|
||||
| operator=EQ_SINGLE // ==
|
||||
| operator=NOT_EQ // !=
|
||||
| operator=LT_EQ // <=
|
||||
| operator=LT // <
|
||||
| operator=GT_EQ // >=
|
||||
| operator=GT // >
|
||||
| operator=NOT? IN COHORT? // in, not in; in cohort; not in cohort
|
||||
| operator=NOT? (LIKE | ILIKE) // like, not like, ilike, not ilike
|
||||
| operator=REGEX_SINGLE // ~
|
||||
| operator=REGEX_DOUBLE // =~
|
||||
| operator=NOT_REGEX // !~
|
||||
| operator=IREGEX_SINGLE // ~*
|
||||
| operator=IREGEX_DOUBLE // =~*
|
||||
| operator=NOT_IREGEX // !~*
|
||||
) right=columnExpr # ColumnExprPrecedence3
|
||||
| columnExpr IS NOT? NULL_SQL # ColumnExprIsNull
|
||||
| columnExpr NULLISH columnExpr # ColumnExprNullish
|
||||
| NOT columnExpr # ColumnExprNot
|
||||
| columnExpr AND columnExpr # ColumnExprAnd
|
||||
| columnExpr OR columnExpr # ColumnExprOr
|
||||
// TODO(ilezhankin): `BETWEEN a AND b AND c` is parsed in a wrong way: `BETWEEN (a AND b) AND c`
|
||||
| columnExpr NOT? BETWEEN columnExpr AND columnExpr # ColumnExprBetween
|
||||
| <assoc=right> columnExpr QUERY columnExpr COLON columnExpr # ColumnExprTernaryOp
|
||||
| columnExpr (AS identifier | AS STRING_LITERAL) # ColumnExprAlias
|
||||
| (tableIdentifier DOT)? ASTERISK # ColumnExprAsterisk // single-column only
|
||||
| LPAREN selectSetStmt RPAREN # ColumnExprSubquery // single-column only
|
||||
| LPAREN columnExpr RPAREN # ColumnExprParens // single-column only
|
||||
| LPAREN columnExprList RPAREN # ColumnExprTuple
|
||||
| LBRACKET columnExprList? RBRACKET # ColumnExprArray
|
||||
| LBRACE (kvPairList)? RBRACE # ColumnExprDict
|
||||
| columnLambdaExpr # ColumnExprLambda
|
||||
| columnIdentifier # ColumnExprIdentifier
|
||||
;
|
||||
|
||||
columnLambdaExpr:
|
||||
( LPAREN identifier (COMMA identifier)* COMMA? RPAREN
|
||||
| identifier (COMMA identifier)* COMMA?
|
||||
| LPAREN RPAREN
|
||||
)
|
||||
ARROW (columnExpr | block)
|
||||
;
|
||||
|
||||
tSQLxChildElement
|
||||
: tSQLxTagElement
|
||||
| TSQLX_TEXT_TEXT
|
||||
| LBRACE columnExpr RBRACE;
|
||||
|
||||
tSQLxTagElement
|
||||
: LT identifier tSQLxTagAttribute* SLASH_GT
|
||||
| LT identifier tSQLxTagAttribute* GT tSQLxChildElement* LT_SLASH identifier GT
|
||||
;
|
||||
tSQLxTagAttribute
|
||||
: identifier EQ_SINGLE string
|
||||
| identifier EQ_SINGLE LBRACE columnExpr RBRACE
|
||||
| identifier
|
||||
;
|
||||
|
||||
withExprList: withExpr (COMMA withExpr)* COMMA?;
|
||||
withExpr
|
||||
: identifier AS LPAREN selectSetStmt RPAREN # WithExprSubquery
|
||||
// NOTE: asterisk and subquery goes before |columnExpr| so that we can mark them as multi-column expressions.
|
||||
| columnExpr AS identifier # WithExprColumn
|
||||
;
|
||||
|
||||
|
||||
// This is slightly different in TSQL compared to ClickHouse SQL
|
||||
// TSQL allows unlimited ("*") nestedIdentifier-s "properties.b.a.a.w.a.s".
|
||||
// We parse and convert "databaseIdentifier.tableIdentifier.columnIdentifier.nestedIdentifier.*"
|
||||
// to just one ast.Field(chain=['a','b','columnIdentifier','on','and','on']).
|
||||
columnIdentifier: placeholder | ((tableIdentifier DOT)? nestedIdentifier);
|
||||
nestedIdentifier: identifier (DOT identifier)*;
|
||||
tableExpr
|
||||
: tableIdentifier # TableExprIdentifier
|
||||
| tableFunctionExpr # TableExprFunction
|
||||
| LPAREN selectSetStmt RPAREN # TableExprSubquery
|
||||
| tableExpr (alias | AS identifier) # TableExprAlias
|
||||
| tSQLxTagElement # TableExprTag
|
||||
| placeholder # TableExprPlaceholder
|
||||
;
|
||||
tableFunctionExpr: identifier LPAREN tableArgList? RPAREN;
|
||||
tableIdentifier: (databaseIdentifier DOT)? nestedIdentifier;
|
||||
tableArgList: columnExpr (COMMA columnExpr)* COMMA?;
|
||||
|
||||
// Databases
|
||||
|
||||
databaseIdentifier: identifier;
|
||||
|
||||
// Basics
|
||||
|
||||
floatingLiteral
|
||||
: FLOATING_LITERAL
|
||||
| DOT (DECIMAL_LITERAL | OCTAL_LITERAL)
|
||||
| DECIMAL_LITERAL DOT (DECIMAL_LITERAL | OCTAL_LITERAL)? // can't move this to the lexer or it will break nested tuple access: t.1.2
|
||||
;
|
||||
numberLiteral: (PLUS | DASH)? (floatingLiteral | OCTAL_LITERAL | DECIMAL_LITERAL | HEXADECIMAL_LITERAL | INF | NAN_SQL);
|
||||
literal
|
||||
: numberLiteral
|
||||
| STRING_LITERAL
|
||||
| NULL_SQL
|
||||
;
|
||||
interval: SECOND | MINUTE | HOUR | DAY | WEEK | MONTH | QUARTER | YEAR;
|
||||
keyword
|
||||
// except NULL_SQL, INF, NAN_SQL
|
||||
: ALL | AND | ANTI | ANY | ARRAY | AS | ASCENDING | ASOF | BETWEEN | BOTH | BY | CASE
|
||||
| CAST | COHORT | COLLATE | CROSS | CUBE | CURRENT | DATE | DESC | DESCENDING
|
||||
| DISTINCT | ELSE | END | EXTRACT | FINAL | FIRST
|
||||
| FOR | FOLLOWING | FROM | FULL | GROUP | HAVING | ID | IS
|
||||
| IF | ILIKE | IN | INNER | INTERVAL | JOIN | KEY
|
||||
| LAST | LEADING | LEFT | LIKE | LIMIT
|
||||
| NOT | NULLS | OFFSET | ON | OR | ORDER | OUTER | OVER | PARTITION
|
||||
| PRECEDING | PREWHERE | RANGE | RETURN | RIGHT | ROLLUP | ROW
|
||||
| ROWS | SAMPLE | SELECT | SEMI | SETTINGS | SUBSTRING
|
||||
| THEN | TIES | TIMESTAMP | TOTALS | TRAILING | TRIM | TRUNCATE | TO | TOP
|
||||
| UNBOUNDED | UNION | USING | WHEN | WHERE | WINDOW | WITH
|
||||
;
|
||||
keywordForAlias
|
||||
: DATE | FIRST | ID | KEY
|
||||
;
|
||||
alias: IDENTIFIER | keywordForAlias; // |interval| can't be an alias, otherwise 'INTERVAL 1 SOMETHING' becomes ambiguous.
|
||||
identifier: IDENTIFIER | interval | keyword;
|
||||
enumValue: string EQ_SINGLE numberLiteral;
|
||||
placeholder: LBRACE columnExpr RBRACE;
|
||||
|
||||
string: STRING_LITERAL | templateString;
|
||||
templateString : QUOTE_SINGLE_TEMPLATE stringContents* QUOTE_SINGLE ;
|
||||
stringContents : STRING_ESCAPE_TRIGGER columnExpr RBRACE | STRING_TEXT;
|
||||
|
||||
// These are magic "full template strings", which are used to parse "full text field" templates without the surrounding SQL.
|
||||
// We will need to add F' to the start of the string to change the lexer's mode.
|
||||
fullTemplateString: QUOTE_SINGLE_TEMPLATE_FULL stringContentsFull* EOF ;
|
||||
stringContentsFull : FULL_STRING_ESCAPE_TRIGGER columnExpr RBRACE | FULL_STRING_TEXT;
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,208 @@
|
||||
ALL=1
|
||||
AND=2
|
||||
ANTI=3
|
||||
ANY=4
|
||||
ARRAY=5
|
||||
AS=6
|
||||
ASCENDING=7
|
||||
ASOF=8
|
||||
BETWEEN=9
|
||||
BOTH=10
|
||||
BY=11
|
||||
CASE=12
|
||||
CAST=13
|
||||
CATCH=14
|
||||
COHORT=15
|
||||
COLLATE=16
|
||||
CROSS=17
|
||||
CUBE=18
|
||||
CURRENT=19
|
||||
DATE=20
|
||||
DAY=21
|
||||
DESC=22
|
||||
DESCENDING=23
|
||||
DISTINCT=24
|
||||
ELSE=25
|
||||
END=26
|
||||
EXCEPT=27
|
||||
EXTRACT=28
|
||||
FINAL=29
|
||||
FINALLY=30
|
||||
FIRST=31
|
||||
FN=32
|
||||
FOLLOWING=33
|
||||
FOR=34
|
||||
FROM=35
|
||||
FULL=36
|
||||
FUN=37
|
||||
GROUP=38
|
||||
HAVING=39
|
||||
HOUR=40
|
||||
ID=41
|
||||
IF=42
|
||||
ILIKE=43
|
||||
IN=44
|
||||
INF=45
|
||||
INNER=46
|
||||
INTERSECT=47
|
||||
INTERVAL=48
|
||||
IS=49
|
||||
JOIN=50
|
||||
KEY=51
|
||||
LAST=52
|
||||
LEADING=53
|
||||
LEFT=54
|
||||
LET=55
|
||||
LIKE=56
|
||||
LIMIT=57
|
||||
MINUTE=58
|
||||
MONTH=59
|
||||
NAN_SQL=60
|
||||
NOT=61
|
||||
NULL_SQL=62
|
||||
NULLS=63
|
||||
OFFSET=64
|
||||
ON=65
|
||||
OR=66
|
||||
ORDER=67
|
||||
OUTER=68
|
||||
OVER=69
|
||||
PARTITION=70
|
||||
PRECEDING=71
|
||||
PREWHERE=72
|
||||
QUARTER=73
|
||||
RANGE=74
|
||||
RETURN=75
|
||||
RIGHT=76
|
||||
ROLLUP=77
|
||||
ROW=78
|
||||
ROWS=79
|
||||
SAMPLE=80
|
||||
SECOND=81
|
||||
SELECT=82
|
||||
SEMI=83
|
||||
SETTINGS=84
|
||||
SUBSTRING=85
|
||||
THEN=86
|
||||
THROW=87
|
||||
TIES=88
|
||||
TIMESTAMP=89
|
||||
TO=90
|
||||
TOP=91
|
||||
TOTALS=92
|
||||
TRAILING=93
|
||||
TRIM=94
|
||||
TRUNCATE=95
|
||||
TRY=96
|
||||
UNBOUNDED=97
|
||||
UNION=98
|
||||
USING=99
|
||||
WEEK=100
|
||||
WHEN=101
|
||||
WHERE=102
|
||||
WHILE=103
|
||||
WINDOW=104
|
||||
WITH=105
|
||||
YEAR=106
|
||||
ESCAPE_CHAR_COMMON=107
|
||||
IDENTIFIER=108
|
||||
FLOATING_LITERAL=109
|
||||
OCTAL_LITERAL=110
|
||||
DECIMAL_LITERAL=111
|
||||
HEXADECIMAL_LITERAL=112
|
||||
STRING_LITERAL=113
|
||||
ARROW=114
|
||||
ASTERISK=115
|
||||
BACKQUOTE=116
|
||||
BACKSLASH=117
|
||||
COLON=118
|
||||
COMMA=119
|
||||
CONCAT=120
|
||||
DASH=121
|
||||
DOLLAR=122
|
||||
DOT=123
|
||||
EQ_DOUBLE=124
|
||||
EQ_SINGLE=125
|
||||
GT_EQ=126
|
||||
GT=127
|
||||
HASH=128
|
||||
IREGEX_SINGLE=129
|
||||
IREGEX_DOUBLE=130
|
||||
LBRACE=131
|
||||
LBRACKET=132
|
||||
LPAREN=133
|
||||
LT_EQ=134
|
||||
LT=135
|
||||
LT_SLASH=136
|
||||
NOT_EQ=137
|
||||
NOT_IREGEX=138
|
||||
NOT_REGEX=139
|
||||
NULL_PROPERTY=140
|
||||
NULLISH=141
|
||||
PERCENT=142
|
||||
PLUS=143
|
||||
QUERY=144
|
||||
QUOTE_DOUBLE=145
|
||||
QUOTE_SINGLE_TEMPLATE=146
|
||||
QUOTE_SINGLE_TEMPLATE_FULL=147
|
||||
QUOTE_SINGLE=148
|
||||
REGEX_SINGLE=149
|
||||
REGEX_DOUBLE=150
|
||||
RBRACE=151
|
||||
RBRACKET=152
|
||||
RPAREN=153
|
||||
SEMICOLON=154
|
||||
SLASH=155
|
||||
SLASH_GT=156
|
||||
UNDERSCORE=157
|
||||
MULTI_LINE_COMMENT=158
|
||||
SINGLE_LINE_COMMENT=159
|
||||
WHITESPACE=160
|
||||
STRING_TEXT=161
|
||||
STRING_ESCAPE_TRIGGER=162
|
||||
FULL_STRING_TEXT=163
|
||||
FULL_STRING_ESCAPE_TRIGGER=164
|
||||
TAG_WS=165
|
||||
TAGC_WS=166
|
||||
TSQLX_TEXT_TEXT=167
|
||||
TSQLX_TEXT_WS=168
|
||||
'->'=114
|
||||
'*'=115
|
||||
'`'=116
|
||||
'\\'=117
|
||||
':'=118
|
||||
','=119
|
||||
'||'=120
|
||||
'-'=121
|
||||
'$'=122
|
||||
'.'=123
|
||||
'=='=124
|
||||
'>='=126
|
||||
'#'=128
|
||||
'~*'=129
|
||||
'=~*'=130
|
||||
'{'=131
|
||||
'['=132
|
||||
'('=133
|
||||
'<='=134
|
||||
'<'=135
|
||||
'</'=136
|
||||
'!~*'=138
|
||||
'!~'=139
|
||||
'?.'=140
|
||||
'??'=141
|
||||
'%'=142
|
||||
'+'=143
|
||||
'?'=144
|
||||
'"'=145
|
||||
'f\''=146
|
||||
'F\''=147
|
||||
'\''=148
|
||||
'~'=149
|
||||
'=~'=150
|
||||
'}'=151
|
||||
']'=152
|
||||
')'=153
|
||||
';'=154
|
||||
'/'=155
|
||||
'_'=157
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,166 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { CharStreams, CommonTokenStream } from "antlr4ts";
|
||||
import { TSQLLexer } from "./TSQLLexer.js";
|
||||
import { TSQLParser } from "./TSQLParser.js";
|
||||
|
||||
describe("TSQLParser", () => {
|
||||
function parse(input: string) {
|
||||
const inputStream = CharStreams.fromString(input);
|
||||
const lexer = new TSQLLexer(inputStream);
|
||||
const tokenStream = new CommonTokenStream(lexer);
|
||||
const parser = new TSQLParser(tokenStream);
|
||||
return parser;
|
||||
}
|
||||
|
||||
describe("select statements", () => {
|
||||
it("should parse a simple SELECT statement", () => {
|
||||
const parser = parse("SELECT * FROM users;");
|
||||
const tree = parser.select();
|
||||
|
||||
expect(tree).toBeDefined();
|
||||
// The select rule can return selectStmt, selectSetStmt, or tSQLxTagElement
|
||||
// Most SELECT statements are wrapped in selectSetStmt
|
||||
const selectSetStmt = tree.selectSetStmt();
|
||||
expect(selectSetStmt).toBeDefined();
|
||||
|
||||
// Get the underlying selectStmt from selectSetStmt -> selectStmtWithParens
|
||||
const selectStmtWithParens = selectSetStmt!.selectStmtWithParens();
|
||||
const selectStmt = selectStmtWithParens.selectStmt();
|
||||
|
||||
expect(selectStmt).toBeDefined();
|
||||
expect(selectStmt!.SELECT()).toBeDefined();
|
||||
expect(selectStmt!.columnExprList()).toBeDefined();
|
||||
expect(selectStmt!.fromClause()).toBeDefined();
|
||||
});
|
||||
|
||||
it("should parse SELECT with WHERE clause", () => {
|
||||
const parser = parse("SELECT id, name FROM users WHERE id = 1;");
|
||||
const tree = parser.select();
|
||||
|
||||
expect(tree).toBeDefined();
|
||||
const selectSetStmt = tree.selectSetStmt();
|
||||
const selectStmt = selectSetStmt!.selectStmtWithParens().selectStmt()!;
|
||||
|
||||
expect(selectStmt.whereClause()).toBeDefined();
|
||||
expect(selectStmt.whereClause()!.WHERE()).toBeDefined();
|
||||
expect(selectStmt.whereClause()!.columnExpr()).toBeDefined();
|
||||
});
|
||||
|
||||
it("should parse SELECT with multiple columns", () => {
|
||||
const parser = parse("SELECT id, name, email FROM users;");
|
||||
const tree = parser.select();
|
||||
|
||||
expect(tree).toBeDefined();
|
||||
const selectSetStmt = tree.selectSetStmt();
|
||||
const selectStmt = selectSetStmt!.selectStmtWithParens().selectStmt()!;
|
||||
|
||||
const columnList = selectStmt.columnExprList();
|
||||
expect(columnList).toBeDefined();
|
||||
// columnExprList contains comma-separated expressions
|
||||
expect(columnList.columnExpr().length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
it("should parse SELECT with DISTINCT", () => {
|
||||
const parser = parse("SELECT DISTINCT id FROM users;");
|
||||
const tree = parser.select();
|
||||
|
||||
expect(tree).toBeDefined();
|
||||
const selectSetStmt = tree.selectSetStmt();
|
||||
const selectStmt = selectSetStmt!.selectStmtWithParens().selectStmt()!;
|
||||
|
||||
expect(selectStmt.DISTINCT()).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("expressions", () => {
|
||||
it("should parse a simple addition expression", () => {
|
||||
const parser = parse("1 + 2");
|
||||
const tree = parser.expr();
|
||||
|
||||
expect(tree).toBeDefined();
|
||||
const columnExpr = tree.columnExpr();
|
||||
expect(columnExpr).toBeDefined();
|
||||
// Check that the expression has children (the operands and operator)
|
||||
expect(columnExpr.text).toBeDefined();
|
||||
const text = columnExpr.text;
|
||||
expect(text).toContain("1");
|
||||
expect(text).toContain("2");
|
||||
expect(text).toContain("+");
|
||||
});
|
||||
|
||||
it("should parse arithmetic expressions with parentheses", () => {
|
||||
const parser = parse("(1 + 2) * 3");
|
||||
const tree = parser.expr();
|
||||
|
||||
expect(tree).toBeDefined();
|
||||
const columnExpr = tree.columnExpr();
|
||||
expect(columnExpr).toBeDefined();
|
||||
// The expression should contain the operators
|
||||
const text = columnExpr.text;
|
||||
expect(text).toContain("+");
|
||||
expect(text).toContain("*");
|
||||
expect(text).toContain("1");
|
||||
expect(text).toContain("2");
|
||||
expect(text).toContain("3");
|
||||
});
|
||||
|
||||
it("should parse string literals", () => {
|
||||
const parser = parse("'hello world'");
|
||||
const tree = parser.expr();
|
||||
|
||||
expect(tree).toBeDefined();
|
||||
const columnExpr = tree.columnExpr();
|
||||
expect(columnExpr).toBeDefined();
|
||||
const text = columnExpr.text;
|
||||
expect(text).toContain("hello");
|
||||
expect(text).toContain("world");
|
||||
});
|
||||
|
||||
it("should parse numeric literals", () => {
|
||||
const parser = parse("42");
|
||||
const tree = parser.expr();
|
||||
|
||||
expect(tree).toBeDefined();
|
||||
const columnExpr = tree.columnExpr();
|
||||
expect(columnExpr).toBeDefined();
|
||||
expect(columnExpr.text).toContain("42");
|
||||
});
|
||||
});
|
||||
|
||||
describe("program", () => {
|
||||
it("should parse an empty program", () => {
|
||||
const parser = parse("");
|
||||
const tree = parser.program();
|
||||
|
||||
expect(tree).toBeDefined();
|
||||
expect(tree.EOF()).toBeDefined();
|
||||
expect(tree.declaration().length).toBe(0);
|
||||
});
|
||||
|
||||
it("should parse variable declarations", () => {
|
||||
const parser = parse("let x := 1");
|
||||
const tree = parser.program();
|
||||
|
||||
expect(tree).toBeDefined();
|
||||
expect(tree.declaration().length).toBe(1);
|
||||
const declaration = tree.declaration(0);
|
||||
|
||||
// Check if it's a varDecl
|
||||
const varDecl = declaration.varDecl();
|
||||
expect(varDecl).toBeDefined();
|
||||
expect(varDecl!.LET()).toBeDefined();
|
||||
expect(varDecl!.identifier()).toBeDefined();
|
||||
expect(varDecl!.expression()).toBeDefined();
|
||||
});
|
||||
|
||||
it("should parse multiple declarations", () => {
|
||||
const parser = parse("let x := 1; let y := 2");
|
||||
const tree = parser.program();
|
||||
|
||||
expect(tree).toBeDefined();
|
||||
// Count only non-empty declarations (varDecl or non-empty statements)
|
||||
const varDecls = tree.declaration().filter((d) => d.varDecl() !== undefined);
|
||||
expect(varDecls.length).toBe(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user