packages feed

ascii-th 1.1.0.0 → 1.1.1.0

raw patch · 5 files changed

+291/−69 lines, 5 filesdep +ascii-casedep +ascii-caselessdep ~ascii-supersetPVP ok

version bump matches the API change (PVP)

Dependencies added: ascii-case, ascii-caseless

Dependency ranges changed: ascii-superset

API changes (from Hackage documentation)

+ ASCII.QuasiQuoters: caseless :: QuasiQuoter
+ ASCII.QuasiQuoters: lower :: QuasiQuoter
+ ASCII.QuasiQuoters: upper :: QuasiQuoter
+ ASCII.TemplateHaskell: caselessIsStringPat :: [CaselessChar] -> Q Pat
+ ASCII.TemplateHaskell: caselessListExp :: [CaselessChar] -> Q Exp
+ ASCII.TemplateHaskell: caselessListPat :: [CaselessChar] -> Q Pat

Files

ascii-th.cabal view
@@ -1,7 +1,7 @@ cabal-version: 3.0  name: ascii-th-version: 1.1.0.0+version: 1.1.1.0 synopsis: Template Haskell support for ASCII category: Data, Text @@ -28,7 +28,9 @@     default-language: Haskell2010     ghc-options: -Wall     build-depends:-        ascii-char ^>= 1.0+        ascii-case ^>= 1.0+      , ascii-caseless ^>= 0.0+      , ascii-char ^>= 1.0       , ascii-superset ^>= 1.1.0       , base ^>= 4.14 || ^>= 4.15 || ^>= 4.16 || ^>= 4.17 
changelog.md view
@@ -1,3 +1,17 @@+### 1.1.1.0 (2023-01-03)++New in `ASCII.QuasiQuoters`:++- `caseless`+- `upper`+- `lower`++New in `ASCII.TemplateHaskell`:++- `caselessListExp`+- `caselessListPat`+- `caselessIsStringPat`+ ### 1.1.0.0 (2023-01-03)  Change `ascii-superset` version from 1.0 to 1.1
library/ASCII/QuasiQuoters.hs view
@@ -6,21 +6,29 @@ -} module ASCII.QuasiQuoters   (-    char,-    string,+    {- * Character -} char,+    {- * String -} string,+    {- * Caseless string -} caseless,+    {- * Upper-case string -} upper,+    {- * Lower-case string -} lower,   )   where +import ASCII.Case (Case (..))+import ASCII.Caseless (CaselessChar) import ASCII.Char (Char)-import ASCII.Superset (toCharListMaybe, toCharMaybe)-import ASCII.TemplateHaskell (isCharExp, isCharPat, isStringExp, isStringPat) import Control.Monad (return, (>=>)) import Control.Monad.Fail (MonadFail, fail)+import Data.Functor ((<$>)) import Data.Maybe (Maybe (..)) import Language.Haskell.TH.Quote (QuasiQuoter (..)) import Language.Haskell.TH.Syntax (Exp, Pat, Q) +import qualified ASCII.Case as Case+import qualified ASCII.Superset as S+import qualified ASCII.TemplateHaskell as TH import qualified Data.Char as Unicode+import qualified Data.List as List import qualified Data.String as Unicode  {-| An expression pattern corresponding to an ASCII character@@ -58,7 +66,7 @@ -}  char :: QuasiQuoter-char = expPatQQ requireOneAscii isCharExp isCharPat+char = expPatQQ requireOneAscii TH.isCharExp TH.isCharPat  {- | An expression or pattern corresponding to an ASCII string @@ -100,22 +108,146 @@ -}  string :: QuasiQuoter-string = expPatQQ requireAsciiList isStringExp isStringPat+string = expPatQQ requireAsciiList TH.isStringExp TH.isStringPat +{-| An expression or pattern corresponding to a case-insensitive ASCII string++=== In an expression context++A monomorphic expression of type @['CaselessChar']@.++@+[caseless|Hello!|] ==+    [LetterH, LetterE, LetterL, LetterL, LetterO, ExclamationMark]+@++=== In a pattern context++A case-insensitive match of any type belonging to the+'ASCII.Superset.ToCaselessString' class.++@+let+    x = case "Hello!" :: 'Text' of+          [caseless|Bye!|] -> 1+          [caseless|hEllo!|] -> 2+          _ -> 3+in+    x == 2+@++-}+caseless :: QuasiQuoter+caseless = expPatQQ requireAsciiListCI TH.caselessListExp TH.caselessIsStringPat++{-| An expression or pattern corresponding to an ASCII string where all the+letters are of lower case++The letters in the body of the quasi-quotation may be written in any case+you like; they will be converted to lower case automatically.++=== In an expression context++The expression can become any type belonging to the 'ASCII.Superset.FromString'+class. Any letters in the quoted content will be converted to lower case.++@+[lower|Hello!|] == ("hello!" :: 'Text')+@++=== In a pattern context++The pattern matches a value of a type satisfying the 'ASCII.Superset.ToString'+constraint. A value matches this pattern if:++* All of the letters in the tested value are in lower case+* The tested value satisfies a case-insensitive comparison+  with the quasi-quoted content++@+let+    x = case "hi!" :: 'Text' of+          [lower|wow|] -> 1+          [lower|Hi!|] -> 2+          _ -> 3+in+    x == 2+@++-}+lower :: QuasiQuoter+lower = ofCase LowerCase++{-| An expression or pattern corresponding to an ASCII string where all the+letters are of upper case++The letters in the body of the quasi-quotation may be written in any case+you like; they will be converted to upper case automatically.++=== In an expression context++The expression can become any type belonging to the 'ASCII.Superset.FromString'+class. Any letters in the quoted content will be converted to upper case.++@+[upper|Hello!|] == ("HELLO!" :: 'Text')+@++=== In a pattern context++The pattern matches a value of a type satisfying the 'ASCII.Superset.ToString'+constraint. A value matches this pattern if:++* All of the letters in the tested value are in upper case+* The tested value satisfies a case-insensitive comparison+  with the quasi-quoted content++@+let+    x = case "HI!" :: 'Text' of+          [QQ.upper|wow|] -> 1+          [QQ.upper|Hi!|] -> 2+          _ -> 3+in+    x == 2+@++-}+upper :: QuasiQuoter+upper = ofCase UpperCase++ofCase :: Case -> QuasiQuoter+ofCase c = expPatQQ (requireAsciiToCase c) TH.isStringExp TH.isStringPat++{-| Require the string to consist of exactly one ASCII character -} requireOneAscii :: Unicode.String -> Q Char requireOneAscii = requireOne >=> requireAscii +{-| Require the list to consist of exactly one element -} oneMaybe :: [a] -> Maybe a oneMaybe xs = case xs of [x] -> Just x; _ -> Nothing +{-| Require the string to consist of exactly one character -} requireOne :: Unicode.String -> Q Unicode.Char requireOne = oneMaybe || "Must be exactly one character." +{-| Require the character to be ASCII -} requireAscii :: Unicode.Char -> Q Char-requireAscii = toCharMaybe || "Must be an ASCII character."+requireAscii = S.toCharMaybe || "Must be an ASCII character." +{-| Require the string to consist of all ASCII characters -} requireAsciiList :: Unicode.String -> Q [Char]-requireAsciiList = toCharListMaybe || "Must be only ASCII characters."+requireAsciiList = S.toCharListMaybe || "Must be only ASCII characters."++{-| Require the string to consist of all ASCII characters,+and return them with letter case discarded -}+requireAsciiListCI :: Unicode.String -> Q [CaselessChar]+requireAsciiListCI = S.toCaselessCharListMaybe || "Must be only ASCII characters."++{-| Require the string to consist of all ASCII characters,+and return them converted to the given case -}+requireAsciiToCase :: Case -> Unicode.String -> Q [Char]+requireAsciiToCase c s = List.map (Case.toCase c) <$> requireAsciiList s  (||) :: (a -> Maybe b) -> Unicode.String -> a -> Q b f || msg = \a -> case f a of Just b -> return b; Nothing -> fail msg
library/ASCII/TemplateHaskell.hs view
@@ -1,15 +1,21 @@ module ASCII.TemplateHaskell   (-    {- * Characters          -} charExp,     charPat,-    {- * Character lists     -} charListExp, charListPat,-    {- * Character supersets -} isCharExp,   isCharPat,-    {- * String supersets    -} isStringExp, isStringPat,+    {- * Monomorphic -}+    {- ** Character -} charExp, charPat,+    {- ** String -} charListExp, charListPat,+    {- ** Caseless string -} caselessListExp, caselessListPat,++    {- * Polymorphic -}+    {- ** Character -} isCharExp, isCharPat,+    {- ** String -} isStringExp, isStringPat,+    {- ** Caseless string -} caselessIsStringPat,   )   where  import qualified ASCII.Char as ASCII import qualified ASCII.Superset as S +import ASCII.Caseless (CaselessChar) import Data.Data (Data) import Data.Maybe (Maybe (..)) import Language.Haskell.TH.Syntax (Exp, Pat, Q, dataToExpQ, dataToPatQ)@@ -88,6 +94,12 @@  -} +caselessListExp :: [CaselessChar] -> Q Exp+caselessListExp = exp++caselessListPat :: [CaselessChar] -> Q Pat+caselessListPat = pat+ isCharExp :: ASCII.Char -> Q Exp isCharExp x = [| S.fromChar $(charExp x) |] @@ -115,3 +127,8 @@ {-| Pattern that matches a type with a 'S.ToString' constraint -} isStringPat :: [ASCII.Char] -> Q Pat isStringPat xs = [p| (S.toCharListMaybe -> Just $(charListPat xs)) |]++{-| Matches a value of any type belonging to the 'S.ToCaselessString'+class, as if it were a list of 'CaselessChar' -}+caselessIsStringPat :: [CaselessChar] -> Q Pat+caselessIsStringPat xs = [p| (S.toCaselessCharListMaybe -> Just $(caselessListPat xs)) |]
test/Main.hs view
@@ -1,8 +1,7 @@ module Main (main) where -import ASCII.QuasiQuoters (char, string)-import ASCII.TemplateHaskell (charExp, charListExp, charListPat, charPat,-                              isCharExp, isCharPat)+import qualified ASCII.QuasiQuoters as QQ+import qualified ASCII.TemplateHaskell as TH  import ASCII.Char (Char (..)) import ASCII.Refinement (ASCII, asciiUnsafe)@@ -14,6 +13,7 @@ import Prelude (Integer) import System.IO (IO) +import qualified ASCII.Caseless as Caseless import qualified Data.ByteString.Builder as BS.Builder import qualified Data.ByteString.Lazy as LBS @@ -22,62 +22,119 @@ main :: IO () main = hspec $ do -    describe "char quasi-quoter" $ do-        it "can be ASCII.Char" $ shouldBe @Char [char|e|] SmallLetterE-        it "can be Word8" $ shouldBe @Word8 [char|e|] 101-        it "can be a pattern" $ do-            let x = case Tilde of [char|@|] -> 1; [char|~|] -> 2; _ -> 3-            shouldBe @Integer x 2+    describe "ASCII.QuasiQuoters" $ do -    describe "string quasi-quoter" $ do-        it "can be [ASCII.Char]" $ shouldBe @[Char] [string|Hello!|]-            [CapitalLetterH, SmallLetterE, SmallLetterL,-             SmallLetterL, SmallLetterO, ExclamationMark]-        it "can be String" $ shouldBe @String [string|Hello!|] "Hello!"-        it "can be Text" $ shouldBe @Text [string|Hello!|] "Hello!"-        it "can be bytestring Builder" $ shouldBe @LBS.ByteString-            (BS.Builder.toLazyByteString [string|Hello!|]) "Hello!"-        it "can be a pattern" $ do-            let x = case [CapitalLetterH, SmallLetterI] of-                      [string|Bye|] -> 1; [string|Hi|] -> 2; _ -> 3-            shouldBe @Integer x 2+        describe "char" $ do+            it "can be ASCII.Char" $ shouldBe @Char [QQ.char|e|] SmallLetterE+            it "can be Word8" $ shouldBe @Word8 [QQ.char|e|] 101+            it "can be a pattern" $ do+                let x = case Tilde of [QQ.char|@|] -> 1; [QQ.char|~|] -> 2; _ -> 3+                shouldBe @Integer x 2 -    describe "charExp" $-        it "is a Char expression" $-            shouldBe @Char $(charExp CapitalLetterF) CapitalLetterF+        describe "string" $ do+            it "can be [ASCII.Char]" $ shouldBe @[Char] [QQ.string|Hello!|]+                [CapitalLetterH, SmallLetterE, SmallLetterL,+                SmallLetterL, SmallLetterO, ExclamationMark]+            it "can be String" $ shouldBe @String [QQ.string|Hello!|] "Hello!"+            it "can be Text" $ shouldBe @Text [QQ.string|Hello!|] "Hello!"+            it "can be bytestring Builder" $ shouldBe @LBS.ByteString+                (BS.Builder.toLazyByteString [QQ.string|Hello!|]) "Hello!"+            it "can be a pattern" $ do+                let x = case [CapitalLetterH, SmallLetterI] of+                          [QQ.string|Bye|] -> 1; [QQ.string|Hi|] -> 2; _ -> 3+                shouldBe @Integer x 2 -    describe "charPat" $ do-        it "is a Char pattern" $ do-            let x = case SmallLetterS of $(charPat SmallLetterR) -> 1;-                                         $(charPat SmallLetterS) -> 2;-                                         _ -> 3-            shouldBe @Integer x 2+        describe "caseless" $ do+            it "can be [CaselessChar]" $ shouldBe [QQ.caseless|Hello!|]+                [Caseless.LetterH, Caseless.LetterE, Caseless.LetterL,+                Caseless.LetterL, Caseless.LetterO, Caseless.ExclamationMark]+            it "can be a pattern over [CaselessChar]" $ do+                let x = case [Caseless.LetterH, Caseless.LetterI] of+                          [QQ.caseless|Bye|] -> 1; [QQ.caseless|Hi|] -> 2; _ -> 3+                shouldBe @Integer x 2+            it "can be a pattern over Text" $ do+                let x = case "Hello!" :: Text of+                          [QQ.caseless|Bye!|] -> 1; [QQ.caseless|Hello!|] -> 2; _ -> 3+                shouldBe @Integer x 2+            it "matches Text in a case-insensitive manner" $ do+                let x = case "Hello!" :: Text of+                          [QQ.caseless|Bye!|] -> 1; [QQ.caseless|hEllo!|] -> 2; _ -> 3+                shouldBe @Integer x 2 -    describe "charListExp" $ do-        it "is a [Char] expression" $ shouldBe @[Char]-            $(charListExp [CapitalLetterH, SmallLetterI])-            [CapitalLetterH, SmallLetterI]+        describe "upper" $ do+            it "can be Text" $ shouldBe [QQ.upper|Hello!|] ("HELLO!" :: Text)+            it "can match Text" $ do+                let x = case "HI!" :: Text of+                          [QQ.upper|wow|] -> 1+                          [QQ.upper|Hi!|] -> 2+                          _ -> 3+                shouldBe @Integer x 2+            it "only matches all upper-case Text" $ do+                let x = case "Hi!" :: Text of+                          [QQ.upper|Hi!|] -> 1+                          _ -> 2+                shouldBe @Integer x 2 -    describe "charListPat" $ do-        it "is a [Char] pattern" $ do-            let x = case [CapitalLetterH, SmallLetterI] of-                      $(charListPat [CapitalLetterH, SmallLetterA]) -> 1-                      $(charListPat [CapitalLetterH, SmallLetterI]) -> 2-                      _ -> 3-            shouldBe @Integer x 2+        describe "lower" $ do+            it "can be Text" $ shouldBe [QQ.lower|Hello!|] ("hello!" :: Text)+            it "can match Text" $ do+                let x = case "hi!" :: Text of+                          [QQ.lower|wow|] -> 1+                          [QQ.lower|Hi!|] -> 2+                          _ -> 3+                shouldBe @Integer x 2+            it "only matches all lower-case Text" $ do+                let x = case "Hi!" :: Text of+                          [QQ.lower|Hi!|] -> 1+                          _ -> 2+                shouldBe @Integer x 2 -    describe "isCharExp" $ do-        it "can be ASCII.Char expression" $-            shouldBe @Char $(isCharExp CapitalLetterA) CapitalLetterA-        it "can be a Word8 expression" $-            shouldBe @Word8 $(isCharExp CapitalLetterA) 65-        it "can be an ASCII Word8 expression" $-            shouldBe @(ASCII Word8)-                $(isCharExp CapitalLetterA) (asciiUnsafe 65)+    describe "ASCII.TemplateHaskell" $ do -    describe "isCharPat" $ do-        it "can be a Word8 pattern" $ do-            let x = case (66 :: Word8) of $(isCharPat CapitalLetterA) -> 1-                                          $(isCharPat CapitalLetterB) -> 2-                                          _ -> 3-            shouldBe @Word8 x 2+        describe "charExp" $+            it "is a Char expression" $+                shouldBe @Char $(TH.charExp CapitalLetterF) CapitalLetterF++        describe "charPat" $ do+            it "is a Char pattern" $ do+                let x = case SmallLetterS of $(TH.charPat SmallLetterR) -> 1;+                                             $(TH.charPat SmallLetterS) -> 2;+                                             _ -> 3+                shouldBe @Integer x 2++        describe "charListExp" $ do+            it "is a [Char] expression" $ shouldBe @[Char]+                $(TH.charListExp [CapitalLetterH, SmallLetterI])+                [CapitalLetterH, SmallLetterI]++        describe "charListPat" $ do+            it "is a [Char] pattern" $ do+                let x = case [CapitalLetterH, SmallLetterI] of+                          $(TH.charListPat [CapitalLetterH, SmallLetterA]) -> 1+                          $(TH.charListPat [CapitalLetterH, SmallLetterI]) -> 2+                          _ -> 3+                shouldBe @Integer x 2++        describe "isCharExp" $ do+            it "can be ASCII.Char expression" $+                shouldBe @Char $(TH.isCharExp CapitalLetterA) CapitalLetterA+            it "can be a Word8 expression" $+                shouldBe @Word8 $(TH.isCharExp CapitalLetterA) 65+            it "can be an ASCII Word8 expression" $+                shouldBe @(ASCII Word8)+                    $(TH.isCharExp CapitalLetterA) (asciiUnsafe 65)++        describe "isCharPat" $ do+            it "can be a Word8 pattern" $ do+                let x = case (66 :: Word8) of $(TH.isCharPat CapitalLetterA) -> 1+                                              $(TH.isCharPat CapitalLetterB) -> 2+                                              _ -> 3+                shouldBe @Integer x 2++        describe "caselessIsStringPat" $ do+            it "matches a cased string regardless of its case" $ do+                let x = case [QQ.string|Hi!|] :: Text of+                      $(TH.caselessIsStringPat [Caseless.LetterG, Caseless.LetterO, Caseless.FullStop]) -> 1+                      $(TH.caselessIsStringPat [Caseless.LetterH, Caseless.LetterI, Caseless.ExclamationMark]) -> 2+                      _ -> 3+                shouldBe @Integer x 2