predicate-typed-0.7.3.0: src/Predicate/Data/Char.hs
{-# OPTIONS -Wall #-}
{-# OPTIONS -Wno-compat #-}
{-# OPTIONS -Wincomplete-record-updates #-}
{-# OPTIONS -Wincomplete-uni-patterns #-}
{-# OPTIONS -Wredundant-constraints #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE NoOverloadedLists #-}
{-# LANGUAGE NoStarIsType #-}
{- |
promoted character functions
-}
module Predicate.Data.Char (
-- ** constructor
Char1
-- ** character predicates
, IsLower
, IsUpper
, IsDigit
, IsSpace
, IsPunctuation
, IsControl
, IsHexDigit
, IsOctDigit
, IsSeparator
, IsLatin1
-- ** string predicates
, IsLowerAll
, IsUpperAll
, IsDigitAll
, IsSpaceAll
, IsPunctuationAll
, IsControlAll
, IsHexDigitAll
, IsOctDigitAll
, IsSeparatorAll
, IsLatin1All
-- ** change case
, ToTitle
, ToUpper
, ToLower
) where
import Predicate.Core
import Predicate.Util
import Control.Lens hiding (iall)
import qualified Data.Text.Lens as DTL
import GHC.TypeLits (Symbol, KnownSymbol)
import qualified GHC.TypeLits as GL
import Data.Proxy
import Data.Char
-- $setup
-- >>> :set -XDataKinds
-- >>> :set -XTypeApplications
-- >>> :set -XTypeOperators
-- >>> :set -XOverloadedStrings
-- >>> :set -XNoOverloadedLists
-- >>> import qualified Data.Text as T
-- >>> import Predicate.Prelude
-- | extracts the first character from a non empty 'GHC.TypeLits.Symbol'
--
-- >>> pz @(Char1 "aBc") ()
-- PresentT 'a'
--
data Char1 (s :: Symbol) -- gets the first char from the Symbol [requires that Symbol is not empty]
instance ( KnownSymbol s
, GL.CmpSymbol s "" ~ 'GT
) => P (Char1 s) a where
type PP (Char1 s) a = Char
eval _ opts _ =
case symb @s of
[] -> errorInProgram "Char1: found empty Symbol/string"
c:_ -> pure $ mkNode opts (PresentT c) ("Char1 " <> showL opts c) []
-- | a predicate for determining if a character belongs to the given character set
--
-- >>> pz @(Map '(IsControl, IsLatin1, IsHexDigit, IsOctDigit, IsDigit, IsPunctuation, IsSeparator, IsSpace) Id) "abc134"
-- PresentT [(False,True,True,False,False,False,False,False),(False,True,True,False,False,False,False,False),(False,True,True,False,False,False,False,False),(False,True,True,True,True,False,False,False),(False,True,True,True,True,False,False,False),(False,True,True,True,True,False,False,False)]
--
data IsCharSet (cs :: CharSet)
instance ( x ~ Char
, GetCharSet cs
) => P (IsCharSet cs) x where
type PP (IsCharSet cs) x = Bool
eval _ opts c =
let msg0 = "Is" ++ drop 1 (show cs)
(cs,f) = getCharSet @cs
b = f c
in pure $ mkNodeB opts b (msg0 <> showVerbose opts " | " [c]) []
-- | predicate similar to 'Data.Char.isLower'
--
-- >>> pz @IsLower 'X'
-- FalseT
--
-- >>> pz @IsLower '1'
-- FalseT
--
-- >>> pz @IsLower 'a'
-- TrueT
--
data IsLower
type IsLowerT = IsCharSet 'CLower
instance P IsLowerT x => P IsLower x where
type PP IsLower x = PP IsLowerT x
eval _ = evalBool (Proxy @IsLowerT)
-- | predicate similar to 'Data.Char.isUpper'
--
data IsUpper
type IsUpperT = IsCharSet 'CUpper
instance P IsUpperT x => P IsUpper x where
type PP IsUpper x = PP IsUpperT x
eval _ = evalBool (Proxy @IsUpperT)
-- | predicate similar to 'Data.Char.isDigit'
--
-- >>> pz @IsDigit 'g'
-- FalseT
--
-- >>> pz @IsDigit '9'
-- TrueT
--
data IsDigit
type IsDigitT = IsCharSet 'CNumber
instance P IsDigitT x => P IsDigit x where
type PP IsDigit x = Bool
eval _ = evalBool (Proxy @IsDigitT)
-- | predicate similar to 'Data.Char.isSpace'
--
-- >>> pz @IsSpace '\t'
-- TrueT
--
-- >>> pz @IsSpace ' '
-- TrueT
--
-- >>> pz @IsSpace 'x'
-- FalseT
--
data IsSpace
type IsSpaceT = IsCharSet 'CSpace
instance P IsSpaceT x => P IsSpace x where
type PP IsSpace x = Bool
eval _ = evalBool (Proxy @IsSpaceT)
-- | predicate similar to 'Data.Char.isPunctuation'
--
data IsPunctuation
type IsPunctuationT = IsCharSet 'CPunctuation
instance P IsPunctuationT x => P IsPunctuation x where
type PP IsPunctuation x = Bool
eval _ = evalBool (Proxy @IsPunctuationT)
-- | predicate similar to 'Data.Char.isControl'
--
data IsControl
type IsControlT = IsCharSet 'CControl
instance P IsControlT x => P IsControl x where
type PP IsControl x = Bool
eval _ = evalBool (Proxy @IsControlT)
-- | predicate similar to 'Data.Char.isHexDigit'
--
-- >>> pz @IsHexDigit 'A'
-- TrueT
--
-- >>> pz @IsHexDigit 'g'
-- FalseT
--
data IsHexDigit
type IsHexDigitT = IsCharSet 'CHexDigit
instance P IsHexDigitT x => P IsHexDigit x where
type PP IsHexDigit x = Bool
eval _ = evalBool (Proxy @IsHexDigitT)
-- | predicate similar to 'Data.Char.isOctDigit'
--
data IsOctDigit
type IsOctDigitT = IsCharSet 'COctDigit
instance P IsOctDigitT x => P IsOctDigit x where
type PP IsOctDigit x = Bool
eval _ = evalBool (Proxy @IsOctDigitT)
-- | predicate similar to 'Data.Char.isSeparator'
--
data IsSeparator
type IsSeparatorT = IsCharSet 'CSeparator
instance P IsSeparatorT x => P IsSeparator x where
type PP IsSeparator x = Bool
eval _ = evalBool (Proxy @IsSeparatorT)
-- | predicate similar to 'Data.Char.isLatin1'
--
data IsLatin1
type IsLatin1T = IsCharSet 'CLatin1
instance P IsLatin1T x => P IsLatin1 x where
type PP IsLatin1 x = Bool
eval _ = evalBool (Proxy @IsLatin1T)
-- | a predicate for determining if a string 'Data.Text.IsText' belongs to the given character set
--
-- >>> pl @('Just Uncons >> IsUpper &* IsLowerAll) "AbcdE"
-- False ((>>) False | {True (&*) False | (IsLowerAll | "bcdE")})
-- FalseT
--
-- >>> pl @('Just Uncons >> IsUpper &* IsLowerAll) "Abcde"
-- True ((>>) True | {True (&*) True})
-- TrueT
--
-- >>> pl @('Just Uncons >> IsUpper &* IsLowerAll) "xbcde"
-- False ((>>) False | {False (&*) True | (IsUpper | "x")})
-- FalseT
--
-- >>> pl @('Just Uncons >> IsUpper &* IsLowerAll) "X"
-- True ((>>) True | {True (&*) True})
-- TrueT
--
-- >>> pz @( '(IsControlAll, IsLatin1All , IsHexDigitAll , IsOctDigitAll , IsDigitAll , IsPunctuationAll , IsSeparatorAll , IsSpaceAll)) "abc134"
-- PresentT (False,True,True,False,False,False,False,False)
--
-- >>> pl @(SplitAts [1,2,10] Id >> Para '[IsLowerAll, IsDigitAll, IsUpperAll]) "abdefghi"
-- Present [True,False,False] ((>>) [True,False,False] | {Para(0) [True,False,False] | ["a","bd","efghi"]})
-- PresentT [True,False,False]
--
-- >>> pl @(SplitAts [1,2,10] Id >> BoolsQuick "" '[IsLowerAll, IsDigitAll, IsUpperAll]) "a98efghi"
-- False ((>>) False | {Bool(2) [] (IsUpperAll | "efghi")})
-- FalseT
--
-- >>> pl @(SplitAts [1,2,10] Id >> BoolsQuick "" '[IsLowerAll, IsDigitAll, IsUpperAll || IsLowerAll]) "a98efghi"
-- True ((>>) True | {Bools})
-- TrueT
--
-- >>> pl @(SplitAts [1,2,10] Id >> BoolsQuick "" '[IsLowerAll, IsDigitAll, IsUpperAll || IsLowerAll]) "a98efgHi"
-- False ((>>) False | {Bool(2) [] (False || False | (IsUpperAll | "efgHi") || (IsLowerAll | "efgHi"))})
-- FalseT
--
data IsCharSetAll (cs :: CharSet)
instance (GetCharSet cs
, Show a
, DTL.IsText a
) => P (IsCharSetAll cs) a where
type PP (IsCharSetAll cs) a = Bool
eval _ opts as =
let b = allOf DTL.text f as
msg0 = "Is" ++ drop 1 (show cs) ++ "All"
(cs,f) = getCharSet @cs
in pure $ mkNodeB opts b (msg0 <> showVerbose opts " | " as) []
data CharSet = CLower
| CUpper
| CNumber
| CSpace
| CPunctuation
| CControl
| CHexDigit
| COctDigit
| CSeparator
| CLatin1
deriving Show
class GetCharSet (cs :: CharSet) where
getCharSet :: (CharSet, Char -> Bool)
instance GetCharSet 'CLower where
getCharSet = (CLower, isLower)
instance GetCharSet 'CUpper where
getCharSet = (CUpper, isUpper)
instance GetCharSet 'CNumber where
getCharSet = (CNumber, isNumber)
instance GetCharSet 'CSpace where
getCharSet = (CSpace, isSpace)
instance GetCharSet 'CPunctuation where
getCharSet = (CPunctuation, isPunctuation)
instance GetCharSet 'CControl where
getCharSet = (CControl, isControl)
instance GetCharSet 'CHexDigit where
getCharSet = (CHexDigit, isHexDigit)
instance GetCharSet 'COctDigit where
getCharSet = (COctDigit, isOctDigit)
instance GetCharSet 'CSeparator where
getCharSet = (CSeparator, isSeparator)
instance GetCharSet 'CLatin1 where
getCharSet = (CLatin1, isLatin1)
-- | predicate for determining if a string is all lowercase
--
-- >>> pz @IsLowerAll "abc"
-- TrueT
--
-- >>> pz @IsLowerAll "abcX"
-- FalseT
--
-- >>> pz @IsLowerAll (T.pack "abcX")
-- FalseT
--
-- >>> pz @IsLowerAll "abcdef213"
-- FalseT
--
-- >>> pz @IsLowerAll ""
-- TrueT
--
data IsLowerAll
type IsLowerAllT = IsCharSetAll 'CLower
instance P IsLowerAllT x => P IsLowerAll x where
type PP IsLowerAll x = PP IsLowerAllT x
eval _ = evalBool (Proxy @IsLowerAllT)
data IsUpperAll
type IsUpperAllT = IsCharSetAll 'CUpper
instance P IsUpperAllT x => P IsUpperAll x where
type PP IsUpperAll x = PP IsUpperAllT x
eval _ = evalBool (Proxy @IsUpperAllT)
-- | predicate for determining if the string is all digits
--
-- >>> pz @IsDigitAll "213G"
-- FalseT
--
-- >>> pz @IsDigitAll "929"
-- TrueT
--
data IsDigitAll
type IsDigitAllT = IsCharSetAll 'CNumber
instance P IsDigitAllT x => P IsDigitAll x where
type PP IsDigitAll x = Bool
eval _ = evalBool (Proxy @IsDigitAllT)
-- | predicate for determining if the string is all spaces
--
-- >>> pz @IsSpaceAll "213G"
-- FalseT
--
-- >>> pz @IsSpaceAll " "
-- TrueT
--
-- >>> pz @IsSpaceAll ""
-- TrueT
--
data IsSpaceAll
type IsSpaceAllT = IsCharSetAll 'CSpace
instance P IsSpaceAllT x => P IsSpaceAll x where
type PP IsSpaceAll x = Bool
eval _ = evalBool (Proxy @IsSpaceAllT)
data IsPunctuationAll
type IsPunctuationAllT = IsCharSetAll 'CPunctuation
instance P IsPunctuationAllT x => P IsPunctuationAll x where
type PP IsPunctuationAll x = Bool
eval _ = evalBool (Proxy @IsPunctuationAllT)
data IsControlAll
type IsControlAllT = IsCharSetAll 'CControl
instance P IsControlAllT x => P IsControlAll x where
type PP IsControlAll x = Bool
eval _ = evalBool (Proxy @IsControlAllT)
-- | predicate for determining if the string is all hex digits
--
-- >>> pz @IsHexDigitAll "01efA"
-- TrueT
--
-- >>> pz @IsHexDigitAll "01egfA"
-- FalseT
--
data IsHexDigitAll
type IsHexDigitAllT = IsCharSetAll 'CHexDigit
instance P IsHexDigitAllT x => P IsHexDigitAll x where
type PP IsHexDigitAll x = Bool
eval _ = evalBool (Proxy @IsHexDigitAllT)
data IsOctDigitAll
type IsOctDigitAllT = IsCharSetAll 'COctDigit
instance P IsOctDigitAllT x => P IsOctDigitAll x where
type PP IsOctDigitAll x = Bool
eval _ = evalBool (Proxy @IsOctDigitAllT)
data IsSeparatorAll
type IsSeparatorAllT = IsCharSetAll 'CSeparator
instance P IsSeparatorAllT x => P IsSeparatorAll x where
type PP IsSeparatorAll x = Bool
eval _ = evalBool (Proxy @IsSeparatorAllT)
data IsLatin1All
type IsLatin1AllT = IsCharSetAll 'CLatin1
instance P IsLatin1AllT x => P IsLatin1All x where
type PP IsLatin1All x = Bool
eval _ = evalBool (Proxy @IsLatin1AllT)
-- | converts a string 'Data.Text.Lens.IsText' value to lower case
--
-- >>> pz @ToLower "HeLlO wOrld!"
-- PresentT "hello world!"
--
data ToLower
instance ( Show a
, DTL.IsText a
) => P ToLower a where
type PP ToLower a = a
eval _ opts as =
let msg0 = "ToLower"
xs = as & DTL.text %~ toLower
in pure $ mkNode opts (PresentT xs) (show01 opts msg0 xs as) []
-- | converts a string 'Data.Text.Lens.IsText' value to upper case
--
-- >>> pz @ToUpper "HeLlO wOrld!"
-- PresentT "HELLO WORLD!"
--
data ToUpper
instance ( Show a
, DTL.IsText a
) => P ToUpper a where
type PP ToUpper a = a
eval _ opts as =
let msg0 = "ToUpper"
xs = as & DTL.text %~ toUpper
in pure $ mkNode opts (PresentT xs) (show01 opts msg0 xs as) []
-- | converts a string 'Data.Text.Lens.IsText' value to title case
--
-- >>> pz @ToTitle "HeLlO wOrld!"
-- PresentT "Hello world!"
--
-- >>> data Color = Red | White | Blue | Green | Black deriving (Show,Eq,Enum,Bounded,Read)
-- >>> pz @(ToTitle >> ReadP Color Id) "red"
-- PresentT Red
--
data ToTitle
instance ( Show a
, DTL.IsText a
) => P ToTitle a where
type PP ToTitle a = a
eval _ opts as =
let msg0 = "ToTitle"
xs = toTitleAll (as ^. DTL.unpacked) ^. DTL.packed
in pure $ mkNode opts (PresentT xs) (show01 opts msg0 xs as) []
toTitleAll :: String -> String
toTitleAll (x:xs) = toUpper x : map toLower xs
toTitleAll [] = []