{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE Safe #-}

-- |
-- Module      : Data.Char.Brackets
-- Description : Determine and manipulate bracket characters.
-- Maintainer  : hapytexeu+gh@gmail.com
-- Stability   : experimental
-- Portability : POSIX
--
-- Unicode considers 60 characters to be brackets: brackets are organized in /pairs/: each opening bracket has a corresponding closing bracket and vice versa.
--
-- The following characters are considered brackets where the first character is closed by the last character, the second by the last but one, etc.:
--
-- @
-- ([{༺༼᚛⁅⁽₍⌈⌊〈❨❪❬❮❰❲❴⟅⟦⟨⟪⟬⟮⦃⦅⦇⦉⦋⦍⦏⦑⦓⦕⦗⧘⧚⧼⸢⸤⸦⸨〈《「『【〔〖〘〚﹙﹛﹝([{⦅「」⦆}])﹞﹜﹚〛〙〗〕】』」》〉⸩⸧⸥⸣⧽⧛⧙⦘⦖⦔⦒⦎⦐⦌⦊⦈⦆⦄⟯⟭⟫⟩⟧⟆❵❳❱❯❭❫❩〉⌋⌉₎⁾⁆᚜༽༻}])
-- @
--
-- These characters span over several code blocks.
module Data.Char.Brackets
  ( -- * Listing and converting brackets
    bracketMaps,
    brackets,
    openBrackets,
    closeBrackets,
    toOpen,
    toClose,

    -- * Check the given bracket type
    BracketType (Open, Close),
    isBracket,
    bracketType,
    bracketType',
    isOpenBracket,
    isCloseBracket,

    -- * Determine the opposite bracket
    getOppositeChar,
    getOppositeChar',
  )
where

import Control.Applicative ((<|>))
import Control.DeepSeq (NFData)
import Data.Data (Data)
import Data.Hashable (Hashable)
import Data.Map (Map, fromList, lookup, member)
import Data.Maybe (fromMaybe)
import Data.Tuple (swap)
import GHC.Generics (Generic)
import Test.QuickCheck.Arbitrary (Arbitrary (arbitrary), arbitraryBoundedEnum)
import Prelude hiding (lookup)

-- | A data type that is used to specify the /type/ of bracket.
data BracketType
  = -- | The bracket is used to "open" a context.
    Open
  | -- | The bracket is used to "close" a context.
    Close
  deriving (BracketType
forall a. a -> a -> Bounded a
maxBound :: BracketType
$cmaxBound :: BracketType
minBound :: BracketType
$cminBound :: BracketType
Bounded, Typeable BracketType
BracketType -> DataType
BracketType -> Constr
(forall b. Data b => b -> b) -> BracketType -> BracketType
forall a.
Typeable a
-> (forall (c :: * -> *).
    (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> a -> c a)
-> (forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c a)
-> (a -> Constr)
-> (a -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c a))
-> (forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c a))
-> ((forall b. Data b => b -> b) -> a -> a)
-> (forall r r'.
    (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall r r'.
    (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall u. (forall d. Data d => d -> u) -> a -> [u])
-> (forall u. Int -> (forall d. Data d => d -> u) -> a -> u)
-> (forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> Data a
forall u. Int -> (forall d. Data d => d -> u) -> BracketType -> u
forall u. (forall d. Data d => d -> u) -> BracketType -> [u]
forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> BracketType -> r
forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> BracketType -> r
forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> BracketType -> m BracketType
forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> BracketType -> m BracketType
forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c BracketType
forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> BracketType -> c BracketType
forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c BracketType)
forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e))
-> Maybe (c BracketType)
gmapMo :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> BracketType -> m BracketType
$cgmapMo :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> BracketType -> m BracketType
gmapMp :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> BracketType -> m BracketType
$cgmapMp :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> BracketType -> m BracketType
gmapM :: forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> BracketType -> m BracketType
$cgmapM :: forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> BracketType -> m BracketType
gmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> BracketType -> u
$cgmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> BracketType -> u
gmapQ :: forall u. (forall d. Data d => d -> u) -> BracketType -> [u]
$cgmapQ :: forall u. (forall d. Data d => d -> u) -> BracketType -> [u]
gmapQr :: forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> BracketType -> r
$cgmapQr :: forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> BracketType -> r
gmapQl :: forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> BracketType -> r
$cgmapQl :: forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> BracketType -> r
gmapT :: (forall b. Data b => b -> b) -> BracketType -> BracketType
$cgmapT :: (forall b. Data b => b -> b) -> BracketType -> BracketType
dataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e))
-> Maybe (c BracketType)
$cdataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e))
-> Maybe (c BracketType)
dataCast1 :: forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c BracketType)
$cdataCast1 :: forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c BracketType)
dataTypeOf :: BracketType -> DataType
$cdataTypeOf :: BracketType -> DataType
toConstr :: BracketType -> Constr
$ctoConstr :: BracketType -> Constr
gunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c BracketType
$cgunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c BracketType
gfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> BracketType -> c BracketType
$cgfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> BracketType -> c BracketType
Data, Int -> BracketType
BracketType -> Int
BracketType -> [BracketType]
BracketType -> BracketType
BracketType -> BracketType -> [BracketType]
BracketType -> BracketType -> BracketType -> [BracketType]
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: BracketType -> BracketType -> BracketType -> [BracketType]
$cenumFromThenTo :: BracketType -> BracketType -> BracketType -> [BracketType]
enumFromTo :: BracketType -> BracketType -> [BracketType]
$cenumFromTo :: BracketType -> BracketType -> [BracketType]
enumFromThen :: BracketType -> BracketType -> [BracketType]
$cenumFromThen :: BracketType -> BracketType -> [BracketType]
enumFrom :: BracketType -> [BracketType]
$cenumFrom :: BracketType -> [BracketType]
fromEnum :: BracketType -> Int
$cfromEnum :: BracketType -> Int
toEnum :: Int -> BracketType
$ctoEnum :: Int -> BracketType
pred :: BracketType -> BracketType
$cpred :: BracketType -> BracketType
succ :: BracketType -> BracketType
$csucc :: BracketType -> BracketType
Enum, BracketType -> BracketType -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: BracketType -> BracketType -> Bool
$c/= :: BracketType -> BracketType -> Bool
== :: BracketType -> BracketType -> Bool
$c== :: BracketType -> BracketType -> Bool
Eq, forall x. Rep BracketType x -> BracketType
forall x. BracketType -> Rep BracketType x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep BracketType x -> BracketType
$cfrom :: forall x. BracketType -> Rep BracketType x
Generic, Eq BracketType
BracketType -> BracketType -> Bool
BracketType -> BracketType -> Ordering
BracketType -> BracketType -> BracketType
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: BracketType -> BracketType -> BracketType
$cmin :: BracketType -> BracketType -> BracketType
max :: BracketType -> BracketType -> BracketType
$cmax :: BracketType -> BracketType -> BracketType
>= :: BracketType -> BracketType -> Bool
$c>= :: BracketType -> BracketType -> Bool
> :: BracketType -> BracketType -> Bool
$c> :: BracketType -> BracketType -> Bool
<= :: BracketType -> BracketType -> Bool
$c<= :: BracketType -> BracketType -> Bool
< :: BracketType -> BracketType -> Bool
$c< :: BracketType -> BracketType -> Bool
compare :: BracketType -> BracketType -> Ordering
$ccompare :: BracketType -> BracketType -> Ordering
Ord, ReadPrec [BracketType]
ReadPrec BracketType
Int -> ReadS BracketType
ReadS [BracketType]
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [BracketType]
$creadListPrec :: ReadPrec [BracketType]
readPrec :: ReadPrec BracketType
$creadPrec :: ReadPrec BracketType
readList :: ReadS [BracketType]
$creadList :: ReadS [BracketType]
readsPrec :: Int -> ReadS BracketType
$creadsPrec :: Int -> ReadS BracketType
Read, Int -> BracketType -> ShowS
[BracketType] -> ShowS
BracketType -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [BracketType] -> ShowS
$cshowList :: [BracketType] -> ShowS
show :: BracketType -> String
$cshow :: BracketType -> String
showsPrec :: Int -> BracketType -> ShowS
$cshowsPrec :: Int -> BracketType -> ShowS
Show)

