{-# OPTIONS #-}
module Language.Python.Common.ParserMonad
( P
, execParser
, execParserKeepComments
, runParser
, thenP
, returnP
, setLocation
, getLocation
, getInput
, setInput
, getLastToken
, setLastToken
, setLastEOL
, getLastEOL
, ParseError (..)
, ParseState (..)
, initialState
, pushStartCode
, popStartCode
, getStartCode
, getIndent
, pushIndent
, popIndent
, getIndentStackDepth
, getParen
, pushParen
, popParen
, getParenStackDepth
, addComment
, getComments
, spanError
) where
import Language.Python.Common.SrcLocation (SrcLocation (..), SrcSpan (..), Span (..))
import Language.Python.Common.Token (Token (..))
import Language.Python.Common.ParseError (ParseError (..))
import Control.Applicative ((<$>))
import Control.Monad.State.Class
import Control.Monad.State.Strict as State
import Control.Monad.Error as Error
import Language.Python.Common.Pretty
internalError :: String -> P a
internalError = throwError . StrError
spanError :: Span a => a -> String -> P b
spanError x str = throwError $ StrError $ unwords [prettyText $ getSpan x, str]
data ParseState =
ParseState
{ location :: !SrcLocation
, input :: !String
, previousToken :: !Token
, startCodeStack :: [Int]
, indentStack :: [Int]
, parenStack :: [Token]
, lastEOL :: !SrcSpan
, comments :: [Token]
}
deriving Show
initToken :: Token
initToken = NewlineToken SpanEmpty
initialState :: SrcLocation -> String -> [Int] -> ParseState
initialState initLoc inp scStack
= ParseState
{ location = initLoc
, input = inp
, previousToken = initToken
, startCodeStack = scStack
, indentStack = [1]
, parenStack = []
, lastEOL = SpanEmpty
, comments = []
}
type P a = StateT ParseState (Either ParseError) a
execParser :: P a -> ParseState -> Either ParseError a
execParser = evalStateT
execParserKeepComments :: P a -> ParseState -> Either ParseError (a, [Token])
execParserKeepComments parser state =
evalStateT (parser >>= \x -> getComments >>= \c -> return (x, c)) state
runParser :: P a -> ParseState -> Either ParseError (a, ParseState)
runParser = runStateT
{-# INLINE returnP #-}
returnP :: a -> P a
returnP = return
{-# INLINE thenP #-}
thenP :: P a -> (a -> P b) -> P b
thenP = (>>=)
setLastEOL :: SrcSpan -> P ()
setLastEOL span = modify $ \s -> s { lastEOL = span }
getLastEOL :: P SrcSpan
getLastEOL = gets lastEOL
setLocation :: SrcLocation -> P ()
setLocation loc = modify $ \s -> s { location = loc }
getLocation :: P SrcLocation
getLocation = gets location
getInput :: P String
getInput = gets input
setInput :: String -> P ()
setInput inp = modify $ \s -> s { input = inp }
getLastToken :: P Token
getLastToken = gets previousToken
setLastToken :: Token -> P ()
setLastToken tok = modify $ \s -> s { previousToken = tok }
pushStartCode :: Int -> P ()
pushStartCode code = do
oldStack <- gets startCodeStack
modify $ \s -> s { startCodeStack = code : oldStack }
popStartCode :: P ()
popStartCode = do
oldStack <- gets startCodeStack
case oldStack of
[] -> internalError "fatal error in lexer: attempt to pop empty start code stack"
_:rest -> modify $ \s -> s { startCodeStack = rest }
getStartCode :: P Int
getStartCode = do
oldStack <- gets startCodeStack
case oldStack of
[] -> internalError "fatal error in lexer: start code stack empty on getStartCode"
code:_ -> return code
pushIndent :: Int -> P ()
pushIndent indent = do
oldStack <- gets indentStack
modify $ \s -> s { indentStack = indent : oldStack }
popIndent :: P ()
popIndent = do
oldStack <- gets indentStack
case oldStack of
[] -> internalError "fatal error in lexer: attempt to pop empty indentation stack"
_:rest -> modify $ \s -> s { indentStack = rest }
getIndent :: P Int
getIndent = do
oldStack <- gets indentStack
case oldStack of
[] -> internalError "fatal error in lexer: indent stack empty on getIndent"
indent:_ -> return indent
getIndentStackDepth :: P Int
getIndentStackDepth = gets (length . indentStack)
pushParen :: Token -> P ()
pushParen symbol = do
oldStack <- gets parenStack
modify $ \s -> s { parenStack = symbol : oldStack }
popParen :: P ()
popParen = do
oldStack <- gets parenStack
case oldStack of
[] -> internalError "fatal error in lexer: attempt to pop empty paren stack"
_:rest -> modify $ \s -> s { parenStack = rest }
getParen :: P (Maybe Token)
getParen = do
oldStack <- gets parenStack
case oldStack of
[] -> return Nothing
symbol:_ -> return $ Just symbol
getParenStackDepth :: P Int
getParenStackDepth = gets (length . parenStack)
addComment :: Token -> P ()
addComment c = do
oldComments <- gets comments
modify $ \s -> s { comments = c : oldComments }
getComments :: P [Token]
getComments = reverse <$> gets comments