grammar Hamelin;

identifierEOF
    : identifier EOF
    ;

queryEOF
    : query EOF
    ;

commandEOF
    : command EOF
    ;

expressionEOF
    : expression EOF
    ;

pipelineEOF
    : pipeline EOF
    ;

simpleIdentifierEOF
    : simpleIdentifier EOF
    ;

query
    : (WITH simpleIdentifier ASSIGN pipeline)+ pipeline                                 #withQuery
    | pipeline                                                                          #standaloneQuery
    | expression                                                                        #expressionQuery
    ;

pipeline
    : command ( PIPE command )*                                                         #pipelineAlt
    ;

command
    : LET_COMMAND assignment (COMMA assignment)* COMMA?                                 #letCommand
    | WHERE_COMMAND expression                                                          #whereCommand
    | SELECT_COMMAND assignmentClause (COMMA assignmentClause)* COMMA?                  #selectCommand
    | DROP_COMMAND selection (COMMA selection)* COMMA?                                  #dropCommand
    | FROM_COMMAND fromClause (COMMA fromClause)* COMMA?                                #fromCommand
    | UNION_COMMAND fromClause (COMMA fromClause)* COMMA?                               #unionCommand
    | LIMIT_COMMAND expression                                                          #limitCommand
    | PARSE_COMMAND src=expression? string
                    AS? identifier (COMMA identifier)* COMMA? NODROP?                   #parseCommand
    | WITHIN_COMMAND expression                                                         #withinCommand
    | AGG_COMMAND (assignmentClause (COMMA assignmentClause)*)? COMMA?
                  (BY groupClause (COMMA groupClause)*)? COMMA?
                  (SORT BY? sortExpression (COMMA sortExpression)*)? COMMA?             #aggCommand
    | SORT BY? sortExpression (COMMA sortExpression)* COMMA?                            #sortCommand
    | WINDOW_COMMAND assignmentClause (COMMA assignmentClause)* COMMA?
                     (BY groupClause (COMMA groupClause)*)? COMMA?
                     (SORT BY? sortExpression (COMMA sortExpression)*)? COMMA?
                     (WITHIN_COMMAND within=expression)?                                #windowCommand
    | APPEND_COMMAND tableReference (DISTINCT_BY selection (COMMA selection)*)? COMMA?  #appendCommand
    | (JOIN_COMMAND | LOOKUP_COMMAND) fromClause (ON on=expression)?                    #joinCommand
    | EXPLODE_COMMAND assignmentClause                                                  #explodeCommand
    | (UNNEST_COMMAND | ROWS_COMMAND) expression                                        #unnestCommand
    | NEST_COMMAND identifier                                                           #nestCommand
    | MATCH_COMMAND pattern+
                    (AGG_COMMAND assignmentClause (COMMA assignmentClause)*)? COMMA?
                    (BY groupClause (COMMA groupClause)*)? COMMA?
                    (SORT BY? sortExpression (COMMA sortExpression)*)? COMMA?
                    (WITHIN_COMMAND within=expression)? COMMA?                          #matchCommand
    ;

assignmentClause: assignment | expression;
groupClause: assignmentClause;
assignment: identifier ASSIGN expression;
selection: identifier;
sortExpression: expression (ASC | DESC)?;
tableAlias: simpleIdentifier ASSIGN tableReference;
fromClause: tableAlias | tableReference;

