iteratee-parsec-0.0.5: src/Text/Parsec/Iteratee/LinkedList.hs
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{- |
Module : $Header$
Description : Module providing simple implementation based on mutable linked
list.
Copyright : (c) Maciej Piechotka
License : MIT
Maintainer : uzytkownik2@gmail.com
Stability : none
Portability : portable
Module providing simple implementation based on mutable linked list.
It is optimised for longer parsers.
-}
module Text.Parsec.Iteratee.LinkedList
(
Reference(..),
Cursor,
NextCursor,
mkCursor,
parsecIteratee,
safeParsecIteratee,
)
where
import Control.Concurrent.MVar
import Control.Exception
import Control.Monad
import Control.Monad.ST
import Control.Monad.Trans.Class
import Data.Monoid
import Data.Iteratee
import Data.IORef
import Data.ListLike as LL
import Data.STRef
import Data.Typeable
import Text.Parsec hiding (Stream)
import qualified Text.Parsec as P
-- | Class notifing a reference in monad.
-- Probably should be in separate module.
class Monad m => Reference r m where
-- | Create new reference
newRef :: a -- ^ An initial value
-> m (r a) -- ^ A new reference
-- | Reads a reference
readRef :: r a -- ^ Reference
-> m a -- ^ Value hold by reference
-- | Write to reference
writeRef :: r a -- ^ Reference
-> a -- ^ New value
-> m ()
-- | Modify the reference. Default implementation is provided but it MUST
-- be overloaded if the reference is atomic to provide an atomic write
modifyRef :: r a -- ^ Reference
-> (a -> m (a, b)) -- ^ Computation
-> m b -- ^ Result of computation
modifyRef r f = readRef r >>= f >>= \(a, b) -> writeRef r a >> return b
instance Reference IORef IO where
newRef = newIORef
readRef = readIORef
writeRef = writeIORef
instance Reference (STRef s) (ST s) where
newRef = newSTRef
readRef = readSTRef
writeRef = writeSTRef
instance Reference MVar IO where
newRef = newMVar
readRef = readMVar
writeRef = putMVar
-- | Specify the 3 possible states of next cursor - existence, non-existence
-- and not being evaluated
data (Monad m, Reference r m, ListLike s el) => NextCursor r m s el
-- | Points to next cursor
= NextCursor (Cursor r m s el)
-- | States that next cursor does not exists
| None
-- | Next cursor is not evaluated
| Uneval
-- | Cursor holds current value and reference to possible next cursor
data (Monad m, Reference r m) => Cursor r m s el
= Cursor (r (NextCursor r m s el)) s
-- | Creates new cursor
mkCursor :: (Monad m, Reference r m, ListLike s el) => m (Cursor r m s el)
mkCursor = (\r -> return $! Cursor r LL.empty) =<< newRef Uneval
instance (Monad m, Nullable s, Reference r m, ListLike s el) =>
P.Stream (Cursor r m s el) (Iteratee s m) el where
uncons = unconsStream
unconsStream :: (Monad m, Nullable s, Reference r m, ListLike s el)
=> Cursor r m s el
-> Iteratee s m (Maybe (el, Cursor r m s el))
unconsStream p@(Cursor r c)
| LL.null c = unconsCursor r =<< lift (readRef r)
| otherwise = return $! unconsChunk p
unconsCursor :: (Monad m, Nullable s, Reference r m, ListLike s el)
=> r (NextCursor r m s el)
-> NextCursor r m s el
-> Iteratee s m (Maybe (el, Cursor r m s el))
unconsCursor _ (NextCursor c) = return $! unconsChunk c
unconsCursor _ None = return $! Nothing
unconsCursor r Uneval = icont (extendCursor r) Nothing
insertCursor :: (Monad m, Reference r m, ListLike s el)
=> r (NextCursor r m s el)
-> s
-> m (Cursor r m s el)
insertCursor r s = do
r' <- newRef Uneval
let c = Cursor r' s
writeRef r (NextCursor c)
return c
extendCursor :: (Monad m, Nullable s, Reference r m, ListLike s el)
=> r (NextCursor r m s el)
-> Stream s
-> Iteratee s m (Maybe (el, Cursor r m s el))
extendCursor r (Chunk s)
| LL.null s = liftI (extendCursor r)
| otherwise = return . unconsChunk =<< lift (insertCursor r s)
extendCursor r (EOF Nothing)
= const (return Nothing) =<< lift (writeRef r None)
extendCursor r (EOF (Just se@(SomeException e)))
| typeOf e == seekType = throwErr $ unhandled
| otherwise = throwRecoverableErr se (extendCursor r)
where seekType = typeOf (undefined :: SeekException)
unhandled
= SomeException $! EnumUnhandledIterException (IterException e)
unconsChunk :: (Monad m, Reference r m, ListLike s el)
=> Cursor r m s el -> Maybe (el, Cursor r m s el)
unconsChunk (Cursor r s) = Just (LL.head s, Cursor r $ LL.tail s)
concatCursor :: (Monad m, Reference r m, ListLike s el)
=> Cursor r m s el -> m s
concatCursor (Cursor r s) = liftM (s `mappend`) (concatCursor' =<< readRef r)
concatCursor' :: (Monad m, Reference r m, ListLike s el)
=> NextCursor r m s el -> m s
concatCursor' (NextCursor n) = concatCursor n
concatCursor' _ = return $! mempty
-- | Runs parser. If it suceed the remaining part of stream stands in stream,
-- however if it fails the stream is not in defined state.
parsecIteratee :: (Monad m, Reference r m, Nullable c, ListLike c el)
=> ParsecT (Cursor r m c el) u (Iteratee c m) a
-- ^ Parser to run
-> u -- ^ A user state
-> SourceName -- ^ Source name
-> Iteratee c m (Either ParseError a)
parsecIteratee p u sn = do
c <- lift mkCursor
res <- runParserT (liftM2 (,) p getInput) u sn c
case res of
Right (a, c') -> do sc <- lift $ concatCursor c'
idone (Right a) (Chunk sc)
Left err -> return $! Left err
-- | Runs parser. If it suceed the remaining part of stream stands in stream,
-- however if it fails everything stands in stream.
safeParsecIteratee :: (Monad m, Reference r m, Nullable c, ListLike c el)
=> ParsecT (Cursor r m c el) u (Iteratee c m) a
-- ^ Parser to run
-> u -- ^ A user state
-> SourceName -- ^ Source name
-> Iteratee c m (Either ParseError a)
safeParsecIteratee p u sn = do
c <- lift mkCursor
Right (c', res) <- runParserT (parsecSafe p) u sn c
sc <- lift $ concatCursor c'
idone res (Chunk sc)
parsecSafe :: Monad m
=> ParsecT s u m a
-> ParsecT s u m (s, Either ParseError a)
parsecSafe p = mkPT $ \s -> do
r <- unConsume =<< runParsecT p s
case r of
Error pe -> let rp = Ok (stateInput s, Left pe) s (unknownError s)
in return $! Empty $ return $! rp
Ok v s' _ -> let rp = Ok (stateInput s', Right v) s' (unknownError s')
in return $! Consumed $! return $! rp
where unConsume (Consumed x) = x
unConsume (Empty x) = x