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

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

-- | Concatenate 'String' list into one 'Keyword'.
concatStr :: [String] -> Keyword
concatStr :: [String] -> Keyword
concatStr =  String -> Keyword
word forall b c a. (b -> c) -> (a -> b) -> a -> c
. 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 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 forall a b. (a -> b) -> a -> b
$ String
"(" forall a. a -> [a] -> [a]
: ([Keyword]
ws [Keyword] -> Keyword -> [String]
`sepBy'` Keyword
d) 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 forall a b. (a -> b) -> a -> b
$ Keyword -> DString
toDString Keyword
x forall a. Semigroup a => a -> a -> a
<> Keyword -> DString
toDString Keyword
y

concat' :: [Keyword] -> Keyword
concat' :: [Keyword] -> Keyword
concat' =  DString -> Keyword
fromDString forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Monoid a => [a] -> a
mconcat forall b c a. (b -> c) -> (a -> b) -> a -> c
. 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 = 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 []       = forall a. Monoid a => a
mempty
  d xs :: [Keyword]
xs@(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 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 = 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 forall b c a. (b -> c) -> (a -> b) -> a -> c
. Keyword -> Keyword
u 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 forall a b. (a -> b) -> a -> b
$ Keyword -> Keyword -> Keyword
op (String -> Keyword
word String
a) (String -> Keyword
word String
b)