expression
    // Keep this list in precedence order (important!)
    : operator=(MINUS | PLUS) expression                                                #unaryPrefixOperator
    | expression (SECOND_TRUNC | MINUTE_TRUNC | HOUR_TRUNC | DAY_TRUNC | WEEK_TRUNC |
                  MONTH_TRUNC | QUARTER_TRUNC | YEAR_TRUNC)                             #tsTrunc
    | left=expression operator=DOT right=simpleIdentifier                               #deref
    | value=expression LBRACKET index=expression RBRACKET                               #indexAccess
    | left=expression operator=(ASTERISK | SLASH | PERCENT) right=expression            #binaryOperator
    | left=expression operator=(PLUS | MINUS) right=expression                          #binaryOperator
    | operator=RANGE expression                                                         #unaryPrefixOperator
    | expression operator=RANGE                                                         #unaryPostfixOperator
    | left=expression operator=RANGE right=expression                                   #binaryOperator
    | left=expression operator=AS right=hamelintype                                     #cast
    | left=expression
        operator=(EQ | NEQ | LT | LTE | GT | GTE | IS | ISNOT | IN | NOTIN)
        right=expression                                                                #binaryOperator
    | operator=NOT expression                                                           #unaryPrefixOperator
    | left=expression operator=AND right=expression                                     #binaryOperator
    | left=expression operator=OR right=expression                                      #binaryOperator

    // Complex Literals
    | left=expression operator=COLON right=expression                                   #pairLiteral
    | LCURLY simpleIdentifier COLON expression
      (COMMA simpleIdentifier COLON expression)* COMMA? RCURLY                          #structLiteral
    | LPARENS ((expression COMMA) |
               (expression COMMA expression (COMMA expression)* COMMA?)) RPARENS        #tupleLiteral
    | LBRACKET (expression (COMMA expression)* COMMA?)? RBRACKET                        #arrayLiteral

    // Function Calls
    | functionName=simpleIdentifier
        LPARENS (namedArgument (COMMA namedArgument)* COMMA?)? RPARENS                  #functionCall
    | functionName=simpleIdentifier LPARENS
        positionalArgument (COMMA positionalArgument)*
                           (COMMA namedArgument)* COMMA? RPARENS                        #functionCall

    // Primitive Literals
    | NULL                                                                              #nullLiteral
    | number                                                                            #numericLiteral
    | TRUE                                                                              #booleanLiteral
    | FALSE                                                                             #booleanLiteral
    | string                                                                            #stringLiteral
    | BINARY_LITERAL                                                                    #binaryLiteral
    | RANGE                                                                             #unboundRangeLiteral
    | (NANOSECOND_INTERVAL | MICROSECOND_INTERVAL | MILLISECOND_INTERVAL |
       SECOND_INTERVAL | MINUTE_INTERVAL | HOUR_INTERVAL | DAY_INTERVAL |
       WEEK_INTERVAL | MONTH_INTERVAL | QUARTER_INTERVAL | YEAR_INTERVAL)               #intervalLiteral
    | ROWS_LITERAL                                                                      #rowsLiteral
    | (SECOND_TRUNC | MINUTE_TRUNC | HOUR_TRUNC | DAY_TRUNC | WEEK_TRUNC |
       MONTH_TRUNC | QUARTER_TRUNC | YEAR_TRUNC)                                        #tsTruncTimestampLiteral

    // Environment references
    | columnReference                                                                   #columnReferenceAlt

    // Keep this at the bottom to use it the mechanism that query authors have to specify precedence.
    | LPARENS expression RPARENS                                                        #parenthesizedExpression
    ;

hamelintype
    : simpleIdentifier LPARENS hamelintype (COMMA hamelintype)* COMMA? RPARENS          #parameterizedType
    | simpleIdentifier LPARENS INTEGER_VALUE (COMMA INTEGER_VALUE)* COMMA? RPARENS      #typeWithArguments
    | LCURLY simpleIdentifier COLON hamelintype
             (COMMA simpleIdentifier COLON hamelintype)* COMMA? RCURLY                  #structType
    | LPARENS hamelintype (COMMA hamelintype )* COMMA? RPARENS                          #tupleType
    | simpleIdentifier                                                                  #simpleType
    ;

pattern
    : fromClause quantifier?                                                            #quantified
    | LPARENS pattern+ RPARENS quantifier                                               #nested
    ;

quantifier
    : ASTERISK                                                                          #AnyNumber
    | PLUS                                                                              #AtLeastOne
    | QUESTIONMARK                                                                      #ZeroOrOne
    | LCURLY INTEGER_VALUE RCURLY                                                       #Exactly
    ;

columnReference
    : simpleIdentifier
    ;

tableReference
    : identifier
    ;

identifier
    : simpleIdentifier
    | simpleIdentifier (DOT simpleIdentifier)+
    ;

simpleIdentifier
    : IDENTIFIER                                                                        #unquotedIdentifier
    | BACKQUOTED_IDENTIFIER                                                             #backQuotedIdentifier
    ;

