/* Context-free grammar for Doctrine Query Language
 *
 * Document syntax:
 *  - non-terminals begin with an upper case character
 *  - terminals begin with a lower case character
 *  - parentheses (...) are used for grouping
 *  - square brackets [...] are used for defining an optional part, eg. zero or
 *    one time
 *  - curly brackets {...} are used for repetion, eg. zero or more times
 *  - double quotation marks "..." define a terminal string
 *  - a vertical bar | represents an alternative
 *
 * At a first glance we'll support SQL-99 based queries
 * Initially Select and Sub-select DQL will not support LIMIT and OFFSET (due to limit-subquery algorithm)
 */


/*
 * TERMINALS
 *
 * identifier (name, email, ...)
 * string ('foo', 'bar''s house', '%ninja%', ...)
 * char ('/', '\\', ' ', ...)
 * integer (-1, 0, 1, 34, ...)
 * float (-0.23, 0.007, 1.245342E+8, ...)
 * boolean (false, true)
 */


/*
 * QUERY LANGUAGE (START)
 */
QueryLanguage ::= SelectStatement | UpdateStatement | DeleteStatement


/*
 * STATEMENTS
 */
SelectStatement ::= SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]
UpdateStatement ::= UpdateClause [WhereClause]
DeleteStatement ::= DeleteClause [WhereClause]


/*
 * IDENTIFIERS
 */
 
/* Alias Identification usage */
IdentificationVariable ::= identifier

/* Alias Identification declaration */
AliasIdentificationVariable :: = identifier

/* identifier that must be a class name */
AbstractSchemaName ::= identifier

/* identifier that must be a field */
FieldIdentificationVariable ::= identifier

/* identifier that must be a collection-valued association field (to-many) */
CollectionValuedAssociationField ::= FieldIdentificationVariable

/* identifier that must be a single-valued association field (to-one) */
SingleValuedAssociationField ::= FieldIdentificationVariable

/* identifier that must be an embedded class state field (for the future) */
EmbeddedClassStateField ::= FieldIdentificationVariable

/* identifier that must be a simple state field (name, email, ...) */
SimpleStateField ::= FieldIdentificationVariable


/*
 * PATH EXPRESSIONS
 */
JoinAssociationPathExpression             ::= JoinCollectionValuedPathExpression | JoinSingleValuedAssociationPathExpression
JoinCollectionValuedPathExpression        ::= IdentificationVariable "." CollectionValuedAssociationField
JoinSingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField
AssociationPathExpression                 ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression
SingleValuedPathExpression                ::= StateFieldPathExpression | SingleValuedAssociationPathExpression
StateFieldPathExpression                  ::= SimpleStateFieldPathExpression | SimpleStateFieldAssociationPathExpression
SingleValuedAssociationPathExpression     ::= IdentificationVariable "." {SingleValuedAssociationField "."}* SingleValuedAssociationField
CollectionValuedPathExpression            ::= IdentificationVariable "." {SingleValuedAssociationField "."}* CollectionValuedAssociationField
StateField                                ::= {EmbeddedClassStateField "."}* SimpleStateField
SimpleStateFieldPathExpression            ::= IdentificationVariable "." SimpleStateField
SimpleStateFieldAssociationPathExpression ::= SingleValuedAssociationPathExpression "." StateField


/*
 * CLAUSES
 */
SelectClause        ::= "SELECT" ["ALL" | "DISTINCT"] SelectExpression {"," SelectExpression}*
SimpleSelectClause  ::= "SELECT" ["ALL" | "DISTINCT"] SimpleSelectExpression
DeleteClause        ::= "DELETE" ["FROM"] AbstractSchemaName [["AS"] AliasIdentificationVariable]
WhereClause         ::= "WHERE" ConditionalExpression
FromClause          ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}*
SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}*
HavingClause        ::= "HAVING" ConditionalExpression
GroupByClause       ::= "GROUP" "BY" GroupByItem {"," GroupByItem}*
OrderByClause       ::= "ORDER" "BY" OrderByItem {"," OrderByItem}*
LimitClause         ::= "LIMIT" integer
OffsetClause        ::= "OFFSET" integer
UpdateClause        ::= "UPDATE" AbstractSchemaName [["AS"] AliasIdentificationVariable] "SET" UpdateItem {"," UpdateItem}*
Subselect           ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]


