iteratee-parsec-0.0.3: 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.Monad
import Control.Monad.ST
import Control.Monad.Trans.Class
import Data.Monoid
import Data.Iteratee
import Data.Iteratee.Base.StreamChunk (StreamChunk)
import Data.IORef
import qualified Data.ListLike as LL
import Data.STRef
import Text.Parsec
-- | 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, StreamChunk c el) => NextCursor r m c el
-- | Points to next cursor
= NextCursor (Cursor r m c 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, StreamChunk c el) => Cursor r m c el =
Cursor (r (NextCursor r m c el)) (c el)
-- | Creates new cursor
mkCursor :: (Monad m, Reference r m, StreamChunk c el) => m (Cursor r m c el)
mkCursor = newRef Uneval >>= \r -> (return $! Cursor r LL.empty)
instance (Monad m, Reference r m, StreamChunk c el) =>
Stream (Cursor r m c el) (IterateeG c el m) el where
uncons = unconsStream
unconsStream :: (Monad m, Reference r m, StreamChunk c el)
=> Cursor r m c el
-> IterateeG c el m (Maybe (el, Cursor r m c el))
unconsStream p@(Cursor r c)
| LL.null c = IterateeG $ \st -> join $ modifyRef r $ unconsCursor st p
| otherwise = return $! justUnconsCursor p
unconsCursor :: forall r m c el. (Monad m, Reference r m, StreamChunk c el)
=> StreamG c el
-> Cursor r m c el
-> NextCursor r m c el
-> m (NextCursor r m c el,
m (IterGV c el m (Maybe (el, Cursor r m c el))))
unconsCursor st _ rv@(NextCursor p@(Cursor r c))
| LL.null c = return $! (rv, join $ modifyRef r $ unconsCursor st p)
| otherwise = return $! (rv, return $! Done (justUnconsCursor p) st)
unconsCursor st _ rv@None
= return $! (rv, return $! Done Nothing st)
unconsCursor (Chunk c) p rv@Uneval
| LL.null c = return $! (rv, return $! Cont (unconsStream p) Nothing)
| otherwise = do r <- newRef Uneval :: m (r (NextCursor r m c el))
let p' = Cursor r c
ra = Done (justUnconsCursor p') (Chunk LL.empty)
return $! (NextCursor p', return $! ra)
unconsCursor st@(EOF Nothing) _ Uneval
= return $! (None, return $! Done Nothing st)
unconsCursor (EOF (Just e)) _ rv@Uneval
= return $! (rv, return $! Cont (throwErr e) (Just e))
justUnconsCursor :: (Monad m, Reference r m, StreamChunk c el) =>
Cursor r m c el -> Maybe (el, Cursor r m c el)
justUnconsCursor (Cursor r c) = Just $! (LL.head c, Cursor r $ LL.tail c)
concatCursor :: (Monad m, Reference r m, StreamChunk c el)
=> Cursor r m c el -> m (c el)
concatCursor c = liftM mconcat (concatCursor' c)
concatCursor' :: (Monad m, Reference r m, StreamChunk c el)
=> Cursor r m c el -> m [c el]
concatCursor' (Cursor r v) =
liftM2 (:) (return v) (readRef r >>= concatNextCursor')
concatNextCursor' :: (Monad m, Reference r m, StreamChunk c el)
=> NextCursor r m c el -> m [c el]
concatNextCursor' (NextCursor c) = concatCursor' $! c
concatNextCursor' _ = return $! []
-- | 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, StreamChunk c el)
=> ParsecT (Cursor r m c el) u (IterateeG c el m) a
-- ^ Parser to run
-> u -- ^ A user state
-> SourceName -- ^ Source name
-> IterateeG c el 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'
liftI $! Done (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, StreamChunk c el)
=> ParsecT (Cursor r m c el) u (IterateeG c el m) a
-- ^ Parser to run
-> u -- ^ A user state
-> SourceName -- ^ Source name
-> IterateeG c el 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'
liftI $! Done 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