string
    : SINGLE_QUOTED_STRING                                                              #basicSingleQuotedStringLiteral
    | DOUBLE_QUOTED_STRING                                                              #basicDoubleQuotedStringLiteral
    | value=SINGLE_QUOTED_UNICODE_STRING (UESCAPE uesc=SINGLE_QUOTED_STRING)?           #unicodeSingleQuotedStringLiteral
    | value=DOUBLE_QUOTED_UNICODE_STRING (UESCAPE uesc=DOUBLE_QUOTED_STRING)?           #unicodeDoubleQuotedStringLiteral
    ;

number
    : value=DECIMAL_VALUE                                                               #decimalLiteral
    | value=DOUBLE_VALUE                                                                #scientificLiteral
    | value=INTEGER_VALUE                                                               #integerLiteral
    ;

positionalArgument
    : expression
    ;

namedArgument
    : simpleIdentifier ASSIGN expression
    ;

//
// COMMANDS
//

LET_COMMAND: 'LET' | 'let';
WHERE_COMMAND: 'WHERE' | 'where';
SELECT_COMMAND: 'SELECT' | 'select';
DROP_COMMAND: 'DROP' | 'drop';
FROM_COMMAND: 'FROM' | 'from';
UNION_COMMAND: 'UNION' | 'union';
LIMIT_COMMAND: 'LIMIT' | 'limit';
PARSE_COMMAND: 'PARSE' | 'parse';
WITHIN_COMMAND: 'WITHIN' | 'within';
AGG_COMMAND: 'AGG' | 'agg';
WINDOW_COMMAND: 'WINDOW' | 'window';
APPEND_COMMAND: 'APPEND' | 'append';
JOIN_COMMAND: 'JOIN' | 'join';
LOOKUP_COMMAND: 'LOOKUP' | 'lookup';
EXPLODE_COMMAND: 'EXPLODE' | 'explode';
UNNEST_COMMAND: 'UNNEST' | 'unnest';
NEST_COMMAND: 'NEST' | 'nest';
ROWS_COMMAND: 'ROWS' | 'rows';
MATCH_COMMAND: 'MATCH' | 'match';

AS: 'AS' | 'as';
NODROP: 'NODROP' | 'nodrop';

//
// Operators
//

PLUS: '+';
MINUS: '-';
ASTERISK: '*';
SLASH: '/';
PERCENT: '%';
LCURLY: '{';
RCURLY: '}';
COLON: ':';
QUESTIONMARK: '?';

EQ: '==';
NEQ: '!=';
LT: '<';
LTE: '<=';
GT: '>';
GTE: '>=';
RANGE: '..';

ASSIGN: '=';

//
// Keywords
//

AND: 'AND' | 'and';
FALSE: 'FALSE' | 'false';
IS: 'IS' | 'is';
NOT: 'NOT' | 'not';
ISNOT: 'IS NOT' | 'is not';
IN: 'IN' | 'in';
NOTIN: 'NOT IN' | 'not in';
NULL: 'NULL' | 'null';
OR: 'OR' | 'or';
TRUE: 'TRUE' | 'true';
UESCAPE: 'UESCAPE' | 'uescape';
WITH: 'WITH' | 'with';
BY: 'BY' | 'by';
SORT: 'SORT' | 'sort';
ASC: 'ASC' | 'asc';
DESC: 'DESC' | 'desc';
DISTINCT_BY: 'DISTINCT BY' | 'distinct by';
ON: 'ON' | 'on';
WHEN: 'WHEN' | 'when';

//
// Symbols
//

COMMA: ',';
PIPE: '|';
LPARENS: '(';
RPARENS: ')';
DOT: '.';
LBRACKET: '[';
RBRACKET: ']';

//
// Literals
//

SINGLE_QUOTED_STRING
        : '\'' ( ~'\'' | '\'\'')* '\''
        ;
DOUBLE_QUOTED_STRING
        : '"' ( ~'"' | '""')* '"'
        ;

SINGLE_QUOTED_UNICODE_STRING
        : 'U&\'' ( ~'\'' | '\'\'')* '\''    // Unicode string with default escape character: U&'Hello winter \2603 !'
        ;

DOUBLE_QUOTED_UNICODE_STRING
        : 'U&"' ( ~'"' | '""')* '"'         // Unicode string with custom escape character: U&'Hello winter #2603 !' UESCAPE '#'
        ;