instance Arbitrary BracketType where
  arbitrary :: Gen BracketType
arbitrary = forall a. (Bounded a, Enum a) => Gen a
arbitraryBoundedEnum

instance Hashable BracketType

instance NFData BracketType

-- | A list of 2-tuples where the first item
-- of each tuple is the opening bracket, and the
-- second item its closing counterpart.
bracketMaps :: [(Char, Char)]
bracketMaps :: [(Char, Char)]
bracketMaps =
  [ (Char
'(', Char
')'),
    (Char
'[', Char
']'),
    (Char
'{', Char
'}'),
    (Char
'\x0f3a', Char
'\x0f3b'),
    (Char
'\x0f3c', Char
'\x0f3d'),
    (Char
'\x169b', Char
'\x169c'),
    (Char
'\x2045', Char
'\x2046'),
    (Char
'\x207d', Char
'\x207e'),
    (Char
'\x208d', Char
'\x208e'),
    (Char
'\x2308', Char
'\x2309'),
    (Char
'\x230a', Char
'\x230b'),
    (Char
'\x2329', Char
'\x232a'),
    (Char
'\x2768', Char
'\x2769'),
    (Char
'\x276a', Char
'\x276b'),
    (Char
'\x276c', Char
'\x276d'),
    (Char
'\x276e', Char
'\x276f'),
    (Char
'\x2770', Char
'\x2771'),
    (Char
'\x2772', Char
'\x2773'),
    (Char
'\x2774', Char
'\x2775'),
    (Char
'\x27c5', Char
'\x27c6'),
    (Char
'\x27e6', Char
'\x27e7'),
    (Char
'\x27e8', Char
'\x27e9'),
    (Char
'\x27ea', Char
'\x27eb'),
    (Char
'\x27ec', Char
'\x27ed'),
    (Char
'\x27ee', Char
'\x27ef'),
    (Char
'\x2983', Char
'\x2984'),
    (Char
'\x2985', Char
'\x2986'),
    (Char
'\x2987', Char
'\x2988'),
    (Char
'\x2989', Char
'\x298a'),
    (Char
'\x298b', Char
'\x298c'),
    (Char
'\x298d', Char
'\x2990'),
    (Char
'\x298f', Char
'\x298e'),
    (Char
'\x2991', Char
'\x2992'),
    (Char
'\x2993', Char
'\x2994'),
    (Char
'\x2995', Char
'\x2996'),
    (Char
'\x2997', Char
'\x2998'),
    (Char
'\x29d8', Char
'\x29d9'),
    (Char
'\x29da', Char
'\x29db'),
    (Char
'\x29fc', Char
'\x29fd'),
    (Char
'\x2e22', Char
'\x2e23'),
    (Char
'\x2e24', Char
'\x2e25'),
    (Char
'\x2e26', Char
'\x2e27'),
    (Char
'\x2e28', Char
'\x2e29'),
    (Char
'\x3008', Char
'\x3009'),
    (Char
'\x300a', Char
'\x300b'),
    (Char
'\x300c', Char
'\x300d'),
    (Char
'\x300e', Char
'\x300f'),
    (Char
'\x3010', Char
'\x3011'),
    (Char
'\x3014', Char
'\x3015'),
    (Char
'\x3016', Char
'\x3017'),
    (Char
'\x3018', Char
'\x3019'),
    (Char
'\x301a', Char
'\x301b'),
    (Char
'\xfe59', Char
'\xfe5a'),
    (Char
'\xfe5b', Char
'\xfe5c'),
    (Char
'\xfe5d', Char
'\xfe5e'),
    (Char
'\xff08', Char
'\xff09'),
    (Char
'\xff3b', Char
'\xff3d'),
    (Char
'\xff5b', Char
'\xff5d'),
    (Char
'\xff5f', Char
'\xff60'),
    (Char
'\xff62', Char
'\xff63')
  ]

