2Module : Idris.Parser.Stack
3Description : Idris parser stack and its primitives.
6Maintainer : The Idris Community.
8{-# LANGUAGE ConstraintKinds, FlexibleContexts, MultiParamTypeClasses #-}
9module Idris.Parser.Stack
22 -- * Tracking the extent of productions
24 -- The parser stack automatically builds up the extent of any parse from
25 -- the extents of sub-parsers wrapped with @trackExtent@.
35import Idris.Core.TT (FC(..))
36import Idris.Output (Message(..))
38import Control.Arrow (app)
39import Control.Monad.State.Strict (StateT(..), evalStateT)
40import Control.Monad.Writer.Strict (MonadWriter(..), WriterT(..), listen,
42import qualified Data.List.NonEmpty as NonEmpty
43import Data.Void (Void(..))
44import System.FilePath (addTrailingPathSeparator, splitFileName)
45import qualified Text.Megaparsec as P
46import qualified Util.Pretty as PP
50-- | Our parser stack with state of type @s@
51type Parser s = StateT s (WriterT FC (P.Parsec Void String))
53-- | A constraint for parsing without state
54type Parsing m = (P.MonadParsec Void String m, MonadWriter FC m)
56-- | Run the Idris parser stack
57runparser :: Parser st res -> st -> String -> String -> Either ParseError res
58runparser p i inputname s =
59 case P.parse (runWriterT (evalStateT p i)) inputname s of
60 Left err -> Left $ ParseError s err
61 Right v -> Right $ fst v
65data ParseError = ParseError String (P.ParseError (P.Token String) Void)
67instance Message ParseError where
68 messageExtent (ParseError _ err) = sourcePositionFC pos
70 (pos NonEmpty.:| _) = P.errorPos err
71 messageText (ParseError _ err) = PP.text . init . P.parseErrorTextPretty $ err
72 messageSource (ParseError src _) = Just src
74-- | A fully formatted parse error, with caret and bar, etc.
75prettyError :: ParseError -> String
76prettyError (ParseError s err) = P.parseErrorPretty' s err
78{- * Mark and restore -}
80type Mark = P.State String
82-- | Retrieve the parser state so we can restart from this point later.
83mark :: Parsing m => m Mark
84mark = P.getParserState
86restore :: Parsing m => Mark -> m ()
87restore = P.setParserState
89{- * Tracking the extent of productions -}
91sourcePositionFC :: P.SourcePos -> FC
92sourcePositionFC (P.SourcePos name line column) =
93 FC f (lineNumber, columnNumber) (lineNumber, columnNumber)
95 lineNumber = P.unPos line
96 columnNumber = P.unPos column
97 (dir, file) = splitFileName name
98 f = if dir == addTrailingPathSeparator "."
102-- | Get the current parse position.
104-- This is useful when the position is needed in a way unrelated to the
105-- heirarchy of parsers. Prefer using @withExtent@ and friends.
106getFC :: Parsing m => m FC
107getFC = sourcePositionFC <$> P.getPosition
109-- | Add an extent (widen) our current parsing context.
110addExtent :: MonadWriter FC m => FC -> m ()
113-- | Run a parser and track its extent.
115-- Wrap bare Megaparsec parsers with this to make them "visible" in error
116-- messages. Do not wrap whitespace or comment parsers. If you find an
117-- extent is taking trailing whitespace, it's likely there's a double-wrapped
118-- parser (usually via @Idris.Parser.Helpers.token@).
119trackExtent :: Parsing m => m a -> m a
120trackExtent p = do (FC f (sr, sc) _) <- getFC
122 (FC f _ (er, ec)) <- getFC
123 addExtent (FC f (sr, sc) (er, max 1 (ec - 1)))
126-- | Run a parser, discard its value, and return its extent.
127extent :: MonadWriter FC m => m a -> m FC
128extent = fmap snd . withExtent
130-- | Run a parser and return its value and extent.
131withExtent :: MonadWriter FC m => m a -> m (a, FC)
134-- | Run a parser and inject the extent after via function application.
135appExtent :: MonadWriter FC m => m (FC -> a) -> m a
136appExtent = fmap app . withExtent
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list