ascii-superset 1.1.1.0 → 1.2.0.0
raw patch · 7 files changed
+136/−17 lines, 7 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ ASCII.CaseRefinement: refineCharToCase :: forall letterCase char. KnownCase letterCase => CharSuperset char => ASCII char -> ASCII'case letterCase char
+ ASCII.CaseRefinement: refineStringToCase :: forall letterCase char. KnownCase letterCase => StringSuperset char => ASCII char -> ASCII'case letterCase char
+ ASCII.Superset: toCaseChar :: CharSuperset char => Case -> char -> char
+ ASCII.Superset: toCaseString :: StringSuperset string => Case -> string -> string
Files
- ascii-superset.cabal +1/−1
- changelog.md +28/−0
- library/ASCII/CaseRefinement.hs +17/−3
- library/ASCII/Refinement.hs +4/−1
- library/ASCII/Refinement.hs-boot +5/−0
- library/ASCII/Superset.hs +41/−9
- test/Main.hs +40/−3
ascii-superset.cabal view
@@ -1,7 +1,7 @@ cabal-version: 3.0 name: ascii-superset-version: 1.1.1.0+version: 1.2.0.0 synopsis: Representing ASCII with refined supersets category: Data, Text
changelog.md view
@@ -1,3 +1,31 @@+### 1.2.0.0 (2023-01-03)++Add to the `CharSuperset` class a new method:++```haskell+toCaseChar :: Case -> char -> char+```++Add to the `StringSuperset` class a new method:++```haskell+toCaseString :: Case -> string -> string+```++Add to the `ASCII.CaseRefinement` module:++```haskell+refineCharToCase :: forall letterCase char.+ KnownCase letterCase => CharSuperset char =>+ ASCII char -> ASCII'case letterCase char+```++```haskell+refineStringToCase :: forall letterCase char.+ KnownCase letterCase => StringSuperset char =>+ ASCII char -> ASCII'case letterCase char+```+ ### 1.1.1.0 (2023-01-03) Add to `ASCII.CaseRefinement` the `KnownCase` class.
library/ASCII/CaseRefinement.hs view
@@ -3,21 +3,23 @@ {- * ASCII'case type constructor -} ASCII'case, lift, asciiCaseUnsafe, {- ** Aliases -} {- $aliases -} ASCII'upper, ASCII'lower, {- * Character functions -} validateChar, fromCaselessChar,- toCaselessChar, substituteChar, asCaselessChar,+ toCaselessChar, substituteChar, asCaselessChar, refineCharToCase, {- * String functions -} validateString, fromCaselessCharList,- toCaselessCharList, substituteString, mapChars,+ toCaselessCharList, substituteString, mapChars, refineStringToCase, {- * KnownCase -} KnownCase (..), ) where import ASCII.Case (Case (..)) import ASCII.Caseless (CaselessChar)+import {-# source #-} ASCII.Refinement (ASCII) import ASCII.Superset (CharSuperset, StringSuperset) import qualified ASCII.Case as Case import qualified ASCII.Caseless as Caseless import qualified ASCII.Char as ASCII import qualified ASCII.Superset as Superset+import {-# source #-} qualified ASCII.Refinement as Refinement import Control.Monad (guard) import Data.Bool (Bool (..))@@ -145,7 +147,7 @@ ASCII'case letterCase superset {- ^ Character that is known to be ASCII, and in the particular case if it is a letter -} -> CaselessChar-toCaselessChar = Caseless.disregardCase . Superset.toCharUnsafe . lift+toCaselessChar = Caseless.disregardCase . Superset.toCharUnsafe . lift {-| Given a character from a larger set that is known to represent an ASCII character, manipulate it as if it were an ASCII character -}@@ -158,6 +160,12 @@ where g = Caseless.toCase (theCase @letterCase) . f . Caseless.assumeCaseUnsafe (theCase @letterCase) +{-| Given an ASCII superset character that is known to be valid ASCII,+refine it further by converting it to a particular letter case -}+refineCharToCase :: forall letterCase char. KnownCase letterCase => CharSuperset char =>+ ASCII char -> ASCII'case letterCase char+refineCharToCase = asciiCaseUnsafe . Superset.toCaseChar (theCase @letterCase) . Refinement.lift+ --- {-| Return 'Just' an 'ASCII'case' string if the input consists entirely of ASCII@@ -206,3 +214,9 @@ mapChars f = asciiCaseUnsafe . Superset.mapCharsUnsafe g . lift where g = Caseless.toCase (theCase @letterCase) . f . Caseless.assumeCaseUnsafe (theCase @letterCase)++{-| Given an ASCII superset string that is known to be valid ASCII,+refine it further by converting it to a particular letter case -}+refineStringToCase :: forall letterCase char. KnownCase letterCase => StringSuperset char =>+ ASCII char -> ASCII'case letterCase char+refineStringToCase = asciiCaseUnsafe . Superset.toCaseString (theCase @letterCase) . Refinement.lift
library/ASCII/Refinement.hs view
@@ -10,6 +10,7 @@ import qualified ASCII.Isomorphism as I import qualified ASCII.Superset as S +import ASCII.CaseRefinement (KnownCase (..), ASCII'case, ASCII'upper, ASCII'lower) import Data.Data (Data) import Data.Eq (Eq) import Data.Function (id, ($), (.))@@ -66,7 +67,8 @@ instance S.CharSuperset char => S.FromChar (ASCII char) where fromChar = asciiUnsafe . S.fromChar -instance S.CharSuperset char => S.CharSuperset (ASCII char)+instance S.CharSuperset char => S.CharSuperset (ASCII char) where+ toCaseChar c = asciiUnsafe . S.toCaseChar c . lift instance S.CharSuperset char => I.CharIso (ASCII char) where toChar = S.toCharUnsafe@@ -86,6 +88,7 @@ instance S.StringSuperset string => S.StringSuperset (ASCII string) where substituteString = id+ toCaseString c = asciiUnsafe . S.toCaseString c . lift instance S.StringSuperset string => I.StringIso (ASCII string) where toCharList = S.toCharListUnsafe
+ library/ASCII/Refinement.hs-boot view
@@ -0,0 +1,5 @@+module ASCII.Refinement where++data ASCII superset++lift :: ASCII superset -> superset
library/ASCII/Superset.hs view
@@ -1,7 +1,7 @@ module ASCII.Superset ( {- * Characters -}- {- ** Class -} ToCaselessChar (..), ToChar (..), FromChar (..), CharSuperset,+ {- ** Class -} ToCaselessChar (..), ToChar (..), FromChar (..), CharSuperset (..), {- ** Functions -} asCharUnsafe, toCharMaybe, toCaselessCharMaybe, toCharOrFail, toCaselessCharOrFail, toCharSub, toCaselessCharSub, substituteChar, convertCharMaybe, convertCharOrFail, @@ -12,6 +12,7 @@ ) where +import ASCII.Case (Case (..)) import ASCII.Caseless (CaselessChar) import Control.Monad (return) import Control.Monad.Fail (MonadFail (fail))@@ -20,7 +21,9 @@ import Data.Functor (fmap) import Data.Maybe (Maybe (..)) import Data.Ord ((<=), (>=))+import Prelude ((+), (-)) +import qualified ASCII.Case as Case import qualified ASCII.Caseless as Caseless import qualified ASCII.Char as ASCII import qualified Data.Bool as Bool@@ -82,8 +85,13 @@ - a total conversion from ASCII; and - a partial conversion to ASCII -}-class (ToChar char, FromChar char) => CharSuperset char+class (ToChar char, FromChar char) => CharSuperset char where + {- | Convert a character in the superset to the designated case,+ if it is an ASCII letter of the opposite case. Otherwise, return+ the argument unmodified. -}+ toCaseChar :: Case -> char -> char+ {-| Manipulate a character as if it were an ASCII 'ASCII.Char', assuming that it is Defined only where 'isAsciiChar' is satisfied. -}@@ -194,6 +202,10 @@ mapCharsUnsafe :: (ASCII.Char -> ASCII.Char) -> string -> string mapCharsUnsafe f = fromCharList . List.map f . toCharListUnsafe + {- | Convert each character in the superset to the designated case, if it is+ an ASCII letter of the opposite case. Leaves other characters unchanged. -}+ toCaseString :: Case -> string -> string+ toCharListMaybe :: ToString string => string -> Maybe [ASCII.Char] toCharListMaybe = toCharListOrFail @@ -229,7 +241,7 @@ --- Instances --- --- | 'CaselessChar' is trivially convertible to itself. (This instance is uninteresting.)+-- | 'CaselessChar' is trivially convertible to itself. instance ToCaselessChar CaselessChar where isAsciiCaselessChar _ = Bool.True toCaselessCharUnsafe = id@@ -247,8 +259,9 @@ instance FromChar ASCII.Char where fromChar = id --- | 'ASCII.Char' is trivially a superset of itself. (This instance is uninteresting.)-instance CharSuperset ASCII.Char+-- | 'ASCII.Char' is trivially a superset of itself.+instance CharSuperset ASCII.Char where+ toCaseChar = Case.toCase --- @@ -263,7 +276,10 @@ instance FromChar Unicode.Char where fromChar = Unicode.chr . ASCII.toInt -instance CharSuperset Unicode.Char+instance CharSuperset Unicode.Char where+ toCaseChar UpperCase x | x >= 'a' && x <= 'z' = Unicode.chr (Unicode.ord x - 32)+ toCaseChar LowerCase x | x >= 'A' && x <= 'Z' = Unicode.chr (Unicode.ord x + 32)+ toCaseChar _ x = x --- @@ -278,7 +294,10 @@ instance FromChar Nat.Natural where fromChar = Prelude.fromIntegral . ASCII.toInt -instance CharSuperset Nat.Natural+instance CharSuperset Nat.Natural where+ toCaseChar UpperCase x | x >= 97 && x <= 122 = x - 32+ toCaseChar LowerCase x | x >= 65 && x <= 90 = x + 32+ toCaseChar _ x = x --- @@ -293,7 +312,10 @@ instance FromChar Int.Int where fromChar = ASCII.toInt -instance CharSuperset Int.Int+instance CharSuperset Int.Int where+ toCaseChar UpperCase x | x >= 97 && x <= 122 = x - 32+ toCaseChar LowerCase x | x >= 65 && x <= 90 = x + 32+ toCaseChar _ x = x --- @@ -308,7 +330,10 @@ instance FromChar Word.Word8 where fromChar = Prelude.fromIntegral . ASCII.toInt -instance CharSuperset Word.Word8+instance CharSuperset Word.Word8 where+ toCaseChar UpperCase x | x >= 97 && x <= 122 = x - 32+ toCaseChar LowerCase x | x >= 65 && x <= 90 = x + 32+ toCaseChar _ x = x --- @@ -327,6 +352,7 @@ instance CharSuperset char => StringSuperset [char] where substituteString = List.map substituteChar+ toCaseString c = List.map (toCaseChar c) --- @@ -346,6 +372,7 @@ instance StringSuperset T.Text where substituteString = T.map substituteChar mapCharsUnsafe f = T.map (asCharUnsafe f)+ toCaseString c = T.map (toCaseChar c) --- @@ -365,6 +392,7 @@ instance StringSuperset LT.Text where substituteString = LT.map substituteChar mapCharsUnsafe f = LT.map (asCharUnsafe f)+ toCaseString c = LT.map (toCaseChar c) --- @@ -384,6 +412,7 @@ instance StringSuperset TB.Builder where substituteString = TB.fromLazyText . substituteString . TB.toLazyText mapCharsUnsafe f = TB.fromLazyText . mapCharsUnsafe f . TB.toLazyText+ toCaseString c = TB.fromLazyText . toCaseString c . TB.toLazyText --- @@ -403,6 +432,7 @@ instance StringSuperset BS.ByteString where substituteString = BS.map substituteChar mapCharsUnsafe f = BS.map (asCharUnsafe f)+ toCaseString c = BS.map (toCaseChar c) --- @@ -422,6 +452,7 @@ instance StringSuperset LBS.ByteString where substituteString = LBS.map substituteChar mapCharsUnsafe f = LBS.map (asCharUnsafe f)+ toCaseString c = LBS.map (toCaseChar c) --- @@ -441,3 +472,4 @@ instance StringSuperset BSB.Builder where substituteString = BSB.lazyByteString . substituteString . BSB.toLazyByteString mapCharsUnsafe f = BSB.lazyByteString . mapCharsUnsafe f . BSB.toLazyByteString+ toCaseString c = BSB.lazyByteString . toCaseString c . BSB.toLazyByteString
test/Main.hs view
@@ -2,20 +2,29 @@ import Test.Hspec -import ASCII.Refinement (ASCII, asciiUnsafe)+import ASCII.Case (Case (..)) import ASCII.CaseRefinement (ASCII'lower, ASCII'upper, asciiCaseUnsafe)+import ASCII.Char (Char (..))+import ASCII.Refinement (ASCII, asciiUnsafe) +import qualified ASCII.Case as Case+import qualified ASCII.Caseless as CC import qualified ASCII.CaseRefinement as CaseRefinement+import qualified ASCII.Char as ASCII import qualified ASCII.Lift as Lift import qualified ASCII.Refinement as Refinement+import qualified ASCII.Superset as Superset -import ASCII.Char (Char (..))-import qualified ASCII.Caseless as CC+import qualified Data.Foldable as Foldable +import Data.Function ((&)) import Data.Text (Text) import Data.Word (Word8)+import Numeric.Natural (Natural) import Prelude +import qualified Data.Char as Unicode+ main :: IO () main = hspec $ do @@ -116,3 +125,31 @@ let f x = CaseRefinement.validateString x :: Maybe (ASCII'upper Text) f "HELLO" `shouldBe` Just (asciiCaseUnsafe "HELLO") f "Hello" `shouldBe` Nothing++ describe "case conversion" $ do++ describe "toCaseChar" $ do+ let check :: forall a. Eq a => Superset.CharSuperset a => Expectation+ check =+ ([UpperCase, LowerCase] & Foldable.all (\c ->+ ASCII.allCharacters & Foldable.all (\x ->+ Superset.toCaseChar c (Superset.fromChar @a x)+ == Superset.fromChar @a (Case.toCase c x)+ ))) `shouldBe` True+ it "ASCII.Char" $ check @ASCII.Char+ it "Unicode.Char" $ check @Unicode.Char+ it "Natural" $ check @Natural+ it "Int" $ check @Int+ it "Word8" $ check @Word8++ describe "toCaseString" $ do+ it "Text" $ do+ let x = "012 abc DEF ﬓ" :: Text+ Superset.toCaseString UpperCase x `shouldBe` "012 ABC DEF ﬓ"+ Superset.toCaseString LowerCase x `shouldBe` "012 abc def ﬓ"++ describe "refineStringToCase" $ do+ it "Text" $ do+ let x = asciiUnsafe "Hi!" :: ASCII Text+ CaseRefinement.refineStringToCase x `shouldBe` (asciiCaseUnsafe "HI!" :: ASCII'upper Text)+ CaseRefinement.refineStringToCase x `shouldBe` (asciiCaseUnsafe "hi!" :: ASCII'lower Text)