module Text.HTML.TagSoup.HT.Parser.MTL (
T,
nextChar, withDefault,
run, gets, tell, mfix,
allowFail, allowEmit,
module Control.Monad.Identity,
) where
import qualified Text.HTML.TagSoup.HT.Position as Position
import qualified Text.HTML.TagSoup.HT.Parser.Status as Status
import Text.HTML.TagSoup.HT.Utility (mapSnd, )
import Control.Monad.Writer (WriterT(..), mapWriterT, tell, lift, liftM, )
import Control.Monad.State (StateT(..), mapStateT, gets, )
import Control.Monad.Fix (mfix)
import Control.Monad.Identity (Identity(..), )
import Data.Monoid (Monoid)
{-
We need to declare whether parts of the parser
can or cannot fail and can or cannot emit something through the writer.
This way we can get rid of the former functions 'force' and 'ignoreEmit'
which generate errors at run-time
if the assumptions of non-failing or non-emission were wrong.
Now, since we declare this properties in types,
runtime errors cannot happen.
-}
type T output fail = WriterT output (StateT Status.T fail)
run :: Monad fail =>
T output fail a -> Status.T -> fail (a, Status.T, output)
run p =
liftM (\((a,w),st) -> (a,st,w)) . runStateT (runWriterT p)
nextChar :: Monoid output => T output Maybe Char
nextChar =
lift $
StateT $ \ (Status.Cons pos str) ->
case str of
[] -> Nothing
(c:cs) -> Just (c, Status.Cons (Position.updateOnChar c pos) cs)
-- this replaces 'ignoreEmit'
allowEmit ::
Monad fail =>
T () fail a -> T [w] fail a
allowEmit =
mapWriterT (fmap (mapSnd (const [])))
allowFail' ::
StateT s Identity a -> StateT s Maybe a
allowFail' =
mapStateT (Just . runIdentity)
-- this replaces 'force'
allowFail :: T output Identity a -> T output Maybe a
allowFail =
mapWriterT allowFail'
withDefault' ::
StateT s Maybe a ->
StateT s Identity a ->
StateT s Identity a
withDefault' p q =
StateT $ \st ->
maybe (runStateT q st) Identity (runStateT p st)
withDefault ::
T output Maybe a ->
T output Identity a ->
T output Identity a
withDefault p q =
WriterT $ withDefault' (runWriterT p) (runWriterT q)