Skip to content

Instantly share code, notes, and snippets.

@nithinivi
Last active August 29, 2025 07:53
Show Gist options
  • Select an option

  • Save nithinivi/24bb5b4f1d386af47111fec922e5bb7b to your computer and use it in GitHub Desktop.

Select an option

Save nithinivi/24bb5b4f1d386af47111fec922e5bb7b to your computer and use it in GitHub Desktop.
grammar SQLConditions;
// Parser rules
condition
: NOT? disjunction
;
disjunction
: conjunction (OR conjunction)*
;
conjunction
: predicate (AND predicate)*
;
predicate
: expression
| LPAREN condition RPAREN
;
expression
: column comp_op value
| column NOT? IN LPAREN value_list RPAREN
| column NOT? BETWEEN value AND value
;
comp_op
: EQ
| NEQ
| LT
| LTE
| GT
| GTE
;
value_list
: value (COMMA value)*
;
column
: identifier
;
value
: string
| number
| date
| time
| timestamp
;
identifier
: IDENTIFIER
;
string
: STRING
;
number
: NUMBER
;
date
: DATE_LITERAL
;
time
: TIME_LITERAL
;
timestamp
: TIMESTAMP_LITERAL
;
// Lexer rules
NOT : 'NOT';
AND : 'AND';
OR : 'OR';
IN : 'IN';
BETWEEN : 'BETWEEN';
EQ : '=';
NEQ : '<>';
LT : '<';
LTE : '<=';
GT : '>';
GTE : '>=';
LPAREN : '(';
RPAREN : ')';
COMMA : ',';
// Values
STRING
: '\'' (~['\r\n])* '\''
;
NUMBER
: DIGIT+
;
DATE_LITERAL
: 'DATE' '\'' DIGIT DIGIT DIGIT DIGIT '-' DIGIT DIGIT '-' DIGIT DIGIT '\''
;
TIME_LITERAL
: 'TIME' '\'' DIGIT DIGIT ':' DIGIT DIGIT (':' DIGIT DIGIT)? '\''
;
TIMESTAMP_LITERAL
: 'TIMESTAMP' '\'' DIGIT DIGIT DIGIT DIGIT '-' DIGIT DIGIT '-' DIGIT DIGIT ' ' DIGIT DIGIT ':' DIGIT DIGIT (':' DIGIT DIGIT)? '\''
;
IDENTIFIER
: LETTER (LETTER | DIGIT)*
;
fragment LETTER : [a-zA-Z];
fragment DIGIT : [0-9];
WS
: [ \t\r\n]+ -> skip
;
grammar SQLConditions1;
// ----------------- Parser Rules -----------------
condition
: NOT? disjunction
;
disjunction
: conjunction (OR conjunction)*
;
conjunction
: predicate (AND predicate)*
;
predicate
: expression
| LPAREN condition RPAREN
;
expression
: column comp_op value
| column NOT? IN LPAREN value_list RPAREN
| column NOT? BETWEEN value AND value
;
comp_op
: EQ
| NEQ
| LT
| LTE
| GT
| GTE
;
value_list
: value (COMMA value)*
;
column
: identifier | path_string
;
value
: STRING
| NUMBER
| DATE_LITERAL
| TIME_LITERAL
| TIMESTAMP_LITERAL
| relative
;
path_string
: DOLLAR identifier DOLLAR path_part*
;
path_part
: DOT identifier
| LBRACK identifier RBRACK
| LBRACK NUMBER RBRACK
;
relative
: NUMBER TIME_UNIT
;
// ----------------- Lexer Rules -----------------
NOT : 'NOT';
AND : 'AND';
OR : 'OR';
IN : 'IN';
BETWEEN : 'BETWEEN';
EQ : '=';
NEQ : '<>';
LT : '<';
LTE : '<=';
GT : '>';
GTE : '>=';
LPAREN : '(';
RPAREN : ')';
COMMA : ',';
DOT : '.';
LBRACK : '[';
RBRACK : ']';
DOLLAR : '$';
TIME_UNIT : [dhmMY]; // d=days, h=hours, m=minutes, M=months, Y=years
// ----------------- Literals -----------------
STRING
: '\'' (~['\\\r\n] | ESCAPE)* '\''
;
NUMBER
: '-'? INT ('.' [0-9]+)? EXP?
;
DATE_LITERAL
: 'DATE' '\'' DIGIT DIGIT DIGIT DIGIT '-' DIGIT DIGIT '-' DIGIT DIGIT '\''
;
TIME_LITERAL
: 'TIME' '\'' DIGIT DIGIT ':' DIGIT DIGIT (':' DIGIT DIGIT)? '\''
;
TIMESTAMP_LITERAL
: 'TIMESTAMP' '\'' DIGIT DIGIT DIGIT DIGIT '-' DIGIT DIGIT '-' DIGIT DIGIT ' ' DIGIT DIGIT ':' DIGIT DIGIT (':' DIGIT DIGIT)? '\''
;
identifier
: IDENTIFIER
;
IDENTIFIER
: LETTER (LETTER | DIGIT)*
;
// ----------------- Fragments -----------------
fragment LETTER : [a-zA-Z];
fragment DIGIT : [0-9];
fragment HEX : [0-9a-fA-F];
fragment ESCAPE
: '\\' (['"\\/bfnrt] | 'u' HEX HEX HEX HEX)
;
fragment INT
: '0'
| [1-9] [0-9]*
;
fragment EXP
: [Ee] [+-]? [0-9]+
;
// ----------------- Whitespace -----------------
WS
: [ \t\r\n]+ -> skip
;
<condition> ::= [ "NOT" ] <disjunction>
<disjunction> ::= <conjunction> { "OR" <conjunction> }
<conjunction> ::= <predicate> { "AND" <predicate> }
<predicate> ::= <expression>
| "(" <condition> ")"
<expression> ::= <column> <comp_op> <value>
| <column> [ "NOT" ] "IN" "(" <value_list> ")"
| <column> [ "NOT" ] "BETWEEN" <value> "AND" <value>
<comp_op> ::= "=" | "<>" | "<" | "<=" | ">" | ">="
<value_list> ::= <value> { "," <value> }
<column> ::= <identifier>
<value> ::= <string>
| <number>
| <date>
| <time>
| <timestamp>
<identifier> ::= <letter> { <letter_or_digit> }
<string> ::= "'" { <any_char_except_quote> } "'"
<number> ::= <digit> { <digit> }
<date> ::= "DATE" "'" <date_literal> "'"
<time> ::= "TIME" "'" <time_literal> "'"
<timestamp> ::= "TIMESTAMP" "'" <timestamp_literal> "'"
<date_literal> ::= <digit><digit><digit><digit> "-" <digit><digit> "-" <digit><digit>
<time_literal> ::= <digit><digit> ":" <digit><digit> [ ":" <digit><digit> ]
<timestamp_literal>::= <date_literal> " " <time_literal>
<letter> ::= "A" | ... | "Z" | "a" | ... | "z"
<letter_or_digit> ::= <letter> | <digit>
<digit> ::= "0" | ... | "9"
<any_char_except_quote> ::= any character except "'"
grammar SPQL;
condition
: NOT? disjunction
;
disjunction
: conjunction (OR conjunction)*
;
conjunction
: predicate (AND predicate)*
;
predicate
: expression
| LPAREN condition RPAREN
;
expression
: column comp_op value
| column NOT? IN LPAREN value_list RPAREN
| column NOT? BETWEEN value AND value
;
comp_op
: EQ
| NEQ
| LT
| LTE
| GT
| GTE
;
column
: UNQUOTED_STRING | path_string
;
path_string
: DOLLAR UNQUOTED_STRING DOLLAR path_part*
;
path_part
: DOT UNQUOTED_STRING
| LBRACK UNQUOTED_STRING RBRACK
| LBRACK NUMBER RBRACK
;
value_list
: value (COMMA value)*
;
value
: QUOTED_STRING
| NUMBER
| DATE_LITERAL
| TIME_LITERAL
| TIMESTAMP_LITERAL
| relative_time
;
relative_time
: NUMBER RELATIVE_TIME_UNIT
;
// Lexer rule
NOT : 'NOT';
AND : 'AND';
OR : 'OR';
IN : 'IN';
BETWEEN : 'BETWEEN';
EQ : '=';
NEQ : '<>';
LT : '<';
LTE : '<=';
GT : '>';
GTE : '>=';
LPAREN : '(';
RPAREN : ')';
COMMA : ',';
DOT : '.';
LBRACK : '[';
RBRACK : ']';
DOLLAR : '$';
// date
RELATIVE_TIME_UNIT : [dhmMY]; // d=days, h=hours, m=minutes, M=months, Y=years
DATE_LITERAL
: 'DATE' '\'' DIGIT DIGIT DIGIT DIGIT '-' DIGIT DIGIT '-' DIGIT DIGIT '\''
;
TIME_LITERAL
: 'TIME' '\'' DIGIT DIGIT ':' DIGIT DIGIT (':' DIGIT DIGIT)? '\''
;
TIMESTAMP_LITERAL
: 'TIMESTAMP' '\'' DIGIT DIGIT DIGIT DIGIT '-' DIGIT DIGIT '-' DIGIT DIGIT ' ' DIGIT DIGIT ':' DIGIT DIGIT (':' DIGIT DIGIT)? '\''
;
// number
NUMBER
: '-'? INT ('.' [0-9]+)? EXP?
;
// string
QUOTED_STRING
: '"' (ESC | ~["\\\r\n])* '"'
;
UNQUOTED_STRING
: UNQUOTED_CHAR+
;
// ---- Fragments ----
// Number
fragment INT
: '0'
| [1-9] [0-9]*
;
fragment EXP
: [Ee] [+-]? [0-9]+
;
// String
fragment UNQUOTED_CHAR
: LETTER
| DIGIT
| SYMBOL
;
fragment LETTER
: [a-zA-Z]
;
fragment DIGIT
: [0-9]
;
fragment SYMBOL
: [_] // DSL needs
;
fragment ESC
: '\\' (["\\/bfnrt] | UNICODE)
;
fragment UNICODE
: 'u' HEX HEX HEX HEX
;
fragment HEX
: [0-9a-fA-F]
;
grammar SQLConditions;
// Parser rules
condition
: NOT? disjunction
;
disjunction
: conjunction (OR conjunction)*
;
conjunction
: predicate (AND predicate)*
;
predicate
: expression
| LPAREN condition RPAREN
;
expression
: column comp_op value
| column NOT? IN LPAREN value_list RPAREN
| column NOT? BETWEEN value AND value
;
comp_op
: EQ
| NEQ
| LT
| LTE
| GT
| GTE
;
value_list
: value (COMMA value)*
;
column
: identifier
;
value
: path_string
| string
| number
| date
| time
| timestamp
;
path_string
: DOLLAR string DOLLAR path_part*
;
path_part
: DOT identifier
| LBRACK string RBRACK
| LBRACK number RBRACK
;
// Lexer rules
NOT : 'NOT';
AND : 'AND';
OR : 'OR';
IN : 'IN';
BETWEEN : 'BETWEEN';
EQ : '=';
NEQ : '<>';
LT : '<';
LTE : '<=';
GT : '>';
GTE : '>=';
LPAREN : '(';
RPAREN : ')';
COMMA : ',';
DOT : '.';
LBRACK : '[';
RBRACK : ']';
DOLLAR : '$';
// Values
STRING
: '\'' (~['\r\n])* '\''
;
NUMBER
: DIGIT+
;
DATE_LITERAL
: 'DATE' '\'' DIGIT DIGIT DIGIT DIGIT '-' DIGIT DIGIT '-' DIGIT DIGIT '\''
;
TIME_LITERAL
: 'TIME' '\'' DIGIT DIGIT ':' DIGIT DIGIT (':' DIGIT DIGIT)? '\''
;
TIMESTAMP_LITERAL
: 'TIMESTAMP' '\'' DIGIT DIGIT DIGIT DIGIT '-' DIGIT DIGIT '-' DIGIT DIGIT ' ' DIGIT DIGIT ':' DIGIT DIGIT (':' DIGIT DIGIT)? '\''
;
IDENTIFIER
: LETTER (LETTER | DIGIT)*
;
fragment LETTER : [a-zA-Z];
fragment DIGIT : [0-9];
WS
: [ \t\r\n]+ -> skip
;

Comments are disabled for this gist.