content: {-# LANGUAGE Trustworthy #-}
  {-# LANGUAGE NoImplicitPrelude #-}
  
  -----------------------------------------------------------------------------
  -- |
  -- Module      :  Data.Char
  -- Copyright   :  (c) The University of Glasgow 2001
  -- License     :  BSD-style (see the file libraries/base/LICENSE)
  -- 
  -- Maintainer  :  libraries@haskell.org
  -- Stability   :  stable
  -- Portability :  portable
  --
  -- The Char type and associated operations.
  --
  -----------------------------------------------------------------------------
  
  module Data.Char
      (
        Char
  
      -- * Character classification
      -- | Unicode characters are divided into letters, numbers, marks,
      -- punctuation, symbols, separators (including spaces) and others
      -- (including control characters).
      , isControl, isSpace
      , isLower, isUpper, isAlpha, isAlphaNum, isPrint
      , isDigit, isOctDigit, isHexDigit
      , isLetter, isMark, isNumber, isPunctuation, isSymbol, isSeparator
  
      -- ** Subranges
      , isAscii, isLatin1
      , isAsciiUpper, isAsciiLower
  
      -- ** Unicode general categories
      , GeneralCategory(..), generalCategory
  
      -- * Case conversion
      , toUpper, toLower, toTitle
  
      -- * Single digit characters
      , digitToInt
      , intToDigit
  
      -- * Numeric representations
      , ord
      , chr
  
      -- * String representations
      , showLitChar
      , lexLitChar
      , readLitChar
      ) where
  
  import GHC.Base
  import GHC.Arr (Ix)
  import GHC.Char
  import GHC.Real (fromIntegral)
  import GHC.Show
  import GHC.Read (Read, readLitChar, lexLitChar)
  import GHC.Unicode
  import GHC.Num
  import GHC.Enum
  
  -- $setup
  -- Allow the use of Prelude in doctests.
  -- >>> import Prelude
  
  -- | Convert a single digit 'Char' to the corresponding 'Int'.  This
  -- function fails unless its argument satisfies 'isHexDigit', but
  -- recognises both upper- and lower-case hexadecimal digits (that
  -- is, @\'0\'@..@\'9\'@, @\'a\'@..@\'f\'@, @\'A\'@..@\'F\'@).
  --
  -- ==== __Examples__
  --
  -- Characters @\'0\'@ through @\'9\'@ are converted properly to
  -- @0..9@:
  --
  -- >>> map digitToInt ['0'..'9']
  -- [0,1,2,3,4,5,6,7,8,9]
  --
  -- Both upper- and lower-case @\'A\'@ through @\'F\'@ are converted
  -- as well, to @10..15@.
  --
  -- >>> map digitToInt ['a'..'f']
  -- [10,11,12,13,14,15]
  -- >>> map digitToInt ['A'..'F']
  -- [10,11,12,13,14,15]
  --
  -- Anything else throws an exception:
  --
  -- >>> digitToInt 'G'
  -- *** Exception: Char.digitToInt: not a digit 'G'
  -- >>> digitToInt '♥'
  -- *** Exception: Char.digitToInt: not a digit '\9829'
  --
  digitToInt :: Char -> Int
  digitToInt c
    | (fromIntegral dec::Word) <= 9 = dec
    | (fromIntegral hexl::Word) <= 5 = hexl + 10
    | (fromIntegral hexu::Word) <= 5 = hexu + 10
    | otherwise = error ("Char.digitToInt: not a digit " ++ show c) -- sigh
    where
      dec = ord c - ord '0'
      hexl = ord c - ord 'a'
      hexu = ord c - ord 'A'
  
  -- | Unicode General Categories (column 2 of the UnicodeData table) in
  -- the order they are listed in the Unicode standard (the Unicode
  -- Character Database, in particular).
  --
  -- ==== __Examples__
  --
  -- Basic usage:
  --
  -- >>> :t OtherLetter
  -- OtherLetter :: GeneralCategory
  --
  -- 'Eq' instance:
  --
  -- >>> UppercaseLetter == UppercaseLetter
  -- True
  -- >>> UppercaseLetter == LowercaseLetter
  -- False
  --
  -- 'Ord' instance:
  --
  -- >>> NonSpacingMark <= MathSymbol
  -- True
  --
  -- 'Enum' instance:
  --
  -- >>> enumFromTo ModifierLetter SpacingCombiningMark
  -- [ModifierLetter,OtherLetter,NonSpacingMark,SpacingCombiningMark]
  --
  -- 'Read' instance:
  --
  -- >>> read "DashPunctuation" :: GeneralCategory
  -- DashPunctuation
  -- >>> read "17" :: GeneralCategory
  -- *** Exception: Prelude.read: no parse
  --
  -- 'Show' instance:
  --
  -- >>> show EnclosingMark
  -- "EnclosingMark"
  --
  -- 'Bounded' instance:
  --
  -- >>> minBound :: GeneralCategory
  -- UppercaseLetter
  -- >>> maxBound :: GeneralCategory
  -- NotAssigned
  --
  -- 'Ix' instance:
  --
  --  >>> import Data.Ix ( index )
  --  >>> index (OtherLetter,Control) FinalQuote
  --  12
  --  >>> index (OtherLetter,Control) Format
  --  *** Exception: Error in array index
  --
  data GeneralCategory
          = UppercaseLetter       -- ^ Lu: Letter, Uppercase
          | LowercaseLetter       -- ^ Ll: Letter, Lowercase
          | TitlecaseLetter       -- ^ Lt: Letter, Titlecase
          | ModifierLetter        -- ^ Lm: Letter, Modifier
          | OtherLetter           -- ^ Lo: Letter, Other
          | NonSpacingMark        -- ^ Mn: Mark, Non-Spacing
          | SpacingCombiningMark  -- ^ Mc: Mark, Spacing Combining
          | EnclosingMark         -- ^ Me: Mark, Enclosing
          | DecimalNumber         -- ^ Nd: Number, Decimal
          | LetterNumber          -- ^ Nl: Number, Letter
          | OtherNumber           -- ^ No: Number, Other
          | ConnectorPunctuation  -- ^ Pc: Punctuation, Connector
          | DashPunctuation       -- ^ Pd: Punctuation, Dash
          | OpenPunctuation       -- ^ Ps: Punctuation, Open
          | ClosePunctuation      -- ^ Pe: Punctuation, Close
          | InitialQuote          -- ^ Pi: Punctuation, Initial quote
          | FinalQuote            -- ^ Pf: Punctuation, Final quote
          | OtherPunctuation      -- ^ Po: Punctuation, Other
          | MathSymbol            -- ^ Sm: Symbol, Math
          | CurrencySymbol        -- ^ Sc: Symbol, Currency
          | ModifierSymbol        -- ^ Sk: Symbol, Modifier
          | OtherSymbol           -- ^ So: Symbol, Other
          | Space                 -- ^ Zs: Separator, Space
          | LineSeparator         -- ^ Zl: Separator, Line
          | ParagraphSeparator    -- ^ Zp: Separator, Paragraph
          | Control               -- ^ Cc: Other, Control
          | Format                -- ^ Cf: Other, Format
          | Surrogate             -- ^ Cs: Other, Surrogate
          | PrivateUse            -- ^ Co: Other, Private Use
          | NotAssigned           -- ^ Cn: Other, Not Assigned
          deriving (Eq, Ord, Enum, Read, Show, Bounded, Ix)
  
  -- | The Unicode general category of the character. This relies on the
  -- 'Enum' instance of 'GeneralCategory', which must remain in the
  -- same order as the categories are presented in the Unicode
  -- standard.
  --
  -- ==== __Examples__
  --
  -- Basic usage:
  --
  -- >>> generalCategory 'a'
  -- LowercaseLetter
  -- >>> generalCategory 'A'
  -- UppercaseLetter
  -- >>> generalCategory '0'
  -- DecimalNumber
  -- >>> generalCategory '%'
  -- OtherPunctuation
  -- >>> generalCategory '♥'
  -- OtherSymbol
  -- >>> generalCategory '\31'
  -- Control
  -- >>> generalCategory ' '
  -- Space
  --
  generalCategory :: Char -> GeneralCategory
  generalCategory c = toEnum $ fromIntegral $ wgencat $ fromIntegral $ ord c
  
  -- derived character classifiers
  
  -- | Selects alphabetic Unicode characters (lower-case, upper-case and
  -- title-case letters, plus letters of caseless scripts and
  -- modifiers letters). This function is equivalent to
  -- 'Data.Char.isAlpha'.
  --
  -- This function returns 'True' if its argument has one of the
  -- following 'GeneralCategory's, or 'False' otherwise:
  --
  -- * 'UppercaseLetter'
  -- * 'LowercaseLetter'
  -- * 'TitlecaseLetter'
  -- * 'ModifierLetter'
  -- * 'OtherLetter'
  --
  -- These classes are defined in the
  -- <http://www.unicode.org/reports/tr44/tr44-14.html#GC_Values_Table Unicode Character Database>,
  -- part of the Unicode standard. The same document defines what is
  -- and is not a \"Letter\".
  --
  -- ==== __Examples__
  --
  -- Basic usage:
  --
  -- >>> isLetter 'a'
  -- True
  -- >>> isLetter 'A'
  -- True
  -- >>> isLetter '0'
  -- False
  -- >>> isLetter '%'
  -- False
  -- >>> isLetter '♥'
  -- False
  -- >>> isLetter '\31'
  -- False
  --
  -- Ensure that 'isLetter' and 'isAlpha' are equivalent.
  --
  -- >>> let chars = [(chr 0)..]
  -- >>> let letters = map isLetter chars
  -- >>> let alphas = map isAlpha chars
  -- >>> letters == alphas
  -- True
  --
  isLetter :: Char -> Bool
  isLetter c = case generalCategory c of
          UppercaseLetter         -> True
          LowercaseLetter         -> True
          TitlecaseLetter         -> True
          ModifierLetter          -> True
          OtherLetter             -> True
          _                       -> False
  
  -- | Selects Unicode mark characters, for example accents and the
  -- like, which combine with preceding characters.
  --
  -- This function returns 'True' if its argument has one of the
  -- following 'GeneralCategory's, or 'False' otherwise:
  --
  -- * 'NonSpacingMark'
  -- * 'SpacingCombiningMark'
  -- * 'EnclosingMark'
  --
  -- These classes are defined in the
  -- <http://www.unicode.org/reports/tr44/tr44-14.html#GC_Values_Table Unicode Character Database>,
  -- part of the Unicode standard. The same document defines what is
  -- and is not a \"Mark\".
  --
  -- ==== __Examples__
  --
  -- Basic usage:
  --
  -- >>> isMark 'a'
  -- False
  -- >>> isMark '0'
  -- False
  --
  -- Combining marks such as accent characters usually need to follow
  -- another character before they become printable:
  --
  -- >>> map isMark "ò"
  -- [False,True]
  --
  -- Puns are not necessarily supported:
  --
  -- >>> isMark '✓'
  -- False
  --
  isMark :: Char -> Bool
  isMark c = case generalCategory c of
          NonSpacingMark          -> True
          SpacingCombiningMark    -> True
          EnclosingMark           -> True
          _                       -> False
  
  -- | Selects Unicode numeric characters, including digits from various
  -- scripts, Roman numerals, et cetera.
  --
  -- This function returns 'True' if its argument has one of the
  -- following 'GeneralCategory's, or 'False' otherwise:
  --
  -- * 'DecimalNumber'
  -- * 'LetterNumber'
  -- * 'OtherNumber'
  --
  -- These classes are defined in the
  -- <http://www.unicode.org/reports/tr44/tr44-14.html#GC_Values_Table Unicode Character Database>,
  -- part of the Unicode standard. The same document defines what is
  -- and is not a \"Number\".
  --
  -- ==== __Examples__
  --
  -- Basic usage:
  --
  -- >>> isNumber 'a'
  -- False
  -- >>> isNumber '%'
  -- False
  -- >>> isNumber '3'
  -- True
  --
  -- ASCII @\'0\'@ through @\'9\'@ are all numbers:
  --
  -- >>> and $ map isNumber ['0'..'9']
  -- True
  --
  -- Unicode Roman numerals are \"numbers\" as well:
  --
  -- >>> isNumber 'Ⅸ'
  -- True
  --
  isNumber :: Char -> Bool
  isNumber c = case generalCategory c of
          DecimalNumber           -> True
          LetterNumber            -> True
          OtherNumber             -> True
          _                       -> False
  
  -- | Selects Unicode punctuation characters, including various kinds
  -- of connectors, brackets and quotes.
  --
  -- This function returns 'True' if its argument has one of the
  -- following 'GeneralCategory's, or 'False' otherwise:
  --
  -- * 'ConnectorPunctuation'
  -- * 'DashPunctuation'
  -- * 'OpenPunctuation'
  -- * 'ClosePunctuation'
  -- * 'InitialQuote'
  -- * 'FinalQuote'
  -- * 'OtherPunctuation'
  --
  -- These classes are defined in the
  -- <http://www.unicode.org/reports/tr44/tr44-14.html#GC_Values_Table Unicode Character Database>,
  -- part of the Unicode standard. The same document defines what is
  -- and is not a \"Punctuation\".
  --
  -- ==== __Examples__
  --
  -- Basic usage:
  --
  -- >>> isPunctuation 'a'
  -- False
  -- >>> isPunctuation '7'
  -- False
  -- >>> isPunctuation '♥'
  -- False
  -- >>> isPunctuation '"'
  -- True
  -- >>> isPunctuation '?'
  -- True
  -- >>> isPunctuation '—'
  -- True
  --
  isPunctuation :: Char -> Bool
  isPunctuation c = case generalCategory c of
          ConnectorPunctuation    -> True
          DashPunctuation         -> True
          OpenPunctuation         -> True
          ClosePunctuation        -> True
          InitialQuote            -> True
          FinalQuote              -> True
          OtherPunctuation        -> True
          _                       -> False
  
  -- | Selects Unicode symbol characters, including mathematical and
  -- currency symbols.
  --
  -- This function returns 'True' if its argument has one of the
  -- following 'GeneralCategory's, or 'False' otherwise:
  --
  -- * 'MathSymbol'
  -- * 'CurrencySymbol'
  -- * 'ModifierSymbol'
  -- * 'OtherSymbol'
  --
  -- These classes are defined in the
  -- <http://www.unicode.org/reports/tr44/tr44-14.html#GC_Values_Table Unicode Character Database>,
  -- part of the Unicode standard. The same document defines what is
  -- and is not a \"Symbol\".
  --
  -- ==== __Examples__
  --
  -- Basic usage:
  --
  -- >>> isSymbol 'a'
  -- False
  -- >>> isSymbol '6'
  -- False
  -- >>> isSymbol '='
  -- True
  --
  -- The definition of \"math symbol\" may be a little
  -- counter-intuitive depending on one's background:
  --
  -- >>> isSymbol '+'
  -- True
  -- >>> isSymbol '-'
  -- False
  --
  isSymbol :: Char -> Bool
  isSymbol c = case generalCategory c of
          MathSymbol              -> True
          CurrencySymbol          -> True
          ModifierSymbol          -> True
          OtherSymbol             -> True
          _                       -> False
  
  -- | Selects Unicode space and separator characters.
  --
  -- This function returns 'True' if its argument has one of the
  -- following 'GeneralCategory's, or 'False' otherwise:
  --
  -- * 'Space'
  -- * 'LineSeparator'
  -- * 'ParagraphSeparator'
  --
  -- These classes are defined in the
  -- <http://www.unicode.org/reports/tr44/tr44-14.html#GC_Values_Table Unicode Character Database>,
  -- part of the Unicode standard. The same document defines what is
  -- and is not a \"Separator\".
  --
  -- ==== __Examples__
  --
  -- Basic usage:
  --
  -- >>> isSeparator 'a'
  -- False
  -- >>> isSeparator '6'
  -- False
  -- >>> isSeparator ' '
  -- True
  --
  -- Warning: newlines and tab characters are not considered
  -- separators.
  --
  -- >>> isSeparator '\n'
  -- False
  -- >>> isSeparator '\t'
  -- False
  --
  -- But some more exotic characters are (like HTML's @&nbsp;@):
  --
  -- >>> isSeparator '\160'
  -- True
  --
  isSeparator :: Char -> Bool
  isSeparator c = case generalCategory c of
          Space                   -> True
          LineSeparator           -> True
          ParagraphSeparator      -> True
          _                       -> False
  
  