cln!: Clean up journal parsing.

parseAndFinaliseJournal' has been removed. In the unlikely event you
needed it in your code, you can replace it with:
parseAndFinaliseJournal' parser iopts fp t =>
initialiseAndParseJournal parser iopts fp t
>>= liftEither . journalApplyAliases (aliasesFromOpts iopts)
>>= journalFinalise iopts fp t

Some parsers have been generalised from JournalParser to TextParser.
This commit is contained in:
Stephen Morgan 2022-03-14 14:22:27 +11:00 committed by Simon Michael
parent e91fb8e0db
commit 8968733630
3 changed files with 38 additions and 46 deletions

View File

@ -35,7 +35,7 @@ module Hledger.Read.Common (
-- * parsing utilities -- * parsing utilities
parseAndFinaliseJournal, parseAndFinaliseJournal,
parseAndFinaliseJournal', initialiseAndParseJournal,
journalFinalise, journalFinalise,
journalCheckAccountsDeclared, journalCheckAccountsDeclared,
journalCheckCommoditiesDeclared, journalCheckCommoditiesDeclared,
@ -121,7 +121,7 @@ where
--- ** imports --- ** imports
import Control.Applicative.Permutations (runPermutation, toPermutationWithDefault) import Control.Applicative.Permutations (runPermutation, toPermutationWithDefault)
import qualified Control.Monad.Fail as Fail (fail) import qualified Control.Monad.Fail as Fail (fail)
import Control.Monad.Except (ExceptT(..), liftEither, runExceptT, throwError) import Control.Monad.Except (ExceptT(..), liftEither, withExceptT)
import Control.Monad.State.Strict hiding (fail) import Control.Monad.State.Strict hiding (fail)
import Data.Bifunctor (bimap, second) import Data.Bifunctor (bimap, second)
import Data.Char (digitToInt, isDigit, isSpace) import Data.Char (digitToInt, isDigit, isSpace)
@ -144,7 +144,7 @@ import Text.Megaparsec
import Text.Megaparsec.Char (char, char', digitChar, newline, string) import Text.Megaparsec.Char (char, char', digitChar, newline, string)
import Text.Megaparsec.Char.Lexer (decimal) import Text.Megaparsec.Char.Lexer (decimal)
import Text.Megaparsec.Custom import Text.Megaparsec.Custom
(attachSource, customErrorBundlePretty, finalErrorBundlePretty, parseErrorAt, parseErrorAtRegion) (FinalParseError, attachSource, customErrorBundlePretty, finalErrorBundlePretty, parseErrorAt, parseErrorAtRegion)
import Hledger.Data import Hledger.Data
import Hledger.Query (Query(..), filterQuery, parseQueryTerm, queryEndDate, queryStartDate, queryIsDate, simplifyQuery) import Hledger.Query (Query(..), filterQuery, parseQueryTerm, queryEndDate, queryStartDate, queryIsDate, simplifyQuery)
@ -249,42 +249,32 @@ commodityStyleFromRawOpts rawOpts =
parseCommodity optStr = case amountp'' optStr of parseCommodity optStr = case amountp'' optStr of
Left _ -> Left optStr Left _ -> Left optStr
Right (Amount acommodity _ astyle _) -> Right (acommodity, astyle) Right (Amount acommodity _ astyle _) -> Right (acommodity, astyle)
-- | Given a parser to ParsedJournal, input options, file path and -- | Given a parser to ParsedJournal, input options, file path and
-- content: run the parser on the content, and finalise the result to -- content: run the parser on the content, and finalise the result to
-- get a Journal; or throw an error. -- get a Journal; or throw an error.
parseAndFinaliseJournal :: ErroringJournalParser IO ParsedJournal -> InputOpts parseAndFinaliseJournal :: ErroringJournalParser IO ParsedJournal -> InputOpts
-> FilePath -> Text -> ExceptT String IO Journal -> FilePath -> Text -> ExceptT String IO Journal
parseAndFinaliseJournal parser iopts f txt = do parseAndFinaliseJournal parser iopts f txt =
let y = first3 . toGregorian $ _ioDay iopts initialiseAndParseJournal parser iopts f txt >>= journalFinalise iopts f txt
initJournal = nulljournal{ jparsedefaultyear = Just y, jincludefilestack = [f] }
eep <- liftIO $ runExceptT $ runParserT (evalStateT parser initJournal) f txt
-- TODO: urgh.. clean this up somehow
case eep of
Left finalParseError -> throwError $ finalErrorBundlePretty $ attachSource f txt finalParseError
Right ep -> case ep of
Left e -> throwError $ customErrorBundlePretty e
Right pj -> journalFinalise iopts f txt pj
-- | Like parseAndFinaliseJournal but takes a (non-Erroring) JournalParser. -- | Given a parser to ParsedJournal, input options, file path and
-- Also, applies command-line account aliases before finalising. -- content: run the parser on the content. This is all steps of
-- Used for timeclock/timedot. -- 'parseAndFinaliseJournal' without the finalisation step, and is used when
-- TODO: get rid of this, use parseAndFinaliseJournal instead -- you need to perform other actions before finalisation, as in parsing
parseAndFinaliseJournal' :: JournalParser IO ParsedJournal -> InputOpts -- Timeclock and Timedot files.
-> FilePath -> Text -> ExceptT String IO Journal initialiseAndParseJournal :: ErroringJournalParser IO ParsedJournal -> InputOpts
parseAndFinaliseJournal' parser iopts f txt = do -> FilePath -> Text -> ExceptT String IO Journal
let y = first3 . toGregorian $ _ioDay iopts initialiseAndParseJournal parser iopts f txt =
initJournal = nulljournal prettyParseErrors $ runParserT (evalStateT parser initJournal) f txt
{ jparsedefaultyear = Just y where
, jincludefilestack = [f] } y = first3 . toGregorian $ _ioDay iopts
ep <- liftIO $ runParserT (evalStateT parser initJournal) f txt initJournal = nulljournal{jparsedefaultyear = Just y, jincludefilestack = [f]}
-- see notes above -- Flatten parse errors and final parse errors, and output each as a pretty String.
case ep of prettyParseErrors :: ExceptT FinalParseError IO (Either (ParseErrorBundle Text CustomErr) a)
Left e -> throwError $ customErrorBundlePretty e -> ExceptT String IO a
Right pj -> prettyParseErrors = withExceptT customErrorBundlePretty . liftEither
-- apply any command line account aliases. Can fail with a bad replacement pattern. <=< withExceptT (finalErrorBundlePretty . attachSource f txt)
case journalApplyAliases (aliasesFromOpts iopts) pj of
Left e -> throwError e
Right pj' -> journalFinalise iopts f txt pj'
{- HLINT ignore journalFinalise "Redundant <&>" -} -- silence this warning, the code is clearer as is {- HLINT ignore journalFinalise "Redundant <&>" -} -- silence this warning, the code is clearer as is
-- NB activates TH, may slow compilation ? https://github.com/ndmitchell/hlint/blob/master/README.md#customizing-the-hints -- NB activates TH, may slow compilation ? https://github.com/ndmitchell/hlint/blob/master/README.md#customizing-the-hints

View File

@ -58,7 +58,7 @@ where
--- ** imports --- ** imports
import Control.Monad import Control.Monad
import Control.Monad.Except (ExceptT) import Control.Monad.Except (ExceptT, liftEither)
import Control.Monad.State.Strict import Control.Monad.State.Strict
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Data.Text (Text) import Data.Text (Text)
@ -88,7 +88,9 @@ reader = Reader
-- format, saving the provided file path and the current time, or give an -- format, saving the provided file path and the current time, or give an
-- error. -- error.
parse :: InputOpts -> FilePath -> Text -> ExceptT String IO Journal parse :: InputOpts -> FilePath -> Text -> ExceptT String IO Journal
parse = parseAndFinaliseJournal' timeclockfilep parse iopts fp t = initialiseAndParseJournal timeclockfilep iopts fp t
>>= liftEither . journalApplyAliases (aliasesFromOpts iopts)
>>= journalFinalise iopts fp t
--- ** parsers --- ** parsers
@ -124,5 +126,3 @@ timeclockentryp = do
account <- fromMaybe "" <$> optional (lift skipNonNewlineSpaces1 >> modifiedaccountnamep) account <- fromMaybe "" <$> optional (lift skipNonNewlineSpaces1 >> modifiedaccountnamep)
description <- T.pack . fromMaybe "" <$> lift (optional (skipNonNewlineSpaces1 >> restofline)) description <- T.pack . fromMaybe "" <$> lift (optional (skipNonNewlineSpaces1 >> restofline))
return $ TimeclockEntry sourcepos (read [code]) datetime account description return $ TimeclockEntry sourcepos (read [code]) datetime account description

View File

@ -41,7 +41,7 @@ where
--- ** imports --- ** imports
import Control.Monad import Control.Monad
import Control.Monad.Except (ExceptT) import Control.Monad.Except (ExceptT, liftEither)
import Control.Monad.State.Strict import Control.Monad.State.Strict
import Data.Char (isSpace) import Data.Char (isSpace)
import Data.List (foldl') import Data.List (foldl')
@ -71,7 +71,9 @@ reader = Reader
-- | Parse and post-process a "Journal" from the timedot format, or give an error. -- | Parse and post-process a "Journal" from the timedot format, or give an error.
parse :: InputOpts -> FilePath -> Text -> ExceptT String IO Journal parse :: InputOpts -> FilePath -> Text -> ExceptT String IO Journal
parse = parseAndFinaliseJournal' timedotp parse iopts fp t = initialiseAndParseJournal timedotp iopts fp t
>>= liftEither . journalApplyAliases (aliasesFromOpts iopts)
>>= journalFinalise iopts fp t
--- ** utilities --- ** utilities
@ -173,7 +175,7 @@ entryp = do
lift skipNonNewlineSpaces lift skipNonNewlineSpaces
hrs <- hrs <-
try (lift followingcommentp >> return 0) try (lift followingcommentp >> return 0)
<|> (durationp <* <|> (lift durationp <*
(try (lift followingcommentp) <|> (newline >> return ""))) (try (lift followingcommentp) <|> (newline >> return "")))
mcs <- getDefaultCommodityAndStyle mcs <- getDefaultCommodityAndStyle
let let
@ -194,9 +196,9 @@ entryp = do
lift $ traceparse' "entryp" lift $ traceparse' "entryp"
return t return t
durationp :: JournalParser m Quantity durationp :: TextParser m Quantity
durationp = do durationp = do
lift $ traceparse "durationp" traceparse "durationp"
try numericquantityp <|> dotquantityp try numericquantityp <|> dotquantityp
-- <* traceparse' "durationp" -- <* traceparse' "durationp"
@ -209,12 +211,12 @@ durationp = do
-- 1.5h -- 1.5h
-- 90m -- 90m
-- @ -- @
numericquantityp :: JournalParser m Quantity numericquantityp :: TextParser m Quantity
numericquantityp = do numericquantityp = do
-- lift $ traceparse "numericquantityp" -- lift $ traceparse "numericquantityp"
(q, _, _, _) <- lift $ numberp Nothing (q, _, _, _) <- numberp Nothing
msymbol <- optional $ choice $ map (string . fst) timeUnits msymbol <- optional $ choice $ map (string . fst) timeUnits
lift skipNonNewlineSpaces skipNonNewlineSpaces
let q' = let q' =
case msymbol of case msymbol of
Nothing -> q Nothing -> q
@ -239,7 +241,7 @@ timeUnits =
-- @ -- @
-- .... .. -- .... ..
-- @ -- @
dotquantityp :: JournalParser m Quantity dotquantityp :: TextParser m Quantity
dotquantityp = do dotquantityp = do
-- lift $ traceparse "dotquantityp" -- lift $ traceparse "dotquantityp"
dots <- filter (not.isSpace) <$> many (oneOf (". " :: [Char])) dots <- filter (not.isSpace) <$> many (oneOf (". " :: [Char]))