{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveDataTypeable #-}
module Language.Rust.Parser.ParseMonad (
P,
execParser,
execParser',
initPos,
PState(..),
getPState,
setPState,
getPosition,
setPosition,
getInput,
setInput,
popToken,
pushToken,
swapToken,
ParseFail(..),
parseError,
) where
import Language.Rust.Data.InputStream ( InputStream )
import Language.Rust.Data.Position ( Spanned, Position, initPos, prettyPosition )
import Language.Rust.Syntax.Token ( Token )
import Control.Monad.Fail as Fail
import Control.Exception ( Exception )
import Data.Maybe ( listToMaybe )
import Data.Typeable ( Typeable )
newtype P a = P { unParser :: forall r. PState
-> (a -> PState -> r)
-> (String -> Position -> r)
-> r
}
data PState = PState
{ curPos :: !Position
, curInput :: !InputStream
, prevPos :: Position
, pushedTokens :: [Spanned Token]
, swapFunction :: Token -> Token
}
instance Functor P where
fmap f m = P $ \ !s pOk pFailed -> unParser m s (pOk . f) pFailed
instance Applicative P where
pure x = P $ \ !s pOk _ -> pOk x s
m <*> k = P $ \ !s pOk pFailed ->
let pOk' x s' = unParser k s' (pOk . x) pFailed
in unParser m s pOk' pFailed
instance Monad P where
return = pure
m >>= k = P $ \ !s pOk pFailed ->
let pOk' x s' = unParser (k x) s' pOk pFailed
in unParser m s pOk' pFailed
instance Fail.MonadFail P where
fail msg = P $ \ !s _ pFailed -> pFailed msg (curPos s)
data ParseFail = ParseFail Position String deriving (Eq, Typeable)
instance Show ParseFail where
showsPrec p (ParseFail pos msg) = showParen (p >= 11) (showString err)
where err = unwords [ "parse failure at", prettyPosition pos, "(" ++ msg ++ ")" ]
instance Exception ParseFail
execParser :: P a -> InputStream -> Position -> Either ParseFail a
execParser p input pos = execParser' p input pos id
execParser' :: P a -> InputStream -> Position -> (Token -> Token) -> Either ParseFail a
execParser' parser input pos swap = unParser parser
initialState
(\result _ -> Right result)
(\message errPos -> Left (ParseFail errPos message))
where initialState = PState
{ curPos = pos
, curInput = input
, prevPos = error "ParseMonad.execParser: Touched undefined position!"
, pushedTokens = []
, swapFunction = swap
}
swapToken :: Token -> P Token
swapToken t = P $ \ !s@PState{ swapFunction = f } pOk _ -> pOk (f $! t) s
getPState :: P PState
getPState = P $ \ !s pOk _ -> pOk s s
setPState :: PState -> P ()
setPState s = P $ \ _ pOk _ -> pOk () s
modifyPState :: (PState -> PState) -> P ()
modifyPState f = P $ \ !s pOk _ -> pOk () (f $! s)
getPosition :: P Position
getPosition = curPos <$> getPState
setPosition :: Position -> P ()
setPosition pos = modifyPState $ \ s -> s{ curPos = pos }
getInput :: P InputStream
getInput = curInput <$> getPState
setInput :: InputStream -> P ()
setInput i = modifyPState $ \s -> s{ curInput = i }
pushToken :: Spanned Token -> P ()
pushToken tok = modifyPState $ \s@PState{ pushedTokens = toks } -> s{ pushedTokens = tok : toks }
popToken :: P (Maybe (Spanned Token))
popToken = P $ \ !s@PState{ pushedTokens = toks } pOk _ -> pOk (listToMaybe toks) s{ pushedTokens = drop 1 toks }
parseError :: Show b => b -> P a
parseError b = Fail.fail ("Syntax error: the symbol `" ++ show b ++ "' does not fit here")