-- |
-- Module      : Language.Sql
-- Copyright   : Jean Carlo Giambastiani Lopes, 2017
-- License     : MIT
-- Stability   : experimental
--
-- A SQL parser implementation of the minimum required for a ODBC driver.
--
-- /EBNF:/
--     
-- > 
-- > character = ? any character ?;
-- > 
-- > letter = lower-case-letter | upper-case-letter;
-- > 
-- > lower-case-letter = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' 
-- >                   | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' 
-- >                   | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' ;
-- > 
-- > upper-case-letter = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' 
-- >                   | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T'
-- >                   | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' ;
-- > 
-- > digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ;
module Language.Sql.AST
where
import Data.List.NonEmpty

-- | /EBNF:/
--
-- > multiplication-operator = '*' | '/' ;
data MultiplicationOperator
    = Multiply
    | Divide
    deriving Show

-- | /EBNF:/
--
-- > sign = '+' | '-' ;
data Sign
    = Plus
    | Minus
    deriving Show

-- | /EBNF:/
--
-- > dynamic-parameter = '?' ;
data DynamicParameter = DynamicParameter
    deriving Show

-- | /EBNF:/
--
-- > comparison-operator = '<' | '>' | '<=' | '>=' | '=' | '<>' ;
data ComparisonOperator
    = LessThan
    | GreaterThan
    | LessOrEqualTo
    | GreaterOrEqualTo
    | Equality
    | Inequality
    deriving Show

-- | /EBNF:/
--
-- > unsigned-integer = digit, { digit };
newtype UnsignedInteger = UnsignedInteger Int
    deriving Show

-- | /EBNF:/
--
-- > literal = "'", { character - "'" | "''" }, "'";
newtype Literal = Literal String
    deriving Show

-- | /EBNF:/
--
-- > user-defined-name = letter, { digit | letter | '_' };
newtype UserDefinedName = UserDefinedName String
    deriving Show

-- | /EBNF:/
--
-- > table-identifier = user-defined-name;
newtype TableIdentifier = TableIdentifier UserDefinedName
    deriving Show

-- | /EBNF:/
--
-- > base-table-identifier = user-defined-name;
newtype BaseTableIdentifier = BaseTableIdentifier UserDefinedName
    deriving Show

-- | /EBNF:/
--
-- > column-identifier = user-defined-name;
newtype ColumnIdentifier = ColumnIdentifier UserDefinedName
    deriving Show

-- | /EBNF:/
--
-- > table-name = table-identifier;
newtype TableName = TableName TableIdentifier
    deriving Show

-- | /EBNF:/
--
-- > column-name = [ table-name, '.' ], column-identifier;
data ColumnName = ColumnName (Maybe TableName) ColumnIdentifier
    deriving Show

-- | /EBNF:/
--
-- > base-table-name = base-table-identifier;
newtype BaseTableName = BaseTableName BaseTableIdentifier
    deriving Show

-- | /EBNF:/
--
-- > table-reference = table-name;
newtype TableReference = TableReference TableName
    deriving Show

-- | /EBNF:/
--
-- > table-reference-list = table-reference,  { ',', table-reference };    
newtype TableReferenceList = TableReferenceList (NonEmpty TableReference)
    deriving Show

-- | /EBNF:/
--
-- > sort-ordering = 'ASC' | 'DESC' ;
data SortOrdering
    = Ascending
    | Descending
    deriving Show

-- | /EBNF:/
--
-- > sort-specification = (unsigned-integer | column-name), [ sort-ordering ];
data SortSpecification
    = SortByColumnPosition UnsignedInteger (Maybe SortOrdering)
    | SortByColumnName ColumnName (Maybe SortOrdering)
    deriving Show

-- | /EBNF:/
--
-- > order-by-clause = 'ORDER', 'BY', sort-specification, { ',', sort-specification };
newtype OrderByClause = OrderByClause (NonEmpty SortSpecification)
    deriving Show

-- | /EBNF:/
--
-- > data-type = 'char', '(', unsigned-integer, ')'
-- >           | 'varchar', '(', unsigned-integer, ')'
-- >           ;    
data DataType
    = Char UnsignedInteger
    | VarChar UnsignedInteger
    deriving Show

-- | /EBNF:/
--
-- > expression = term, [ expression-op ];
data Expression = Expression Term (Maybe ExpressionOp)
    deriving Show

-- | /EBNF:/
--
-- > expression-op = sign, expression;
data ExpressionOp = ExpressionOp Sign Expression
    deriving Show

-- | /EBNF:/
--
-- > factor = [ sign ], primary;
data Factor = Factor (Maybe Sign) Primary
    deriving Show    

-- | /EBNF:/
--
-- > term = factor, [ term-op ]
data Term = Term Factor (Maybe TermOp)
    deriving Show

-- | /EBNF:/
--
-- > term-op = multiplication-operator, term
data TermOp = TermOp MultiplicationOperator Term
    deriving Show

-- | /EBNF:/
--
-- > primary = column-name | dynamic-parameter | literal | '(', expression, ')';    
data Primary
    = PrimaryColumnName ColumnName
    | PrimaryDynamicParameter DynamicParameter
    | PrimaryLiteral Literal
    | PrimaryExpression Expression
    deriving Show

-- | /EBNF:/
--
-- > select-sublist = expression;
newtype SelectSubList = SelectSubList Expression
    deriving Show

