# parse, min length 0
parse ::= (sql_stmt_list)* ;

# sql_stmt_list, min length 2
sql_stmt_list ::= (";")* select_stmt (";" (";")* select_stmt)* (";")* ;

# select_stmt, min length 2
select_stmt ::= (common_table_stmt | EMPTY) select_core (compound_operator select_core)* (order_by_stmt | EMPTY) (limit_stmt | EMPTY) ;

# expr, min length 1
expr ::= ( literal_value
	| BIND_PARAMETER
	| ((any_name "." | EMPTY) any_name "." | EMPTY) any_name
	| unary_operator expr
	| expr "||" expr
	| expr ("*" | "/" | "%") expr
	| expr ("+" | "-") expr
	| expr ("<<" | ">>" | "&" | "|") expr
	| expr ("<" | "<=" | ">" | ">=") expr
	| expr ( "="
        | "=="
        | "!="
        | "<>"
        | "IS "
        | "IS " "NOT "
        | "IN "
        | "LIKE "
        | "GLOB "
        | "MATCH "
        | "REGEXP ") expr
	| expr "AND " expr
	| expr "OR " expr
	| any_name "(" ((("DISTINCT " | EMPTY) expr ("," expr)* | "*") | EMPTY) ")" (filter_clause | EMPTY) (over_clause | EMPTY)
	| "(" expr ("," expr)* ")"
	| "CAST " "(" expr "AS " type_name ")"
	| expr "COLLATE " any_name
	| expr ("NOT " | EMPTY) ("LIKE " | "GLOB " | "REGEXP " | "MATCH ") expr ("ESCAPE " expr | EMPTY)
	| expr ("ISNULL " | "NOTNULL " | "NOT " "NULL ")
	| expr "IS " ("NOT " | EMPTY) expr
	| expr ("NOT " | EMPTY) "BETWEEN " expr "AND " expr
	| expr ("NOT " | EMPTY) "IN " ( "(" ((select_stmt | expr ("," expr)*) | EMPTY) ")"
	| (any_name "." | EMPTY) any_name
	| (any_name "." | EMPTY) any_name "(" (expr ("," expr)* | EMPTY) ")")
	| (("NOT " | EMPTY) "EXISTS " | EMPTY) "(" select_stmt ")"
	| "CASE " (expr | EMPTY) "WHEN " expr "THEN " expr ("WHEN " expr "THEN " expr)* ("ELSE " expr | EMPTY) "END "
	| raise_function) ;

# asc_desc, min length 1
asc_desc ::= ("ASC " | "DESC ") ;

# IDENTIFIER, min length 1
IDENTIFIER ::= ("id1 " | "id2 " | "id3 " | "id4 ") ;

# type_name, min length 0
type_name ::= (any_name)* ( ("(" signed_number ")" | "(" signed_number "," signed_number ")")
	| EMPTY) ;

# signed_number, min length 1
signed_number ::= (("+" | "-") | EMPTY) NUMERIC_LITERAL ;

# literal_value, min length 1
literal_value ::= ( NUMERIC_LITERAL
	| STRING_LITERAL
	| BLOB_LITERAL
	| "NULL "
	| "TRUE "
	| "FALSE "
	| "CURRENT_TIME "
	| "CURRENT_DATE "
	| "CURRENT_TIMESTAMP ") ;

# NUMERIC_LITERAL, min length 1
NUMERIC_LITERAL ::= DIGIT (DIGIT)* ("." (DIGIT)* | EMPTY) ;

# common_table_expression, min length 6
common_table_expression ::= any_name ("(" any_name ("," any_name)* ")" | EMPTY) "AS " "(" select_stmt ")" ;

# order_by_stmt, min length 3
order_by_stmt ::= "ORDER " "BY " ordering_term ("," ordering_term)* ;

# limit_stmt, min length 2
limit_stmt ::= "LIMIT " expr (("OFFSET " | ",") expr | EMPTY) ;

# any_name, min length 1
any_name ::= (IDENTIFIER | keyword | STRING_LITERAL | "(" any_name ")") ;

# BIND_PARAMETER, min length 1
BIND_PARAMETER ::= ("?" (DIGIT)* | (":" | "@" | "$") IDENTIFIER) ;

# unary_operator, min length 1
unary_operator ::= ("-" | "+" | "~" | "NOT ") ;

