{-|

Use of these quasi-quoters in a pattern context requires the @ViewPatterns@
language extension.

-}
module ASCII.QuasiQuoters
  (
    {- * Character -} char,
    {- * String -} string,
    {- * Caseless string -} caseless,
    {- * Upper-case string -} upper,
    {- * Lower-case string -} lower,
  )
  where

import ASCII.Case (Case (..))
import ASCII.Caseless (CaselessChar)
import ASCII.Char (Char)
import Control.Monad (return, (>=>))
import Control.Monad.Fail (MonadFail, fail)
import Data.Maybe (Maybe (..))
import Language.Haskell.TH.Quote (QuasiQuoter (..))
import Language.Haskell.TH.Syntax (Exp, Pat, Q)

import qualified ASCII.Superset as S
import qualified ASCII.TemplateHaskell as TH
import qualified Data.Char as Unicode
import qualified Data.String as Unicode

{-| An expression pattern corresponding to an ASCII character

=== In an expression context

The result will have a 'ASCII.Superset.FromChar' constraint.

The quasi-quoted string must consist of a single character that is within the
ASCII character set.

@
[char|e|] == SmallLetterE

[char|e|] == (101 :: Word8)
@

Since this is polymorphic, a type signature is recommended.

=== In a pattern context

The pattern matches a value of a type satisfying the 'ASCII.Superset.ToChar'
constraint.

@
let
    x = case Tilde of
          [char|@|] -> 1
          [char|~|] -> 2
          _ -> 3
in
    x == 2
@

-}

char :: QuasiQuoter
char :: QuasiQuoter
char = forall a.
(String -> Q a) -> (a -> Q Exp) -> (a -> Q Pat) -> QuasiQuoter
expPatQQ String -> Q Char
requireOneAscii Char -> Q Exp
TH.isCharExp Char -> Q Pat
TH.isCharPat

{- | An expression or pattern corresponding to an ASCII string

=== In an expression context

The result will have a 'ASCII.Superset.FromString' constraint.

The quasi-quoted string must consist only of characters are within the ASCII
character set.

@
[string|Hello!|] ==
    [CapitalLetterH,SmallLetterE,SmallLetterL,SmallLetterL,SmallLetterO,ExclamationMark]

[string|Hello!|] == ("Hello!" :: 'Data.String.String')

[string|Hello!|] == ("Hello!" :: 'Data.Text.Text')

'Data.ByteString.Builder.toLazyByteString' [string|Hello!|] == "Hello!"
@

Since this is polymorphic, a type signature is recommended.

=== In a pattern context

The pattern matches a value of a type satisfying the 'ASCII.Superset.ToString'
constraint.

@
let
    x = case [CapitalLetterH, SmallLetterI] of
          [string|Bye|] -> 1
          [string|Hi|] -> 2
          _ -> 3
in
    x == 2
@

-}

string :: QuasiQuoter
string :: QuasiQuoter
string = forall a.
(String -> Q a) -> (a -> Q Exp) -> (a -> Q Pat) -> QuasiQuoter
expPatQQ String -> Q [Char]
requireAsciiList [Char] -> Q Exp
TH.isStringExp [Char] -> Q Pat
TH.isStringPat

{-| An expression or pattern corresponding to a case-insensitive ASCII string

=== In an expression context

A monomorphic expression of type @['CaselessChar']@.

@
[caseless|Hello!|] ==
    [LetterH, LetterE, LetterL, LetterL, LetterO, ExclamationMark]
@

=== In a pattern context

A case-insensitive match of any type belonging to the
'ASCII.Superset.ToCaselessString' class.

@
let
    x = case "Hello!" :: 'Text' of
          [caseless|Bye!|] -> 1
          [caseless|hEllo!|] -> 2
          _ -> 3
in
    x == 2
@

-}
caseless :: QuasiQuoter
caseless :: QuasiQuoter
caseless = forall a.
(String -> Q a) -> (a -> Q Exp) -> (a -> Q Pat) -> QuasiQuoter
expPatQQ String -> Q [CaselessChar]
requireAsciiListCI [CaselessChar] -> Q Exp
TH.caselessListExp [CaselessChar] -> Q Pat
TH.caselessIsStringPat