-- | The list of all brackets characters.
brackets ::
  -- | The list of all 'Char's that are brackets.
  [Char]
brackets :: String
brackets = [Char
ci | ~(Char
ca, Char
cb) <- [(Char, Char)]
bracketMaps, Char
ci <- [Char
ca, Char
cb]]

-- | A list of 'Char's that contains all opening brackets.
openBrackets ::
  -- | The list of all 'Char's that are /opening brackets/.
  [Char]
openBrackets :: String
openBrackets = forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> a
fst [(Char, Char)]
bracketMaps

-- | A list of 'Char's that contains all closing brackets.
closeBrackets ::
  -- | The list of all 'Char's that are /closing brackets/.
  [Char]
closeBrackets :: String
closeBrackets = forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> b
snd [(Char, Char)]
bracketMaps

-- | A 'Map' that maps the given /open bracket/
-- characters to the corresponding /close bracket/.
toClose :: Map Char Char
toClose :: Map Char Char
toClose = forall k a. Ord k => [(k, a)] -> Map k a
fromList [(Char, Char)]
bracketMaps

-- | A 'Map' that maps the given /close bracket/
-- characters to the corresponding /open bracket/.
toOpen :: Map Char Char
toOpen :: Map Char Char
toOpen = forall k a. Ord k => [(k, a)] -> Map k a
fromList (forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> (b, a)
swap [(Char, Char)]
bracketMaps)