/*
 * ITEMS
 */
OrderByItem ::= StateFieldPathExpression ["ASC" | "DESC"]
GroupByItem ::= SingleValuedPathExpression
UpdateItem  ::= [IdentificationVariable"."]{StateField | SingleValuedAssociationField} "=" NewValue
NewValue    ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary |
                EnumPrimary | SimpleEntityExpression | "NULL"


/*
 * FROM/JOIN/INDEX BY
 */
IdentificationVariableDeclaration          ::= RangeVariableDeclaration [IndexBy] {JoinVariableDeclaration}*
SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration | AssociationPathExpression 
                                               ["AS"] AliasIdentificationVariable
JoinVariableDeclaration                    ::= Join [IndexBy]
RangeVariableDeclaration                   ::= AbstractSchemaName ["AS"] AliasIdentificationVariable
Join                                       ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" JoinAssociationPathExpression 
                                               ["AS"] AliasIdentificationVariable [("ON" | "WITH") ConditionalExpression]
IndexBy                                    ::= "INDEX" "BY" SimpleStateFieldPathExpression


/* 
 * SELECT EXPRESSION
 */
SelectExpression       ::= IdentificationVariable ["." "*"] |
                           (StateFieldPathExpression | AggregateExpression |
                           "(" Subselect ")" ) [["AS"] FieldIdentificationVariable]
SimpleSelectExpression ::= SingleValuedPathExpression | IdentificationVariable | AggregateExpression


/*
 * CONDITIONAL EXPRESSIONS
 */
ConditionalExpression       ::= ConditionalTerm | ConditionalExpression "OR" ConditionalTerm
ConditionalTerm             ::= ConditionalFactor | ConditionalTerm "AND" ConditionalFactor
ConditionalFactor           ::= ["NOT"] ConditionalPrimary
ConditionalPrimary          ::= SimpleConditionalExpression | "(" ConditionalExpression ")"
SimpleConditionalExpression ::= ComparisonExpression | BetweenExpression | LikeExpression |
                                InExpression | NullComparisonExpression | ExistsExpression |
                                EmptyCollectionComparisonExpression | CollectionMemberExpression
/* EmptyCollectionComparisonExpression and CollectionMemberExpression are for the future */


/*
 * COLLECTION EXPRESSIONS (FOR THE FUTURE)
 */
EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY"
CollectionMemberExpression          ::= EntityExpression ["NOT"] "MEMBER" ["OF"] CollectionValuedPathExpression


/*
 * LITERAL VALUES
 */
Literal ::= string | char | integer | float | boolean | InputParameter


/*
 * INPUT PARAMETER
 */
InputParameter      ::= PositionalParameter | NamedParameter
PositionalParameter ::= "?" integer
NamedParameter      ::= ":" string


/*
 * ARITHMETIC EXPRESSIONS
 */
ArithmeticExpression       ::= SimpleArithmeticExpression | "(" Subselect ")"
SimpleArithmeticExpression ::= ArithmeticTerm | SimpleArithmeticExpression ("+"|"-") ArithmeticTerm
ArithmeticTerm             ::= ArithmeticFactor | ArithmeticTerm ("*" |"/") ArithmeticFactor
ArithmeticFactor           ::= [("+" | "-")] ArithmeticPrimary
ArithmeticPrimary          ::= StateFieldPathExpression | Literal | "(" SimpleArithmeticExpression ")" | Function | AggregateExpression


/*
 * STRING/BOOLEAN/DATE/ENTITY/ENUM EXPRESSIONS
 */
