packages feed

trifecta-0.13: Text/Trifecta/Parser/It.hs

{-# LANGUAGE MultiParamTypeClasses, BangPatterns, MagicHash, UnboxedTuples #-}
module Text.Trifecta.Parser.It 
  ( It(Pure, It, result)
  , needIt
  , wantIt
  , fillIt
  , lineIt
  , sliceIt
  , stepIt
  ) where

import Control.Applicative
import Control.Comonad
import Control.Monad
import Data.Monoid
import Data.ByteString as Strict
import Data.ByteString.Lazy as Lazy
import Data.Functor.Bind
import Data.Functor.Plus
import Text.Trifecta.Rope as Rope
import Text.Trifecta.Delta
import Text.Trifecta.Bytes
import Text.Trifecta.Util.MaybePair
import Text.Trifecta.Parser.Step

data It a
  = Pure { result :: a } 
  | It { result :: a, _it :: Rope -> It a }

instance Functor It where
  fmap f (Pure a) = Pure (f a)
  fmap f (It a k) = It (f a) (fmap f . k)

instance Applicative It where
  pure = Pure
  Pure f  <*> Pure a  = Pure (f a)
  Pure f  <*> It a ka = It (f a) (fmap f . ka)
  It f kf <*> Pure a  = It (f a) (fmap ($a) . kf)
  It f kf <*> It a ka = It (f a) (\r -> kf r <*> ka r)

instance Monad It where
  return = Pure
  Pure a >>= f = f a
  It a k >>= f = It (result (f a)) (k >=> f)

instance Apply It where (<.>) = (<*>) 
instance Bind It where (>>-) = (>>=) 

instance Extend It where
  duplicate p@Pure{} = Pure p
  duplicate p@(It _ k) = It p (duplicate . k)

  extend f p@Pure{} = Pure (f p)
  extend f p@(It _ k) = It (f p) (extend f . k)

instance Comonad It where
  extract = result

needIt :: a -> (Rope -> Maybe a) -> It a
needIt z f = k where 
  k = It z $ \r -> case f r of 
    Just a -> Pure a
    Nothing -> k

wantIt :: a -> (Rope -> (# Bool, a #)) -> It a
wantIt z f = It z k where 
  k r = case f r of
    (# False, a #) -> It a k
    (# True,  a #) -> Pure a

-- given a position, go there, and grab the text forward from that point
fillIt :: Delta -> It (MaybePair Delta Strict.ByteString)
fillIt n = wantIt NothingPair $ \r -> 
  (# bytes n < bytes (rewind (delta r))
  ,  grabLine n r NothingPair JustPair #) 
                                       
-- return the text of the line that contains a given position
lineIt :: Delta -> It (Maybe Strict.ByteString)
lineIt n = wantIt Nothing $ \r -> 
  (# bytes n < bytes (rewind (delta r))
  ,  grabLine n r Nothing (const Just) #)

sliceIt :: Delta -> Delta -> It Strict.ByteString
sliceIt !i !j = wantIt mempty $ \r -> 
  (# bytes j < bytes (rewind (delta r))
  ,  grabRest i r mempty $ const $ 
     Strict.concat . 
     Lazy.toChunks . 
     Lazy.take (fromIntegral (bj - bi)) #)
  where
    bi = bytes i
    bj = bytes j

stepIt :: It a -> Step e a
stepIt = go mempty where
  go r (Pure a) = StepDone r mempty a
  go r (It a k) = StepCont r (pure a) (\s -> go s (k s))