content: {-# LANGUAGE OverloadedStrings #-}
  
  -- |
  -- Module      : Language.SQL.Keyword.Concat
  -- Copyright   : 2013 Kei Hibino
  -- License     : BSD3
  --
  -- Maintainer  : ex8k.hibino@gmail.com
  -- Stability   : experimental
  -- Portability : unknown
  --
  -- Concatenations on 'Keyword' types
  module Language.SQL.Keyword.Concat (
    -- * List concatenation functions
    -- $listConcatenation
    unwords',
  
    sepBy, parenSepBy,
  
    -- * Binary operators
    -- $binaryOperators
    defineBinOp,
  
    strBinOp,
  
    as, (<.>), (|*|),
  
    (.||.),
    (.=.), (.<.), (.<=.), (.>.), (.>=.), (.<>.),
    and, or, in',
    (<++>),
  
    fold,
  
    -- * Unary operator
    defineUniOp, paren,
    strUniOp
    ) where
  
  import Prelude hiding (and, or, not)
  import Data.List (intersperse)
  import Data.Monoid (mempty, mconcat, (<>))
  
  import Language.SQL.Keyword.Internal.Type (Keyword (..), word, wordShow, toDString, fromDString)
  
  
  {- $listConcatenation
  Functions to concatenate 'Keyword' list.
  -}
  
  -- | Separate 'Keyword' list with delimiter 'Keyword' and map to 'String' list.
  sepBy' :: [Keyword] -> Keyword -> [String]
  ws `sepBy'` d =  map wordShow . intersperse d $ ws
  
  -- | Concatenate 'Keyword' list like unwords on 'String' list.
  unwords' :: [Keyword] -> Keyword
  unwords' =  mconcat
  
  -- | Concatenate 'String' list into one 'Keyword'.
  concatStr :: [String] -> Keyword
  concatStr =  word . concat
  
  -- | Separate 'Keyword' list with delimiter 'Keyword' and concatenate into one 'Keyword'.
  sepBy :: [Keyword] -> Keyword -> Keyword
  ws `sepBy` d = concatStr $ ws `sepBy'` d
  
  -- | Do 'sepBy' and enclose by paren
  parenSepBy :: [Keyword] -> Keyword -> Keyword
  ws `parenSepBy` d = concatStr $ "(" : (ws `sepBy'` d) ++ [")"]
  
  {- $binaryOperators
  Binary operators on SQL. Result is concatenated into one 'Keyword'.
  -}
  
  -- | Directly concatenate SQL string without whitespaces.
  (<++>) :: Keyword -> Keyword -> Keyword
  x <++> y = fromDString $ toDString x <> toDString y
  
  concat' :: [Keyword] -> Keyword
  concat' =  fromDString . mconcat . map toDString
  
  -- | Define binary operator on 'Keyword' type.
  --   Result is not delimited by whitespace like concat on 'String' list.
  defineBinOp' :: Keyword -> Keyword -> Keyword -> Keyword
  defineBinOp' op a b = concat' [a, op, b]
  
  -- | Define binary operator on 'Keyword' type.
  --   Result is delimited by whitespace like unwords on 'String' list.
  defineBinOp :: Keyword -> Keyword -> Keyword -> Keyword
  defineBinOp op a b = mconcat [a, op, b]
  
  -- | Binary operator to create qualified name on SQL.
  (<.>) :: Keyword -> Keyword -> Keyword
  (<.>)  =  defineBinOp' "."
  
  -- | Binary operator to create comma separated words.
  (|*|) :: Keyword -> Keyword -> Keyword
  (|*|)  =  defineBinOp' ", "
  
  -- | Binary operator for SQL string expression concatenation.
  (.||.) :: Keyword -> Keyword -> Keyword
  (.||.) =  defineBinOp "||"
  
  -- | Binary eq operator for SQL expression.
  (.=.) :: Keyword -> Keyword -> Keyword
  (.=.)  =  defineBinOp "="
  
  -- | Binary not eq operator for SQL expression.
  (.<>.) :: Keyword -> Keyword -> Keyword
  (.<>.) =  defineBinOp "<>"
  
  -- | Binary lt operator for SQL expression.
  (.<.) :: Keyword -> Keyword -> Keyword
  (.<.)  =  defineBinOp "<"
  
  -- | Binary le operator for SQL expression.
  (.<=.) :: Keyword -> Keyword -> Keyword
  (.<=.) =  defineBinOp "<="
  
  -- | Binary gt operator for SQL expression.
  (.>.) :: Keyword -> Keyword -> Keyword
  (.>.)  =  defineBinOp ">"
  
  -- | Binary ge operator for SQL expression.
  (.>=.) :: Keyword -> Keyword -> Keyword
  (.>=.) =  defineBinOp ">="
  
  -- | Binary operator for SQL name alias.
  as :: Keyword -> Keyword -> Keyword
  as     =  defineBinOp AS
  
  -- | Binary `AND` operator for SQL boolean expression.
  and :: Keyword -> Keyword -> Keyword
  and    =  defineBinOp AND
  
  -- | Binary `OR` operator for SQL boolean expression.
  or :: Keyword -> Keyword -> Keyword
  or     =  defineBinOp OR
  
  -- | Fold operation using binary operator with empty result of zero length case.
  fold :: (Keyword -> Keyword -> Keyword) -- ^ Binary operator used in fold
       -> [Keyword]                       -- ^ List to fold
       -> Keyword                         -- ^ Result
  fold op =  d  where
    d []       = mempty
    d xs@(_:_) = foldr1 op xs
  
  -- | Define unary operator on 'Keyword' type represented by specified 'Keyword'.
  --   Result is delimited by whitespace like unwords on 'String' list.
  defineUniOp :: Keyword -> Keyword -> Keyword
  defineUniOp op e = mconcat [op, e]
  
  -- | Uni operator to create Parend words.
  paren :: Keyword -> Keyword
  paren w = concat' ["(", w, ")"]
  
  -- | Binary `IN` operator for SQL.
  in' :: Keyword -> Keyword -> Keyword
  in'    =  defineBinOp IN
  
  infixr 6 <++>
  infixr 5 .||.
  infixr 4 .=., .<., .<=., .>., .>=., .<>.
  infix  4 `in'`
  infixr 3 `and`
  infixr 2 `or`
  infixr 1 |*|
  
  -- | Define uni operator of string from 'Keyword' uni operator.
  strUniOp :: (Keyword -> Keyword) -> String -> String
  strUniOp u = wordShow . u . word
  
  -- | Define binary operator of string from 'Keyword' binary operator.
  strBinOp :: (Keyword -> Keyword -> Keyword) -> String -> String -> String
  strBinOp op a b = wordShow $ op (word a) (word b)
  