lib: Re-implement 'includedirectivep' with the new 'ExceptT' layer

- Parse errors encountered in include files are treated as "final" parse
  errors in the parent file, preventing backtracking and fixing an issue
  in #853
This commit is contained in:
Alex Chen 2018-09-27 13:44:42 -06:00
parent 3e54fc77a4
commit 31d4e930e7

View File

@ -189,6 +189,7 @@ includedirectivep = do
void newline void newline
where where
getFilePaths :: MonadIO m => SourcePos -> FilePath -> JournalParser m [FilePath]
getFilePaths parserpos filename = do getFilePaths parserpos filename = do
curdir <- lift $ expandPath (takeDirectory $ sourceName parserpos) "" curdir <- lift $ expandPath (takeDirectory $ sourceName parserpos) ""
`orRethrowIOError` (show parserpos ++ " locating " ++ filename) `orRethrowIOError` (show parserpos ++ " locating " ++ filename)
@ -205,41 +206,32 @@ includedirectivep = do
else customFailure $ parseErrorAt parserpos $ else customFailure $ parseErrorAt parserpos $
"No existing files match pattern: " ++ filename "No existing files match pattern: " ++ filename
parseChild :: MonadIO m => SourcePos -> FilePath -> ErroringJournalParser m ()
parseChild parentpos filepath = do parseChild parentpos filepath = do
parentfilestack <- fmap sourceName . statePos <$> getParserState parentj <- get
when (filepath `elem` parentfilestack) $ customFailure $
parseErrorAt parentpos ("Cyclic include: " ++ filepath) let parentfilestack = jincludefilestack parentj
when (filepath `elem` parentfilestack) $
fail ("Cyclic include: " ++ filepath)
childInput <- lift $ readFilePortably filepath childInput <- lift $ readFilePortably filepath
`orRethrowIOError` (show parentpos ++ " reading " ++ filepath) `orRethrowIOError` (show parentpos ++ " reading " ++ filepath)
let initChildj = newJournalWithParseStateFrom filepath parentj
-- save parent state let parser = choiceInState
parentParserState <- getParserState [ journalp
parentj <- get
let childj = newJournalWithParseStateFrom parentj
-- set child state
setInput childInput
pushPosition $ initialPos filepath
put childj
-- parse include file
let parsers = [ journalp
, timeclockfilep , timeclockfilep
, timedotfilep , timedotfilep
] -- can't include a csv file yet, that reader is special ] -- can't include a csv file yet, that reader is special
updatedChildj <- journalAddFile (filepath, childInput) <$> updatedChildj <- journalAddFile (filepath, childInput) <$>
region (withSource childInput) (choiceInState parsers) parseIncludeFile parser initChildj filepath childInput
-- restore parent state, prepending the child's parse info -- discard child's parse info, combine other fields
setParserState parentParserState
put $ updatedChildj <> parentj put $ updatedChildj <> parentj
-- discard child's parse info, prepend its (reversed) list data, combine other fields
newJournalWithParseStateFrom :: Journal -> Journal newJournalWithParseStateFrom :: FilePath -> Journal -> Journal
newJournalWithParseStateFrom j = mempty{ newJournalWithParseStateFrom filepath j = mempty{
jparsedefaultyear = jparsedefaultyear j jparsedefaultyear = jparsedefaultyear j
,jparsedefaultcommodity = jparsedefaultcommodity j ,jparsedefaultcommodity = jparsedefaultcommodity j
,jparseparentaccounts = jparseparentaccounts j ,jparseparentaccounts = jparseparentaccounts j
@ -247,6 +239,7 @@ newJournalWithParseStateFrom j = mempty{
,jcommodities = jcommodities j ,jcommodities = jcommodities j
-- ,jparsetransactioncount = jparsetransactioncount j -- ,jparsetransactioncount = jparsetransactioncount j
,jparsetimeclockentries = jparsetimeclockentries j ,jparsetimeclockentries = jparsetimeclockentries j
,jincludefilestack = filepath : jincludefilestack j
} }
-- | Lift an IO action into the exception monad, rethrowing any IO -- | Lift an IO action into the exception monad, rethrowing any IO