-- | Check if the given 'Char' is a /bracket/ character.
isBracket ::
  -- | The given 'Char' to test.
  Char ->
  -- | 'True' if the given 'Char' is an open bracket; 'False' otherwise.
  Bool
isBracket :: Char -> Bool
isBracket Char
c = forall {a}. Map Char a -> Bool
go Map Char Char
toClose Bool -> Bool -> Bool
|| forall {a}. Map Char a -> Bool
go Map Char Char
toOpen
  where
    go :: Map Char a -> Bool
go = forall k a. Ord k => k -> Map k a -> Bool
member Char
c

-- | Check if the given 'Char' is an /open bracket/.
isOpenBracket ::
  -- | The given 'Char' to test.
  Char ->
  -- | 'True' if the 'Char' is an /open bracket/; 'False' otherwise.
  Bool
isOpenBracket :: Char -> Bool
isOpenBracket = (forall k a. Ord k => k -> Map k a -> Bool
`member` Map Char Char
toClose)

-- | Check if the given 'Char' is a /close bracket/.
isCloseBracket ::
  -- | The given 'Char' to test.
  Char ->
  -- | 'True' if the 'Char' is an /close bracket/; 'False' otherwise.
  Bool
isCloseBracket :: Char -> Bool
isCloseBracket = (forall k a. Ord k => k -> Map k a -> Bool
`member` Map Char Char
toOpen)

-- | Check the 'BracketType' of the 'Char' wrapped in a 'Just' data construct;
-- 'Nothing' if the given 'Char' is /not/ a /bracket/ character.
bracketType :: Char -> Maybe BracketType
bracketType :: Char -> Maybe BracketType
bracketType Char
c
  | forall {a}. Map Char a -> Bool
go Map Char Char
toClose = forall a. a -> Maybe a
Just BracketType
Open
  | forall {a}. Map Char a -> Bool
go Map Char Char
toOpen = forall a. a -> Maybe a
Just BracketType
Close
  | Bool
otherwise = forall a. Maybe a
Nothing
  where
    go :: Map Char a -> Bool
go = forall k a. Ord k => k -> Map k a -> Bool
member Char
c

-- | Check the 'BracketType' of the 'Char'. For a 'Char' that is /not/ a /bracket/
-- the behavior is unspecified.
bracketType' :: Char -> BracketType
bracketType' :: Char -> BracketType
bracketType' Char
c
  | forall k a. Ord k => k -> Map k a -> Bool
member Char
c Map Char Char
toClose = BracketType
Open
  | Bool
otherwise = BracketType
Close

-- | Get the bracket character that is the /counterpart/
-- of the given /bracket/ character wrapped in a 'Just' data
-- constructor. If the given 'Char' is not a bracket, 'Nothing'
-- is returned.
getOppositeChar ::
  -- | The given 'Char' for which we want to determine the opposite bracket.
  Char ->
  -- | The opposite bracket wrapped in a 'Just' if the given 'Char' is a bracket character; 'Nothing' otherwise.
  Maybe Char
getOppositeChar :: Char -> Maybe Char
getOppositeChar Char
c = forall {a}. Map Char a -> Maybe a
go Map Char Char
toClose forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall {a}. Map Char a -> Maybe a
go Map Char Char
toOpen
  where
    go :: Map Char a -> Maybe a
go = forall k a. Ord k => k -> Map k a -> Maybe a
lookup Char
c

-- | Get the bracket character that is the /counterpart/
-- of the given /bracket/ character. If the given 'Char'
-- is not a bracket, the given 'Char' is returned.
getOppositeChar' ::
  -- | The given 'Char' for which we want to determine the opposite bracket.
  Char ->
  -- | The opposite bracket if the given 'Char' is a /bracket/; otherwise the given 'Char'.
  Char
getOppositeChar' :: Char -> Char
getOppositeChar' = forall a. a -> Maybe a -> a
fromMaybe forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Char -> Maybe Char
getOppositeChar