{-# LANGUAGE ConstraintKinds, FlexibleContexts, MultiParamTypeClasses #-}
module Idris.Parser.Stack
(
Parser(..)
, Parsing(..)
, runparser
, ParseError
, prettyError
, Mark
, mark
, restore
, getFC
, addExtent
, trackExtent
, extent
, withExtent
, appExtent
)
where
import Idris.Core.TT (FC(..))
import Idris.Output (Message(..))
import Control.Arrow (app)
import Control.Monad.State.Strict (StateT(..), evalStateT)
import Control.Monad.Writer.Strict (MonadWriter(..), WriterT(..), listen,
runWriterT, tell)
import qualified Data.List.NonEmpty as NonEmpty
import Data.Void (Void(..))
import System.FilePath (addTrailingPathSeparator, splitFileName)
import qualified Text.Megaparsec as P
import qualified Util.Pretty as PP
type Parser s = StateT s (WriterT FC (P.Parsec Void String))
type Parsing m = (P.MonadParsec Void String m, MonadWriter FC m)
runparser :: Parser st res -> st -> String -> String -> Either ParseError res
runparser p i inputname s =
case P.parse (runWriterT (evalStateT p i)) inputname s of
Left err -> Left $ ParseError s err
Right v -> Right $ fst v
data ParseError = ParseError String (P.ParseError (P.Token String) Void)
instance Message ParseError where
messageExtent (ParseError _ err) = sourcePositionFC pos
where
(pos NonEmpty.:| _) = P.errorPos err
messageText (ParseError _ err) = PP.text . init . P.parseErrorTextPretty $ err
messageSource (ParseError src _) = Just src
prettyError :: ParseError -> String
prettyError (ParseError s err) = P.parseErrorPretty' s err
type Mark = P.State String
mark :: Parsing m => m Mark
mark = P.getParserState
restore :: Parsing m => Mark -> m ()
restore = P.setParserState
sourcePositionFC :: P.SourcePos -> FC
sourcePositionFC (P.SourcePos name line column) =
FC f (lineNumber, columnNumber) (lineNumber, columnNumber)
where
lineNumber = P.unPos line
columnNumber = P.unPos column
(dir, file) = splitFileName name
f = if dir == addTrailingPathSeparator "."
then file
else name
getFC :: Parsing m => m FC
getFC = sourcePositionFC <$> P.getPosition
addExtent :: MonadWriter FC m => FC -> m ()
addExtent = tell
trackExtent :: Parsing m => m a -> m a
trackExtent p = do ~(FC f (sr, sc) _) <- getFC
result <- p
~(FC f _ (er, ec)) <- getFC
addExtent (FC f (sr, sc) (er, max 1 (ec - 1)))
return result
extent :: MonadWriter FC m => m a -> m FC
extent = fmap snd . withExtent
withExtent :: MonadWriter FC m => m a -> m (a, FC)
withExtent = listen
appExtent :: MonadWriter FC m => m (FC -> a) -> m a
appExtent = fmap app . withExtent