{-| The 'Char' type has 128 nullary constructors, listed in
    order according to each character's 7-bit numeric code. -}
module ASCII.Char
  (
    {- * The @Char@ type -} Char (..),
    {- * @Int@ -} toInt, fromIntMaybe, fromIntUnsafe,
    {- * @Word8@ -} toWord8, fromWord8Maybe, fromWord8Unsafe,
    {- * Enumeration -} allCharacters,
    {- * Notes -} {- $notes -}
  )
  where

import Data.Bool (otherwise)
import Data.Data (Data)
import Data.Eq (Eq)
import Data.Hashable (Hashable)
import Data.Int (Int)
import Data.Maybe (Maybe (..))
import Data.Ord (Ord, (<), (>))
import Data.Word (Word8)
import GHC.Generics (Generic)
import Prelude (Bounded, Enum, enumFromTo, fromEnum, fromIntegral, maxBound, minBound, toEnum)
import Text.Show (Show)

-- | A character in the ASCII character set

data Char =
      Null | StartOfHeading | StartOfText | EndOfText | EndOfTransmission | Enquiry
    | Acknowledgement | Bell | Backspace | HorizontalTab | LineFeed | VerticalTab
    | FormFeed | CarriageReturn | ShiftOut | ShiftIn | DataLinkEscape

    | DeviceControl1 | DeviceControl2 | DeviceControl3 | DeviceControl4

    | NegativeAcknowledgement | SynchronousIdle | EndOfTransmissionBlock
    | Cancel | EndOfMedium | Substitute | Escape

    | FileSeparator | GroupSeparator | RecordSeparator | UnitSeparator

    | Space
    | ExclamationMark  -- ^ !
    | QuotationMark    -- ^ "
    | NumberSign       -- ^ #
    | DollarSign       -- ^ $
    | PercentSign      -- ^ %
    | Ampersand        -- ^ &
    | Apostrophe       -- ^ #
    | LeftParenthesis  -- ^ (
    | RightParenthesis -- ^ )
    | Asterisk         -- ^ \*
    | PlusSign         -- ^ +
    | Comma            -- ^ ,
    | HyphenMinus      -- ^ \-
    | FullStop         -- ^ .
    | Slash            -- ^ /

    | Digit0 | Digit1 | Digit2 | Digit3 | Digit4 | Digit5 | Digit6 | Digit7 | Digit8 | Digit9

    | Colon            -- ^ :
    | Semicolon        -- ^ ;
    | LessThanSign     -- ^ \<
    | EqualsSign       -- ^ =
    | GreaterThanSign  -- ^ \>
    | QuestionMark     -- ^ ?
    | AtSign           -- ^ \@

    | CapitalLetterA | CapitalLetterB | CapitalLetterC | CapitalLetterD | CapitalLetterE
    | CapitalLetterF | CapitalLetterG | CapitalLetterH | CapitalLetterI | CapitalLetterJ
    | CapitalLetterK | CapitalLetterL | CapitalLetterM | CapitalLetterN | CapitalLetterO
    | CapitalLetterP | CapitalLetterQ | CapitalLetterR | CapitalLetterS | CapitalLetterT
    | CapitalLetterU | CapitalLetterV | CapitalLetterW | CapitalLetterX | CapitalLetterY
    | CapitalLetterZ

    | LeftSquareBracket  -- ^ \[
    | Backslash          -- ^ \\
    | RightSquareBracket -- ^ \]
    | Caret              -- ^ \^
    | Underscore         -- ^ _
    | GraveAccent        -- ^ \`

    | SmallLetterA | SmallLetterB | SmallLetterC | SmallLetterD | SmallLetterE
    | SmallLetterF | SmallLetterG | SmallLetterH | SmallLetterI | SmallLetterJ
    | SmallLetterK | SmallLetterL | SmallLetterM | SmallLetterN | SmallLetterO
    | SmallLetterP | SmallLetterQ | SmallLetterR | SmallLetterS | SmallLetterT
    | SmallLetterU | SmallLetterV | SmallLetterW | SmallLetterX | SmallLetterY
    | SmallLetterZ

    | LeftCurlyBracket   -- ^ \{
    | VerticalLine       -- ^ \|
    | RightCurlyBracket  -- ^ \}
    | Tilde              -- ^ \~
    | Delete

{-| ASCII characters can be compared for equality using '(==)'.
Comparisons are case-sensitive; @'SmallLetterA' '/=' 'CapitalLetterA'@. -}
deriving stock instance Eq Char

{-| ASCII characters are ordered; for example, the letter /A/ is "less than"
('<') the letter /B/ because it appears earlier in the list. The ordering of
ASCII characters is the same as the ordering of the corresponding Unicode
'Data.Char.Char's. -}

deriving stock instance Ord Char

{-| The 'Enum' instance allows us to use range syntax, for example
@['SmallLetterA' .. 'SmallLetterZ']@ is a list all lower-case letters from /a/
to /z/. Instead of 'toEnum' and 'fromEnum', consider using 'toInt' and
'fromIntMaybe'. -}
deriving stock instance Enum Char

{-| The least character is 'Null', and the greatest character is 'Delete'. You
can write @(['minBound' .. 'maxBound'] :: [ASCII.'Char'])@ to get a list of all
the ASCII characters. -}
deriving stock instance Bounded Char

{-| 'show' produces the name of a constructor. For example, the character @e@ is
shown as “@SmallLetterE@”. See "ASCII.Char" for the complete list of constructor
names. -}
deriving stock instance Show Char

{-| The 'Data' instance allows ASCII characters to be used with generic
programming in the “SYB” style. (See the
<https://hackage.haskell.org/package/syb syb> package and the 2003 paper
<https://www.microsoft.com/en-us/research/wp-content/uploads/2003/01/hmap.pdf Scrap Your Boilerplate>
by Ralf Lämmel and Simon Peyton Jones.) -}
deriving stock instance Data Char

{-| The 'Generic' instance allows ASCII characters to be used with generic
programming in the “generic deriving” style. (See the
<https://hackage.haskell.org/package/generic-data generic-data> package and the 2010 paper
<http://dreixel.net/research/pdf/gdmh.pdf A generic deriving mechanism for Haskell>
by José Pedro Magalhães, Atze Dijkstra, Johan Jeuring, and Andres Löh.) -}
deriving stock instance Generic Char

{-| The 'Hashable' instance lets us collect ASCII characters in hash-based sets,
and it lets us use ASCII characters as keys in hash-based maps. (See the
@unordered-containers@ package.) -}
deriving anyclass instance Hashable Char

{-| Converts an ASCII character to its corresponding numeric value between 0 and 127

@
toInt Null == 0
toInt CapitalLetterA == 6
toInt SmallLetterA == 97
toInt Delete == 127
@

-}

toInt :: Char -> Int
toInt :: Char -> Int
toInt = forall a. Enum a => a -> Int
Prelude.fromEnum

{-| Converts an ASCII character to its corresponding byte between 0 and 127

@
toWord8 Null == 0
toWord8 CapitalLetterA == 6
toWord8 SmallLetterA == 97
toWord8 Delete == 127
@
-}
toWord8 :: Char -> Word8
toWord8 :: Char -> Word8
toWord8 Char
x = forall a b. (Integral a, Num b) => a -> b
Prelude.fromIntegral (forall a. Enum a => a -> Int
Prelude.fromEnum Char
x)

{-| Returns 'Just' the ASCII character corresponding to a numeric value between
    0 and 127, or 'Nothing' for numbers outside this range

@
fromIntMaybe (-1) == Nothing
fromIntMaybe 0 == Just Null
fromIntMaybe 65 == Just CapitalLetterA
fromIntMaybe 127 == Just Delete
fromIntMaybe 128 == Nothing
@

-}

fromIntMaybe :: Int -> Maybe Char
fromIntMaybe :: Int -> Maybe Char
fromIntMaybe Int
x | Int
x forall a. Ord a => a -> a -> Bool
< Int
0     = forall a. Maybe a
Nothing
               | Int
x forall a. Ord a => a -> a -> Bool
> Int
127   = forall a. Maybe a
Nothing
               | Bool
otherwise = forall a. a -> Maybe a
Just (Int -> Char
fromIntUnsafe Int
x)

{-| Returns 'Just' the ASCII character corresponding to a byte between
    0 and 127, or 'Nothing' for bytes above this range

@
fromWord8Maybe 0 == Just Null
fromWord8Maybe 65 == Just CapitalLetterA
fromWord8Maybe 127 == Just Delete
fromWord8Maybe 128 == Nothing
@

-}
fromWord8Maybe :: Word8 -> Maybe Char
fromWord8Maybe :: Word8 -> Maybe Char
fromWord8Maybe Word8
x | Word8
x forall a. Ord a => a -> a -> Bool
> Word8
127   = forall a. Maybe a
Nothing
                 | Bool
otherwise = forall a. a -> Maybe a
Just (Word8 -> Char
fromWord8Unsafe Word8
x)

{-| The inverse of 'toInt'

This is marked as /unsafe/ because it is undefined for numbers below 0
or above 127. The safe variant of this function is 'fromIntMaybe'.

@
fromIntUnsafe (-1) == undefined
fromIntUnsafe 65 == CapitalLetterA
fromIntUnsafe 66 == CapitalLetterB
fromIntUnsafe 67 == CapitalLetterC
fromIntUnsafe 128 == undefined
@

-}

fromIntUnsafe :: Int -> Char
fromIntUnsafe :: Int -> Char
fromIntUnsafe = forall a. Enum a => Int -> a
Prelude.toEnum

{-| The inverse of 'toWord8'

This is marked as /unsafe/ because it is undefined bytes above 127.
The safe variant of this function is 'fromWord8Maybe'.

@
fromWord8Unsafe 65 == CapitalLetterA
fromWord8Unsafe 66 == CapitalLetterB
fromWord8Unsafe 67 == CapitalLetterC
fromWord8Unsafe 128 == undefined
@

-}
fromWord8Unsafe :: Word8 -> Char
fromWord8Unsafe :: Word8 -> Char
fromWord8Unsafe Word8
x = Int -> Char
fromIntUnsafe (forall a b. (Integral a, Num b) => a -> b
Prelude.fromIntegral Word8
x)

allCharacters :: [Char]
allCharacters :: [Char]
allCharacters = forall a. Enum a => a -> a -> [a]
Prelude.enumFromTo forall a. Bounded a => a
Prelude.minBound forall a. Bounded a => a
Prelude.maxBound

{- $notes

There are 128 characters in total.

@
length allCharacters == 128
@

Null is the first character.

@
minBound == Null
@

Delete is the last character.

@
maxBound == Delete
@

-}