    {-# 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]
    [Keyword]
    ws sepBy' :: [Keyword] -> Keyword -> [String]
    `sepBy'` Keyword
    d =  (Keyword -> String) -> [Keyword] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map Keyword -> String
    wordShow ([Keyword] -> [String])
    -> ([Keyword] -> [Keyword]) -> [Keyword] -> [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Keyword -> [Keyword] -> [Keyword]
    forall a. a -> [a] -> [a]
    intersperse Keyword
    d ([Keyword] -> [String]) -> [Keyword] -> [String]
    forall a b. (a -> b) -> a -> b
    $ [Keyword]
    ws

    -- | Concatenate 'Keyword' list like unwords on 'String' list.
    unwords' :: [Keyword] -> Keyword
    unwords' :: [Keyword] -> Keyword
    unwords' =  [Keyword] -> Keyword
    forall a. Monoid a => [a] -> a
    mconcat

    -- | Concatenate 'String' list into one 'Keyword'.
    concatStr :: [String] -> Keyword
    concatStr :: [String] -> Keyword
    concatStr =  String -> Keyword
    word (String -> Keyword) -> ([String] -> String) -> [String] -> Keyword
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [String] -> String
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat

    -- | Separate 'Keyword' list with delimiter 'Keyword' and concatenate into one 'Keyword'.
    sepBy :: [Keyword] -> Keyword -> Keyword
    [Keyword]
    ws sepBy :: [Keyword] -> Keyword -> Keyword
    `sepBy` Keyword
    d = [String] -> Keyword
    concatStr ([String] -> Keyword) -> [String] -> Keyword
    forall a b. (a -> b) -> a -> b
    $ [Keyword]
    ws [Keyword] -> Keyword -> [String]
    `sepBy'` Keyword
    d

    -- | Do 'sepBy' and enclose by paren
    parenSepBy :: [Keyword] -> Keyword -> Keyword
    [Keyword]
    ws parenSepBy :: [Keyword] -> Keyword -> Keyword
    `parenSepBy` Keyword
    d = [String] -> Keyword
    concatStr ([String] -> Keyword) -> [String] -> Keyword
    forall a b. (a -> b) -> a -> b
    $ String
    "(" String -> [String] -> [String]
    forall a. a -> [a] -> [a]
    : ([Keyword]
    ws [Keyword] -> Keyword -> [String]
    `sepBy'` Keyword
    d) [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++ [String
    ")"]

    {- $binaryOperators
    Binary operators on SQL. Result is concatenated into one 'Keyword'.
    -}

    -- | Directly concatenate SQL string without whitespaces.
    (<++>) :: Keyword -> Keyword -> Keyword
    Keyword
    x <++> :: Keyword -> Keyword -> Keyword
    <++> Keyword
    y = DString -> Keyword
    fromDString (DString -> Keyword) -> DString -> Keyword
    forall a b. (a -> b) -> a -> b
    $ Keyword -> DString
    toDString Keyword
    x DString -> DString -> DString
    forall a. Semigroup a => a -> a -> a
    <> Keyword -> DString
    toDString Keyword
    y

    concat' :: [Keyword] -> Keyword
    concat' :: [Keyword] -> Keyword
    concat' =  DString -> Keyword
    fromDString (DString -> Keyword)
    -> ([Keyword] -> DString) -> [Keyword] -> Keyword
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [DString] -> DString
    forall a. Monoid a => [a] -> a
    mconcat ([DString] -> DString)
    -> ([Keyword] -> [DString]) -> [Keyword] -> DString
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Keyword -> DString) -> [Keyword] -> [DString]
    forall a b. (a -> b) -> [a] -> [b]
    map Keyword -> DString
    toDString

    -- | Define binary operator on 'Keyword' type.
    --   Result is not delimited by whitespace like concat on 'String' list.
    defineBinOp' :: Keyword -> Keyword -> Keyword -> Keyword
    defineBinOp' :: Keyword -> Keyword -> Keyword -> Keyword
    defineBinOp' Keyword
    op Keyword
    a Keyword
    b = [Keyword] -> Keyword
    concat' [Keyword
    a, Keyword
    op, Keyword
    b]

    -- | Define binary operator on 'Keyword' type.
    --   Result is delimited by whitespace like unwords on 'String' list.
    defineBinOp :: Keyword -> Keyword -> Keyword -> Keyword
    defineBinOp :: Keyword -> Keyword -> Keyword -> Keyword
    defineBinOp Keyword
    op Keyword
    a Keyword
    b = [Keyword] -> Keyword
    forall a. Monoid a => [a] -> a
    mconcat [Keyword
    a, Keyword
    op, Keyword
    b]

    -- | Binary operator to create qualified name on SQL.
    (<.>) :: Keyword -> Keyword -> Keyword
    <.> :: Keyword -> Keyword -> Keyword
    (<.>)  =  Keyword -> Keyword -> Keyword -> Keyword
    defineBinOp' Keyword
    "."

    -- | Binary operator to create comma separated words.
    (|*|) :: Keyword -> Keyword -> Keyword
    |*| :: Keyword -> Keyword -> Keyword
    (|*|)  =  Keyword -> Keyword -> Keyword -> Keyword
    defineBinOp' Keyword
    ", "

    -- | Binary operator for SQL string expression concatenation.
    (.||.) :: Keyword -> Keyword -> Keyword
    .||. :: Keyword -> Keyword -> Keyword
    (.||.) =  Keyword -> Keyword -> Keyword -> Keyword
    defineBinOp Keyword
    "||"

    -- | Binary eq operator for SQL expression.
    (.=.) :: Keyword -> Keyword -> Keyword
    .=. :: Keyword -> Keyword -> Keyword
    (.=.)  =  Keyword -> Keyword -> Keyword -> Keyword
    defineBinOp Keyword
    "="

    -- | Binary not eq operator for SQL expression.
    (.<>.) :: Keyword -> Keyword -> Keyword
    .<>. :: Keyword -> Keyword -> Keyword
    (.<>.) =  Keyword -> Keyword -> Keyword -> Keyword
    defineBinOp Keyword
    "<>"

    -- | Binary lt operator for SQL expression.
    (.<.) :: Keyword -> Keyword -> Keyword
    .<. :: Keyword -> Keyword -> Keyword
    (.<.)  =  Keyword -> Keyword -> Keyword -> Keyword
    defineBinOp Keyword
    "<"

    -- | Binary le operator for SQL expression.
    (.<=.) :: Keyword -> Keyword -> Keyword
    .<=. :: Keyword -> Keyword -> Keyword
    (.<=.) =  Keyword -> Keyword -> Keyword -> Keyword
    defineBinOp Keyword
    "<="

    -- | Binary gt operator for SQL expression.
    (.>.) :: Keyword -> Keyword -> Keyword
    .>. :: Keyword -> Keyword -> Keyword
    (.>.)  =  Keyword -> Keyword -> Keyword -> Keyword
    defineBinOp Keyword
    ">"

    -- | Binary ge operator for SQL expression.
    (.>=.) :: Keyword -> Keyword -> Keyword
    .>=. :: Keyword -> Keyword -> Keyword
    (.>=.) =  Keyword -> Keyword -> Keyword -> Keyword
    defineBinOp Keyword
    ">="

    -- | Binary operator for SQL name alias.
    as :: Keyword -> Keyword -> Keyword
    as :: Keyword -> Keyword -> Keyword
    as     =  Keyword -> Keyword -> Keyword -> Keyword
    defineBinOp Keyword
    AS

    -- | Binary `AND` operator for SQL boolean expression.
    and :: Keyword -> Keyword -> Keyword
    and :: Keyword -> Keyword -> Keyword
    and    =  Keyword -> Keyword -> Keyword -> Keyword
    defineBinOp Keyword
    AND

    -- | Binary `OR` operator for SQL boolean expression.
    or :: Keyword -> Keyword -> Keyword
    or :: Keyword -> Keyword -> Keyword
    or     =  Keyword -> Keyword -> Keyword -> Keyword
    defineBinOp Keyword
    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 :: (Keyword -> Keyword -> Keyword) -> [Keyword] -> Keyword
    fold Keyword -> Keyword -> Keyword
    op =  [Keyword] -> Keyword
    d  where
      d :: [Keyword] -> Keyword
    d []       = Keyword
    forall a. Monoid a => a
    mempty
      d xs :: [Keyword]
    xs@(Keyword
    _:[Keyword]
    _) = (Keyword -> Keyword -> Keyword) -> [Keyword] -> Keyword
    forall a. (a -> a -> a) -> [a] -> a
    forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
    foldr1 Keyword -> Keyword -> Keyword
    op [Keyword]
    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 :: Keyword -> Keyword -> Keyword
    defineUniOp Keyword
    op Keyword
    e = [Keyword] -> Keyword
    forall a. Monoid a => [a] -> a
    mconcat [Keyword
    op, Keyword
    e]

    -- | Uni operator to create Parend words.
    paren :: Keyword -> Keyword
    paren :: Keyword -> Keyword
    paren Keyword
    w = [Keyword] -> Keyword
    concat' [Keyword
    "(", Keyword
    w, Keyword
    ")"]

    -- | Binary `IN` operator for SQL.
    in' :: Keyword -> Keyword -> Keyword
    in' :: Keyword -> Keyword -> Keyword
    in'    =  Keyword -> Keyword -> Keyword -> Keyword
    defineBinOp Keyword
    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 :: (Keyword -> Keyword) -> String -> String
    strUniOp Keyword -> Keyword
    u = Keyword -> String
    wordShow (Keyword -> String) -> (String -> Keyword) -> String -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Keyword -> Keyword
    u (Keyword -> Keyword) -> (String -> Keyword) -> String -> Keyword
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> Keyword
    word

    -- | Define binary operator of string from 'Keyword' binary operator.
    strBinOp :: (Keyword -> Keyword -> Keyword) -> String -> String -> String
    strBinOp :: (Keyword -> Keyword -> Keyword) -> String -> String -> String
    strBinOp Keyword -> Keyword -> Keyword
    op String
    a String
    b = Keyword -> String
    wordShow (Keyword -> String) -> Keyword -> String
    forall a b. (a -> b) -> a -> b
    $ Keyword -> Keyword -> Keyword
    op (String -> Keyword
    word String
    a) (String -> Keyword
    word String
    b)