-- |
-- Module      : Language.SQL.Keyword.Internal.Type
-- Copyright   : 2013-2019 Kei Hibino
-- License     : BSD3
--
-- Maintainer  : ex8k.hibino@gmail.com
-- Stability   : experimental
-- Portability : unknown
--
-- This module defines package internal types.
module Language.SQL.Keyword.Internal.Type (
  -- * SQL keyword type interface.
  Keyword (..), word, wordShow,

  -- * Low-level diff string interface.
  fromDString, toDString,
  DString, dString, showDString, isEmptyDString
  ) where

import Data.String (IsString(..))
import Data.List (find)
import Data.Semigroup (Semigroup (..))
import Data.Monoid (Monoid (..))


-- | Diff String type for low-cost concatenation.
newtype DString = DString (String -> String)

-- | Make 'DString' from 'String'
dString :: String -> DString
dString :: String -> DString
dString =  (String -> String) -> DString
DString forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [a] -> [a] -> [a]
(++)

-- | Show 'DString' into 'String'
showDString :: DString -> String
showDString :: DString -> String
showDString (DString String -> String
f) = String -> String
f []

-- | 'DString' is empty or not.
isEmptyDString :: DString -> Bool
isEmptyDString :: DString -> Bool
isEmptyDString = forall (t :: * -> *) a. Foldable t => t a -> Bool
null forall b c a. (b -> c) -> (a -> b) -> a -> c
. DString -> String
showDString

instance Eq DString where
  DString
x == :: DString -> DString -> Bool
== DString
y = DString -> String
showDString DString
x forall a. Eq a => a -> a -> Bool
== DString -> String
showDString DString
y

instance Show DString where
  show :: DString -> String
show = DString -> String
showDString

instance Read DString where
  readsPrec :: Int -> ReadS DString
readsPrec Int
_ String
s = [(String -> DString
dString String
s, [])]

dappend :: DString -> DString -> DString
DString String -> String
f dappend :: DString -> DString -> DString
`dappend` DString String -> String
g = (String -> String) -> DString
DString forall a b. (a -> b) -> a -> b
$ String -> String
f forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
g

instance Semigroup DString where
  <> :: DString -> DString -> DString
(<>) = DString -> DString -> DString
dappend

instance Monoid DString where
  mempty :: DString
mempty  = (String -> String) -> DString
DString forall a. a -> a
id
  mappend :: DString -> DString -> DString
mappend = forall a. Semigroup a => a -> a -> a
(<>)

-- | Type represent SQL keywords.
data Keyword = SELECT | ALL | DISTINCT | ON
             | GROUP | COUNT | SUM | AVG | MAX | MIN | EVERY | ANY | SOME
             | CUBE | ROLLUP | GROUPING | SETS | HAVING
             | FOR

             | ORDER | BY | ASC | DESC | NULLS | LAST

             | OFFSET
             | LIMIT
             | FETCH | FIRST | NEXT | PERCENT
             | ROW | ROWS | ONLY | TIES

             | UNION | EXCEPT | INTERSECT

             | DELETE | USING | RETURNING

             | FROM | AS | WITH
             | JOIN | INNER | LEFT | RIGHT | FULL | NATURAL | OUTER

             | UPDATE | SET | DEFAULT

             | WHERE

             | INSERT | INTO | VALUES

             | MERGE

             | OVER | PARTITION
             | DENSE_RANK | RANK | ROW_NUMBER
             | PERCENT_RANK | CUME_DIST
             | LAG | LEAD | FIRST_VALUE | LAST_VALUE

             | CASE | END | WHEN | ELSE | THEN

             | LIKE | SIMILAR
             | AND | OR | NOT
             | EXISTS

             | IS | NULL | IN

             | DATE | TIME | TIMESTAMP | TIMESTAMPTZ | INTERVAL

             | Sequence !DString
             deriving (ReadPrec [Keyword]
ReadPrec Keyword
Int -> ReadS Keyword
ReadS [Keyword]
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [Keyword]
$creadListPrec :: ReadPrec [Keyword]
readPrec :: ReadPrec Keyword
$creadPrec :: ReadPrec Keyword
readList :: ReadS [Keyword]
$creadList :: ReadS [Keyword]
readsPrec :: Int -> ReadS Keyword
$creadsPrec :: Int -> ReadS Keyword
Read, Int -> Keyword -> String -> String
[Keyword] -> String -> String
Keyword -> String
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
showList :: [Keyword] -> String -> String
$cshowList :: [Keyword] -> String -> String
show :: Keyword -> String
$cshow :: Keyword -> String
showsPrec :: Int -> Keyword -> String -> String
$cshowsPrec :: Int -> Keyword -> String -> String
Show)

             {-
                  | (:?)
                  | (:+) | (:-) | (:*) | (:/)
                  | OPEN | CLOSE
             -}