// Note: we allow any character inside the binary literal and validate
// its a correct literal when the AST is being constructed. This
// allows us to provide more meaningful error messages to the user
BINARY_LITERAL
    : 'x\'' (~'\'')* '\''
    ;

NANOSECOND_INTERVAL
    : DECIMAL_INTEGER 'ns'
    ;

MICROSECOND_INTERVAL
    : DECIMAL_INTEGER 'us'
    ;

MILLISECOND_INTERVAL
    : DECIMAL_INTEGER 'ms'
    ;

SECOND_INTERVAL
    : DECIMAL_INTEGER  ('s' | 'sec' | 'secs' | 'second' | 'seconds')
    ;

MINUTE_INTERVAL
    : DECIMAL_INTEGER  ('m' | 'min' | 'mins' | 'minute' | 'minutes')
    ;

HOUR_INTERVAL
    : DECIMAL_INTEGER ('h' | 'hr' | 'hrs' | 'hour' | 'hours')
    ;

DAY_INTERVAL
    : DECIMAL_INTEGER ('d' | 'day' | 'days')
    ;

WEEK_INTERVAL
    : DECIMAL_INTEGER ('w' | 'week' | 'weeks')
    ;

MONTH_INTERVAL
    : DECIMAL_INTEGER ('mon' | 'month' | 'months')
    ;

QUARTER_INTERVAL
    : DECIMAL_INTEGER ('q' | 'qtr' | 'qtrs' | 'quarter' | 'quarters')
    ;

YEAR_INTERVAL
    : DECIMAL_INTEGER ('y' | 'yr' | 'yrs' | 'year' | 'years')
    ;

ROWS_LITERAL
    : DECIMAL_INTEGER ('r' | 'row' | 'rows')
    ;

SECOND_TRUNC
    : '@'  ('s' | 'sec' | 'secs' | 'second' | 'seconds')
    ;

MINUTE_TRUNC
    : '@'  ('m' | 'min' | 'mins' | 'minute' | 'minutes')
    ;

HOUR_TRUNC
    : '@' ('h' | 'hr' | 'hrs' | 'hour' | 'hours')
    ;

DAY_TRUNC
    : '@' ('d' | 'day' | 'days')
    ;

WEEK_TRUNC
    : '@' ('w' | 'week' | 'weeks')
    ;

MONTH_TRUNC
    : '@' ('mon' | 'month' | 'months')
    ;

QUARTER_TRUNC
    : '@' ('q' | 'qtr' | 'qtrs' | 'quarter' | 'quarters')
    ;

YEAR_TRUNC
    : '@' ('y' | 'yr' | 'yrs' | 'year' | 'years')
    ;

INTEGER_VALUE
    : DECIMAL_INTEGER
    | HEXADECIMAL_INTEGER
    | OCTAL_INTEGER
    | BINARY_INTEGER
    ;

DECIMAL_VALUE
    : DECIMAL_INTEGER '.' DECIMAL_INTEGER
    | '.' DECIMAL_INTEGER
    ;

DOUBLE_VALUE
    : DIGIT+ ('.' DIGIT*)? EXPONENT
    | '.' DIGIT+ EXPONENT
    ;

IDENTIFIER
    : [a-zA-Z_][a-zA-Z_0-9]*
    ;

BACKQUOTED_IDENTIFIER
    : '`' ( ~'`' | '``' )* '`'
    ;

fragment DECIMAL_INTEGER
    : DIGIT ('_'? DIGIT)*
    ;

fragment HEXADECIMAL_INTEGER
    : '0x' ('_'? (DIGIT | [A-F]))+
    ;

fragment OCTAL_INTEGER
    : '0o' ('_'? [0-7])+
    ;

fragment BINARY_INTEGER
    : '0b' ('_'? [01])+
    ;

fragment EXPONENT
    : 'e' [+-]? DIGIT+
    ;

fragment DIGIT
    : [0-9]
    ;

//
// Comments and whitespace
//

SIMPLE_COMMENT
    : '//' ~[\r\n]* '\r'? '\n'? -> channel(HIDDEN)
    ;

BRACKETED_COMMENT
    : '/*' .*? '*/' -> channel(HIDDEN)
    ;

WS
    : [ \r\n\t]+ -> skip
    ;
