{-# LANGUAGE MultiParamTypeClasses #-} module Data.Rope.Unpackable ( Unpackable(..) ) where import Prelude hiding (head, last, drop) import qualified Prelude import Data.Word (Word8) import Data.Monoid (Monoid, mempty, mappend) import qualified Data.Foldable as F import qualified Data.FingerTree as F (empty, null, split) import Data.FingerTree (FingerTree, ViewL(..),ViewR(..),viewl,viewr,(<|),(><)) import qualified Data.ByteString as S import qualified Data.ByteString.Unsafe as S (unsafeTail, unsafeHead) import qualified Data.ByteString.UTF8 as U import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy.UTF8 as LU import Data.Rope.Body (Count(..), Chunk(..), cons', snoc', measureBody) -- Chunk import Data.Rope.Internal (Annotation(..), drop, Rope(..), body, toLazyByteString, uncons8, unsnoc8) -- Rope, etc. import Codec.Binary.UTF8.Generic (UTF8Bytes) import qualified Codec.Binary.UTF8.Generic as UTF8Bytes class Unpackable a where unpack :: Rope m -> [a] head :: Rope m -> a head = Prelude.head . unpack last :: Rope m -> a uncons :: Annotation m => Rope m -> Maybe (a, Rope m) unsnoc :: Annotation m => Rope m -> Maybe (Rope m, a) newtype F = F { runF :: FingerTree Count Chunk } instance Monoid F where mempty = F F.empty F a `mappend` F b = F (a >< b) instance UTF8Bytes F Int where bsplit 0 (F f) = (mempty, F f) bsplit n (F f) | n >= measureBody f = (F f, mempty) | otherwise = (F (x `snoc'` y'), F (y'' `cons'` z)) where (x, yz) = F.split (> Count n) f Chunk y :< z = viewl yz (y', y'') = S.splitAt (n - measureBody x) y bdrop n = snd . UTF8Bytes.bsplit n buncons f = case viewl (runF f) of Chunk c :< cs -> Just (S.unsafeHead c, F (S.unsafeTail c `cons' ` cs)) EmptyL -> Nothing tail (F f) = case viewl f of Chunk c :< cs -> F (S.unsafeTail c `cons'`cs) EmptyL -> errorEmptyList "tail" elemIndex b = fmap fromIntegral . L.elemIndex b . L.fromChunks . map unchunk . F.toList . runF pack = F . foldr (\l r -> Chunk l <| r) F.empty . L.toChunks . L.pack empty = F F.empty null = F.null . runF -- w2c :: Word8 -> Char -- w2c = unsafeChr . fromIntegral instance Unpackable Word8 where unpack = concatMap (S.unpack . unchunk) . F.toList . body head (Rope t _) = case viewl t of Chunk a :< _ -> S.head a EmptyL -> errorEmptyList "head" last (Rope t _) = case viewr t of _ :> Chunk a -> S.last a EmptyR -> errorEmptyList "last" uncons = uncons8 unsnoc = unsnoc8 instance Unpackable Char where unpack = LU.toString . toLazyByteString head = Prelude.head . unpack last = undefined -- TODO uncons r@(Rope t _) = case UTF8Bytes.decode (F t) of Nothing -> Nothing Just (a,n) -> Just (a, drop n r) unsnoc = undefined -- TODO instance Unpackable S.ByteString where unpack = map unchunk . F.toList . body head r = case viewl (body r) of Chunk a :< _ -> a _ -> errorEmptyList "head" last r = case viewr (body r) of _ :> Chunk a -> a _ -> errorEmptyList "last" uncons r = case viewl (body r) of Chunk a :< as -> Just (a, Rope as (drop' (S.length a) r)) EmptyL -> Nothing unsnoc r = case viewr (body r) of as :> Chunk a -> Just (Rope as (take' (measureBody as) r), a) EmptyR -> Nothing instance Unpackable Chunk where unpack = F.toList . body head r = case viewl (body r) of a :< _ -> a _ -> errorEmptyList "head" last r = case viewr (body r) of _ :> a -> a _ -> errorEmptyList "last" uncons r = case viewl (body r) of Chunk a :< as -> Just (Chunk a, Rope as (drop' (S.length a) r)) EmptyL -> Nothing unsnoc r = case viewr (body r) of as :> Chunk a -> Just (Rope as (take' (measureBody as) r), Chunk a) EmptyR -> Nothing errorEmptyList :: String -> a errorEmptyList t = error $ "Kata.Rope.Unpackable." ++ t ++ ": empty list"