-- | /EBNF:/
--
-- > (* select-list cannot contain parameters *)
-- > select-list = '*'
-- >             | select-sublist, { ',', select-sublist }
-- >             ;
data SelectList
    = SelectAll
    | SelectList (NonEmpty SelectSubList)
    deriving Show

-- | /EBNF:/
--
-- > insert-value = dynamic-parameter | literal | 'NULL' | 'USER' ;
data InsertValue
    = InsertDynamicParameter DynamicParameter
    | InsertLiteral Literal
    | InsertNull
    | InsertUser
    deriving Show

-- | /EBNF:/
--
-- > comparison-predicate = expression, comparison-operator, expression;
data ComparisonPredicate
    = ComparisonPredicate Expression ComparisonOperator Expression
    deriving Show

-- | /EBNF:/
--
-- > not = 'not';
data Not = Not
    deriving Show

-- | /EBNF:/
--
-- > boolean-factor = [ not ], boolean-primary;
data BooleanFactor = BooleanFactor (Maybe Not) BooleanPrimary
    deriving Show

-- | /EBNF:/
--
-- > boolean-primary = comparison-predicate
-- >                 | '(', search-condition, ')'
-- >                 ;
data BooleanPrimary
    = BooleanPrimaryComparison ComparisonPredicate
    | BooleanPrimarySearch SearchCondition
    deriving Show

-- | /EBNF:/
--
-- > boolean-and = 'and', boolean-term;
data BooleanAnd = BooleanAnd BooleanTerm
    deriving Show

-- | /EBNF:/
--
-- > boolean-term = boolean-factor, [ boolean-and ];
data BooleanTerm
    = BooleanTerm BooleanFactor (Maybe BooleanAnd)
    deriving Show

-- | /EBNF:/
--
-- > boolean-or = 'or', search-condition;
data BooleanOr = BooleanOr SearchCondition
    deriving Show

-- | /EBNF:/
--
-- > search-condition = boolean-term, [ boolean-or ];
data SearchCondition = SearchCondition BooleanTerm (Maybe BooleanOr)
    deriving Show

-- | /EBNF:/
--
-- > distinct = 'DISTINCT';
data Distinct = Distinct
    deriving Show

-- | /EBNF:/
--
-- > column-and-type = column-identifier, data-type;
data ColumnAndType = ColumnAndType ColumnIdentifier DataType
    deriving Show

-- | /EBNF:/
--
-- > create-table-statement = 'CREATE', 'TABLE', base-table-name, '(',
-- >                              column-and-type, { ',', column-and-type }
-- >                          ')'
data CreateTable = CreateTable BaseTableName (NonEmpty ColumnAndType)
    deriving Show

-- | /EBNF:/
--
-- > drop-table-statement = 'DROP', 'TABLE', base-table-name;
data Drop = Drop BaseTableName
    deriving Show

-- | /EBNF:/
--
-- > select-statement = 'SELECT', [ distinct ], select-list,
-- >                    'FROM' table-reference-list,
-- >                    ['WHERE' search-condition],
-- >                    [order-by-clause];
data Select
    = Select (Maybe Distinct) SelectList
      TableReferenceList 
      (Maybe SearchCondition)
      (Maybe OrderByClause)
    deriving Show

-- | /EBNF:/
--
-- > delete-statement-searched = 'DELETE', 'FROM', table-name
-- >                           , [ 'WHERE', search-condition ];
data Delete = Delete TableName (Maybe SearchCondition)
    deriving Show

-- | Type alias for:
--   /EBNF:/
-- 
-- > column-identifier, { ',', column-identifier }
type ColumnIdList = NonEmpty ColumnIdentifier

-- | Type alias for:
--   /EBNF:/
-- 
-- > insert-value, { ',', insert-value }
type InsertValueList = NonEmpty InsertValue

-- | /EBNF:/
--
-- > insert-statement = 'INSERT', 'INTO', table-name
-- >                  , [ '(', column-identifier, { ',', column-identifier } , ')' ],
-- >                  , 'VALUES', '(', insert-value, { ',', insert-value } ')'
data Insert
    = Insert TableName (Maybe ColumnIdList) InsertValueList
    deriving Show

-- | /EBNF:/
--
-- > update-value = expression | 'NULL';
data UpdateValue
    = UpdateValueExpression Expression
    | UpdateValueNull
    deriving Show

-- | /EBNF:/
--
-- > update-column = column-identifier, '=', update-value;
data UpdateColumn = UpdateColumn ColumnIdentifier UpdateValue
    deriving Show

-- | /EBNF:/
--
-- > update-column-list = update-column, { ',', update-column };
type UpdateColumnList = NonEmpty UpdateColumn

-- | /EBNF:/
--
-- > update-statement-searched = 'UPDATE', table-name
-- >                           , 'SET', update-column-list
-- >                           , [ 'WHERE', search-condition ]
-- >                           ;
data Update = Update TableName UpdateColumnList (Maybe SearchCondition)
    deriving Show

-- | /EBNF:/
--
-- > statement = create-table-statement
-- >           | delete-statement-searched
-- >           | drop-table-statement
-- >           | insert-statement
-- >           | select-statement
-- >           | update-statement-searched
-- >           ;
data Statement
    = CreateStatement CreateTable
    | DeleteStatement Delete
    | DropStatement Drop
    | InsertStatement Insert
    | SelectStatement Select
    | UpdateStatement Update
    deriving Show

-- | /EBNF:/
--
-- > statement-list = statenment, { ';', statement };
newtype StatementList = StatementList (NonEmpty Statement)
    deriving Show