module Text.HTML.TagSoup.HT.Parser.Custom (
T,
nextChar, withDefault,
run, gets, tell, mfix,
allowFail, allowEmit,
Identity(..),
) where
import qualified Text.HTML.TagSoup.HT.Position as Position
import qualified Text.HTML.TagSoup.HT.Parser.Status as Status
import Control.Monad (MonadPlus, mzero, mplus, liftM, )
import Control.Monad.Fix (MonadFix, mfix, fix, )
import Data.Monoid (Monoid, mempty, mappend, )
newtype Identity a = Identity {runIdentity :: a}
instance Monad Identity where
return = Identity
m >>= k = k $ runIdentity m
instance MonadFix Identity where
mfix f = Identity $ fix (runIdentity . f)
{- |
This type is essentially equivalent to @RWST () output Status.T fail a@,
but its monadic combinator @>>=@ is defined more lazily.
See Parser.MTL.
-}
newtype T output fail a =
Cons { run :: Status.T -> fail (a, Status.T, output) }
instance (Monoid output, Monad fail) => Monad (T output fail) where
return a = Cons $ \s -> return (a, s, mempty)
m >>= k = Cons $ \s -> do
~(a, s', w) <- run m s
~(b, s'',w') <- run (k a) s'
return (b, s'', mappend w w')
fail msg = Cons $ \_ -> fail msg
instance (Monoid output, MonadPlus fail) => MonadPlus (T output fail) where
mzero = Cons $ \_ -> mzero
m `mplus` n = Cons $ \s -> run m s `mplus` run n s
{- |
Cf. 'Control.Monad.State.gets'
-}
gets :: (Monoid output, Monad fail) => (Status.T -> a) -> T output fail a
gets f =
Cons $ \ st -> return (f st, st, mempty)
{- |
Cf. 'Control.Monad.Writer.tell'
-}
tell :: (Monad fail) => output -> T output fail ()
tell ws = Cons $ \ st -> return ((),st,ws)
instance (Monoid output, MonadFix fail) => MonadFix (T output fail) where
mfix f = Cons $ \ st -> mfix $ \ ~(a, _, _) -> run (f a) st
nextChar :: (Monoid output, MonadPlus fail) => T output fail Char
nextChar =
Cons $ \ (Status.Cons pos str) ->
case str of
[] -> mzero
(c:cs) -> return (c, Status.Cons (Position.updateOnChar c pos) cs, mempty)
-- this replaces 'ignoreEmit'
allowEmit ::
Monad fail =>
T () fail a -> T [w] fail a
allowEmit p =
Cons $ liftM (\ ~(a,s,_) -> (a,s,mempty)) . run p
-- this replaces 'force'
allowFail :: T output Identity a -> T output Maybe a
allowFail p =
Cons $ return . runIdentity . run p
withDefault ::
T output Maybe a ->
T output Identity a ->
T output Identity a
withDefault p q =
Cons $ \s -> maybe (run q s) Identity (run p s)