# filter_clause, min length 5
filter_clause ::= "FILTER " "(" "WHERE " expr ")" ;

# over_clause, min length 2
over_clause ::= "OVER " ( any_name
	| "(" (any_name | EMPTY) ("PARTITION " "BY " expr ("," expr)* | EMPTY) ("ORDER " "BY " ordering_term ("," ordering_term)* | EMPTY) (frame_spec | EMPTY) ")") ;

# raise_function, min length 4
raise_function ::= "RAISE " "(" ("IGNORE " | ("ROLLBACK " | "ABORT " | "FAIL ") "," STRING_LITERAL) ")" ;

# STRING_LITERAL, min length 2
STRING_LITERAL ::= "'" (ltr)* "'" ;

# BLOB_LITERAL, min length 3
BLOB_LITERAL ::= "X" STRING_LITERAL ;

# common_table_stmt, min length 7
common_table_stmt ::= "WITH " ("RECURSIVE " | EMPTY) common_table_expression ("," common_table_expression)* ;

# select_core, min length 2
select_core ::= ( "SELECT " (("DISTINCT " | "ALL ") | EMPTY) result_column ("," result_column)* ( "FROM " (table_or_subquery ("," table_or_subquery)* | join_clause)
	| EMPTY) ("WHERE " expr | EMPTY) ("GROUP " "BY " expr ("," expr)* ("HAVING " expr | EMPTY) | EMPTY) ( "WINDOW " any_name "AS " window_defn ("," any_name "AS " window_defn)*
	| EMPTY)
	| "VALUES " "(" expr ("," expr)* ")" ("," "(" expr ("," expr)* ")")*) ;

# compound_operator, min length 1
compound_operator ::= ("UNION " ("ALL " | EMPTY) | "INTERSECT " | "EXCEPT ") ;

# join_clause, min length 1
join_clause ::= table_or_subquery (join_operator table_or_subquery (join_constraint | EMPTY))* ;

# table_or_subquery, min length 1
table_or_subquery ::= ( (any_name "." | EMPTY) any_name (("AS " | EMPTY) any_name | EMPTY) (("INDEXED " "BY " any_name | "NOT " "INDEXED ") | EMPTY)
	| (any_name "." | EMPTY) any_name "(" expr ("," expr)* ")" (("AS " | EMPTY) any_name | EMPTY)
	| "(" (table_or_subquery ("," table_or_subquery)* | join_clause) ")"
	| "(" select_stmt ")" (("AS " | EMPTY) any_name | EMPTY)) ;

# join_operator, min length 1
join_operator ::= ( ","
	| ("NATURAL " | EMPTY) (("LEFT " ("OUTER " | EMPTY) | "INNER " | "CROSS ") | EMPTY) "JOIN ") ;

# join_constraint, min length 2
join_constraint ::= ("ON " expr | "USING " "(" any_name ("," any_name)* ")") ;

# result_column, min length 1
result_column ::= ("*" | any_name "." "*" | expr (("AS " | EMPTY) column_alias | EMPTY)) ;

# window_defn, min length 5
window_defn ::= "(" (any_name | EMPTY) ("PARTITION " "BY " expr ("," expr)* | EMPTY) "ORDER " "BY " ordering_term ("," ordering_term)* (frame_spec | EMPTY) ")" ;

# column_alias, min length 1
column_alias ::= (IDENTIFIER | STRING_LITERAL) ;

# ordering_term, min length 1
ordering_term ::= expr ("COLLATE " any_name | EMPTY) (asc_desc | EMPTY) ("NULLS " ("FIRST " | "LAST ") | EMPTY) ;

# frame_spec, min length 3
frame_spec ::= frame_clause ( ("EXCLUDE " "NO " "OTHERS " | "CURRENT " "ROW " | "GROUP " | "TIES ")
	| EMPTY) ;

# frame_clause, min length 3
frame_clause ::= ("RANGE " | "ROWS " | "GROUPS ") (frame_single | "BETWEEN " frame_left "AND " frame_right) ;

# frame_single, min length 2
frame_single ::= (expr "PRECEDING " | "UNBOUNDED " "PRECEDING " | "CURRENT " "ROW ") ;

# frame_left, min length 2
frame_left ::= ( expr "PRECEDING "
	| expr "FOLLOWING "
	| "CURRENT " "ROW "
	| "UNBOUNDED " "PRECEDING ") ;