StringExpression       ::= StringPrimary | "(" Subselect ")"
StringPrimary          ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression
BooleanExpression      ::= BooleanPrimary | "(" Subselect ")"
BooleanPrimary         ::= StateFieldPathExpression | boolean | InputParameter
EnumExpression         ::= EnumPrimary | "(" Subselect ")"
EnumPrimary            ::= StateFieldPathExpression | string | InputParameter
EntityExpression       ::= SingleValuedAssociationPathExpression | SimpleEntityExpression
SimpleEntityExpression ::= IdentificationVariable | InputParameter
DatetimeExpression     ::= DatetimePrimary | "(" Subselect ")"
DatetimePrimary        ::= StateFieldPathExpression | InputParameter | FunctionsReturningDatetime | AggregateExpression


/*
 * AGGREGATE EXPRESSION
 */
AggregateExpression ::= ("AVG" | "MAX" | "MIN" | "SUM") "(" ["DISTINCT"] StateFieldPathExpression ")" |
                        "COUNT" "(" ["DISTINCT"] (IdentificationVariable | SingleValuedAssociationPathExpression | StateFieldPathExpression) ")"


/*
 * QUANTIFIED/BETWEEN/COMPARISON/LIKE/NULL/EXISTS EXPRESSIONS
 */
QuantifiedExpression     ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")"
BetweenExpression        ::= ArithmeticExpression ["NOT"] "BETWEEN" ArithmeticExpression "AND" ArithmeticExpression
ComparisonExpression     ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression ) |
                             StringExpression ComparisonOperator (StringExpression | QuantifiedExpression) |
                             BooleanExpression ("=" | "<>" | "!=") (BooleanExpression | QuantifiedExpression) |
                             EnumExpression ("=" | "<>" | "!=") (EnumExpression | QuantifiedExpression) |
                             DatetimeExpression ComparisonOperator (DatetimeExpression | QuantifiedExpression) |
                             EntityExpression ("=" | "<>") (EntityExpression | QuantifiedExpression)
InExpression             ::= StateFieldPathExpression ["NOT"] "IN" "(" (Literal {"," Literal}* | Subselect) ")"
LikeExpression           ::= ["NOT"] "LIKE" string ["ESCAPE" char]
NullComparisonExpression ::= (SingleValuedPathExpression | InputParameter) "IS" ["NOT"] "NULL"
ExistsExpression         ::= ["NOT"] "EXISTS" "(" Subselect ")"
ComparisonOperator       ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!="


/*
 * FUNCTIONS
 */
FunctionsReturningStrings  ::= PortableFunctionsReturningStrings | OtherFunctionsReturningStrings
FunctionsReturningNumerics ::= PortableFunctionsReturningNumerics | OtherFunctionsReturningNumerics
FunctionsReturningDateTime ::= PortableFunctionsReturningDateTime | OtherFunctionsReturningDateTime


/*
 * OTHER FUNCTIONS: List of all allowed (but not portable) functions here.
 */
OtherFunctionsReturningStrings  ::= ...
OtherFunctionsReturningNumerics ::= ...
OtherFunctionsReturningDateTime ::= ...


/*
 * PORTABLE FUNCTIONS: List all portable functions here
 * @TODO add all supported portable functions here
 */
PortableFunctionsReturningNumerics ::=
        "LENGTH" "(" StringPrimary ")" |
        "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")" |
        "ABS" "(" SimpleArithmeticExpression ")" | "SQRT" "(" SimpleArithmeticExpression ")" |
        "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")" |
        "SIZE" "(" CollectionValuedPathExpression ")"

PortableFunctionsReturningDateTime ::= "CURRENT_DATE" | "CURRENT_TIME" | "CURRENT_TIMESTAMP"

PortableFunctionsReturningStrings ::=
        "CONCAT" "(" StringPrimary "," StringPrimary ")" |
        "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")" |
        "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" |
        "LOWER" "(" StringPrimary ")" |
        "UPPER" "(" StringPrimary ")"