packages feed

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