purescript-0.15.15: src/Language/PureScript/CST/Monad.hs
module Language.PureScript.CST.Monad where
import Prelude
import Data.List (sortOn)
import Data.List.NonEmpty qualified as NE
import Data.Ord (comparing)
import Data.Text (Text)
import Language.PureScript.CST.Errors (ParserError, ParserErrorInfo(..), ParserErrorType(..), ParserWarning, ParserWarningType)
import Language.PureScript.CST.Layout (LayoutStack)
import Language.PureScript.CST.Positions (widen)
import Language.PureScript.CST.Types (Comment, LineFeed, SourcePos(..), SourceRange(..), SourceToken(..), Token, TokenAnn(..))
type LexResult = Either (LexState, ParserError) SourceToken
data LexState = LexState
{ lexPos :: SourcePos
, lexLeading :: [Comment LineFeed]
, lexSource :: Text
, lexStack :: LayoutStack
} deriving (Show)
data ParserState = ParserState
{ parserBuff :: [LexResult]
, parserErrors :: [ParserError]
, parserWarnings :: [ParserWarning]
} deriving (Show)
-- | A bare bones, CPS'ed `StateT s (Except e) a`.
newtype ParserM e s a =
Parser (forall r. s -> (s -> e -> r) -> (s -> a -> r) -> r)
type Parser = ParserM ParserError ParserState
instance Functor (ParserM e s) where
{-# INLINE fmap #-}
fmap f (Parser k) =
Parser $ \st kerr ksucc ->
k st kerr (\st' a -> ksucc st' (f a))
instance Applicative (ParserM e s) where
{-# INLINE pure #-}
pure a = Parser $ \st _ k -> k st a
{-# INLINE (<*>) #-}
Parser k1 <*> Parser k2 =
Parser $ \st kerr ksucc ->
k1 st kerr $ \st' f ->
k2 st' kerr $ \st'' a ->
ksucc st'' (f a)
instance Monad (ParserM e s) where
{-# INLINE return #-}
return = pure
{-# INLINE (>>=) #-}
Parser k1 >>= k2 =
Parser $ \st kerr ksucc ->
k1 st kerr $ \st' a -> do
let Parser k3 = k2 a
k3 st' kerr ksucc
runParser :: ParserState -> Parser a -> (ParserState, Either (NE.NonEmpty ParserError) a)
runParser st (Parser k) = k st left right
where
left st'@ParserState {..} err =
(st', Left $ NE.sortBy (comparing errRange) $ err NE.:| parserErrors)
right st'@ParserState {..} res
| null parserErrors = (st', Right res)
| otherwise = (st', Left $ NE.fromList $ sortOn errRange parserErrors)
runTokenParser :: Parser a -> [LexResult] -> Either (NE.NonEmpty ParserError) ([ParserWarning], a)
runTokenParser p buff = fmap (warnings,) res
where
(ParserState _ _ warnings, res) =
runParser initialState p
initialState = ParserState
{ parserBuff = buff
, parserErrors = []
, parserWarnings = []
}
{-# INLINE throw #-}
throw :: e -> ParserM e s a
throw e = Parser $ \st kerr _ -> kerr st e
parseError :: SourceToken -> Parser a
parseError tok = Parser $ \st kerr _ ->
kerr st $ ParserErrorInfo
{ errRange = tokRange . tokAnn $ tok
, errToks = [tok]
, errStack = [] -- TODO parserStack st
, errType = ErrToken
}
mkParserError :: LayoutStack -> [SourceToken] -> a -> ParserErrorInfo a
mkParserError stack toks ty =
ParserErrorInfo
{ errRange = range
, errToks = toks
, errStack = stack
, errType = ty
}
where
range = case toks of
[] -> SourceRange (SourcePos 0 0) (SourcePos 0 0)
_ -> widen (tokRange . tokAnn $ head toks) (tokRange . tokAnn $ last toks)
addFailure :: [SourceToken] -> ParserErrorType -> Parser ()
addFailure toks ty = Parser $ \st _ ksucc ->
ksucc (st { parserErrors = mkParserError [] toks ty : parserErrors st }) ()
parseFail' :: [SourceToken] -> ParserErrorType -> Parser a
parseFail' toks msg = Parser $ \st kerr _ -> kerr st (mkParserError [] toks msg)
parseFail :: SourceToken -> ParserErrorType -> Parser a
parseFail = parseFail' . pure
addWarning :: [SourceToken] -> ParserWarningType -> Parser ()
addWarning toks ty = Parser $ \st _ ksucc ->
ksucc (st { parserWarnings = mkParserError [] toks ty : parserWarnings st }) ()
pushBack :: SourceToken -> Parser ()
pushBack tok = Parser $ \st _ ksucc ->
ksucc (st { parserBuff = Right tok : parserBuff st }) ()
{-# INLINE tryPrefix #-}
tryPrefix :: Parser a -> Parser b -> Parser (Maybe a, b)
tryPrefix (Parser lhs) rhs = Parser $ \st kerr ksucc ->
lhs st
(\_ _ -> do
let Parser k = (Nothing,) <$> rhs
k st kerr ksucc)
(\st' res -> do
let Parser k = (Just res,) <$> rhs
k st' kerr ksucc)
oneOf :: NE.NonEmpty (Parser a) -> Parser a
oneOf parsers = Parser $ \st kerr ksucc -> do
let
prevErrs = parserErrors st
go (st', Right a) _ = (st', Right a)
go _ (st', Right a) = (st', Right a)
go (st1, Left errs1) (st2, Left errs2)
| errRange (NE.last errs2) > errRange (NE.last errs1) = (st2, Left errs2)
| otherwise = (st1, Left errs1)
case foldr1 go $ runParser (st { parserErrors = [] }) <$> parsers of
(st', Left errs) -> kerr (st' { parserErrors = prevErrs <> NE.tail errs}) $ NE.head errs
(st', Right res) -> ksucc (st' { parserErrors = prevErrs }) res
manyDelimited :: Token -> Token -> Token -> Parser a -> Parser [a]
manyDelimited open close sep p = do
_ <- token open
res <- go1
_ <- token close
pure res
where
go1 =
oneOf $ NE.fromList
[ go2 . pure =<< p
, pure []
]
go2 acc =
oneOf $ NE.fromList
[ token sep *> (go2 . (: acc) =<< p)
, pure (reverse acc)
]
token :: Token -> Parser SourceToken
token t = do
t' <- munch
if t == tokValue t'
then pure t'
else parseError t'
munch :: Parser SourceToken
munch = Parser $ \state@ParserState {..} kerr ksucc ->
case parserBuff of
Right tok : parserBuff' ->
ksucc (state { parserBuff = parserBuff' }) tok
Left (_, err) : _ ->
kerr state err
[] ->
error "Empty input"