lib: refactor includedirectivep to avoid "re-parsing"

This commit is contained in:
Alex Chen 2018-06-06 00:21:00 -06:00
parent b034fa7ca9
commit 4a9e418b7a
2 changed files with 41 additions and 36 deletions

View File

@ -75,7 +75,7 @@ import Prelude ()
import "base-compat-batteries" Prelude.Compat hiding (readFile) import "base-compat-batteries" Prelude.Compat hiding (readFile)
import qualified Control.Exception as C import qualified Control.Exception as C
import Control.Monad import Control.Monad
import Control.Monad.Except (ExceptT(..), runExceptT, throwError) import Control.Monad.Except (ExceptT(..))
import Control.Monad.State.Strict import Control.Monad.State.Strict
import qualified Data.Map.Strict as M import qualified Data.Map.Strict as M
import Data.Text (Text) import Data.Text (Text)
@ -190,36 +190,40 @@ includedirectivep :: MonadIO m => ErroringJournalParser m ()
includedirectivep = do includedirectivep = do
string "include" string "include"
lift (skipSome spacenonewline) lift (skipSome spacenonewline)
filename <- lift restofline filename <- T.unpack <$> takeWhileP Nothing (/= '\n') -- don't consume newline yet
parentpos <- getPosition
parentj <- get -- save parent state
parentParserState <- getParserState
parentj <- get
let childj = newJournalWithParseStateFrom parentj let childj = newJournalWithParseStateFrom parentj
(ej :: Either String ParsedJournal) <- parentpos <- getPosition
liftIO $ runExceptT $ do
let curdir = takeDirectory (sourceName parentpos) -- read child input
filepath <- expandPath curdir filename `orRethrowIOError` (show parentpos ++ " locating " ++ filename) let curdir = takeDirectory (sourceName parentpos)
txt <- readFilePortably filepath `orRethrowIOError` (show parentpos ++ " reading " ++ filepath) filepath <- lift $ expandPath curdir filename `orRethrowIOError` (show parentpos ++ " locating " ++ filename)
(ej1::Either (ParseError Char CustomErr) ParsedJournal) <- childInput <- lift $ readFilePortably filepath `orRethrowIOError` (show parentpos ++ " reading " ++ filepath)
runParserT
(evalStateT -- set child state
(choiceInState setInput childInput
[journalp pushPosition $ initialPos filepath
,timeclockfilep put childj
,timedotfilep
-- can't include a csv file yet, that reader is special -- parse include file
]) let parsers = [ journalp
childj) , timeclockfilep
filepath txt , timedotfilep
either ] -- can't include a csv file yet, that reader is special
(throwError updatedChildj <- journalAddFile (filepath, childInput) <$>
. ((show parentpos ++ " in included file " ++ show filename ++ ":\n") ++) region (withSource childInput) (choiceInState parsers)
. parseErrorPretty)
(return . journalAddFile (filepath, txt)) -- restore parent state, prepending the child's parse info
ej1 setParserState parentParserState
case ej of put $ updatedChildj <> parentj
Left e -> throwError e -- discard child's parse info, prepend its (reversed) list data, combine other fields
Right childj -> modify' (\parentj -> childj <> parentj)
-- discard child's parse info, prepend its (reversed) list data, combine other fields void newline
newJournalWithParseStateFrom :: Journal -> Journal newJournalWithParseStateFrom :: Journal -> Journal
newJournalWithParseStateFrom j = mempty{ newJournalWithParseStateFrom j = mempty{
@ -234,11 +238,12 @@ newJournalWithParseStateFrom j = mempty{
-- | 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.
orRethrowIOError :: IO a -> String -> ExceptT String IO a orRethrowIOError :: MonadIO m => IO a -> String -> TextParser m a
orRethrowIOError io msg = orRethrowIOError io msg = do
ExceptT $ eResult <- liftIO $ (Right <$> io) `C.catch` \(e::C.IOException) -> pure $ Left $ printf "%s:\n%s" msg (show e)
(Right <$> io) case eResult of
`C.catch` \(e::C.IOException) -> return $ Left $ printf "%s:\n%s" msg (show e) Right res -> pure res
Left errMsg -> fail errMsg
accountdirectivep :: JournalParser m () accountdirectivep :: JournalParser m ()
accountdirectivep = do accountdirectivep = do

View File

@ -83,7 +83,7 @@ reader = Reader
parse :: InputOpts -> FilePath -> Text -> ExceptT String IO Journal parse :: InputOpts -> FilePath -> Text -> ExceptT String IO Journal
parse = parseAndFinaliseJournal timeclockfilep parse = parseAndFinaliseJournal timeclockfilep
timeclockfilep :: ErroringJournalParser IO ParsedJournal timeclockfilep :: MonadIO m => ErroringJournalParser m ParsedJournal
timeclockfilep = do many timeclockitemp timeclockfilep = do many timeclockitemp
eof eof
j@Journal{jparsetimeclockentries=es} <- get j@Journal{jparsetimeclockentries=es} <- get