{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE BangPatterns, MagicHash, CPP #-}
#if __GLASGOW_HASKELL__ >= 702
{-# LANGUAGE Trustworthy #-}
#endif
module Data.Text.Lazy
(
Text
, pack
, unpack
, singleton
, empty
, fromChunks
, toChunks
, toStrict
, fromStrict
, foldrChunks
, foldlChunks
, cons
, snoc
, append
, uncons
, head
, last
, tail
, init
, null
, length
, compareLength
, map
, intercalate
, intersperse
, transpose
, reverse
, replace
, toCaseFold
, toLower
, toUpper
, toTitle
, justifyLeft
, justifyRight
, center
, foldl
, foldl'
, foldl1
, foldl1'
, foldr
, foldr1
, concat
, concatMap
, any
, all
, maximum
, minimum
, scanl
, scanl1
, scanr
, scanr1
, mapAccumL
, mapAccumR
, replicate
, unfoldr
, unfoldrN
, take
, takeEnd
, drop
, dropEnd
, takeWhile
, dropWhile
, dropWhileEnd
, dropAround
, strip
, stripStart
, stripEnd
, splitAt
, span
, breakOn
, breakOnEnd
, break
, group
, groupBy
, inits
, tails
, splitOn
, split
, chunksOf
, lines
, words
, unlines
, unwords
, isPrefixOf
, isSuffixOf
, isInfixOf
, stripPrefix
, stripSuffix
, commonPrefixes
, filter
, find
, breakOnAll
, partition
, index
, count
, zip
, zipWith
) where
import Prelude (Char, Bool(..), Maybe(..), String,
Eq(..), Ord(..), Ordering(..), Read(..), Show(..),
(&&), (||), (+), (-), (.), ($), (++),
error, flip, fmap, fromIntegral, not, otherwise, quot)
import qualified Prelude as P
#if defined(HAVE_DEEPSEQ)
import Control.DeepSeq (NFData(..))
#endif
import Data.Int (Int64)
import qualified Data.List as L
import Data.Char (isSpace)
import Data.Data (Data(gfoldl, toConstr, gunfold, dataTypeOf))
import Data.Data (mkNoRepType)
import Data.Monoid (Monoid(..))
import Data.String (IsString(..))
import qualified Data.Text as T
import qualified Data.Text.Internal as T
import qualified Data.Text.Internal.Fusion.Common as S
import qualified Data.Text.Unsafe as T
import qualified Data.Text.Internal.Lazy.Fusion as S
import Data.Text.Internal.Fusion.Types (PairS(..))
import Data.Text.Internal.Lazy.Fusion (stream, unstream)
import Data.Text.Internal.Lazy (Text(..), chunk, empty, foldlChunks, foldrChunks)
import Data.Text.Internal (firstf, safe, text)
import qualified Data.Text.Internal.Functions as F
import Data.Text.Internal.Lazy.Search (indices)
#if __GLASGOW_HASKELL__ >= 702
import qualified GHC.CString as GHC
#else
import qualified GHC.Base as GHC
#endif
import GHC.Prim (Addr#)
equal :: Text -> Text -> Bool
equal Empty Empty = True
equal Empty _ = False
equal _ Empty = False
equal (Chunk a as) (Chunk b bs) =
case compare lenA lenB of
LT -> a == (T.takeWord16 lenA b) &&
as `equal` Chunk (T.dropWord16 lenA b) bs
EQ -> a == b && as `equal` bs
GT -> T.takeWord16 lenB a == b &&
Chunk (T.dropWord16 lenB a) as `equal` bs
where lenA = T.lengthWord16 a
lenB = T.lengthWord16 b
instance Eq Text where
(==) = equal
{-# INLINE (==) #-}
instance Ord Text where
compare = compareText
compareText :: Text -> Text -> Ordering
compareText Empty Empty = EQ
compareText Empty _ = LT
compareText _ Empty = GT
compareText (Chunk a0 as) (Chunk b0 bs) = outer a0 b0
where
outer ta@(T.Text arrA offA lenA) tb@(T.Text arrB offB lenB) = go 0 0
where
go !i !j
| i >= lenA = compareText as (chunk (T.Text arrB (offB+j) (lenB-j)) bs)
| j >= lenB = compareText (chunk (T.Text arrA (offA+i) (lenA-i)) as) bs
| a < b = LT
| a > b = GT
| otherwise = go (i+di) (j+dj)
where T.Iter a di = T.iter ta i
T.Iter b dj = T.iter tb j
instance Show Text where
showsPrec p ps r = showsPrec p (unpack ps) r
instance Read Text where
readsPrec p str = [(pack x,y) | (x,y) <- readsPrec p str]
instance Monoid Text where
mempty = empty
mappend = append
mconcat = concat
instance IsString Text where
fromString = pack
#if defined(HAVE_DEEPSEQ)
instance NFData Text where
rnf Empty = ()
rnf (Chunk _ ts) = rnf ts
#endif
instance Data Text where
gfoldl f z txt = z pack `f` (unpack txt)
toConstr _ = error "Data.Text.Lazy.Text.toConstr"
gunfold _ _ = error "Data.Text.Lazy.Text.gunfold"
dataTypeOf _ = mkNoRepType "Data.Text.Lazy.Text"
pack :: String -> Text
pack = unstream . S.streamList . L.map safe
{-# INLINE [1] pack #-}
unpack :: Text -> String
unpack t = S.unstreamList (stream t)
{-# INLINE [1] unpack #-}
unpackCString# :: Addr# -> Text
unpackCString# addr# = unstream (S.streamCString# addr#)
{-# NOINLINE unpackCString# #-}
{-# RULES "TEXT literal" forall a.
unstream (S.streamList (L.map safe (GHC.unpackCString# a)))
= unpackCString# a #-}
{-# RULES "TEXT literal UTF8" forall a.
unstream (S.streamList (L.map safe (GHC.unpackCStringUtf8# a)))
= unpackCString# a #-}
singleton :: Char -> Text
singleton c = Chunk (T.singleton c) Empty
{-# INLINE [1] singleton #-}
{-# RULES
"LAZY TEXT singleton -> fused" [~1] forall c.
singleton c = unstream (S.singleton c)
"LAZY TEXT singleton -> unfused" [1] forall c.
unstream (S.singleton c) = singleton c
#-}
fromChunks :: [T.Text] -> Text
fromChunks cs = L.foldr chunk Empty cs
toChunks :: Text -> [T.Text]
toChunks cs = foldrChunks (:) [] cs
toStrict :: Text -> T.Text
toStrict t = T.concat (toChunks t)
{-# INLINE [1] toStrict #-}
fromStrict :: T.Text -> Text
fromStrict t = chunk t Empty
{-# INLINE [1] fromStrict #-}
cons :: Char -> Text -> Text
cons c t = Chunk (T.singleton c) t
{-# INLINE [1] cons #-}
infixr 5 `cons`
{-# RULES
"LAZY TEXT cons -> fused" [~1] forall c t.
cons c t = unstream (S.cons c (stream t))
"LAZY TEXT cons -> unfused" [1] forall c t.
unstream (S.cons c (stream t)) = cons c t
#-}
snoc :: Text -> Char -> Text
snoc t c = foldrChunks Chunk (singleton c) t
{-# INLINE [1] snoc #-}
{-# RULES
"LAZY TEXT snoc -> fused" [~1] forall t c.
snoc t c = unstream (S.snoc (stream t) c)
"LAZY TEXT snoc -> unfused" [1] forall t c.
unstream (S.snoc (stream t) c) = snoc t c
#-}
append :: Text -> Text -> Text
append xs ys = foldrChunks Chunk ys xs
{-# INLINE [1] append #-}
{-# RULES
"LAZY TEXT append -> fused" [~1] forall t1 t2.
append t1 t2 = unstream (S.append (stream t1) (stream t2))
"LAZY TEXT append -> unfused" [1] forall t1 t2.
unstream (S.append (stream t1) (stream t2)) = append t1 t2
#-}
uncons :: Text -> Maybe (Char, Text)
uncons Empty = Nothing
uncons (Chunk t ts) = Just (T.unsafeHead t, ts')
where ts' | T.compareLength t 1 == EQ = ts
| otherwise = Chunk (T.unsafeTail t) ts
{-# INLINE uncons #-}
head :: Text -> Char
head t = S.head (stream t)
{-# INLINE head #-}
tail :: Text -> Text
tail (Chunk t ts) = chunk (T.tail t) ts
tail Empty = emptyError "tail"
{-# INLINE [1] tail #-}
{-# RULES
"LAZY TEXT tail -> fused" [~1] forall t.
tail t = unstream (S.tail (stream t))
"LAZY TEXT tail -> unfused" [1] forall t.
unstream (S.tail (stream t)) = tail t
#-}
init :: Text -> Text
init (Chunk t0 ts0) = go t0 ts0
where go t (Chunk t' ts) = Chunk t (go t' ts)
go t Empty = chunk (T.init t) Empty
init Empty = emptyError "init"
{-# INLINE [1] init #-}
{-# RULES
"LAZY TEXT init -> fused" [~1] forall t.
init t = unstream (S.init (stream t))
"LAZY TEXT init -> unfused" [1] forall t.
unstream (S.init (stream t)) = init t
#-}
null :: Text -> Bool
null Empty = True
null _ = False
{-# INLINE [1] null #-}
{-# RULES
"LAZY TEXT null -> fused" [~1] forall t.
null t = S.null (stream t)
"LAZY TEXT null -> unfused" [1] forall t.
S.null (stream t) = null t
#-}
isSingleton :: Text -> Bool
isSingleton = S.isSingleton . stream
{-# INLINE isSingleton #-}
last :: Text -> Char
last Empty = emptyError "last"
last (Chunk t ts) = go t ts
where go _ (Chunk t' ts') = go t' ts'
go t' Empty = T.last t'
{-# INLINE [1] last #-}
{-# RULES
"LAZY TEXT last -> fused" [~1] forall t.
last t = S.last (stream t)
"LAZY TEXT last -> unfused" [1] forall t.
S.last (stream t) = last t
#-}
length :: Text -> Int64
length = foldlChunks go 0
where go l t = l + fromIntegral (T.length t)
{-# INLINE [1] length #-}
{-# RULES
"LAZY TEXT length -> fused" [~1] forall t.
length t = S.length (stream t)
"LAZY TEXT length -> unfused" [1] forall t.
S.length (stream t) = length t
#-}
compareLength :: Text -> Int64 -> Ordering
compareLength t n = S.compareLengthI (stream t) n
{-# INLINE [1] compareLength #-}
map :: (Char -> Char) -> Text -> Text
map f t = unstream (S.map (safe . f) (stream t))
{-# INLINE [1] map #-}
intercalate :: Text -> [Text] -> Text
intercalate t = concat . (F.intersperse t)
{-# INLINE intercalate #-}
intersperse :: Char -> Text -> Text
intersperse c t = unstream (S.intersperse (safe c) (stream t))
{-# INLINE intersperse #-}
justifyLeft :: Int64 -> Char -> Text -> Text
justifyLeft k c t
| len >= k = t
| otherwise = t `append` replicateChar (k-len) c
where len = length t
{-# INLINE [1] justifyLeft #-}
{-# RULES
"LAZY TEXT justifyLeft -> fused" [~1] forall k c t.
justifyLeft k c t = unstream (S.justifyLeftI k c (stream t))
"LAZY TEXT justifyLeft -> unfused" [1] forall k c t.
unstream (S.justifyLeftI k c (stream t)) = justifyLeft k c t
#-}
justifyRight :: Int64 -> Char -> Text -> Text
justifyRight k c t
| len >= k = t
| otherwise = replicateChar (k-len) c `append` t
where len = length t
{-# INLINE justifyRight #-}
center :: Int64 -> Char -> Text -> Text
center k c t
| len >= k = t
| otherwise = replicateChar l c `append` t `append` replicateChar r c
where len = length t
d = k - len
r = d `quot` 2
l = d - r
{-# INLINE center #-}
transpose :: [Text] -> [Text]
transpose ts = L.map (\ss -> Chunk (T.pack ss) Empty)
(L.transpose (L.map unpack ts))
reverse :: Text -> Text
reverse = rev Empty
where rev a Empty = a
rev a (Chunk t ts) = rev (Chunk (T.reverse t) a) ts
replace :: Text
-> Text
-> Text
-> Text
replace s d = intercalate d . splitOn s
{-# INLINE replace #-}
toCaseFold :: Text -> Text
toCaseFold t = unstream (S.toCaseFold (stream t))
{-# INLINE [0] toCaseFold #-}
toLower :: Text -> Text
toLower t = unstream (S.toLower (stream t))
{-# INLINE toLower #-}
toUpper :: Text -> Text
toUpper t = unstream (S.toUpper (stream t))
{-# INLINE toUpper #-}
toTitle :: Text -> Text
toTitle t = unstream (S.toTitle (stream t))
{-# INLINE toTitle #-}
foldl :: (a -> Char -> a) -> a -> Text -> a
foldl f z t = S.foldl f z (stream t)
{-# INLINE foldl #-}
foldl' :: (a -> Char -> a) -> a -> Text -> a
foldl' f z t = S.foldl' f z (stream t)
{-# INLINE foldl' #-}
foldl1 :: (Char -> Char -> Char) -> Text -> Char
foldl1 f t = S.foldl1 f (stream t)
{-# INLINE foldl1 #-}
foldl1' :: (Char -> Char -> Char) -> Text -> Char
foldl1' f t = S.foldl1' f (stream t)
{-# INLINE foldl1' #-}
foldr :: (Char -> a -> a) -> a -> Text -> a
foldr f z t = S.foldr f z (stream t)
{-# INLINE foldr #-}
foldr1 :: (Char -> Char -> Char) -> Text -> Char
foldr1 f t = S.foldr1 f (stream t)
{-# INLINE foldr1 #-}
concat :: [Text] -> Text
concat = to
where
go Empty css = to css
go (Chunk c cs) css = Chunk c (go cs css)
to [] = Empty
to (cs:css) = go cs css
{-# INLINE concat #-}
concatMap :: (Char -> Text) -> Text -> Text
concatMap f = concat . foldr ((:) . f) []
{-# INLINE concatMap #-}
any :: (Char -> Bool) -> Text -> Bool
any p t = S.any p (stream t)
{-# INLINE any #-}
all :: (Char -> Bool) -> Text -> Bool
all p t = S.all p (stream t)
{-# INLINE all #-}
maximum :: Text -> Char
maximum t = S.maximum (stream t)
{-# INLINE maximum #-}
minimum :: Text -> Char
minimum t = S.minimum (stream t)
{-# INLINE minimum #-}
scanl :: (Char -> Char -> Char) -> Char -> Text -> Text
scanl f z t = unstream (S.scanl g z (stream t))
where g a b = safe (f a b)
{-# INLINE scanl #-}
scanl1 :: (Char -> Char -> Char) -> Text -> Text
scanl1 f t0 = case uncons t0 of
Nothing -> empty
Just (t,ts) -> scanl f t ts
{-# INLINE scanl1 #-}
scanr :: (Char -> Char -> Char) -> Char -> Text -> Text
scanr f v = reverse . scanl g v . reverse
where g a b = safe (f b a)
scanr1 :: (Char -> Char -> Char) -> Text -> Text
scanr1 f t | null t = empty
| otherwise = scanr f (last t) (init t)
mapAccumL :: (a -> Char -> (a,Char)) -> a -> Text -> (a, Text)
mapAccumL f = go
where
go z (Chunk c cs) = (z'', Chunk c' cs')
where (z', c') = T.mapAccumL f z c
(z'', cs') = go z' cs
go z Empty = (z, Empty)
{-# INLINE mapAccumL #-}
mapAccumR :: (a -> Char -> (a,Char)) -> a -> Text -> (a, Text)
mapAccumR f = go
where
go z (Chunk c cs) = (z'', Chunk c' cs')
where (z'', c') = T.mapAccumR f z' c
(z', cs') = go z cs
go z Empty = (z, Empty)
{-# INLINE mapAccumR #-}
replicate :: Int64 -> Text -> Text
replicate n t
| null t || n <= 0 = empty
| isSingleton t = replicateChar n (head t)
| otherwise = concat (rep 0)
where rep !i | i >= n = []
| otherwise = t : rep (i+1)
{-# INLINE [1] replicate #-}
replicateChar :: Int64 -> Char -> Text
replicateChar n c = unstream (S.replicateCharI n (safe c))
{-# INLINE replicateChar #-}
{-# RULES
"LAZY TEXT replicate/singleton -> replicateChar" [~1] forall n c.
replicate n (singleton c) = replicateChar n c
#-}
unfoldr :: (a -> Maybe (Char,a)) -> a -> Text
unfoldr f s = unstream (S.unfoldr (firstf safe . f) s)
{-# INLINE unfoldr #-}
unfoldrN :: Int64 -> (a -> Maybe (Char,a)) -> a -> Text
unfoldrN n f s = unstream (S.unfoldrN n (firstf safe . f) s)
{-# INLINE unfoldrN #-}
take :: Int64 -> Text -> Text
take i _ | i <= 0 = Empty
take i t0 = take' i t0
where take' 0 _ = Empty
take' _ Empty = Empty
take' n (Chunk t ts)
| n < len = Chunk (T.take (fromIntegral n) t) Empty
| otherwise = Chunk t (take' (n - len) ts)
where len = fromIntegral (T.length t)
{-# INLINE [1] take #-}
{-# RULES
"LAZY TEXT take -> fused" [~1] forall n t.
take n t = unstream (S.take n (stream t))
"LAZY TEXT take -> unfused" [1] forall n t.
unstream (S.take n (stream t)) = take n t
#-}
takeEnd :: Int64 -> Text -> Text
takeEnd n t0
| n <= 0 = empty
| otherwise = takeChunk n empty . L.reverse . toChunks $ t0
where takeChunk _ acc [] = acc
takeChunk i acc (t:ts)
| i <= l = chunk (T.takeEnd (fromIntegral i) t) acc
| otherwise = takeChunk (i-l) (Chunk t acc) ts
where l = fromIntegral (T.length t)
drop :: Int64 -> Text -> Text
drop i t0
| i <= 0 = t0
| otherwise = drop' i t0
where drop' 0 ts = ts
drop' _ Empty = Empty
drop' n (Chunk t ts)
| n < len = Chunk (T.drop (fromIntegral n) t) ts
| otherwise = drop' (n - len) ts
where len = fromIntegral (T.length t)
{-# INLINE [1] drop #-}
{-# RULES
"LAZY TEXT drop -> fused" [~1] forall n t.
drop n t = unstream (S.drop n (stream t))
"LAZY TEXT drop -> unfused" [1] forall n t.
unstream (S.drop n (stream t)) = drop n t
#-}
dropEnd :: Int64 -> Text -> Text
dropEnd n t0
| n <= 0 = t0
| otherwise = dropChunk n . L.reverse . toChunks $ t0
where dropChunk _ [] = empty
dropChunk m (t:ts)
| m >= l = dropChunk (m-l) ts
| otherwise = fromChunks . L.reverse $
T.dropEnd (fromIntegral m) t : ts
where l = fromIntegral (T.length t)
dropWords :: Int64 -> Text -> Text
dropWords i t0
| i <= 0 = t0
| otherwise = drop' i t0
where drop' 0 ts = ts
drop' _ Empty = Empty
drop' n (Chunk (T.Text arr off len) ts)
| n < len' = chunk (text arr (off+n') (len-n')) ts
| otherwise = drop' (n - len') ts
where len' = fromIntegral len
n' = fromIntegral n
takeWhile :: (Char -> Bool) -> Text -> Text
takeWhile p t0 = takeWhile' t0
where takeWhile' Empty = Empty
takeWhile' (Chunk t ts) =
case T.findIndex (not . p) t of
Just n | n > 0 -> Chunk (T.take n t) Empty
| otherwise -> Empty
Nothing -> Chunk t (takeWhile' ts)
{-# INLINE [1] takeWhile #-}
{-# RULES
"LAZY TEXT takeWhile -> fused" [~1] forall p t.
takeWhile p t = unstream (S.takeWhile p (stream t))
"LAZY TEXT takeWhile -> unfused" [1] forall p t.
unstream (S.takeWhile p (stream t)) = takeWhile p t
#-}
dropWhile :: (Char -> Bool) -> Text -> Text
dropWhile p t0 = dropWhile' t0
where dropWhile' Empty = Empty
dropWhile' (Chunk t ts) =
case T.findIndex (not . p) t of
Just n -> Chunk (T.drop n t) ts
Nothing -> dropWhile' ts
{-# INLINE [1] dropWhile #-}
{-# RULES
"LAZY TEXT dropWhile -> fused" [~1] forall p t.
dropWhile p t = unstream (S.dropWhile p (stream t))
"LAZY TEXT dropWhile -> unfused" [1] forall p t.
unstream (S.dropWhile p (stream t)) = dropWhile p t
#-}
dropWhileEnd :: (Char -> Bool) -> Text -> Text
dropWhileEnd p = go
where go Empty = Empty
go (Chunk t Empty) = if T.null t'
then Empty
else Chunk t' Empty
where t' = T.dropWhileEnd p t
go (Chunk t ts) = case go ts of
Empty -> go (Chunk t Empty)
ts' -> Chunk t ts'
{-# INLINE dropWhileEnd #-}
dropAround :: (Char -> Bool) -> Text -> Text
dropAround p = dropWhile p . dropWhileEnd p
{-# INLINE [1] dropAround #-}
stripStart :: Text -> Text
stripStart = dropWhile isSpace
{-# INLINE [1] stripStart #-}
stripEnd :: Text -> Text
stripEnd = dropWhileEnd isSpace
{-# INLINE [1] stripEnd #-}
strip :: Text -> Text
strip = dropAround isSpace
{-# INLINE [1] strip #-}
splitAt :: Int64 -> Text -> (Text, Text)
splitAt = loop
where loop _ Empty = (empty, empty)
loop n t | n <= 0 = (empty, t)
loop n (Chunk t ts)
| n < len = let (t',t'') = T.splitAt (fromIntegral n) t
in (Chunk t' Empty, Chunk t'' ts)
| otherwise = let (ts',ts'') = loop (n - len) ts
in (Chunk t ts', ts'')
where len = fromIntegral (T.length t)
splitAtWord :: Int64 -> Text -> PairS Text Text
splitAtWord _ Empty = empty :*: empty
splitAtWord x (Chunk c@(T.Text arr off len) cs)
| y >= len = let h :*: t = splitAtWord (x-fromIntegral len) cs
in Chunk c h :*: t
| otherwise = chunk (text arr off y) empty :*:
chunk (text arr (off+y) (len-y)) cs
where y = fromIntegral x
breakOn :: Text -> Text -> (Text, Text)
breakOn pat src
| null pat = emptyError "breakOn"
| otherwise = case indices pat src of
[] -> (src, empty)
(x:_) -> let h :*: t = splitAtWord x src
in (h, t)
breakOnEnd :: Text -> Text -> (Text, Text)
breakOnEnd pat src = let (a,b) = breakOn (reverse pat) (reverse src)
in (reverse b, reverse a)
{-# INLINE breakOnEnd #-}
breakOnAll :: Text
-> Text
-> [(Text, Text)]
breakOnAll pat src
| null pat = emptyError "breakOnAll"
| otherwise = go 0 empty src (indices pat src)
where
go !n p s (x:xs) = let h :*: t = splitAtWord (x-n) s
h' = append p h
in (h',t) : go x h' t xs
go _ _ _ _ = []
break :: (Char -> Bool) -> Text -> (Text, Text)
break p t0 = break' t0
where break' Empty = (empty, empty)
break' c@(Chunk t ts) =
case T.findIndex p t of
Nothing -> let (ts', ts'') = break' ts
in (Chunk t ts', ts'')
Just n | n == 0 -> (Empty, c)
| otherwise -> let (a,b) = T.splitAt n t
in (Chunk a Empty, Chunk b ts)
span :: (Char -> Bool) -> Text -> (Text, Text)
span p = break (not . p)
{-# INLINE span #-}
group :: Text -> [Text]
group = groupBy (==)
{-# INLINE group #-}
groupBy :: (Char -> Char -> Bool) -> Text -> [Text]
groupBy _ Empty = []
groupBy eq (Chunk t ts) = cons x ys : groupBy eq zs
where (ys,zs) = span (eq x) xs
x = T.unsafeHead t
xs = chunk (T.unsafeTail t) ts
inits :: Text -> [Text]
inits = (Empty :) . inits'
where inits' Empty = []
inits' (Chunk t ts) = L.map (\t' -> Chunk t' Empty) (L.tail (T.inits t))
++ L.map (Chunk t) (inits' ts)
tails :: Text -> [Text]
tails Empty = Empty : []
tails ts@(Chunk t ts')
| T.length t == 1 = ts : tails ts'
| otherwise = ts : tails (Chunk (T.unsafeTail t) ts')
splitOn :: Text
-> Text
-> [Text]
splitOn pat src
| null pat = emptyError "splitOn"
| isSingleton pat = split (== head pat) src
| otherwise = go 0 (indices pat src) src
where
go _ [] cs = [cs]
go !i (x:xs) cs = let h :*: t = splitAtWord (x-i) cs
in h : go (x+l) xs (dropWords l t)
l = foldlChunks (\a (T.Text _ _ b) -> a + fromIntegral b) 0 pat
{-# INLINE [1] splitOn #-}
{-# RULES
"LAZY TEXT splitOn/singleton -> split/==" [~1] forall c t.
splitOn (singleton c) t = split (==c) t
#-}
split :: (Char -> Bool) -> Text -> [Text]
split _ Empty = [Empty]
split p (Chunk t0 ts0) = comb [] (T.split p t0) ts0
where comb acc (s:[]) Empty = revChunks (s:acc) : []
comb acc (s:[]) (Chunk t ts) = comb (s:acc) (T.split p t) ts
comb acc (s:ss) ts = revChunks (s:acc) : comb [] ss ts
comb _ [] _ = impossibleError "split"
{-# INLINE split #-}
chunksOf :: Int64 -> Text -> [Text]
chunksOf k = go
where
go t = case splitAt k t of
(a,b) | null a -> []
| otherwise -> a : go b
{-# INLINE chunksOf #-}
lines :: Text -> [Text]
lines Empty = []
lines t = let (l,t') = break ((==) '\n') t
in l : if null t' then []
else lines (tail t')
words :: Text -> [Text]
words = L.filter (not . null) . split isSpace
{-# INLINE words #-}
unlines :: [Text] -> Text
unlines = concat . L.map (`snoc` '\n')
{-# INLINE unlines #-}
unwords :: [Text] -> Text
unwords = intercalate (singleton ' ')
{-# INLINE unwords #-}
isPrefixOf :: Text -> Text -> Bool
isPrefixOf Empty _ = True
isPrefixOf _ Empty = False
isPrefixOf (Chunk x xs) (Chunk y ys)
| lx == ly = x == y && isPrefixOf xs ys
| lx < ly = x == yh && isPrefixOf xs (Chunk yt ys)
| otherwise = xh == y && isPrefixOf (Chunk xt xs) ys
where (xh,xt) = T.splitAt ly x
(yh,yt) = T.splitAt lx y
lx = T.length x
ly = T.length y
{-# INLINE [1] isPrefixOf #-}
{-# RULES
"LAZY TEXT isPrefixOf -> fused" [~1] forall s t.
isPrefixOf s t = S.isPrefixOf (stream s) (stream t)
"LAZY TEXT isPrefixOf -> unfused" [1] forall s t.
S.isPrefixOf (stream s) (stream t) = isPrefixOf s t
#-}
isSuffixOf :: Text -> Text -> Bool
isSuffixOf x y = reverse x `isPrefixOf` reverse y
{-# INLINE isSuffixOf #-}
isInfixOf :: Text -> Text -> Bool
isInfixOf needle haystack
| null needle = True
| isSingleton needle = S.elem (head needle) . S.stream $ haystack
| otherwise = not . L.null . indices needle $ haystack
{-# INLINE [1] isInfixOf #-}
{-# RULES
"LAZY TEXT isInfixOf/singleton -> S.elem/S.stream" [~1] forall n h.
isInfixOf (singleton n) h = S.elem n (S.stream h)
#-}
stripPrefix :: Text -> Text -> Maybe Text
stripPrefix p t
| null p = Just t
| otherwise = case commonPrefixes p t of
Just (_,c,r) | null c -> Just r
_ -> Nothing
commonPrefixes :: Text -> Text -> Maybe (Text,Text,Text)
commonPrefixes Empty _ = Nothing
commonPrefixes _ Empty = Nothing
commonPrefixes a0 b0 = Just (go a0 b0 [])
where
go t0@(Chunk x xs) t1@(Chunk y ys) ps
= case T.commonPrefixes x y of
Just (p,a,b)
| T.null a -> go xs (chunk b ys) (p:ps)
| T.null b -> go (chunk a xs) ys (p:ps)
| otherwise -> (fromChunks (L.reverse (p:ps)),chunk a xs, chunk b ys)
Nothing -> (fromChunks (L.reverse ps),t0,t1)
go t0 t1 ps = (fromChunks (L.reverse ps),t0,t1)
stripSuffix :: Text -> Text -> Maybe Text
stripSuffix p t = reverse `fmap` stripPrefix (reverse p) (reverse t)
filter :: (Char -> Bool) -> Text -> Text
filter p t = unstream (S.filter p (stream t))
{-# INLINE filter #-}
find :: (Char -> Bool) -> Text -> Maybe Char
find p t = S.findBy p (stream t)
{-# INLINE find #-}
partition :: (Char -> Bool) -> Text -> (Text, Text)
partition p t = (filter p t, filter (not . p) t)
{-# INLINE partition #-}
index :: Text -> Int64 -> Char
index t n = S.index (stream t) n
{-# INLINE index #-}
count :: Text -> Text -> Int64
count pat src
| null pat = emptyError "count"
| otherwise = go 0 (indices pat src)
where go !n [] = n
go !n (_:xs) = go (n+1) xs
{-# INLINE [1] count #-}
{-# RULES
"LAZY TEXT count/singleton -> countChar" [~1] forall c t.
count (singleton c) t = countChar c t
#-}
countChar :: Char -> Text -> Int64
countChar c t = S.countChar c (stream t)
zip :: Text -> Text -> [(Char,Char)]
zip a b = S.unstreamList $ S.zipWith (,) (stream a) (stream b)
{-# INLINE [0] zip #-}
zipWith :: (Char -> Char -> Char) -> Text -> Text -> Text
zipWith f t1 t2 = unstream (S.zipWith g (stream t1) (stream t2))
where g a b = safe (f a b)
{-# INLINE [0] zipWith #-}
revChunks :: [T.Text] -> Text
revChunks = L.foldl' (flip chunk) Empty
emptyError :: String -> a
emptyError fun = P.error ("Data.Text.Lazy." ++ fun ++ ": empty input")
impossibleError :: String -> a
impossibleError fun = P.error ("Data.Text.Lazy." ++ fun ++ ": impossible case")