module ASCII.QuasiQuoters ( char, string ) where

import ASCII.Char                  ( Char )
import ASCII.Superset              ( toCharMaybe, toCharListMaybe )
import ASCII.TemplateHaskell       ( isCharExp, isCharPat, isStringExp, isStringPat )
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  ( Q, Exp, Pat )

import qualified Data.Char   as Unicode
import qualified Data.String as Unicode

{- | Produces an expression or a pattern corresponding to an ASCII character.

The result will have an 'ASCII.Superset.CharSuperset' constraint; since this is polymorphic, use with a type signature to specify the particular you want is recommended.

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

>>> :set -XQuasiQuotes

>>> [char|e|] :: ASCII.Char
SmallLetterE

>>> [char|e|] :: Word8
101

Use in a pattern context requires enabling the @ViewPatterns@ language extension.

>>> :set -XViewPatterns

>>> case Tilde of [char|@|] -> 1; [char|~|] -> 2; _ -> 3
2

-}

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

{- | Produces an expression or a pattern corresponding to an ASCII string.

The result will have an 'ASCII.Superset.StringSuperset' constraint; since this is polymorphic, use with a type signature to specify the particular you want is recommended.

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

>>> :set -XQuasiQuotes

>>> [string|Hello!|] :: [ASCII.Char]
[CapitalLetterH,SmallLetterE,SmallLetterL,SmallLetterL,SmallLetterO,ExclamationMark]

>>> [string|Hello!|] :: Data.String.String
"Hello!"

>>> [string|Hello!|] :: Data.Text.Text
"Hello!"

>>> Data.ByteString.Builder.toLazyByteString [string|Hello!|]
"Hello!"


Use in a pattern context requires enabling the @ViewPatterns@ language extension.

>>> :set -XViewPatterns

>>> case [CapitalLetterH, SmallLetterI] of [string|Bye|] -> 1; [string|Hi|] -> 2; _ -> 3
2

-}

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

requireOneAscii :: Unicode.String -> Q Char
requireOneAscii :: String -> Q Char
requireOneAscii = String -> Q Char
requireOne (String -> Q Char) -> (Char -> Q Char) -> String -> Q Char
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> Char -> Q Char
requireAscii

oneMaybe :: [a] -> Maybe a
oneMaybe :: [a] -> Maybe a
oneMaybe [a]
xs = case [a]
xs of [a
x] -> a -> Maybe a
forall a. a -> Maybe a
Just a
x; [a]
_ -> Maybe a
forall a. Maybe a
Nothing

requireOne :: Unicode.String -> Q Unicode.Char
requireOne :: String -> Q Char
requireOne = String -> Maybe Char
forall a. [a] -> Maybe a
oneMaybe (String -> Maybe Char) -> String -> String -> Q Char
forall a b. (a -> Maybe b) -> String -> a -> Q b
|| String
"Must be exactly one character."

requireAscii :: Unicode.Char -> Q Char
requireAscii :: Char -> Q Char
requireAscii = Char -> Maybe Char
forall char. CharSuperset char => char -> Maybe Char
toCharMaybe (Char -> Maybe Char) -> String -> Char -> Q Char
forall a b. (a -> Maybe b) -> String -> a -> Q b
|| String
"Must be an ASCII character."

requireAsciiList :: Unicode.String -> Q [Char]
requireAsciiList :: String -> Q [Char]
requireAsciiList = String -> Maybe [Char]
forall string. StringSuperset string => string -> Maybe [Char]
toCharListMaybe (String -> Maybe [Char]) -> String -> String -> Q [Char]
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 || :: (a -> Maybe b) -> String -> a -> Q b
|| String
msg = \a
a -> case a -> Maybe b
f a
a of Just b
b -> b -> Q b
forall (m :: * -> *) a. Monad m => a -> m a
return b
b; Maybe b
Nothing -> String -> Q b
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
msg

expPatQQ :: (Unicode.String -> Q a) -> (a -> Q Exp) -> (a -> Q Pat) -> QuasiQuoter
expPatQQ :: (String -> Q a) -> (a -> Q Exp) -> (a -> Q Pat) -> QuasiQuoter
expPatQQ String -> Q a
f a -> Q Exp
a a -> Q Pat
b =
    QuasiQuoter :: (String -> Q Exp)
-> (String -> Q Pat)
-> (String -> Q Type)
-> (String -> Q [Dec])
-> QuasiQuoter
QuasiQuoter
        { quoteExp :: String -> Q Exp
quoteExp  = String -> Q a
f (String -> Q a) -> (a -> Q Exp) -> String -> Q Exp
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 (String -> Q a) -> (a -> Q Pat) -> String -> Q Pat
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 = String -> Q Type
forall (m :: * -> *) a b. MonadFail m => a -> m b
notType
        , quoteDec :: String -> Q [Dec]
quoteDec  = String -> Q [Dec]
forall (m :: * -> *) a b. MonadFail m => a -> m b
notDec
        }

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

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