# frame_right, min length 2
frame_right ::= ( expr "PRECEDING "
	| expr "FOLLOWING "
	| "CURRENT " "ROW "
	| "UNBOUNDED " "FOLLOWING ") ;

# keyword, min length 1
keyword ::= ( "ABORT "
	| "ACTION "
	| "ADD "
	| "AFTER "
	| "ALL "
	| "ALTER "
	| "ANALYZE "
	| "AND "
	| "AS "
	| "ASC "
	| "ATTACH "
	| "AUTOINCREMENT "
	| "BEFORE "
	| "BEGIN "
	| "BETWEEN "
	| "BY "
	| "CASCADE "
	| "CASE "
	| "CAST "
	| "CHECK "
	| "COLLATE "
	| "COLUMN "
	| "COMMIT "
	| "CONFLICT "
	| "CONSTRAINT "
	| "CREATE "
	| "CROSS "
	| "CURRENT_DATE "
	| "CURRENT_TIME "
	| "CURRENT_TIMESTAMP "
	| "DATABASE "
	| "DEFAULT "
	| "DEFERRABLE "
	| "DEFERRED "
	| "DELETE "
	| "DESC "
	| "DETACH "
	| "DISTINCT "
	| "DROP "
	| "EACH "
	| "ELSE "
	| "END "
	| "ESCAPE "
	| "EXCEPT "
	| "EXCLUSIVE "
	| "EXISTS "
	| "EXPLAIN "
	| "FAIL "
	| "FOR "
	| "FOREIGN "
	| "FROM "
	| "FULL "
	| "GLOB "
	| "GROUP "
	| "HAVING "
	| "IF "
	| "IGNORE "
	| "IMMEDIATE "
	| "IN "
	| "INDEX "
	| "INDEXED "
	| "INITIALLY "
	| "INNER "
	| "INSERT "
	| "INSTEAD "
	| "INTERSECT "
	| "INTO "
	| "IS "
	| "ISNULL "
	| "JOIN "
	| "KEY "
	| "LEFT "
	| "LIKE "
	| "LIMIT "
	| "MATCH "
	| "NATURAL "
	| "NO "
	| "NOT "
	| "NOTNULL "
	| "NULL "
	| "OF "
	| "OFFSET "
	| "ON "
	| "OR "
	| "ORDER "
	| "OUTER "
	| "PLAN "
	| "PRAGMA "
	| "PRIMARY "
	| "QUERY "
	| "RAISE "
	| "RECURSIVE "
	| "REFERENCES "
	| "REGEXP "
	| "REINDEX "
	| "RELEASE "
	| "RENAME "
	| "REPLACE "
	| "RESTRICT "
	| "RIGHT "
	| "ROLLBACK "
	| "ROW "
	| "ROWS "
	| "SAVEPOINT "
	| "SELECT "
	| "SET "
	| "TABLE "
	| "TEMP "
	| "TEMPORARY "
	| "THEN "
	| "TO "
	| "TRANSACTION "
	| "TRIGGER "
	| "UNION "
	| "UNIQUE "
	| "UPDATE "
	| "USING "
	| "VACUUM "
	| "VALUES "
	| "VIEW "
	| "VIRTUAL "
	| "WHEN "
	| "WHERE "
	| "WITH "
	| "WITHOUT "
	| "FIRST_VALUE "
	| "OVER "
	| "PARTITION "
	| "RANGE "
	| "PRECEDING "
	| "UNBOUNDED "
	| "CURRENT "
	| "FOLLOWING "
	| "CUME_DIST "
	| "DENSE_RANK "
	| "LAG "
	| "LAST_VALUE "
	| "LEAD "
	| "NTH_VALUE "
	| "NTILE "
	| "PERCENT_RANK "
	| "RANK "
	| "ROW_NUMBER "
	| "GENERATED "
	| "ALWAYS "
	| "STORED "
	| "TRUE "
	| "FALSE "
	| "WINDOW "
	| "NULLS "
	| "FIRST "
	| "LAST "
	| "FILTER "
	| "GROUPS "
	| "EXCLUDE ") ;

# DIGIT, min length 1
DIGIT ::= ("0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9") ;

# ltr, min length 1
ltr ::= ("a" | "b" | "c") ;

# EMPTY, min length 0
EMPTY ::= /* empty */ ;


