{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, FlexibleContexts #-}
module Text.Parsec.Indentation.Char where
import Text.Parsec.Prim (ParsecT, mkPT, runParsecT,
Stream(..),
Consumed(..), Reply(..),
State(..))
import Text.Parsec.Pos (sourceColumn)
import Text.Parser.Indentation.Implementation (Indentation)
{-# INLINE mkCharIndentStream #-}
mkCharIndentStream :: s -> CharIndentStream s
mkCharIndentStream s = CharIndentStream 1 s
data CharIndentStream s = CharIndentStream { charIndentStreamColumn :: {-# UNPACK #-} !Indentation,
charIndentStreamStream :: !s } deriving (Show)
instance (Stream s m Char) => Stream (CharIndentStream s) m (Char, Indentation) where
uncons (CharIndentStream i s) = do
x <- uncons s
case x of
Nothing -> return Nothing
Just (c, cs) -> return (Just ((c, i), CharIndentStream (updateColumn i c) cs))
{-# INLINE updateColumn #-}
updateColumn :: Integral a => a -> Char -> a
updateColumn _ '\n' = 1
updateColumn i '\t' = i + 8 - ((i-1) `mod` 8)
updateColumn i _ = i + 1
{-# INLINE charIndentStreamParser #-}
charIndentStreamParser :: (Monad m) => ParsecT s u m t -> ParsecT (CharIndentStream s) u m (t, Indentation)
charIndentStreamParser p = mkPT $ \state ->
let go (Ok a state' e) = return (Ok (a, sourceColumn $ statePos state) (state' { stateInput = CharIndentStream (sourceColumn $ statePos state') (stateInput state') }) e)
go (Error e) = return (Error e)
in runParsecT p (state { stateInput = charIndentStreamStream (stateInput state) })
>>= consumed (return . Consumed . go) (return . Empty . go)
{-# 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