packages feed

sdp4text-0.2: src/SDP/Text/Lazy.hs

{-# LANGUAGE Safe, MagicHash, MultiParamTypeClasses, FlexibleInstances #-}

{- |
    Module      :  SDP.Text.Lazy
    Copyright   :  (c) Andrey Mulik 2020
    License     :  BSD-style
    Maintainer  :  work.a.mulik@gmail.com
    Portability :  non-portable (GHC extensions)
    
    "SDP.Text.Lazy" provides @sdp@ instances for lazy 'Text'.
-}
module SDP.Text.Lazy
(
  -- * Exports
  module System.IO.Classes,
  
  module SDP.IndexedM,
  
  -- * Lazy text
  LText, Text, L.toCaseFold, L.toLower, L.toUpper, L.toTitle,
  L.fromChunks, L.toChunks, L.toStrict, L.fromStrict,
  L.foldrChunks, L.foldlChunks
)
where

import Prelude ()
import SDP.SafePrelude
import SDP.IndexedM
import SDP.Text ()

import qualified Data.Text.Lazy.IO as IO
import qualified Data.Text.Lazy as L

import Data.Text.Lazy ( Text )
import Data.Maybe

import SDP.Templates.AnyChunks
import SDP.ByteList.IOUblist
import SDP.ByteList.STUblist

import Control.Exception.SDP

import System.IO.Classes

default ()

--------------------------------------------------------------------------------

-- | 'Text' alias, may reduce ambiguity.
type LText = Text

--------------------------------------------------------------------------------

{- Nullable and Estimate instances. -}

instance Nullable Text
  where
    isNull = L.null
    lzero  = L.empty

instance Estimate Text
  where
    {-# INLINE (<.=>) #-}
    xs <.=> n = xs `L.compareLength` fromIntegral n
    
    {-# INLINE (<==>) #-}
    xs <==> ys = xs `L.compareLength` L.length ys

--------------------------------------------------------------------------------

{- Bordered, Linear and Split instances. -}

instance Bordered Text Int
  where
    lower   _ = 0
    upper  ts = sizeOf ts - 1
    bounds ts = (0, sizeOf ts - 1)
    sizeOf    = fromIntegral . L.length

instance Linear Text Char
  where
    uncons' = L.uncons
    unsnoc' = L.unsnoc
    
    uncons = fromMaybe (pfailEx "(:>)") . L.uncons
    unsnoc = fromMaybe (pfailEx "(:<)") . L.unsnoc
    single = L.singleton
    toHead = L.cons
    toLast = L.snoc
    
    (++) = L.append
    head = L.head
    last = L.last
    tail = L.tail
    init = L.init
    
    (!^) es = L.index es . fromIntegral
    
    write es = (es //) . single ... (,)
    
    replicate n e = L.replicate (fromIntegral n) (L.singleton e)
    
    fromList = L.pack
    reverse  = L.reverse
    
    force = L.fromChunks . map force . L.toChunks
    listR = L.unpack . reverse
    listL = L.unpack
    
    concat = L.concat . toList
    filter = L.filter
    
    concatMap f = concat . foldr ((:) . f) []
    
    intersperse = L.intersperse
    partition   = L.partition
    
    ofoldr f =
      let go = \ ch (i, acc) -> (i + sizeOf ch, ofoldr (f . (+ i)) acc ch)
      in  snd ... L.foldrChunks go . (,) 0
    
    ofoldl f base text =
      let go = \ (i, acc) ch -> (i + sizeOf ch, ofoldl (f . (+ i)) acc ch)
      in  snd $ L.foldlChunks go (upper text, base) text
    
    o_foldl' = L.foldl'
    o_foldr  = L.foldr
    o_foldl  = L.foldl

instance Split Text Char
  where
    take   = L.take     . fromIntegral
    drop   = L.drop     . fromIntegral
    keep   = L.takeEnd  . fromIntegral
    sans   = L.dropEnd  . fromIntegral
    split  = L.splitAt  . fromIntegral
    chunks = L.chunksOf . fromIntegral
    
    replaceBy = L.replace
    splitsOn  = L.splitOn
    splitsBy  = L.split
    
    justifyL = L.justifyLeft  . fromIntegral
    justifyR = L.justifyRight . fromIntegral
    
    isPrefixOf = L.isPrefixOf
    isSuffixOf = L.isSuffixOf
    isInfixOf  = L.isInfixOf
    
    prefix p = L.foldr (\ e c -> p e ? c + 1 $ 0) 0
    suffix p = L.foldl (\ c e -> p e ? c + 1 $ 0) 0
    
    takeWhile = L.takeWhile
    dropWhile = L.dropWhile
    takeEnd   = L.takeWhileEnd
    dropEnd   = L.dropWhileEnd

--------------------------------------------------------------------------------

{- Map and Indexed instances. -}

instance Map Text Int Char
  where
    toMap ascs = isNull ascs ? Z $ assoc (l, u) ascs
      where
        l = fst $ minimumBy cmpfst ascs
        u = fst $ maximumBy cmpfst ascs
    
    toMap' defvalue ascs = isNull ascs ? Z $ assoc' (l, u) defvalue ascs
      where
        l = fst $ minimumBy cmpfst ascs
        u = fst $ maximumBy cmpfst ascs
    
    Z  // ascs = toMap ascs
    es // ascs = runST $ thaw es >>= (`overwrite` ascs) >>= done
    
    (.!) es = L.index es . fromIntegral
    
    kfoldr' = ofoldr'
    kfoldl' = ofoldl'
    kfoldr  = ofoldr
    kfoldl  = ofoldl

instance Indexed Text Int Char
  where
    assoc bnds ascs = runST $ fromAssocs bnds ascs >>= done
    
    assoc' bnds defvalue ascs = runST $ fromAssocs' bnds defvalue ascs >>= done
    
    fromIndexed es = runST $ fromIndexed' es >>= done

--------------------------------------------------------------------------------

{- Thaw and Freeze instances. -}

instance Thaw (ST s) Text (STUblist s Char)
  where
    unsafeThaw = fromChunksM <=< mapM unsafeThaw . L.toChunks
    thaw       = fromChunksM <=< mapM thaw       . L.toChunks

instance Freeze (ST s) (STUblist s Char) Text
  where
    unsafeFreeze = fmap L.fromChunks . mapM unsafeFreeze . toChunks
    freeze       = fmap L.fromChunks . mapM freeze       . toChunks

instance (MonadIO io) => Thaw io Text (MIOUblist io Char)
  where
    unsafeThaw = fromChunksM <=< mapM unsafeThaw . L.toChunks
    thaw       = fromChunksM <=< mapM thaw       . L.toChunks

instance (MonadIO io) => Freeze io (MIOUblist io Char) Text
  where
    unsafeFreeze = fmap L.fromChunks . mapM unsafeFreeze . toChunks
    freeze       = fmap L.fromChunks . mapM freeze       . toChunks

--------------------------------------------------------------------------------

{- IsFile and IsTextFile instances. -}

instance IsFile Text
  where
    hGetContents = liftIO  .  IO.hGetContents
    hPutContents = liftIO ... IO.hPutStr

instance IsTextFile Text
  where
    hPutStrLn = liftIO ... IO.hPutStrLn
    hGetLine  = liftIO  .  IO.hGetLine
    hPutStr   = liftIO ... IO.hPutStr

--------------------------------------------------------------------------------

done :: STUblist s Char -> ST s Text
done =  freeze

pfailEx :: String -> a
pfailEx =  throw . PatternMatchFail . showString "in SDP.Text.Lazy."