dev: Hledger.Utils.Parse: cleanups
This commit is contained in:
parent
c48c41bcd2
commit
79fbfb9f5f
@ -101,10 +101,9 @@ import Data.Functor.Identity (Identity(..))
|
|||||||
import Data.List
|
import Data.List
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import Text.Megaparsec.Char
|
import Text.Megaparsec.Char
|
||||||
-- import Text.Megaparsec.Debug (dbg) -- from megaparsec 9.3+
|
-- import Text.Megaparsec.Debug (dbg)
|
||||||
|
|
||||||
import Control.Monad.Except (ExceptT, MonadError, catchError, throwError)
|
import Control.Monad.Except (ExceptT, MonadError, catchError, throwError)
|
||||||
-- import Control.Monad.State.Strict (StateT, evalStateT)
|
|
||||||
import Control.Monad.Trans.Class (lift)
|
import Control.Monad.Trans.Class (lift)
|
||||||
import qualified Data.List.NonEmpty as NE
|
import qualified Data.List.NonEmpty as NE
|
||||||
import Data.Monoid (Alt(..))
|
import Data.Monoid (Alt(..))
|
||||||
@ -165,13 +164,11 @@ parsewith :: Parsec e Text a -> Text -> Either (ParseErrorBundle Text e) a
|
|||||||
parsewith p = runParser p ""
|
parsewith p = runParser p ""
|
||||||
|
|
||||||
-- | Run a text parser in the identity monad. See also: parseWithState.
|
-- | Run a text parser in the identity monad. See also: parseWithState.
|
||||||
runTextParser, rtp
|
runTextParser, rtp :: TextParser Identity a -> Text -> Either HledgerParseErrors a
|
||||||
:: TextParser Identity a -> Text -> Either HledgerParseErrors a
|
|
||||||
runTextParser = parsewith
|
runTextParser = parsewith
|
||||||
rtp = runTextParser
|
rtp = runTextParser
|
||||||
|
|
||||||
parsewithString
|
parsewithString :: Parsec e String a -> String -> Either (ParseErrorBundle String e) a
|
||||||
:: Parsec e String a -> String -> Either (ParseErrorBundle String e) a
|
|
||||||
parsewithString p = runParser p ""
|
parsewithString p = runParser p ""
|
||||||
|
|
||||||
-- | Run a stateful parser with some initial state on a text.
|
-- | Run a stateful parser with some initial state on a text.
|
||||||
@ -192,20 +189,16 @@ parseWithState'
|
|||||||
-> (Either (ParseErrorBundle s e) a)
|
-> (Either (ParseErrorBundle s e) a)
|
||||||
parseWithState' ctx p = runParser (evalStateT p ctx) ""
|
parseWithState' ctx p = runParser (evalStateT p ctx) ""
|
||||||
|
|
||||||
fromparse
|
fromparse :: (Show t, Show (Token t), Show e) => Either (ParseErrorBundle t e) a -> a
|
||||||
:: (Show t, Show (Token t), Show e) => Either (ParseErrorBundle t e) a -> a
|
|
||||||
fromparse = either parseerror id
|
fromparse = either parseerror id
|
||||||
|
|
||||||
parseerror :: (Show t, Show (Token t), Show e) => ParseErrorBundle t e -> a
|
parseerror :: (Show t, Show (Token t), Show e) => ParseErrorBundle t e -> a
|
||||||
parseerror e = errorWithoutStackTrace $ showParseError e -- PARTIAL:
|
parseerror e = errorWithoutStackTrace $ showParseError e -- PARTIAL:
|
||||||
|
|
||||||
showParseError
|
showParseError :: (Show t, Show (Token t), Show e) => ParseErrorBundle t e -> String
|
||||||
:: (Show t, Show (Token t), Show e)
|
|
||||||
=> ParseErrorBundle t e -> String
|
|
||||||
showParseError e = "parse error at " ++ show e
|
showParseError e = "parse error at " ++ show e
|
||||||
|
|
||||||
showDateParseError
|
showDateParseError :: (Show t, Show (Token t), Show e) => ParseErrorBundle t e -> String
|
||||||
:: (Show t, Show (Token t), Show e) => ParseErrorBundle t e -> String
|
|
||||||
showDateParseError e = printf "date parse error (%s)" (intercalate ", " $ tailErr $ lines $ show e) -- PARTIAL tailError won't be null because showing a parse error
|
showDateParseError e = printf "date parse error (%s)" (intercalate ", " $ tailErr $ lines $ show e) -- PARTIAL tailError won't be null because showing a parse error
|
||||||
|
|
||||||
isNewline :: Char -> Bool
|
isNewline :: Char -> Bool
|
||||||
@ -295,7 +288,6 @@ instance ShowErrorComponent HledgerParseErrorData where
|
|||||||
-- | Fail at a specific source position, given by the raw offset from the
|
-- | Fail at a specific source position, given by the raw offset from the
|
||||||
-- start of the input stream (the number of tokens processed at that
|
-- start of the input stream (the number of tokens processed at that
|
||||||
-- point).
|
-- point).
|
||||||
|
|
||||||
parseErrorAt :: Int -> String -> HledgerParseErrorData
|
parseErrorAt :: Int -> String -> HledgerParseErrorData
|
||||||
parseErrorAt offset = ErrorFailAt offset (offset+1)
|
parseErrorAt offset = ErrorFailAt offset (offset+1)
|
||||||
|
|
||||||
@ -305,7 +297,6 @@ parseErrorAt offset = ErrorFailAt offset (offset+1)
|
|||||||
--
|
--
|
||||||
-- Note that care must be taken to ensure that the specified interval does
|
-- Note that care must be taken to ensure that the specified interval does
|
||||||
-- not span multiple lines of the input source. This will not be checked.
|
-- not span multiple lines of the input source. This will not be checked.
|
||||||
|
|
||||||
parseErrorAtRegion
|
parseErrorAtRegion
|
||||||
:: Int -- ^ Start offset
|
:: Int -- ^ Start offset
|
||||||
-> Int -- ^ End end offset
|
-> Int -- ^ End end offset
|
||||||
@ -325,12 +316,10 @@ parseErrorAtRegion startOffset endOffset msg =
|
|||||||
-- data type is to preserve the content and source position of the excerpt
|
-- data type is to preserve the content and source position of the excerpt
|
||||||
-- so that parse errors raised during "re-parsing" may properly reference
|
-- so that parse errors raised during "re-parsing" may properly reference
|
||||||
-- the original source.
|
-- the original source.
|
||||||
|
|
||||||
data SourceExcerpt = SourceExcerpt Int -- Offset of beginning of excerpt
|
data SourceExcerpt = SourceExcerpt Int -- Offset of beginning of excerpt
|
||||||
Text -- Fragment of source file
|
Text -- Fragment of source file
|
||||||
|
|
||||||
-- | Get the raw text of a source excerpt.
|
-- | Get the raw text of a source excerpt.
|
||||||
|
|
||||||
getExcerptText :: SourceExcerpt -> Text
|
getExcerptText :: SourceExcerpt -> Text
|
||||||
getExcerptText (SourceExcerpt _ txt) = txt
|
getExcerptText (SourceExcerpt _ txt) = txt
|
||||||
|
|
||||||
@ -497,7 +486,6 @@ instance Monoid (FinalParseError' e) where
|
|||||||
-- Megaparsec's 'ParseErrorBundle' type already bundles a parse error with
|
-- Megaparsec's 'ParseErrorBundle' type already bundles a parse error with
|
||||||
-- its full source text and filepath, so we just add a stack of include
|
-- its full source text and filepath, so we just add a stack of include
|
||||||
-- files.
|
-- files.
|
||||||
|
|
||||||
data FinalParseErrorBundle' e = FinalParseErrorBundle'
|
data FinalParseErrorBundle' e = FinalParseErrorBundle'
|
||||||
{ finalErrorBundle :: ParseErrorBundle Text e
|
{ finalErrorBundle :: ParseErrorBundle Text e
|
||||||
, includeFileStack :: [FilePath]
|
, includeFileStack :: [FilePath]
|
||||||
@ -509,12 +497,10 @@ type FinalParseErrorBundle = FinalParseErrorBundle' HledgerParseErrorData
|
|||||||
--- * Constructing and throwing final parse errors
|
--- * Constructing and throwing final parse errors
|
||||||
|
|
||||||
-- | Convert a "regular" parse error into a "final" parse error.
|
-- | Convert a "regular" parse error into a "final" parse error.
|
||||||
|
|
||||||
finalError :: ParseError Text e -> FinalParseError' e
|
finalError :: ParseError Text e -> FinalParseError' e
|
||||||
finalError = FinalError
|
finalError = FinalError
|
||||||
|
|
||||||
-- | Like megaparsec's 'fancyFailure', but as a "final" parse error.
|
-- | Like megaparsec's 'fancyFailure', but as a "final" parse error.
|
||||||
|
|
||||||
finalFancyFailure
|
finalFancyFailure
|
||||||
:: (MonadParsec e s m, MonadError (FinalParseError' e) m)
|
:: (MonadParsec e s m, MonadError (FinalParseError' e) m)
|
||||||
=> S.Set (ErrorFancy e) -> m a
|
=> S.Set (ErrorFancy e) -> m a
|
||||||
@ -523,25 +509,19 @@ finalFancyFailure errSet = do
|
|||||||
throwError $ FinalError $ FancyError offset errSet
|
throwError $ FinalError $ FancyError offset errSet
|
||||||
|
|
||||||
-- | Like 'fail', but as a "final" parse error.
|
-- | Like 'fail', but as a "final" parse error.
|
||||||
|
finalFail :: (MonadParsec e s m, MonadError (FinalParseError' e) m) => String -> m a
|
||||||
finalFail
|
|
||||||
:: (MonadParsec e s m, MonadError (FinalParseError' e) m) => String -> m a
|
|
||||||
finalFail = finalFancyFailure . S.singleton . ErrorFail
|
finalFail = finalFancyFailure . S.singleton . ErrorFail
|
||||||
|
|
||||||
-- | Like megaparsec's 'customFailure', but as a "final" parse error.
|
-- | Like megaparsec's 'customFailure', but as a "final" parse error.
|
||||||
|
finalCustomFailure :: (MonadParsec e s m, MonadError (FinalParseError' e) m) => e -> m a
|
||||||
finalCustomFailure
|
|
||||||
:: (MonadParsec e s m, MonadError (FinalParseError' e) m) => e -> m a
|
|
||||||
finalCustomFailure = finalFancyFailure . S.singleton . ErrorCustom
|
finalCustomFailure = finalFancyFailure . S.singleton . ErrorCustom
|
||||||
|
|
||||||
|
|
||||||
--- * Pretty-printing "final" parse errors
|
--- * Pretty-printing "final" parse errors
|
||||||
|
|
||||||
-- | Pretty-print a "final" parse error: print the stack of include files,
|
-- | Pretty-print a "final" parse error: print the stack of include files,
|
||||||
-- then apply the pretty-printer for parse error bundles. Note that
|
-- then apply the pretty-printer for parse error bundles.
|
||||||
-- 'attachSource' must be used on a "final" parse error before it can be
|
-- Note that 'attachSource' must be used on a "final" parse error before it can be pretty-printed.
|
||||||
-- pretty-printed.
|
|
||||||
|
|
||||||
finalErrorBundlePretty :: FinalParseErrorBundle' HledgerParseErrorData -> String
|
finalErrorBundlePretty :: FinalParseErrorBundle' HledgerParseErrorData -> String
|
||||||
finalErrorBundlePretty bundle =
|
finalErrorBundlePretty bundle =
|
||||||
concatMap showIncludeFilepath (includeFileStack bundle)
|
concatMap showIncludeFilepath (includeFileStack bundle)
|
||||||
@ -549,12 +529,9 @@ finalErrorBundlePretty bundle =
|
|||||||
where
|
where
|
||||||
showIncludeFilepath path = "in file included from " <> path <> ",\n"
|
showIncludeFilepath path = "in file included from " <> path <> ",\n"
|
||||||
|
|
||||||
-- | Supply a filepath and source text to a "final" parse error so that it
|
-- | Attach a filepath and source text to a "final" parse error so that it can be pretty-printed.
|
||||||
-- can be pretty-printed. You must ensure that you provide the appropriate
|
-- You must ensure that you provide the appropriate source text and filepath.
|
||||||
-- source text and filepath.
|
attachSource :: FilePath -> Text -> FinalParseError' e -> FinalParseErrorBundle' e
|
||||||
|
|
||||||
attachSource
|
|
||||||
:: FilePath -> Text -> FinalParseError' e -> FinalParseErrorBundle' e
|
|
||||||
attachSource filePath sourceText finalParseError = case finalParseError of
|
attachSource filePath sourceText finalParseError = case finalParseError of
|
||||||
|
|
||||||
-- A parse error thrown directly with the 'FinalError' constructor
|
-- A parse error thrown directly with the 'FinalError' constructor
|
||||||
@ -581,9 +558,9 @@ attachSource filePath sourceText finalParseError = case finalParseError of
|
|||||||
|
|
||||||
--- * Handling parse errors from include files with "final" parse errors
|
--- * Handling parse errors from include files with "final" parse errors
|
||||||
|
|
||||||
-- | Parse a file with the given parser and initial state, discarding the
|
-- | Parse an include file with the given parser and initial state,
|
||||||
-- final state and re-throwing any parse errors as "final" parse errors.
|
-- discarding the resulting state,
|
||||||
|
-- and re-throwing any parse errors as final parse errors with the file's info attached.
|
||||||
parseIncludeFile
|
parseIncludeFile
|
||||||
:: Monad m
|
:: Monad m
|
||||||
=> StateT st (ParsecT HledgerParseErrorData Text (ExceptT FinalParseError m)) a
|
=> StateT st (ParsecT HledgerParseErrorData Text (ExceptT FinalParseError m)) a
|
||||||
@ -591,26 +568,22 @@ parseIncludeFile
|
|||||||
-> FilePath
|
-> FilePath
|
||||||
-> Text
|
-> Text
|
||||||
-> StateT st (ParsecT HledgerParseErrorData Text (ExceptT FinalParseError m)) a
|
-> StateT st (ParsecT HledgerParseErrorData Text (ExceptT FinalParseError m)) a
|
||||||
parseIncludeFile parser initialState filepath text =
|
parseIncludeFile parser initialState filepath text = catchError parser' handler
|
||||||
catchError parser' handler
|
|
||||||
where
|
where
|
||||||
parser' = do
|
parser' = do
|
||||||
eResult <- lift $ lift $
|
eResult <- lift $ lift $ runParserT (evalStateT parser initialState) filepath text
|
||||||
runParserT (evalStateT parser initialState) filepath text
|
|
||||||
case eResult of
|
case eResult of
|
||||||
Left parseErrorBundle -> throwError $ FinalBundle parseErrorBundle
|
Left parseErrorBundle -> throwError $ FinalBundle parseErrorBundle
|
||||||
Right result -> pure result
|
Right result -> pure result
|
||||||
|
|
||||||
-- Attach source and filepath of the include file to its parse errors
|
-- Attach source and filepath of the include file to its parse errors
|
||||||
handler e = throwError $ FinalBundleWithStack $ attachSource filepath text e
|
handler e = throwError $ FinalBundleWithStack $ attachSource filepath text e
|
||||||
|
|
||||||
|
|
||||||
--- * Helpers
|
--- * Helpers
|
||||||
|
|
||||||
-- Like megaparsec's 'initialState', but instead for 'PosState'. Used when
|
-- | Like megaparsec's 'initialState', but instead for 'PosState'.
|
||||||
-- constructing 'ParseErrorBundle's. The values for "tab width" and "line
|
-- Used when constructing 'ParseErrorBundle's.
|
||||||
-- prefix" are taken from 'initialState'.
|
-- The values for "tab width" and "line prefix" are taken from 'initialState'.
|
||||||
|
|
||||||
initialPosState :: FilePath -> Text -> PosState Text
|
initialPosState :: FilePath -> Text -> PosState Text
|
||||||
initialPosState filePath sourceText = PosState
|
initialPosState filePath sourceText = PosState
|
||||||
{ pstateInput = sourceText
|
{ pstateInput = sourceText
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user