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,49 +206,41 @@ 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)
childInput <- lift $ readFilePortably filepath let parentfilestack = jincludefilestack parentj
`orRethrowIOError` (show parentpos ++ " reading " ++ filepath) when (filepath `elem` parentfilestack) $
fail ("Cyclic include: " ++ filepath)
-- save parent state childInput <- lift $ readFilePortably filepath
parentParserState <- getParserState `orRethrowIOError` (show parentpos ++ " reading " ++ filepath)
parentj <- get let initChildj = newJournalWithParseStateFrom filepath parentj
let childj = newJournalWithParseStateFrom parentj let parser = choiceInState
[ journalp
, timeclockfilep
, timedotfilep
] -- can't include a csv file yet, that reader is special
updatedChildj <- journalAddFile (filepath, childInput) <$>
parseIncludeFile parser initChildj filepath childInput
-- set child state -- discard child's parse info, combine other fields
setInput childInput put $ updatedChildj <> parentj
pushPosition $ initialPos filepath
put childj
-- parse include file
let parsers = [ journalp
, timeclockfilep
, timedotfilep
] -- can't include a csv file yet, that reader is special
updatedChildj <- journalAddFile (filepath, childInput) <$>
region (withSource childInput) (choiceInState parsers)
-- restore parent state, prepending the child's parse info
setParserState parentParserState
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
,jparsealiases = jparsealiases j ,jparsealiases = jparsealiases j
,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
-- error with the given message prepended. -- error with the given message prepended.