{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, FlexibleContexts, UndecidableInstances, TupleSections #-}
{-# OPTIONS -Wall #-}
module Text.Parsec.Indentation (module Text.Parsec.Indentation, I.IndentationRel(..), Indentation, infIndentation) where
import Control.Monad
import Text.Parsec.Prim (ParsecT, mkPT, runParsecT,
Stream(..), Consumed(..), Reply(..),
State(..), getInput, setInput)
import Text.Parsec.Error (Message (Message), addErrorMessage)
import Text.Parser.Indentation.Implementation as I
data IndentStream s = IndentStream { indentationState :: !IndentationState, tokenStream :: !s } deriving (Show)
type IndentationToken t = t
{-# INLINE mkIndentStream #-}
mkIndentStream :: Indentation -> Indentation -> Bool -> IndentationRel -> s -> IndentStream s
mkIndentStream lo hi mode rel s = IndentStream (mkIndentationState lo hi mode rel) s
instance (Monad m, Stream s m (t, Indentation)) => Stream (IndentStream s) m (IndentationToken t) where
uncons (IndentStream is s) = do
x <- uncons s
case x of
Nothing -> return Nothing
Just ((t, i), s') -> return $ updateIndentation is i ok err where
ok is' = Just ( t, IndentStream is' s')
err _ = Nothing
{-# INLINE localState #-}
localState :: (Monad m) => LocalState (ParsecT (IndentStream s) u m a)
localState pre post m = do
IndentStream is s <- getInput
setInput (IndentStream (pre is) s)
x <- m
IndentStream is' s' <- getInput
setInput (IndentStream (post is is') s')
return x
{-# INLINE localStateUnlessAbsMode #-}
localStateUnlessAbsMode :: (Monad m) => LocalState (ParsecT (IndentStream s) u m a)
localStateUnlessAbsMode pre post m = do
a <- liftM (indentationStateAbsMode . indentationState) getInput
if a then m else localState pre post m
{-# INLINE localTokenMode #-}
localTokenMode :: (Monad m) => (IndentationRel -> IndentationRel) -> ParsecT (IndentStream s) u m a -> ParsecT (IndentStream s) u m a
localTokenMode = I.localTokenMode localState
{-# INLINE localIndentation #-}
localIndentation :: (Monad m) => IndentationRel -> ParsecT (IndentStream s) u m a -> ParsecT (IndentStream s) u m a
localIndentation = I.localIndentation localStateUnlessAbsMode
{-# INLINE absoluteIndentation #-}
absoluteIndentation :: (Monad m) => ParsecT (IndentStream s) u m a -> ParsecT (IndentStream s) u m a
absoluteIndentation = I.absoluteIndentation localState
{-# INLINE ignoreAbsoluteIndentation #-}
ignoreAbsoluteIndentation :: (Monad m) => ParsecT (IndentStream s) u m a -> ParsecT (IndentStream s) u m a
ignoreAbsoluteIndentation = I.ignoreAbsoluteIndentation localState
{-# INLINE localAbsoluteIndentation #-}
localAbsoluteIndentation :: (Monad m) => ParsecT (IndentStream s) u m a -> ParsecT (IndentStream s) u m a
localAbsoluteIndentation = I.localAbsoluteIndentation localState
streamToList :: (Monad m, Stream s m t) => s -> m [t]
streamToList s = do
x <- uncons s
case x of
Nothing -> return []
Just (c, s') -> do s'' <- streamToList s'
return (c : s'')
{-# INLINE indentStreamParser #-}
indentStreamParser :: (Monad m) => ParsecT s u m (t, Indentation) -> ParsecT (IndentStream s) u m (IndentationToken t)
indentStreamParser p = mkPT $ \state ->
let IndentStream is s = stateInput state
go f (Ok (a, i) state' e) = updateIndentation is i ok err where
ok is' = return $ f $ return (Ok ( a) (state' {stateInput = IndentStream is' (stateInput state') }) e)
err msg = return $ Empty $ return $ Error (Message ("Invalid indentation. "++msg++show ((stateInput state) { tokenStream = ""})) `addErrorMessage` e)
go f (Error e) = return $ f $ return (Error e)
in runParsecT p (state { stateInput = s }) >>= consumed (go Consumed) (go Empty)
{-# INLINE consumed #-}
consumed :: (Monad m) => (a -> m b) -> (a -> m b) -> Consumed (m a) -> m b
consumed c _ (Consumed m) = m >>= c
consumed _ e (Empty m) = m >>= e