ascii-superset (empty) → 1.0.0.0
raw patch · 6 files changed
+477/−0 lines, 6 filesdep +ascii-chardep +basedep +bytestring
Dependencies added: ascii-char, base, bytestring, hashable, text
Files
- ASCII/Isomorphism.hs +28/−0
- ASCII/Lift.hs +47/−0
- ASCII/Refinement.hs +161/−0
- ASCII/Superset.hs +182/−0
- ascii-superset.cabal +46/−0
- license.txt +13/−0
+ ASCII/Isomorphism.hs view
@@ -0,0 +1,28 @@+module ASCII.Isomorphism ( CharIso (..), asChar, StringIso (..) ) where++import ASCII.Char ( Char )+import ASCII.Superset ( CharSuperset (..), StringSuperset (..) )+import Data.Function ( id, (.) )+import Data.List ( map )++class CharSuperset char => CharIso char+ where+ toChar :: char -> Char++asChar :: CharIso char => (Char -> Char) -> char -> char+asChar f = fromChar . f . toChar++class StringSuperset string => StringIso string+ where+ toCharList :: string -> [Char]+ mapChars :: (Char -> Char) -> string -> string++-- | 'Char' is trivially isomorphic to itself.+instance CharIso Char+ where+ toChar = id++instance CharIso char => StringIso [char]+ where+ toCharList = map toChar+ mapChars f = map (asChar f)
+ ASCII/Lift.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}++module ASCII.Lift ( Lift (..) ) where++import ASCII.Char ( Char )+import ASCII.Refinement ( ASCII )+import ASCII.Superset ( CharSuperset, StringSuperset )++import qualified ASCII.Refinement as R+import qualified ASCII.Superset as S++{- $setup++>>> import ASCII.Char (Char (..))+>>> import Data.Text (Text)+>>> import Data.Word (Word8)++-}++class Lift ascii superset+ where++ {- | Converts from ASCII to any larger type.++ >>> lift CapitalLetterA :: Word8+ 65++ >>> lift [CapitalLetterH,SmallLetterI,ExclamationMark] :: Text+ "Hi!"++ Due to the highly polymorphic nature of the 'lift' function, often it must used with an explicit type signature or type application to avoid any type ambiguity.++ -}++ lift :: ascii -> superset++-- | A value from an ASCII superset that has been refined by the 'ASCII' type constructor may be lifted back into the superset by unwrapping it from the 'ASCII' type.++instance Lift (ASCII superset) superset where lift = R.lift++-- | An ASCII 'Char' may be 'lift'ed into any larger character set (a 'CharSuperset').++instance CharSuperset superset => Lift Char superset where lift = S.fromChar++-- | An ASCII 'Char' list may be 'lift'ed into a string of any larger character set (a 'StringSuperset').++instance StringSuperset superset => Lift [Char] superset where lift = S.fromCharList
+ ASCII/Refinement.hs view
@@ -0,0 +1,161 @@+module ASCII.Refinement+ (+ {- * ASCII type constructor -} ASCII, lift, asciiUnsafe,+ {- * Character functions -} validateChar, fromChar, toChar, substituteChar, asChar,+ {- * String functions -} validateString, fromCharList, toCharList, substituteString, mapChars+ )+ where++import qualified ASCII.Char as ASCII+import qualified ASCII.Superset as S+import qualified ASCII.Isomorphism as I++import ASCII.Superset ( CharSuperset, StringSuperset )+import Data.Bool ( Bool (..) )+import Data.Data ( Data )+import Data.Eq ( Eq )+import Data.Function ( (.), ($), id )+import Data.Hashable ( Hashable )+import Data.List ( map )+import Data.Maybe ( Maybe (..) )+import Data.Monoid ( Monoid )+import Data.Ord ( Ord, (>) )+import Data.Semigroup ( Semigroup )+import GHC.Generics ( Generic )+import Prelude ( succ )+import Text.Show ( Show, showString, showsPrec, showParen, showList )++{- $setup++>>> :set -XOverloadedStrings+>>> import ASCII.Char (Char (..))+>>> import Data.List (map)+>>> import Data.Int (Int)+>>> import Data.String (String)+>>> import Data.Text (Text)++-}++{- | This type constructor indicates that a value from some ASCII superset is valid ASCII. The type parameter is the ASCII superset, which should be a type with an instance of either 'CharSuperset' or 'StringSuperset'.++For example, whereas a 'Data.Text.Text' value may contain a combination of ASCII and non-ASCII characters, a value of type @'ASCII' 'Data.Text.Text'@ may contain only ASCII characters.++-}++newtype ASCII superset = ASCII_Unsafe { lift :: superset }++deriving stock instance Eq superset => Eq (ASCII superset)++deriving stock instance Ord superset => Ord (ASCII superset)++deriving newtype instance Hashable superset => Hashable (ASCII superset)++deriving newtype instance Semigroup superset => Semigroup (ASCII superset)++deriving newtype instance Monoid superset => Monoid (ASCII superset)++deriving stock instance Data superset => Data (ASCII superset)++deriving stock instance Generic (ASCII superset)++instance Show superset => Show (ASCII superset)+ where+ showsPrec d x = showParen (d > app_prec) $+ showString "asciiUnsafe " . showsPrec (succ app_prec) (lift x)+ where app_prec = 10++ showList x = showString "asciiUnsafe " . showList (map lift x)++instance CharSuperset char => CharSuperset (ASCII char)+ where+ isAsciiChar _ = True+ fromChar = asciiUnsafe . S.fromChar+ toCharUnsafe = S.toCharUnsafe . lift++instance CharSuperset char => I.CharIso (ASCII char)+ where+ toChar = S.toCharUnsafe++instance StringSuperset string => StringSuperset (ASCII string)+ where+ isAsciiString _ = True+ fromCharList = asciiUnsafe . S.fromCharList+ toCharListUnsafe = S.toCharListUnsafe . lift+ toCharListSub = S.toCharListUnsafe . lift+ substituteString = id++instance StringSuperset string => I.StringIso (ASCII string)+ where+ toCharList = S.toCharListUnsafe+ mapChars = S.mapCharsUnsafe++asciiUnsafe :: superset -> ASCII superset+asciiUnsafe = ASCII_Unsafe++{- |++>>> map validateChar [-1, 65, 97, 128] :: [Maybe (ASCII Int)]+[Nothing,Just (asciiUnsafe 65),Just (asciiUnsafe 97),Nothing]++-}++validateChar :: CharSuperset superset => superset -> Maybe (ASCII superset)+validateChar x = if S.isAsciiChar x then Just (asciiUnsafe x) else Nothing++substituteChar :: CharSuperset superset => superset -> ASCII superset+substituteChar x = if S.isAsciiChar x then asciiUnsafe x else fromChar ASCII.Substitute++fromChar :: CharSuperset superset => ASCII.Char -> ASCII superset+fromChar = asciiUnsafe . S.fromChar++toChar :: CharSuperset superset => ASCII superset -> ASCII.Char+toChar = S.toCharUnsafe . lift++{- |++>>> fromCharList [CapitalLetterH,SmallLetterI,ExclamationMark] :: ASCII Text+asciiUnsafe "Hi!"++-}++fromCharList :: StringSuperset superset => [ASCII.Char] -> ASCII superset+fromCharList = asciiUnsafe . S.fromCharList++{- |++>>> toCharList (substituteString "Piñata" :: ASCII Text)+[CapitalLetterP,SmallLetterI,Substitute,SmallLetterA,SmallLetterT,SmallLetterA]++-}++toCharList :: StringSuperset superset => ASCII superset -> [ASCII.Char]+toCharList = S.toCharListUnsafe . lift++{- | Forces a string from a larger character set into ASCII by using the 'ASCII.Substitute' character in place of any non-ASCII characters.++>>> substituteString "Cristóbal" :: ASCII Text+asciiUnsafe "Crist\SUBbal"++-}++substituteString :: StringSuperset superset => superset -> ASCII superset+substituteString = asciiUnsafe . S.substituteString++{- |++>>> map validateString ["Hello", "Cristóbal"] :: [Maybe (ASCII Text)]+[Just (asciiUnsafe "Hello"),Nothing]++>>> map validateString ["Hello", "Cristóbal"] :: [Maybe (ASCII String)]+[Just (asciiUnsafe "Hello"),Nothing]++-}++validateString :: StringSuperset superset => superset -> Maybe (ASCII superset)+validateString x = if S.isAsciiString x then Just (asciiUnsafe x) else Nothing++asChar :: CharSuperset superset => (ASCII.Char -> ASCII.Char) -> ASCII superset -> ASCII superset+asChar f = asciiUnsafe . S.asCharUnsafe f . lift++mapChars :: StringSuperset superset => (ASCII.Char -> ASCII.Char) -> ASCII superset -> ASCII superset+mapChars f = asciiUnsafe . S.mapCharsUnsafe f . lift
+ ASCII/Superset.hs view
@@ -0,0 +1,182 @@+module ASCII.Superset (++ {- * Characters -}+ {- ** Class -} CharSuperset (..),+ {- ** Functions -} asCharUnsafe, toCharMaybe, toCharOrFail, toCharSub, substituteChar,++ {- * Strings -}+ {- ** Class -} StringSuperset (..),+ {- ** Functions -} toCharListMaybe, toCharListOrFail++ ) where++import Control.Monad ( return )+import Control.Monad.Fail ( MonadFail (fail) )+import Data.Bool ( Bool, (&&) )+import Data.Function ( (.), id )+import Data.Ord ( (<=), (>=) )+import Data.Maybe ( Maybe (..) )++import qualified ASCII.Char as ASCII+import qualified Data.Bool as Bool+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as LBS+import qualified Data.ByteString.Builder as BSB+import qualified Data.Char as Unicode+import qualified Data.Int as Int+import qualified Data.List as List+import qualified Data.Text as T+import qualified Data.Text.Lazy as LT+import qualified Data.Text.Lazy.Builder as TB+import qualified Data.Word as Word+import qualified Numeric.Natural as Nat+import qualified Prelude+++--- Char ---++class CharSuperset char+ where++ isAsciiChar :: char -> Bool++ fromChar :: ASCII.Char -> char++ toCharUnsafe :: char -> ASCII.Char++asCharUnsafe :: CharSuperset char => (ASCII.Char -> ASCII.Char) -> char -> char+asCharUnsafe f = fromChar . f . toCharUnsafe++toCharMaybe :: CharSuperset char => char -> Maybe ASCII.Char+toCharMaybe = toCharOrFail++toCharOrFail :: (CharSuperset char, MonadFail context) => char -> context ASCII.Char+toCharOrFail x = if isAsciiChar x then return (toCharUnsafe x) else fail "Not an ASCII character"++toCharSub :: CharSuperset char => char -> ASCII.Char+toCharSub x = if isAsciiChar x then toCharUnsafe x else ASCII.Substitute++substituteChar :: CharSuperset char => char -> char+substituteChar x = if isAsciiChar x then x else fromChar ASCII.Substitute+++--- String ---++class StringSuperset string+ where++ isAsciiString :: string -> Bool++ fromCharList :: [ASCII.Char] -> string++ toCharListUnsafe :: string -> [ASCII.Char]++ toCharListSub :: string -> [ASCII.Char]++ substituteString :: string -> string++ mapCharsUnsafe :: (ASCII.Char -> ASCII.Char) -> string -> string+ mapCharsUnsafe f = fromCharList . List.map f . toCharListUnsafe++toCharListMaybe :: StringSuperset string => string -> Maybe [ASCII.Char]+toCharListMaybe = toCharListOrFail++toCharListOrFail :: (StringSuperset string, MonadFail context) => string -> context [ASCII.Char]+toCharListOrFail x = if isAsciiString x then return (toCharListUnsafe x) else fail "String contains non-ASCII characters"+++--- Instances ---++-- | 'ASCII.Char' is trivially a superset of itself.++instance CharSuperset ASCII.Char+ where+ isAsciiChar _ = Bool.True+ fromChar = id+ toCharUnsafe = id++instance CharSuperset Unicode.Char+ where+ isAsciiChar = (<= '\DEL')+ fromChar = Unicode.chr . ASCII.toInt+ toCharUnsafe = ASCII.fromIntUnsafe . Unicode.ord++instance CharSuperset Nat.Natural+ where+ isAsciiChar = (<= 127)+ fromChar = Prelude.fromIntegral . ASCII.toInt+ toCharUnsafe = ASCII.fromIntUnsafe . Prelude.fromIntegral++instance CharSuperset Int.Int+ where+ isAsciiChar x = (x >= 0) && (x <= 127)+ fromChar = ASCII.toInt+ toCharUnsafe = ASCII.fromIntUnsafe++instance CharSuperset Word.Word8+ where+ isAsciiChar = (<= 127)+ fromChar = Prelude.fromIntegral . ASCII.toInt+ toCharUnsafe = ASCII.fromIntUnsafe . Prelude.fromIntegral++instance CharSuperset char => StringSuperset [char]+ where+ isAsciiString = List.all isAsciiChar+ fromCharList = List.map fromChar+ toCharListUnsafe = List.map toCharUnsafe+ toCharListSub = List.map toCharSub+ substituteString = List.map substituteChar++instance StringSuperset T.Text+ where+ isAsciiString = T.all isAsciiChar+ fromCharList = T.pack . fromCharList+ toCharListUnsafe = toCharListUnsafe . T.unpack+ toCharListSub = toCharListSub . T.unpack+ substituteString = T.map substituteChar+ mapCharsUnsafe f = T.map (asCharUnsafe f)++instance StringSuperset LT.Text+ where+ isAsciiString = LT.all isAsciiChar+ fromCharList = LT.pack . fromCharList+ toCharListUnsafe = toCharListUnsafe . LT.unpack+ toCharListSub = toCharListSub . LT.unpack+ substituteString = LT.map substituteChar+ mapCharsUnsafe f = LT.map (asCharUnsafe f)++instance StringSuperset TB.Builder+ where+ isAsciiString = isAsciiString . TB.toLazyText+ fromCharList = TB.fromString . fromCharList+ toCharListUnsafe = toCharListUnsafe . TB.toLazyText+ toCharListSub = toCharListSub . TB.toLazyText+ substituteString = TB.fromLazyText . substituteString . TB.toLazyText+ mapCharsUnsafe f = TB.fromLazyText . mapCharsUnsafe f . TB.toLazyText++instance StringSuperset BS.ByteString+ where+ isAsciiString = BS.all isAsciiChar+ fromCharList = BS.pack . fromCharList+ toCharListUnsafe = toCharListUnsafe . BS.unpack+ toCharListSub = toCharListSub . BS.unpack+ substituteString = BS.map substituteChar+ mapCharsUnsafe f = BS.map (asCharUnsafe f)++instance StringSuperset LBS.ByteString+ where+ isAsciiString = LBS.all isAsciiChar+ fromCharList = LBS.pack . fromCharList+ toCharListUnsafe = toCharListUnsafe . LBS.unpack+ toCharListSub = toCharListSub . LBS.unpack+ substituteString = LBS.map substituteChar+ mapCharsUnsafe f = LBS.map (asCharUnsafe f)++instance StringSuperset BSB.Builder+ where+ isAsciiString = isAsciiString . BSB.toLazyByteString+ fromCharList = BSB.lazyByteString . fromCharList+ toCharListUnsafe = toCharListUnsafe . BSB.toLazyByteString+ toCharListSub = toCharListSub . BSB.toLazyByteString+ substituteString = BSB.lazyByteString . substituteString . BSB.toLazyByteString+ mapCharsUnsafe f = BSB.lazyByteString . mapCharsUnsafe f . BSB.toLazyByteString
+ ascii-superset.cabal view
@@ -0,0 +1,46 @@+cabal-version: 2.0++name: ascii-superset+version: 1.0.0.0+synopsis: Representing ASCII with refined supersets+category: Data, Text++description: This package defines classes which describe what subset of a type is valid as ASCII, as well as a type constructor representing a value of a superset that is known to be valid ASCII.++license: Apache-2.0+license-file: license.txt++author: Chris Martin+maintainer: Chris Martin, Julie Moronuki++homepage: https://github.com/typeclasses/ascii+bug-Reports: https://github.com/typeclasses/ascii/issues++build-type: Simple++tested-with: GHC == 8.8.1, GHC == 8.6.5, GHC == 8.4.3++source-repository head+ type: git+ location: git://github.com/typeclasses/ascii.git++library+ default-language: Haskell2010+ default-extensions: NoImplicitPrelude+ default-extensions: StandaloneDeriving+ default-extensions: DerivingStrategies+ default-extensions: GeneralizedNewtypeDeriving+ default-extensions: DeriveDataTypeable+ default-extensions: DeriveGeneric+ ghc-options: -Wall++ build-depends: base >= 4.11 && < 4.14+ build-depends: bytestring >= 0.10 && < 0.11+ build-depends: ascii-char >= 1.0 && < 1.1+ build-depends: hashable >= 1.2 && < 1.4+ build-depends: text >= 1.2 && < 1.3++ exposed-modules: ASCII.Superset+ exposed-modules: ASCII.Isomorphism+ exposed-modules: ASCII.Refinement+ exposed-modules: ASCII.Lift
+ license.txt view
@@ -0,0 +1,13 @@+Copyright 2020 Typeclass Consulting LLC++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++ http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.