trifecta-0.2: Text/Trifecta/It.hs
{-# LANGUAGE MultiParamTypeClasses, BangPatterns #-}
module Text.Trifecta.It
( It(..)
, getRope
, getMeasure
, getEof
, getWord8
, sliceIt
, slice
, sliced
) where
import Control.Applicative
import Control.Monad
import Control.Monad.Trans.Class
import Data.Semigroup
import Data.Monoid
import Data.Word
import Data.Interned
import Data.Foldable (toList)
import Data.FingerTree as FingerTree
import Data.ByteString as Strict
import Data.ByteString.Lazy as Lazy
import Data.Functor.Bind
import Data.Functor.Plus
import Text.Trifecta.Cursor
import Text.Trifecta.Hunk
import Text.Trifecta.Rope as Rope
import Text.Trifecta.Delta
import Text.Parsec.Prim hiding ((<|>))
data It a
= Done !Rope !Bool a
| Fail !Rope !Bool String
| Cont (Rope -> Bool -> It a)
instance Show a => Show (It a) where
showsPrec d (Done r b a) = showParen (d > 10) $
showString "Done " . showsPrec 11 r . showChar ' ' . showsPrec 11 b . showChar ' ' . showsPrec 11 a
showsPrec d (Fail r b s) = showParen (d > 10) $
showString "Fail " . showsPrec 11 r . showChar ' ' . showsPrec 11 b . showChar ' ' . showsPrec 11 s
showsPrec d (Cont _) = showParen (d > 10) $ showString "Cont ..."
instance Functor It where
fmap f (Done r b a) = Done r b (f a)
fmap _ (Fail r b s) = Fail r b s
fmap f (Cont k) = Cont (\r b -> fmap f (k r b))
instance Apply It where
(<.>) = (<*>)
instance Applicative It where
pure = Done mempty False
(<*>) = ap
instance Alt It where
Fail r b _ <!> Cont k = k r b
Fail r b _ <!> Done _ _ a = Done r b a
Fail r b _ <!> Fail _ _ s = Fail r b s
m <!> _ = m
instance Alternative It where
(<|>) = (<!>)
empty = fail "empty"
instance Bind It where
(>>-) = (>>=)
instance Monad It where
return = Done mempty False
Done (Rope _ t) False a >>= f | FingerTree.null t = f a
Done h e a >>= f = case f a of
Done _ _ b -> Done h e b
Fail _ _ s -> Fail h e s
Cont k -> k h e
Fail r b s >>= _ = Fail r b s
Cont k >>= f = Cont $ \h e -> k h e >>= f
fail = Fail mempty False
instance MonadPlus It where
mplus = (<!>)
mzero = fail "mzero"
getRope :: It Rope
getRope = Cont $ \r e -> Done r e r
getMeasure :: It Cursor
getMeasure = Cont $ \r e -> Done r e (measure r)
getEof :: It Bool
getEof = Cont $ \ r e -> Done r e e
instance Stream Cursor It Char where
uncons (Cursor b d _ _) = (getWord8 b >>= go) <|> return Nothing where
go c | c <= 0x7f = do
Cursor _ _ i t <- getMeasure
return $ Just (toEnum (fromEnum c), Cursor (b + 1) (d <> delta c) i t) -- TODO: add in PathHunks
| otherwise = error "TODO"
-- TODO: finish these
getWord8 :: Int -> It Word8
getWord8 n = Cont go where
go h eof
| n < Rope.lastNewline h eof = Done h eof $ indexByte n h
| eof = Fail h True "Unexpected EOF"
| otherwise = Cont $ \h' -> go (h <> h') -- h' <> h
sliceIt :: Int -> Int -> It Strict.ByteString
sliceIt !i !j = Cont go where
go :: Rope -> Bool -> It Strict.ByteString
go t@(Rope _ h) eof
| j <= cursorBytes (measure h) || eof = Done t eof (sliceStrands h)
| otherwise = Cont $ \t' eof' -> go (t <> t') eof'
sliceStrands :: FingerTree Cursor Strand -> Strict.ByteString
sliceStrands !t
| req <= rmn = Strict.take req first -- yay! sharing
| otherwise = Strict.concat
$ Lazy.toChunks
$ Lazy.take (fromIntegral req)
$ Lazy.fromChunks
$ first : Prelude.foldr iter [] (toList r')
where iter (HunkStrand h) b = unintern h : b
iter (PathStrand _) b = b
(l,r) = FingerTree.split (\m -> cursorBytes m > i) t
HunkStrand (Hunk _ _ a) :< r' = FingerTree.viewl r
first = Strict.drop (i - cursorBytes (measure l)) a
req = j - i
rmn = Strict.length first
slice :: Cursor -> Cursor -> ParsecT Cursor u It Strict.ByteString
slice mark release = lift $ sliceIt (cursorBytes mark) (cursorBytes release)
sliced :: ParsecT Cursor u It a -> ParsecT Cursor u It Strict.ByteString
sliced p = do
mark <- getInput
_ <- p
release <- getInput
slice mark release
-- sliceDelta :: Int -> Int -> It Delta