{-# 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 Prelude hiding (lookup)

import Control.Applicative((<|>))

import Data.Map(Map, fromList, lookup, member)
import Data.Maybe(fromMaybe)
import Data.Tuple(swap)

import Test.QuickCheck.Arbitrary(Arbitrary(arbitrary), arbitraryBoundedEnum)

-- | A data type that is used to specify the /type/ of bracket.
data BracketType
  = Open  -- ^ The bracket is used to "open" a context.
  | Close  -- ^ The bracket is used to "close" a context.
  deriving (BracketType
BracketType -> BracketType -> Bounded BracketType
forall a. a -> a -> Bounded a
maxBound :: BracketType
$cmaxBound :: BracketType
minBound :: BracketType
$cminBound :: BracketType
Bounded, Int -> BracketType
BracketType -> Int
BracketType -> [BracketType]
BracketType -> BracketType
BracketType -> BracketType -> [BracketType]
BracketType -> BracketType -> BracketType -> [BracketType]
(BracketType -> BracketType)
-> (BracketType -> BracketType)
-> (Int -> BracketType)
-> (BracketType -> Int)
-> (BracketType -> [BracketType])
-> (BracketType -> BracketType -> [BracketType])
-> (BracketType -> BracketType -> [BracketType])
-> (BracketType -> BracketType -> BracketType -> [BracketType])
-> Enum 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
(BracketType -> BracketType -> Bool)
-> (BracketType -> BracketType -> Bool) -> Eq BracketType
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, Eq BracketType
Eq BracketType
-> (BracketType -> BracketType -> Ordering)
-> (BracketType -> BracketType -> Bool)
-> (BracketType -> BracketType -> Bool)
-> (BracketType -> BracketType -> Bool)
-> (BracketType -> BracketType -> Bool)
-> (BracketType -> BracketType -> BracketType)
-> (BracketType -> BracketType -> BracketType)
-> Ord 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
$cp1Ord :: Eq BracketType
Ord, ReadPrec [BracketType]
ReadPrec BracketType
Int -> ReadS BracketType
ReadS [BracketType]
(Int -> ReadS BracketType)
-> ReadS [BracketType]
-> ReadPrec BracketType
-> ReadPrec [BracketType]
-> Read 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
(Int -> BracketType -> ShowS)
-> (BracketType -> String)
-> ([BracketType] -> ShowS)
-> Show BracketType
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 = Gen BracketType
forall a. (Bounded a, Enum a) => Gen a
arbitraryBoundedEnum

-- | 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 :: [Char]  -- ^ The list of all 'Char's that are brackets.
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 :: [Char]  -- ^ The list of all 'Char's that are /opening brackets/.
openBrackets :: String
openBrackets = ((Char, Char) -> Char) -> [(Char, Char)] -> String
forall a b. (a -> b) -> [a] -> [b]
map (Char, Char) -> Char
forall a b. (a, b) -> a
fst [(Char, Char)]
bracketMaps

-- | A list of 'Char's that contains all closing brackets.
closeBrackets :: [Char]  -- ^ The list of all 'Char's that are /closing brackets/.
closeBrackets :: String
closeBrackets = ((Char, Char) -> Char) -> [(Char, Char)] -> String
forall a b. (a -> b) -> [a] -> [b]
map (Char, Char) -> Char
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 = [(Char, Char)] -> Map Char Char
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 = [(Char, Char)] -> Map Char Char
forall k a. Ord k => [(k, a)] -> Map k a
fromList (((Char, Char) -> (Char, Char)) -> [(Char, Char)] -> [(Char, Char)]
forall a b. (a -> b) -> [a] -> [b]
map (Char, Char) -> (Char, Char)
forall a b. (a, b) -> (b, a)
swap [(Char, Char)]
bracketMaps)

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

-- | Check if the given 'Char' is an /open bracket/.
isOpenBracket
  :: Char  -- ^ The given 'Char' to test.
  -> Bool  -- ^ 'True' if the 'Char' is an /open bracket/; 'False' otherwise.
isOpenBracket :: Char -> Bool
isOpenBracket = (Char -> Map Char Char -> Bool
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
  :: Char  -- ^ The given 'Char' to test.
  -> Bool  -- ^ 'True' if the 'Char' is an /close bracket/; 'False' otherwise.
isCloseBracket :: Char -> Bool
isCloseBracket = (Char -> Map Char Char -> Bool
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
  | Map Char Char -> Bool
forall a. Map Char a -> Bool
go Map Char Char
toClose = BracketType -> Maybe BracketType
forall a. a -> Maybe a
Just BracketType
Open
  | Map Char Char -> Bool
forall a. Map Char a -> Bool
go Map Char Char
toOpen = BracketType -> Maybe BracketType
forall a. a -> Maybe a
Just BracketType
Close
  | Bool
otherwise = Maybe BracketType
forall a. Maybe a
Nothing
  where go :: Map Char a -> Bool
go = Char -> Map Char a -> Bool
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
  | Char -> Map Char Char -> Bool
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
  :: Char  -- ^ The given 'Char' for which we want to determine the opposite bracket.
  -> Maybe Char  -- ^ The opposite bracket wrapped in a 'Just' if the given 'Char' is a bracket character; 'Nothing' otherwise.
getOppositeChar :: Char -> Maybe Char
getOppositeChar Char
c = Map Char Char -> Maybe Char
forall a. Map Char a -> Maybe a
go Map Char Char
toClose Maybe Char -> Maybe Char -> Maybe Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Map Char Char -> Maybe Char
forall a. Map Char a -> Maybe a
go Map Char Char
toOpen
  where go :: Map Char a -> Maybe a
go = Char -> Map Char a -> Maybe a
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'
  :: Char  -- ^ 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'.
getOppositeChar' :: Char -> Char
getOppositeChar' = Char -> Maybe Char -> Char
forall a. a -> Maybe a -> a
fromMaybe (Char -> Maybe Char -> Char)
-> (Char -> Maybe Char) -> Char -> Char
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Char -> Maybe Char
getOppositeChar