ascii-char 1.0.0.17 → 1.0.1.0
raw patch · 4 files changed
+231/−76 lines, 4 filesdep ~basedep ~hashabledep ~hspecPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base, hashable, hspec
API changes (from Hackage documentation)
+ ASCII.Char: fromWord8Maybe :: Word8 -> Maybe Char
+ ASCII.Char: fromWord8Unsafe :: Word8 -> Char
+ ASCII.Char: toWord8 :: Char -> Word8
Files
- ascii-char.cabal +2/−3
- changelog.md +3/−3
- library/ASCII/Char.hs +168/−45
- test/Main.hs +58/−25
ascii-char.cabal view
@@ -1,7 +1,7 @@ cabal-version: 3.0 name: ascii-char-version: 1.0.0.17+version: 1.0.1.0 synopsis: A Char type representing an ASCII character category: Data, Text @@ -18,7 +18,7 @@ homepage: https://github.com/typeclasses/ascii-char bug-reports: https://github.com/typeclasses/ascii-char/issues -extra-doc-files: *.md+extra-source-files: *.md source-repository head type: git@@ -36,7 +36,6 @@ library import: base- ghc-options: -fno-warn-unused-imports hs-source-dirs: library default-extensions:
changelog.md view
@@ -1,8 +1,8 @@-### 1.0.0.17 (2022-12-30)+### 1.0.1.0 (2023-01-05) -Fix error in cabal file (change `extra-source-files` to `extra-doc-files`)+Add `Word8` conversions: `toWord8`, `fromWord8Maybe`, `fromWord8Unsafe` -### 1.0.0.16 (2022-12-27)+### 1.0.0.16 (2022-10-04) Test suite now uses `hspec`
library/ASCII/Char.hs view
@@ -1,95 +1,178 @@-{- |--The 'Char' type has 128 nullary constructors, listed in order according to each character's 7-bit numeric code.---}-+{-| 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 (..),- {- * Conversions with @Int@ -} toInt, fromIntMaybe, fromIntUnsafe,- {- * Enumeration -} allCharacters+ {- * @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.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, maxBound, minBound, toEnum)+import Prelude (Bounded, Enum, enumFromTo, fromEnum, fromIntegral, maxBound, minBound, toEnum) import Text.Show (Show) -import qualified Data.Char as C---- | A character in the ASCII character set.+-- | 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+ 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+ | 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+ | 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+ | 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+ | 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+ | 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+ | 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+ | LeftCurlyBracket -- ^ \{+ | VerticalLine -- ^ \|+ | RightCurlyBracket -- ^ \}+ | Tilde -- ^ \~+ | Delete --- | ASCII characters can be compared for equality using '(==)'. Comparisons are case-sensitive; @'SmallLetterA' '/=' 'CapitalLetterA'@.+{-| 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 'C.Char's.+{-| 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'.+{-| 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.+{-| 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.+{-| '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.)+{-| 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.)+{-| 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.)+{-| 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.+{-| Converts an ASCII character to its corresponding numeric value between 0 and 127 ->>> map toInt [Null, CapitalLetterA, SmallLetterA, Delete]-[0,65,97,127]+@+toInt Null == 0+toInt CapitalLetterA == 6+toInt SmallLetterA == 97+toInt Delete == 127+@ -} toInt :: Char -> Int toInt = Prelude.fromEnum -{- | Returns 'Just' the ASCII character corresponding to a numeric value between 0 and 127, or 'Nothing' for numbers outside this range.+{-| Converts an ASCII character to its corresponding byte between 0 and 127 ->>> map fromIntMaybe [-1, 0, 65, 127, 128]-[Nothing,Just Null,Just CapitalLetterA,Just Delete,Nothing]+@+toWord8 Null == 0+toWord8 CapitalLetterA == 6+toWord8 SmallLetterA == 97+toWord8 Delete == 127+@+-}+toWord8 :: Char -> Word8+toWord8 x = Prelude.fromIntegral (Prelude.fromEnum 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@@ -97,18 +180,55 @@ | x > 127 = Nothing | otherwise = Just (fromIntUnsafe x) -{- | The inverse of 'toInt'.+{-| Returns 'Just' the ASCII character corresponding to a byte between+ 0 and 127, or 'Nothing' for bytes above this range -This is marked as /unsafe/ because it is undefined for numbers below 0 or above 127. The safe variant of this function is 'fromIntMaybe'.+@+fromWord8Maybe 0 == Just Null+fromWord8Maybe 65 == Just CapitalLetterA+fromWord8Maybe 127 == Just Delete+fromWord8Maybe 128 == Nothing+@ ->>> map fromIntUnsafe [65, 66, 67]-[CapitalLetterA,CapitalLetterB,CapitalLetterC]+-}+fromWord8Maybe :: Word8 -> Maybe Char+fromWord8Maybe x | x > 127 = Nothing+ | otherwise = Just (fromWord8Unsafe 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 = 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 x = fromIntUnsafe (Prelude.fromIntegral x)+ allCharacters :: [Char] allCharacters = Prelude.enumFromTo Prelude.minBound Prelude.maxBound @@ -116,17 +236,20 @@ There are 128 characters in total. ->>> length allCharacters-128+@+length allCharacters == 128+@ Null is the first character. ->>> minBound :: Char-Null+@+minBound == Null+@ Delete is the last character. ->>> maxBound :: Char-Delete+@+maxBound == Delete+@ -}
test/Main.hs view
@@ -3,39 +3,72 @@ import ASCII.Char import Data.Function (($))-import Data.List (length)+import Data.List (length, sort) import Data.Maybe (Maybe (..)) import Prelude (maxBound, minBound) import System.IO (IO)-import Test.Hspec (hspec, it, shouldBe)+import Test.Hspec (describe, hspec, it, shouldBe) main :: IO () main = hspec $ do - it "toInt" $ do- let f = toInt- f Null `shouldBe` 0- f CapitalLetterA `shouldBe` 65- f SmallLetterA `shouldBe` 97- f Delete `shouldBe` 127+ describe "toInt" $ do+ let c ~> n = toInt c `shouldBe` n - it "fromIntMaybe" $ do- let f = fromIntMaybe- f (-1) `shouldBe` Nothing- f 0 `shouldBe` Just Null- f 65 `shouldBe` Just CapitalLetterA- f 127 `shouldBe` Just Delete- f 128 `shouldBe` Nothing+ it "Null → 0" $ Null ~> 0+ it "A → 65" $ CapitalLetterA ~> 65+ it "a → 97" $ SmallLetterA ~> 97+ it "Delete → 127" $ Delete ~> 127 - it "fromIntUnsafe" $ do- let f = fromIntUnsafe- f 65 `shouldBe` CapitalLetterA- f 66 `shouldBe` CapitalLetterB- f 67 `shouldBe` CapitalLetterC+ describe "toWord8" $ do+ let c ~> n = toWord8 c `shouldBe` n - it "allCharacters" $ do- length allCharacters `shouldBe` 128+ it "Null → 0" $ Null ~> 0+ it "A → 65" $ CapitalLetterA ~> 65+ it "a → 97" $ SmallLetterA ~> 97+ it "Delete → 127" $ Delete ~> 127 - it "Bounded" $ do- minBound `shouldBe` Null- maxBound `shouldBe` Delete+ describe "fromIntMaybe" $ do+ let n ~> c = fromIntMaybe n `shouldBe` c++ it "-1 → Nothing" $ (-1) ~> Nothing+ it "0 → Null" $ 0 ~> Just Null+ it "65 → A" $ 65 ~> Just CapitalLetterA+ it "127 → Delete" $ 127 ~> Just Delete+ it "128 → Nothing" $ 128 ~> Nothing++ describe "fromWord8Maybe" $ do+ let n ~> c = fromWord8Maybe n `shouldBe` c++ it "0 → Null" $ 0 ~> Just Null+ it "65 → A" $ 65 ~> Just CapitalLetterA+ it "127 → Delete" $ 127 ~> Just Delete+ it "128 → Nothing" $ 128 ~> Nothing++ describe "fromIntUnsafe" $ do+ let n ~> c = fromIntUnsafe n `shouldBe` c++ it "0 → Null" $ 0 ~> Null+ it "65 → A" $ 65 ~> CapitalLetterA+ it "66 → B" $ 66 ~> CapitalLetterB+ it "67 → C" $ 67 ~> CapitalLetterC+ it "127 → Delete" $ 127 ~> Delete++ describe "fromWord8Unsafe" $ do+ let n ~> c = fromWord8Unsafe n `shouldBe` c++ it "0 → Null" $ 0 ~> Null+ it "65 → A" $ 65 ~> CapitalLetterA+ it "66 → B" $ 66 ~> CapitalLetterB+ it "67 → C" $ 67 ~> CapitalLetterC+ it "127 → Delete" $ 127 ~> Delete++ describe "allCharacters" $ do+ let x = allCharacters++ it "has 128 items" $ length x `shouldBe` 128+ it "is sorted" $ sort x `shouldBe` x++ describe "Bounded" $ do+ it "min bound is Null" $ minBound `shouldBe` Null+ it "max bound is Delete" $ maxBound `shouldBe` Delete