-- | Wrap 'DString' into 'Keyword'
fromDString :: DString -> Keyword
fromDString :: DString -> Keyword
fromDString =  DString -> Keyword
Sequence

-- | Unwrap 'Keyword' into 'DString'
toDString :: Keyword -> DString
toDString :: Keyword -> DString
toDString = Keyword -> DString
d  where
  d :: Keyword -> DString
d (Sequence DString
ds) = DString
ds
  d  Keyword
w            = String -> DString
dString forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> String
show Keyword
w

-- | Make 'Keyword' from String
word :: String -> Keyword
word :: String -> Keyword
word =  DString -> Keyword
fromDString forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> DString
dString

-- | 'Keyword' type with OverloadedString extension,
--   can be involved same list with string literals.
--
-- > selectFoo = [SELECT, "a, b, c", FROM, "foo"]
--
instance IsString Keyword where
  fromString :: String -> Keyword
fromString String
s' = forall {b}. Maybe (Keyword, b) -> String -> Keyword
found (forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find ((forall a. Eq a => a -> a -> Bool
== String
"") forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> b
snd) (forall a. Read a => ReadS a
reads String
s')) String
s'  where
   found :: Maybe (Keyword, b) -> String -> Keyword
found  Maybe (Keyword, b)
Nothing      String
s = String -> Keyword
word String
s
   found (Just (Keyword
w, b
_)) String
_ = Keyword
w

kappend :: Keyword -> Keyword -> Keyword
Keyword
a kappend :: Keyword -> Keyword -> Keyword
`kappend` Keyword
b = DString -> Keyword
fromDString forall a b. (a -> b) -> a -> b
$ Keyword -> DString
toDString Keyword
a DString -> DString -> DString
`append'` Keyword -> DString
toDString Keyword
b
  where
    append' :: DString -> DString -> DString
append' DString
p DString
q
      | DString -> Bool
isEmptyDString DString
p = DString
q
      | DString -> Bool
isEmptyDString DString
q = DString
p
      | Bool
otherwise        = DString
p forall a. Semigroup a => a -> a -> a
<> DString
dspace forall a. Semigroup a => a -> a -> a
<> DString
q
    dspace :: DString
    dspace :: DString
dspace =  String -> DString
dString String
" "

instance Semigroup Keyword where
  <> :: Keyword -> Keyword -> Keyword
(<>) = Keyword -> Keyword -> Keyword
kappend

-- | 'Keyword' default concatenation separate by space.
instance Monoid Keyword where
  mempty :: Keyword
mempty  = DString -> Keyword
fromDString forall a. Monoid a => a
mempty
  mappend :: Keyword -> Keyword -> Keyword
mappend = forall a. Semigroup a => a -> a -> a
(<>)


-- | Show 'Keyword'
wordShow :: Keyword -> String
wordShow :: Keyword -> String
wordShow =  Keyword -> String
d  where
  d :: Keyword -> String
d (Sequence DString
s)   = DString -> String
showDString DString
s
  d Keyword
w              = forall a. Show a => a -> String
show Keyword
w

instance Eq Keyword where
  Keyword
x == :: Keyword -> Keyword -> Bool
== Keyword
y = Keyword -> String
wordShow Keyword
x forall a. Eq a => a -> a -> Bool
== Keyword -> String
wordShow Keyword
y