-- | Functions for working with characters. Character literals are enclosed in @\'a\'@ pair of single quotes.
--
-- Since we don't have a browser and we don't have a default locale, functions
-- like @toLocaleUpper@ and @toLocaleUpper@ are not supported. If you need
-- something like that you can check out the 'text-icu' package which provides
-- functions like @toUpper :: LocaleName -> Text -> Text@.
module Char
( -- * Characters
Char,
-- * ASCII Letters
isUpper,
isLower,
isAlpha,
isAlphaNum,
-- * Digits
isDigit,
isOctDigit,
isHexDigit,
-- * Conversion
toUpper,
toLower,
-- * Unicode Code Points
toCode,
fromCode,
)
where
import Basics
( Bool (..),
Int,
(&&),
(<<),
(<=),
)
import qualified Data.Char
import Prelude (Char, otherwise)
import qualified Prelude
-- | Detect upper case ASCII characters.
--
-- > isUpper 'A' == True
-- > isUpper 'B' == True
-- > ...
-- > isUpper 'Z' == True
--
-- > isUpper '0' == False
-- > isUpper 'a' == False
-- > isUpper '-' == False
-- > isUpper 'Σ' == False
isUpper :: Char -> Bool
isUpper = Data.Char.isUpper
-- | Detect lower case ASCII characters.
--
-- > isLower 'a' == True
-- > isLower 'b' == True
-- > ...
-- > isLower 'z' == True
--
-- > isLower '0' == False
-- > isLower 'A' == False
-- > isLower '-' == False
-- > isLower 'π' == False
isLower :: Char -> Bool
isLower = Data.Char.isLower
-- | Detect upper case and lower case ASCII characters.
--
-- > isAlpha 'a' == True
-- > isAlpha 'b' == True
-- > isAlpha 'E' == True
-- > isAlpha 'Y' == True
--
-- > isAlpha '0' == False
-- > isAlpha '-' == False
-- > isAlpha 'π' == False
isAlpha :: Char -> Bool
isAlpha = Data.Char.isAlpha
-- | Detect upper case and lower case ASCII characters.
--
-- > isAlphaNum 'a' == True
-- > isAlphaNum 'b' == True
-- > isAlphaNum 'E' == True
-- > isAlphaNum 'Y' == True
-- > isAlphaNum '0' == True
-- > isAlphaNum '7' == True
--
-- > isAlphaNum '-' == False
-- > isAlphaNum 'π' == False
isAlphaNum :: Char -> Bool
isAlphaNum = Data.Char.isAlphaNum
-- | Detect digits @0123456789@
--
-- > isDigit '0' == True
-- > isDigit '1' == True
-- > ...
-- > isDigit '9' == True
--
-- > isDigit 'a' == False
-- > isDigit 'b' == False
-- > isDigit 'A' == False
isDigit :: Char -> Bool
isDigit = Data.Char.isDigit
-- | Detect octal digits @01234567@
--
-- > isOctDigit '0' == True
-- > isOctDigit '1' == True
-- > ...
-- > isOctDigit '7' == True
--
-- > isOctDigit '8' == False
-- > isOctDigit 'a' == False
-- > isOctDigit 'A' == False
isOctDigit :: Char -> Bool
isOctDigit = Data.Char.isOctDigit
-- | Detect hexadecimal digits @0123456789abcdefABCDEF@
isHexDigit :: Char -> Bool
isHexDigit = Data.Char.isHexDigit
-- | Convert to upper case.
toUpper :: Char -> Char
toUpper = Data.Char.toUpper
-- | Convert to lower case.
toLower :: Char -> Char
toLower = Data.Char.toLower
-- | Convert to the corresponding Unicode [code point](https://en.wikipedia.org/wiki/Code_point).
--
-- > toCode 'A' == 65
-- > toCode 'B' == 66
-- > toCode '木' == 26408
-- > toCode '𝌆' == 0x1D306
-- > toCode '😃' == 0x1F603
toCode :: Char -> Int
toCode = Prelude.fromIntegral << Data.Char.ord
-- | Convert a Unicode [code point](https://en.wikipedia.org/wiki/Code_point) to a character.
--
-- > fromCode 65 == 'A'
-- > fromCode 66 == 'B'
-- > fromCode 0x6728 == '\26408' -- '木'
-- > fromCode 0x1D306 == '\119558' -- '𝌆'
-- > fromCode 0x1F603 == '\128515' -- '😃'
-- > fromCode (-1) == '\65533' -- '�'
--
-- The full range of unicode is from @0@ to @0x10FFFF@. With numbers outside that
-- range, you get [the replacement character](https://en.wikipedia.org/wiki/Specials_(Unicode_block)#Replacement_character).
fromCode :: Int -> Char
fromCode value
| 0 <= value && value <= 0x10FFFF = Data.Char.chr (Prelude.fromIntegral value)
| otherwise = '\xfffd'