{-| An expression or pattern corresponding to an ASCII string where all the
letters are of lower case

The letters in the body of the quasi-quotation may be written in any case
you like; they will be converted to lower case automatically.

=== In an expression context

The expression can become any type satisfying the
@('ASCII.Superset.ToCasefulString' ''LowerCase')@ constraint.
Any letters in the quoted content will be converted to lower case.

@
[lower|Hello!|] == ("hello!" :: 'Data.Text.Text')

[lower|Hello!|] == ("hello!" :: 'ASCII'lower' 'Data.ByteString.ByteString')
@

=== In a pattern context

The pattern matches a value of a type satisfying the 'ASCII.Superset.ToString'
constraint. A value matches this pattern if:

* All of the letters in the tested value are in lower case
* The tested value satisfies a case-insensitive comparison
  with the quasi-quoted content

@
let
    x = case "hi!" :: 'Text' of
          [lower|wow|] -> 1
          [lower|Hi!|] -> 2
          _ -> 3
in
    x == 2
@

-}
lower :: QuasiQuoter
lower :: QuasiQuoter
lower = forall a.
(String -> Q a) -> (a -> Q Exp) -> (a -> Q Pat) -> QuasiQuoter
expPatQQ String -> Q [CaselessChar]
requireAsciiListCI [CaselessChar] -> Q Exp
TH.lowerStringExp
    (\[CaselessChar]
x -> [Char] -> Q Pat
TH.isStringPat (forall (letterCase :: Case) string.
ToCasefulString letterCase string =>
[CaselessChar] -> string
S.toCasefulString @'LowerCase [CaselessChar]
x))

{-| An expression or pattern corresponding to an ASCII string where all the
letters are of upper case

The letters in the body of the quasi-quotation may be written in any case
you like; they will be converted to upper case automatically.

=== In an expression context

The expression can become any type satisfying the
@('ASCII.Superset.ToCasefulString' ''UpperCase')@ constraint.
Any letters in the quoted content will be converted to upper case.

@
[upper|Hello!|] == ("HELLO!" :: 'Text')

[upper|Hello!|] == ("HELLO!" :: 'ASCII'upper' 'Data.ByteString.ByteString')
@

=== In a pattern context

The pattern matches a value of a type satisfying the 'ASCII.Superset.ToString'
constraint. A value matches this pattern if:

* All of the letters in the tested value are in upper case
* The tested value satisfies a case-insensitive comparison
  with the quasi-quoted content

@
let
    x = case "HI!" :: 'Text' of
          [QQ.upper|wow|] -> 1
          [QQ.upper|Hi!|] -> 2
          _ -> 3
in
    x == 2
@

-}
upper :: QuasiQuoter
upper :: QuasiQuoter
upper = forall a.
(String -> Q a) -> (a -> Q Exp) -> (a -> Q Pat) -> QuasiQuoter
expPatQQ String -> Q [CaselessChar]
requireAsciiListCI [CaselessChar] -> Q Exp
TH.upperStringExp
    (\[CaselessChar]
x -> [Char] -> Q Pat
TH.isStringPat (forall (letterCase :: Case) string.
ToCasefulString letterCase string =>
[CaselessChar] -> string
S.toCasefulString @'UpperCase [CaselessChar]
x))

{-| Require the string to consist of exactly one ASCII character -}
requireOneAscii :: Unicode.String -> Q Char
requireOneAscii :: String -> Q Char
requireOneAscii = String -> Q Char
requireOne forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> Char -> Q Char
requireAscii

{-| Require the list to consist of exactly one element -}
oneMaybe :: [a] -> Maybe a
oneMaybe :: forall a. [a] -> Maybe a
oneMaybe [a]
xs = case [a]
xs of [a
x] -> forall a. a -> Maybe a
Just a
x; [a]
_ -> forall a. Maybe a
Nothing

{-| Require the string to consist of exactly one character -}
requireOne :: Unicode.String -> Q Unicode.Char
requireOne :: String -> Q Char
requireOne = forall a. [a] -> Maybe a
oneMaybe forall a b. (a -> Maybe b) -> String -> a -> Q b
|| String
"Must be exactly one character."

{-| Require the character to be ASCII -}
requireAscii :: Unicode.Char -> Q Char
requireAscii :: Char -> Q Char
requireAscii = forall char. ToChar char => char -> Maybe Char
S.toCharMaybe forall a b. (a -> Maybe b) -> String -> a -> Q b
|| String
"Must be an ASCII character."

{-| Require the string to consist of all ASCII characters -}
requireAsciiList :: Unicode.String -> Q [Char]
requireAsciiList :: String -> Q [Char]
requireAsciiList = forall string. ToString string => string -> Maybe [Char]
S.toCharListMaybe forall a b. (a -> Maybe b) -> String -> a -> Q b
|| String
"Must be only ASCII characters."

{-| Require the string to consist of all ASCII characters,
and return them with letter case discarded -}
requireAsciiListCI :: Unicode.String -> Q [CaselessChar]
requireAsciiListCI :: String -> Q [CaselessChar]
requireAsciiListCI = forall string.
ToCaselessString string =>
string -> Maybe [CaselessChar]
S.toCaselessCharListMaybe forall a b. (a -> Maybe b) -> String -> a -> Q b
|| String
"Must be only ASCII characters."

(||) :: (a -> Maybe b) -> Unicode.String -> a -> Q b
a -> Maybe b
f || :: forall a b. (a -> Maybe b) -> String -> a -> Q b
|| String
msg = \a
a -> case a -> Maybe b
f a
a of Just b
b -> forall (m :: * -> *) a. Monad m => a -> m a
return b
b; Maybe b
Nothing -> forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
msg

expPatQQ :: (Unicode.String -> Q a) -> (a -> Q Exp) -> (a -> Q Pat) -> QuasiQuoter
expPatQQ :: forall a.
(String -> Q a) -> (a -> Q Exp) -> (a -> Q Pat) -> QuasiQuoter
expPatQQ String -> Q a
f a -> Q Exp
a a -> Q Pat
b = QuasiQuoter
    { quoteExp :: String -> Q Exp
quoteExp  = String -> Q a
f forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> a -> Q Exp
a
    , quotePat :: String -> Q Pat
quotePat  = String -> Q a
f forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> a -> Q Pat
b
    , quoteType :: String -> Q Type
quoteType = forall (m :: * -> *) a b. MonadFail m => a -> m b
notType
    , quoteDec :: String -> Q [Dec]
quoteDec  = forall (m :: * -> *) a b. MonadFail m => a -> m b
notDec
    }

notType :: MonadFail m => a -> m b
notType :: forall (m :: * -> *) a b. MonadFail m => a -> m b
notType a
_ = forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Cannot be used in a type context."

notDec :: MonadFail m => a -> m b
notDec :: forall (m :: * -> *) a b. MonadFail m => a -> m b
notDec a
_ = forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Cannot be used in a declaration context."