{-# LANGUAGE OverloadedStrings #-}

-- |
-- Module      : Language.SQL.Keyword.Concat
-- Copyright   : 2013 Kei Hibino
-- License     : BSD3
--
-- Maintainer  : ex8k.hibino@gmail.com
-- Stability   : experimental
-- Portability : unknown
--
-- Concatinations on 'Keyword' types
module Language.SQL.Keyword.Concat (
  -- * List concatination functions
  -- $listConcatination
  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)


{- $listConcatination
Functions to concatinate 'Keyword' list.
-}

-- | Separate 'Keyword' list with delimiter 'Keyword' and map to 'String' list.
sepBy' :: [Keyword] -> Keyword -> [String]
ws :: [Keyword]
ws sepBy' :: [Keyword] -> Keyword -> [String]
`sepBy'` d :: 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

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

-- | Concatinate '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 concatinate into one 'Keyword'.
sepBy :: [Keyword] -> Keyword -> Keyword
ws :: [Keyword]
ws sepBy :: [Keyword] -> Keyword -> Keyword
`sepBy` d :: 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
ws :: [Keyword]
ws parenSepBy :: [Keyword] -> Keyword -> Keyword
`parenSepBy` d :: Keyword
d = [String] -> Keyword
concatStr ([String] -> Keyword) -> [String] -> Keyword
forall a b. (a -> b) -> a -> b
$ "(" String -> [String] -> [String]
forall a. a -> [a] -> [a]
: ([Keyword]
ws [Keyword] -> Keyword -> [String]
`sepBy'` Keyword
d) [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [")"]

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

-- | Directly concatinate SQL string without whitespaces.
(<++>) :: Keyword -> Keyword -> Keyword
x :: Keyword
x <++> :: Keyword -> Keyword -> Keyword
<++> y :: 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' op :: Keyword
op a :: Keyword
a b :: 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 op :: Keyword
op a :: Keyword
a b :: 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' "."

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

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

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

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

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

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

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

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

-- | 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 op :: 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
forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
foldr1 Keyword -> Keyword -> Keyword
op [Keyword]
xs

-- | Define unary operator on 'Keyword' type represeted by specified 'Keyword'.
--   Result is delimited by whitespace like unwords on 'String' list.
defineUniOp :: Keyword -> Keyword -> Keyword
defineUniOp :: Keyword -> Keyword -> Keyword
defineUniOp op :: Keyword
op e :: 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 w :: Keyword
w = [Keyword] -> Keyword
concat' ["(", Keyword
w, ")"]

-- | 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 u :: 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 op :: Keyword -> Keyword -> Keyword
op a :: String
a b :: 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)