more general parser types enabling reuse outside of IO (#439)
This commit is contained in:
		
							parent
							
								
									31e4f538c0
								
							
						
					
					
						commit
						74502f7e50
					
				| @ -57,7 +57,7 @@ runJournalParser p t = runParserT p "" t | |||||||
| rjp = runJournalParser | rjp = runJournalParser | ||||||
| 
 | 
 | ||||||
| -- | Run an error-raising journal parser with a null journal-parsing state. | -- | Run an error-raising journal parser with a null journal-parsing state. | ||||||
| runErroringJournalParser, rejp :: ErroringJournalParser a -> Text -> IO (Either String a) | runErroringJournalParser, rejp :: Monad m => ErroringJournalParser m a -> Text -> m (Either String a) | ||||||
| runErroringJournalParser p t = | runErroringJournalParser p t = | ||||||
|   runExceptT $ |   runExceptT $ | ||||||
|   runJournalParser (evalStateT p mempty) |   runJournalParser (evalStateT p mempty) | ||||||
| @ -70,7 +70,8 @@ genericSourcePos p = GenericSourcePos (sourceName p) (fromIntegral . unPos $ sou | |||||||
| 
 | 
 | ||||||
| -- | Given a megaparsec ParsedJournal parser, balance assertion flag, file | -- | Given a megaparsec ParsedJournal parser, balance assertion flag, file | ||||||
| -- path and file content: parse and post-process a Journal, or give an error. | -- path and file content: parse and post-process a Journal, or give an error. | ||||||
| parseAndFinaliseJournal :: ErroringJournalParser ParsedJournal -> Bool -> FilePath -> Text -> ExceptT String IO Journal | parseAndFinaliseJournal :: ErroringJournalParser IO ParsedJournal -> Bool | ||||||
|  |                         -> FilePath -> Text -> ExceptT String IO Journal | ||||||
| parseAndFinaliseJournal parser assrt f txt = do | parseAndFinaliseJournal parser assrt f txt = do | ||||||
|   t <- liftIO getClockTime |   t <- liftIO getClockTime | ||||||
|   y <- liftIO getCurrentYear |   y <- liftIO getCurrentYear | ||||||
| @ -98,26 +99,26 @@ setYear y = modify' (\j -> j{jparsedefaultyear=Just y}) | |||||||
| getYear :: JournalStateParser m (Maybe Year) | getYear :: JournalStateParser m (Maybe Year) | ||||||
| getYear = fmap jparsedefaultyear get | getYear = fmap jparsedefaultyear get | ||||||
| 
 | 
 | ||||||
| setDefaultCommodityAndStyle :: (CommoditySymbol,AmountStyle) -> ErroringJournalParser () | setDefaultCommodityAndStyle :: (CommoditySymbol,AmountStyle) -> JournalStateParser m () | ||||||
| setDefaultCommodityAndStyle cs = modify' (\j -> j{jparsedefaultcommodity=Just cs}) | setDefaultCommodityAndStyle cs = modify' (\j -> j{jparsedefaultcommodity=Just cs}) | ||||||
| 
 | 
 | ||||||
| getDefaultCommodityAndStyle :: JournalStateParser m (Maybe (CommoditySymbol,AmountStyle)) | getDefaultCommodityAndStyle :: JournalStateParser m (Maybe (CommoditySymbol,AmountStyle)) | ||||||
| getDefaultCommodityAndStyle = jparsedefaultcommodity `fmap` get | getDefaultCommodityAndStyle = jparsedefaultcommodity `fmap` get | ||||||
| 
 | 
 | ||||||
| pushAccount :: AccountName -> ErroringJournalParser () | pushAccount :: AccountName -> JournalStateParser m () | ||||||
| pushAccount acct = modify' (\j -> j{jaccounts = acct : jaccounts j}) | pushAccount acct = modify' (\j -> j{jaccounts = acct : jaccounts j}) | ||||||
| 
 | 
 | ||||||
| pushParentAccount :: AccountName -> ErroringJournalParser () | pushParentAccount :: AccountName -> JournalStateParser m () | ||||||
| pushParentAccount acct = modify' (\j -> j{jparseparentaccounts = acct : jparseparentaccounts j}) | pushParentAccount acct = modify' (\j -> j{jparseparentaccounts = acct : jparseparentaccounts j}) | ||||||
| 
 | 
 | ||||||
| popParentAccount :: ErroringJournalParser () | popParentAccount :: JournalStateParser m () | ||||||
| popParentAccount = do | popParentAccount = do | ||||||
|   j <- get |   j <- get | ||||||
|   case jparseparentaccounts j of |   case jparseparentaccounts j of | ||||||
|     []       -> unexpected (Tokens ('E' :| "nd of apply account block with no beginning")) |     []       -> unexpected (Tokens ('E' :| "nd of apply account block with no beginning")) | ||||||
|     (_:rest) -> put j{jparseparentaccounts=rest} |     (_:rest) -> put j{jparseparentaccounts=rest} | ||||||
| 
 | 
 | ||||||
| getParentAccount :: ErroringJournalParser AccountName | getParentAccount :: JournalStateParser m AccountName | ||||||
| getParentAccount = fmap (concatAccountNames . reverse . jparseparentaccounts) get | getParentAccount = fmap (concatAccountNames . reverse . jparseparentaccounts) get | ||||||
| 
 | 
 | ||||||
| addAccountAlias :: MonadState Journal m => AccountAlias -> m () | addAccountAlias :: MonadState Journal m => AccountAlias -> m () | ||||||
| @ -155,7 +156,7 @@ journalAddFile f j@Journal{jfiles=fs} = j{jfiles=fs++[f]} | |||||||
| 
 | 
 | ||||||
| -- | Terminate parsing entirely, returning the given error message | -- | Terminate parsing entirely, returning the given error message | ||||||
| -- with the given parse position prepended. | -- with the given parse position prepended. | ||||||
| parserErrorAt :: SourcePos -> String -> ErroringJournalParser a | parserErrorAt :: Monad m => SourcePos -> String -> ErroringJournalParser m a | ||||||
| parserErrorAt pos s = throwError $ sourcePosPretty pos ++ ":\n" ++ s | parserErrorAt pos s = throwError $ sourcePosPretty pos ++ ":\n" ++ s | ||||||
| 
 | 
 | ||||||
| --- * parsers | --- * parsers | ||||||
| @ -173,7 +174,7 @@ statusp = | |||||||
| codep :: TextParser m String | codep :: TextParser m String | ||||||
| codep = try (do { some spacenonewline; char '(' <?> "codep"; anyChar `manyTill` char ')' } ) <|> return "" | codep = try (do { some spacenonewline; char '(' <?> "codep"; anyChar `manyTill` char ')' } ) <|> return "" | ||||||
| 
 | 
 | ||||||
| descriptionp :: ErroringJournalParser String | descriptionp :: JournalStateParser m String | ||||||
| descriptionp = many (noneOf (";\n" :: [Char])) | descriptionp = many (noneOf (";\n" :: [Char])) | ||||||
| 
 | 
 | ||||||
| --- ** dates | --- ** dates | ||||||
| @ -212,7 +213,7 @@ datep = do | |||||||
| -- Seconds are optional. | -- Seconds are optional. | ||||||
| -- The timezone is optional and ignored (the time is always interpreted as a local time). | -- The timezone is optional and ignored (the time is always interpreted as a local time). | ||||||
| -- Leading zeroes may be omitted (except in a timezone). | -- Leading zeroes may be omitted (except in a timezone). | ||||||
| datetimep :: ErroringJournalParser LocalTime | datetimep :: JournalStateParser m LocalTime | ||||||
| datetimep = do | datetimep = do | ||||||
|   day <- datep |   day <- datep | ||||||
|   lift $ some spacenonewline |   lift $ some spacenonewline | ||||||
| @ -240,7 +241,7 @@ datetimep = do | |||||||
|   -- return $ localTimeToUTC tz' $ LocalTime day $ TimeOfDay h' m' (fromIntegral s') |   -- return $ localTimeToUTC tz' $ LocalTime day $ TimeOfDay h' m' (fromIntegral s') | ||||||
|   return $ LocalTime day $ TimeOfDay h' m' (fromIntegral s') |   return $ LocalTime day $ TimeOfDay h' m' (fromIntegral s') | ||||||
| 
 | 
 | ||||||
| secondarydatep :: Day -> ErroringJournalParser Day | secondarydatep :: Day -> JournalStateParser m Day | ||||||
| secondarydatep primarydate = do | secondarydatep primarydate = do | ||||||
|   char '=' |   char '=' | ||||||
|   -- kludgy way to use primary date for default year |   -- kludgy way to use primary date for default year | ||||||
| @ -266,7 +267,7 @@ secondarydatep primarydate = do | |||||||
| --- ** account names | --- ** account names | ||||||
| 
 | 
 | ||||||
| -- | Parse an account name, then apply any parent account prefix and/or account aliases currently in effect. | -- | Parse an account name, then apply any parent account prefix and/or account aliases currently in effect. | ||||||
| modifiedaccountnamep :: ErroringJournalParser AccountName | modifiedaccountnamep :: JournalStateParser m AccountName | ||||||
| modifiedaccountnamep = do | modifiedaccountnamep = do | ||||||
|   parent <- getParentAccount |   parent <- getParentAccount | ||||||
|   aliases <- getAccountAliases |   aliases <- getAccountAliases | ||||||
| @ -305,7 +306,7 @@ accountnamep = do | |||||||
| -- | Parse whitespace then an amount, with an optional left or right | -- | Parse whitespace then an amount, with an optional left or right | ||||||
| -- currency symbol and optional price, or return the special | -- currency symbol and optional price, or return the special | ||||||
| -- "missing" marker amount. | -- "missing" marker amount. | ||||||
| spaceandamountormissingp :: ErroringJournalParser MixedAmount | spaceandamountormissingp :: Monad m => JournalStateParser m MixedAmount | ||||||
| spaceandamountormissingp = | spaceandamountormissingp = | ||||||
|   try (do |   try (do | ||||||
|         lift $ some spacenonewline |         lift $ some spacenonewline | ||||||
| @ -426,7 +427,7 @@ priceamountp = | |||||||
|             return $ UnitPrice a)) |             return $ UnitPrice a)) | ||||||
|          <|> return NoPrice |          <|> return NoPrice | ||||||
| 
 | 
 | ||||||
| partialbalanceassertionp :: ErroringJournalParser (Maybe MixedAmount) | partialbalanceassertionp :: Monad m => JournalStateParser m (Maybe MixedAmount) | ||||||
| partialbalanceassertionp = | partialbalanceassertionp = | ||||||
|     try (do |     try (do | ||||||
|           lift (many spacenonewline) |           lift (many spacenonewline) | ||||||
| @ -447,7 +448,7 @@ partialbalanceassertionp = | |||||||
| --          <|> return Nothing | --          <|> return Nothing | ||||||
| 
 | 
 | ||||||
| -- http://ledger-cli.org/3.0/doc/ledger3.html#Fixing-Lot-Prices | -- http://ledger-cli.org/3.0/doc/ledger3.html#Fixing-Lot-Prices | ||||||
| fixedlotpricep :: ErroringJournalParser (Maybe Amount) | fixedlotpricep :: Monad m => JournalStateParser m (Maybe Amount) | ||||||
| fixedlotpricep = | fixedlotpricep = | ||||||
|     try (do |     try (do | ||||||
|           lift (many spacenonewline) |           lift (many spacenonewline) | ||||||
| @ -547,7 +548,7 @@ numberp = do | |||||||
| 
 | 
 | ||||||
| --- ** comments | --- ** comments | ||||||
| 
 | 
 | ||||||
| multilinecommentp :: ErroringJournalParser () | multilinecommentp :: JournalStateParser m () | ||||||
| multilinecommentp = do | multilinecommentp = do | ||||||
|   string "comment" >> lift (many spacenonewline) >> newline |   string "comment" >> lift (many spacenonewline) >> newline | ||||||
|   go |   go | ||||||
| @ -556,13 +557,13 @@ multilinecommentp = do | |||||||
|          <|> (anyLine >> go) |          <|> (anyLine >> go) | ||||||
|     anyLine = anyChar `manyTill` newline |     anyLine = anyChar `manyTill` newline | ||||||
| 
 | 
 | ||||||
| emptyorcommentlinep :: ErroringJournalParser () | emptyorcommentlinep :: JournalStateParser m () | ||||||
| emptyorcommentlinep = do | emptyorcommentlinep = do | ||||||
|   lift (many spacenonewline) >> (commentp <|> (lift (many spacenonewline) >> newline >> return "")) |   lift (many spacenonewline) >> (commentp <|> (lift (many spacenonewline) >> newline >> return "")) | ||||||
|   return () |   return () | ||||||
| 
 | 
 | ||||||
| -- | Parse a possibly multi-line comment following a semicolon. | -- | Parse a possibly multi-line comment following a semicolon. | ||||||
| followingcommentp :: ErroringJournalParser Text | followingcommentp :: JournalStateParser m Text | ||||||
| followingcommentp = | followingcommentp = | ||||||
|   -- ptrace "followingcommentp" |   -- ptrace "followingcommentp" | ||||||
|   do samelinecomment <- lift (many spacenonewline) >> (try semicoloncommentp <|> (newline >> return "")) |   do samelinecomment <- lift (many spacenonewline) >> (try semicoloncommentp <|> (newline >> return "")) | ||||||
| @ -588,7 +589,8 @@ followingcommentp = | |||||||
| -- >>> rejp (followingcommentandtagsp (Just $ fromGregorian 2000 1 2)) "; date:3/4=5/6" | -- >>> rejp (followingcommentandtagsp (Just $ fromGregorian 2000 1 2)) "; date:3/4=5/6" | ||||||
| -- Right ("date:3/4=5/6\n",[("date","3/4=5/6")],Just 2000-03-04,Nothing) | -- Right ("date:3/4=5/6\n",[("date","3/4=5/6")],Just 2000-03-04,Nothing) | ||||||
| -- | -- | ||||||
| followingcommentandtagsp :: Maybe Day -> ErroringJournalParser (Text, [Tag], Maybe Day, Maybe Day) | followingcommentandtagsp :: MonadIO m => Maybe Day | ||||||
|  |                          -> ErroringJournalParser m (Text, [Tag], Maybe Day, Maybe Day) | ||||||
| followingcommentandtagsp mdefdate = do | followingcommentandtagsp mdefdate = do | ||||||
|   -- pdbg 0 "followingcommentandtagsp" |   -- pdbg 0 "followingcommentandtagsp" | ||||||
| 
 | 
 | ||||||
| @ -623,16 +625,16 @@ followingcommentandtagsp mdefdate = do | |||||||
| 
 | 
 | ||||||
|   return (comment, tags, mdate, mdate2) |   return (comment, tags, mdate, mdate2) | ||||||
| 
 | 
 | ||||||
| commentp :: ErroringJournalParser Text | commentp :: JournalStateParser m Text | ||||||
| commentp = commentStartingWithp commentchars | commentp = commentStartingWithp commentchars | ||||||
| 
 | 
 | ||||||
| commentchars :: [Char] | commentchars :: [Char] | ||||||
| commentchars = "#;*" | commentchars = "#;*" | ||||||
| 
 | 
 | ||||||
| semicoloncommentp :: ErroringJournalParser Text | semicoloncommentp :: JournalStateParser m Text | ||||||
| semicoloncommentp = commentStartingWithp ";" | semicoloncommentp = commentStartingWithp ";" | ||||||
| 
 | 
 | ||||||
| commentStartingWithp :: [Char] -> ErroringJournalParser Text | commentStartingWithp :: [Char] -> JournalStateParser m Text | ||||||
| commentStartingWithp cs = do | commentStartingWithp cs = do | ||||||
|   -- ptrace "commentStartingWith" |   -- ptrace "commentStartingWith" | ||||||
|   oneOf cs |   oneOf cs | ||||||
| @ -714,7 +716,7 @@ tagvaluep = do | |||||||
| -- are parsed fully to give useful errors. Missing years can be | -- are parsed fully to give useful errors. Missing years can be | ||||||
| -- inferred only if a default date is provided. | -- inferred only if a default date is provided. | ||||||
| -- | -- | ||||||
| postingdatesp :: Maybe Day -> ErroringJournalParser [(TagName,Day)] | postingdatesp :: Monad m => Maybe Day -> ErroringJournalParser m [(TagName,Day)] | ||||||
| postingdatesp mdefdate = do | postingdatesp mdefdate = do | ||||||
|   -- pdbg 0 $ "postingdatesp" |   -- pdbg 0 $ "postingdatesp" | ||||||
|   let p = ((:[]) <$> datetagp mdefdate) <|> bracketeddatetagsp mdefdate |   let p = ((:[]) <$> datetagp mdefdate) <|> bracketeddatetagsp mdefdate | ||||||
| @ -739,7 +741,7 @@ postingdatesp mdefdate = do | |||||||
| -- >>> rejp (datetagp Nothing) "date:  3/4" | -- >>> rejp (datetagp Nothing) "date:  3/4" | ||||||
| -- Left ...1:9...partial date 3/4 found, but the current year is unknown... | -- Left ...1:9...partial date 3/4 found, but the current year is unknown... | ||||||
| -- | -- | ||||||
| datetagp :: Maybe Day -> ErroringJournalParser (TagName,Day) | datetagp :: Monad m => Maybe Day -> ErroringJournalParser m (TagName,Day) | ||||||
| datetagp mdefdate = do | datetagp mdefdate = do | ||||||
|   -- pdbg 0 "datetagp" |   -- pdbg 0 "datetagp" | ||||||
|   string "date" |   string "date" | ||||||
| @ -795,7 +797,7 @@ datetagp mdefdate = do | |||||||
| -- >>> rejp (bracketeddatetagsp Nothing) "[0123456789/-.=/-.=]" | -- >>> rejp (bracketeddatetagsp Nothing) "[0123456789/-.=/-.=]" | ||||||
| -- Left ...1:15:...bad date, different separators... | -- Left ...1:15:...bad date, different separators... | ||||||
| -- | -- | ||||||
| bracketeddatetagsp :: Maybe Day -> ErroringJournalParser [(TagName, Day)] | bracketeddatetagsp :: Monad m => Maybe Day -> ErroringJournalParser m [(TagName, Day)] | ||||||
| bracketeddatetagsp mdefdate = do | bracketeddatetagsp mdefdate = do | ||||||
|   -- pdbg 0 "bracketeddatetagsp" |   -- pdbg 0 "bracketeddatetagsp" | ||||||
|   char '[' |   char '[' | ||||||
|  | |||||||
| @ -127,7 +127,7 @@ parse _ = parseAndFinaliseJournal journalp | |||||||
| -- >>> rejp (journalp <* eof) "2015/1/1\n a  0\n" | -- >>> rejp (journalp <* eof) "2015/1/1\n a  0\n" | ||||||
| -- Right Journal  with 1 transactions, 1 accounts | -- Right Journal  with 1 transactions, 1 accounts | ||||||
| -- | -- | ||||||
| journalp :: ErroringJournalParser ParsedJournal | journalp :: MonadIO m => ErroringJournalParser m ParsedJournal | ||||||
| journalp = do | journalp = do | ||||||
|   many addJournalItemP |   many addJournalItemP | ||||||
|   eof |   eof | ||||||
| @ -135,7 +135,7 @@ journalp = do | |||||||
| 
 | 
 | ||||||
| -- | A side-effecting parser; parses any kind of journal item | -- | A side-effecting parser; parses any kind of journal item | ||||||
| -- and updates the parse state accordingly. | -- and updates the parse state accordingly. | ||||||
| addJournalItemP :: ErroringJournalParser () | addJournalItemP :: MonadIO m => ErroringJournalParser m () | ||||||
| addJournalItemP = | addJournalItemP = | ||||||
|   -- all journal line types can be distinguished by the first |   -- all journal line types can be distinguished by the first | ||||||
|   -- character, can use choice without backtracking |   -- character, can use choice without backtracking | ||||||
| @ -154,7 +154,7 @@ addJournalItemP = | |||||||
| -- | Parse any journal directive and update the parse state accordingly. | -- | Parse any journal directive and update the parse state accordingly. | ||||||
| -- Cf http://hledger.org/manual.html#directives, | -- Cf http://hledger.org/manual.html#directives, | ||||||
| -- http://ledger-cli.org/3.0/doc/ledger3.html#Command-Directives | -- http://ledger-cli.org/3.0/doc/ledger3.html#Command-Directives | ||||||
| directivep :: ErroringJournalParser () | directivep :: MonadIO m => ErroringJournalParser m () | ||||||
| directivep = (do | directivep = (do | ||||||
|   optional $ char '!' |   optional $ char '!' | ||||||
|   choiceInState [ |   choiceInState [ | ||||||
| @ -174,7 +174,7 @@ directivep = (do | |||||||
|    ] |    ] | ||||||
|   ) <?> "directive" |   ) <?> "directive" | ||||||
| 
 | 
 | ||||||
| includedirectivep :: ErroringJournalParser () | includedirectivep :: MonadIO m => ErroringJournalParser m () | ||||||
| includedirectivep = do | includedirectivep = do | ||||||
|   string "include" |   string "include" | ||||||
|   lift (some spacenonewline) |   lift (some spacenonewline) | ||||||
| @ -227,15 +227,17 @@ orRethrowIOError io msg = | |||||||
|     (Right <$> io) |     (Right <$> io) | ||||||
|     `C.catch` \(e::C.IOException) -> return $ Left $ printf "%s:\n%s" msg (show e) |     `C.catch` \(e::C.IOException) -> return $ Left $ printf "%s:\n%s" msg (show e) | ||||||
| 
 | 
 | ||||||
| accountdirectivep :: ErroringJournalParser () | accountdirectivep :: JournalStateParser m () | ||||||
| accountdirectivep = do | accountdirectivep = do | ||||||
|   string "account" |   string "account" | ||||||
|   lift (some spacenonewline) |   lift (some spacenonewline) | ||||||
|   acct <- lift accountnamep |   acct <- lift accountnamep | ||||||
|   newline |   newline | ||||||
|   _ <- many indentedlinep |   many indentedlinep | ||||||
|   modify' (\j -> j{jaccounts = acct : jaccounts j}) |   modify' (\j -> j{jaccounts = acct : jaccounts j}) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | indentedlinep :: JournalStateParser m String | ||||||
| indentedlinep = lift (some spacenonewline) >> (rstrip <$> lift restofline) | indentedlinep = lift (some spacenonewline) >> (rstrip <$> lift restofline) | ||||||
| 
 | 
 | ||||||
| -- | Parse a one-line or multi-line commodity directive. | -- | Parse a one-line or multi-line commodity directive. | ||||||
| @ -244,14 +246,14 @@ indentedlinep = lift (some spacenonewline) >> (rstrip <$> lift restofline) | |||||||
| -- >>> Right _ <- rejp commoditydirectivep "commodity $\n  format $1.00" | -- >>> Right _ <- rejp commoditydirectivep "commodity $\n  format $1.00" | ||||||
| -- >>> Right _ <- rejp commoditydirectivep "commodity $\n\n" -- a commodity with no format | -- >>> Right _ <- rejp commoditydirectivep "commodity $\n\n" -- a commodity with no format | ||||||
| -- >>> Right _ <- rejp commoditydirectivep "commodity $1.00\n  format $1.00" -- both, what happens ? | -- >>> Right _ <- rejp commoditydirectivep "commodity $1.00\n  format $1.00" -- both, what happens ? | ||||||
| commoditydirectivep :: ErroringJournalParser () | commoditydirectivep :: Monad m => ErroringJournalParser m () | ||||||
| commoditydirectivep = try commoditydirectiveonelinep <|> commoditydirectivemultilinep | commoditydirectivep = try commoditydirectiveonelinep <|> commoditydirectivemultilinep | ||||||
| 
 | 
 | ||||||
| -- | Parse a one-line commodity directive. | -- | Parse a one-line commodity directive. | ||||||
| -- | -- | ||||||
| -- >>> Right _ <- rejp commoditydirectiveonelinep "commodity $1.00" | -- >>> Right _ <- rejp commoditydirectiveonelinep "commodity $1.00" | ||||||
| -- >>> Right _ <- rejp commoditydirectiveonelinep "commodity $1.00 ; blah\n" | -- >>> Right _ <- rejp commoditydirectiveonelinep "commodity $1.00 ; blah\n" | ||||||
| commoditydirectiveonelinep :: ErroringJournalParser () | commoditydirectiveonelinep :: Monad m => JournalStateParser m () | ||||||
| commoditydirectiveonelinep = do | commoditydirectiveonelinep = do | ||||||
|   string "commodity" |   string "commodity" | ||||||
|   lift (some spacenonewline) |   lift (some spacenonewline) | ||||||
| @ -264,7 +266,7 @@ commoditydirectiveonelinep = do | |||||||
| -- | Parse a multi-line commodity directive, containing 0 or more format subdirectives. | -- | Parse a multi-line commodity directive, containing 0 or more format subdirectives. | ||||||
| -- | -- | ||||||
| -- >>> Right _ <- rejp commoditydirectivemultilinep "commodity $ ; blah \n  format $1.00 ; blah" | -- >>> Right _ <- rejp commoditydirectivemultilinep "commodity $ ; blah \n  format $1.00 ; blah" | ||||||
| commoditydirectivemultilinep :: ErroringJournalParser () | commoditydirectivemultilinep :: Monad m => ErroringJournalParser m () | ||||||
| commoditydirectivemultilinep = do | commoditydirectivemultilinep = do | ||||||
|   string "commodity" |   string "commodity" | ||||||
|   lift (some spacenonewline) |   lift (some spacenonewline) | ||||||
| @ -278,7 +280,7 @@ commoditydirectivemultilinep = do | |||||||
| 
 | 
 | ||||||
| -- | Parse a format (sub)directive, throwing a parse error if its | -- | Parse a format (sub)directive, throwing a parse error if its | ||||||
| -- symbol does not match the one given. | -- symbol does not match the one given. | ||||||
| formatdirectivep :: CommoditySymbol -> ErroringJournalParser AmountStyle | formatdirectivep :: Monad m => CommoditySymbol -> ErroringJournalParser m AmountStyle | ||||||
| formatdirectivep expectedsym = do | formatdirectivep expectedsym = do | ||||||
|   string "format" |   string "format" | ||||||
|   lift (some spacenonewline) |   lift (some spacenonewline) | ||||||
| @ -290,7 +292,7 @@ formatdirectivep expectedsym = do | |||||||
|     else parserErrorAt pos $ |     else parserErrorAt pos $ | ||||||
|          printf "commodity directive symbol \"%s\" and format directive symbol \"%s\" should be the same" expectedsym acommodity |          printf "commodity directive symbol \"%s\" and format directive symbol \"%s\" should be the same" expectedsym acommodity | ||||||
| 
 | 
 | ||||||
| applyaccountdirectivep :: ErroringJournalParser () | applyaccountdirectivep :: JournalStateParser m () | ||||||
| applyaccountdirectivep = do | applyaccountdirectivep = do | ||||||
|   string "apply" >> lift (some spacenonewline) >> string "account" |   string "apply" >> lift (some spacenonewline) >> string "account" | ||||||
|   lift (some spacenonewline) |   lift (some spacenonewline) | ||||||
| @ -298,12 +300,12 @@ applyaccountdirectivep = do | |||||||
|   newline |   newline | ||||||
|   pushParentAccount parent |   pushParentAccount parent | ||||||
| 
 | 
 | ||||||
| endapplyaccountdirectivep :: ErroringJournalParser () | endapplyaccountdirectivep :: JournalStateParser m () | ||||||
| endapplyaccountdirectivep = do | endapplyaccountdirectivep = do | ||||||
|   string "end" >> lift (some spacenonewline) >> string "apply" >> lift (some spacenonewline) >> string "account" |   string "end" >> lift (some spacenonewline) >> string "apply" >> lift (some spacenonewline) >> string "account" | ||||||
|   popParentAccount |   popParentAccount | ||||||
| 
 | 
 | ||||||
| aliasdirectivep :: ErroringJournalParser () | aliasdirectivep :: JournalStateParser m () | ||||||
| aliasdirectivep = do | aliasdirectivep = do | ||||||
|   string "alias" |   string "alias" | ||||||
|   lift (some spacenonewline) |   lift (some spacenonewline) | ||||||
| @ -334,12 +336,12 @@ regexaliasp = do | |||||||
|   repl <- rstrip <$> anyChar `manyTill` eolof |   repl <- rstrip <$> anyChar `manyTill` eolof | ||||||
|   return $ RegexAlias re repl |   return $ RegexAlias re repl | ||||||
| 
 | 
 | ||||||
| endaliasesdirectivep :: ErroringJournalParser () | endaliasesdirectivep :: JournalStateParser m () | ||||||
| endaliasesdirectivep = do | endaliasesdirectivep = do | ||||||
|   string "end aliases" |   string "end aliases" | ||||||
|   clearAccountAliases |   clearAccountAliases | ||||||
| 
 | 
 | ||||||
| tagdirectivep :: ErroringJournalParser () | tagdirectivep :: JournalStateParser m () | ||||||
| tagdirectivep = do | tagdirectivep = do | ||||||
|   string "tag" <?> "tag directive" |   string "tag" <?> "tag directive" | ||||||
|   lift (some spacenonewline) |   lift (some spacenonewline) | ||||||
| @ -347,13 +349,13 @@ tagdirectivep = do | |||||||
|   lift restofline |   lift restofline | ||||||
|   return () |   return () | ||||||
| 
 | 
 | ||||||
| endtagdirectivep :: ErroringJournalParser () | endtagdirectivep :: JournalStateParser m () | ||||||
| endtagdirectivep = do | endtagdirectivep = do | ||||||
|   (string "end tag" <|> string "pop") <?> "end tag or pop directive" |   (string "end tag" <|> string "pop") <?> "end tag or pop directive" | ||||||
|   lift restofline |   lift restofline | ||||||
|   return () |   return () | ||||||
| 
 | 
 | ||||||
| defaultyeardirectivep :: ErroringJournalParser () | defaultyeardirectivep :: JournalStateParser m () | ||||||
| defaultyeardirectivep = do | defaultyeardirectivep = do | ||||||
|   char 'Y' <?> "default year" |   char 'Y' <?> "default year" | ||||||
|   lift (many spacenonewline) |   lift (many spacenonewline) | ||||||
| @ -362,7 +364,7 @@ defaultyeardirectivep = do | |||||||
|   failIfInvalidYear y |   failIfInvalidYear y | ||||||
|   setYear y' |   setYear y' | ||||||
| 
 | 
 | ||||||
| defaultcommoditydirectivep :: ErroringJournalParser () | defaultcommoditydirectivep :: Monad m => JournalStateParser m () | ||||||
| defaultcommoditydirectivep = do | defaultcommoditydirectivep = do | ||||||
|   char 'D' <?> "default commodity" |   char 'D' <?> "default commodity" | ||||||
|   lift (some spacenonewline) |   lift (some spacenonewline) | ||||||
| @ -370,7 +372,7 @@ defaultcommoditydirectivep = do | |||||||
|   lift restofline |   lift restofline | ||||||
|   setDefaultCommodityAndStyle (acommodity, astyle) |   setDefaultCommodityAndStyle (acommodity, astyle) | ||||||
| 
 | 
 | ||||||
| marketpricedirectivep :: ErroringJournalParser MarketPrice | marketpricedirectivep :: Monad m => JournalStateParser m MarketPrice | ||||||
| marketpricedirectivep = do | marketpricedirectivep = do | ||||||
|   char 'P' <?> "market price" |   char 'P' <?> "market price" | ||||||
|   lift (many spacenonewline) |   lift (many spacenonewline) | ||||||
| @ -382,7 +384,7 @@ marketpricedirectivep = do | |||||||
|   lift restofline |   lift restofline | ||||||
|   return $ MarketPrice date symbol price |   return $ MarketPrice date symbol price | ||||||
| 
 | 
 | ||||||
| ignoredpricecommoditydirectivep :: ErroringJournalParser () | ignoredpricecommoditydirectivep :: JournalStateParser m () | ||||||
| ignoredpricecommoditydirectivep = do | ignoredpricecommoditydirectivep = do | ||||||
|   char 'N' <?> "ignored-price commodity" |   char 'N' <?> "ignored-price commodity" | ||||||
|   lift (some spacenonewline) |   lift (some spacenonewline) | ||||||
| @ -390,7 +392,7 @@ ignoredpricecommoditydirectivep = do | |||||||
|   lift restofline |   lift restofline | ||||||
|   return () |   return () | ||||||
| 
 | 
 | ||||||
| commodityconversiondirectivep :: ErroringJournalParser () | commodityconversiondirectivep :: Monad m => JournalStateParser m () | ||||||
| commodityconversiondirectivep = do | commodityconversiondirectivep = do | ||||||
|   char 'C' <?> "commodity conversion" |   char 'C' <?> "commodity conversion" | ||||||
|   lift (some spacenonewline) |   lift (some spacenonewline) | ||||||
| @ -404,7 +406,7 @@ commodityconversiondirectivep = do | |||||||
| 
 | 
 | ||||||
| --- ** transactions | --- ** transactions | ||||||
| 
 | 
 | ||||||
| modifiertransactionp :: ErroringJournalParser ModifierTransaction | modifiertransactionp :: MonadIO m => ErroringJournalParser m ModifierTransaction | ||||||
| modifiertransactionp = do | modifiertransactionp = do | ||||||
|   char '=' <?> "modifier transaction" |   char '=' <?> "modifier transaction" | ||||||
|   lift (many spacenonewline) |   lift (many spacenonewline) | ||||||
| @ -412,7 +414,7 @@ modifiertransactionp = do | |||||||
|   postings <- postingsp Nothing |   postings <- postingsp Nothing | ||||||
|   return $ ModifierTransaction valueexpr postings |   return $ ModifierTransaction valueexpr postings | ||||||
| 
 | 
 | ||||||
| periodictransactionp :: ErroringJournalParser PeriodicTransaction | periodictransactionp :: MonadIO m => ErroringJournalParser m PeriodicTransaction | ||||||
| periodictransactionp = do | periodictransactionp = do | ||||||
|   char '~' <?> "periodic transaction" |   char '~' <?> "periodic transaction" | ||||||
|   lift (many spacenonewline) |   lift (many spacenonewline) | ||||||
| @ -421,7 +423,7 @@ periodictransactionp = do | |||||||
|   return $ PeriodicTransaction periodexpr postings |   return $ PeriodicTransaction periodexpr postings | ||||||
| 
 | 
 | ||||||
| -- | Parse a (possibly unbalanced) transaction. | -- | Parse a (possibly unbalanced) transaction. | ||||||
| transactionp :: ErroringJournalParser Transaction | transactionp :: MonadIO m => ErroringJournalParser m Transaction | ||||||
| transactionp = do | transactionp = do | ||||||
|   -- ptrace "transactionp" |   -- ptrace "transactionp" | ||||||
|   sourcepos <- genericSourcePos <$> getPosition |   sourcepos <- genericSourcePos <$> getPosition | ||||||
| @ -533,7 +535,7 @@ test_transactionp = do | |||||||
| 
 | 
 | ||||||
| -- Parse the following whitespace-beginning lines as postings, posting | -- Parse the following whitespace-beginning lines as postings, posting | ||||||
| -- tags, and/or comments (inferring year, if needed, from the given date). | -- tags, and/or comments (inferring year, if needed, from the given date). | ||||||
| postingsp :: Maybe Day -> ErroringJournalParser [Posting] | postingsp :: MonadIO m => Maybe Day -> ErroringJournalParser m [Posting] | ||||||
| postingsp mdate = many (try $ postingp mdate) <?> "postings" | postingsp mdate = many (try $ postingp mdate) <?> "postings" | ||||||
| 
 | 
 | ||||||
| -- linebeginningwithspaces :: Monad m => JournalParser m String | -- linebeginningwithspaces :: Monad m => JournalParser m String | ||||||
| @ -543,7 +545,7 @@ postingsp mdate = many (try $ postingp mdate) <?> "postings" | |||||||
| --   cs <- lift restofline | --   cs <- lift restofline | ||||||
| --   return $ sp ++ (c:cs) ++ "\n" | --   return $ sp ++ (c:cs) ++ "\n" | ||||||
| 
 | 
 | ||||||
| postingp :: Maybe Day -> ErroringJournalParser Posting | postingp :: MonadIO m => Maybe Day -> ErroringJournalParser m Posting | ||||||
| postingp mtdate = do | postingp mtdate = do | ||||||
|   -- pdbg 0 "postingp" |   -- pdbg 0 "postingp" | ||||||
|   lift (some spacenonewline) |   lift (some spacenonewline) | ||||||
|  | |||||||
| @ -82,7 +82,7 @@ reader = Reader | |||||||
| parse :: Maybe FilePath -> Bool -> FilePath -> Text -> ExceptT String IO Journal | parse :: Maybe FilePath -> Bool -> FilePath -> Text -> ExceptT String IO Journal | ||||||
| parse _ = parseAndFinaliseJournal timeclockfilep | parse _ = parseAndFinaliseJournal timeclockfilep | ||||||
| 
 | 
 | ||||||
| timeclockfilep :: ErroringJournalParser ParsedJournal | timeclockfilep :: ErroringJournalParser IO ParsedJournal | ||||||
| timeclockfilep = do many timeclockitemp | timeclockfilep = do many timeclockitemp | ||||||
|                     eof |                     eof | ||||||
|                     j@Journal{jparsetimeclockentries=es} <- get |                     j@Journal{jparsetimeclockentries=es} <- get | ||||||
| @ -105,7 +105,7 @@ timeclockfilep = do many timeclockitemp | |||||||
|                           ] <?> "timeclock entry, or default year or historical price directive" |                           ] <?> "timeclock entry, or default year or historical price directive" | ||||||
| 
 | 
 | ||||||
| -- | Parse a timeclock entry. | -- | Parse a timeclock entry. | ||||||
| timeclockentryp :: ErroringJournalParser TimeclockEntry | timeclockentryp :: JournalStateParser m TimeclockEntry | ||||||
| timeclockentryp = do | timeclockentryp = do | ||||||
|   sourcepos <- genericSourcePos <$> lift getPosition |   sourcepos <- genericSourcePos <$> lift getPosition | ||||||
|   code <- oneOf ("bhioO" :: [Char]) |   code <- oneOf ("bhioO" :: [Char]) | ||||||
|  | |||||||
| @ -51,6 +51,7 @@ import Hledger.Utils hiding (ptrace) | |||||||
| -- easier to toggle this here sometimes | -- easier to toggle this here sometimes | ||||||
| -- import qualified Hledger.Utils (ptrace) | -- import qualified Hledger.Utils (ptrace) | ||||||
| -- ptrace = Hledger.Utils.ptrace | -- ptrace = Hledger.Utils.ptrace | ||||||
|  | ptrace :: Monad m => a -> m a | ||||||
| ptrace = return | ptrace = return | ||||||
| 
 | 
 | ||||||
| reader :: Reader | reader :: Reader | ||||||
| @ -65,12 +66,12 @@ reader = Reader | |||||||
| parse :: Maybe FilePath -> Bool -> FilePath -> Text -> ExceptT String IO Journal | parse :: Maybe FilePath -> Bool -> FilePath -> Text -> ExceptT String IO Journal | ||||||
| parse _ = parseAndFinaliseJournal timedotfilep | parse _ = parseAndFinaliseJournal timedotfilep | ||||||
| 
 | 
 | ||||||
| timedotfilep :: ErroringJournalParser ParsedJournal | timedotfilep :: JournalStateParser m ParsedJournal | ||||||
| timedotfilep = do many timedotfileitemp | timedotfilep = do many timedotfileitemp | ||||||
|                   eof |                   eof | ||||||
|                   get |                   get | ||||||
|     where |     where | ||||||
|       timedotfileitemp :: ErroringJournalParser () |       timedotfileitemp :: JournalStateParser m () | ||||||
|       timedotfileitemp = do |       timedotfileitemp = do | ||||||
|         ptrace "timedotfileitemp" |         ptrace "timedotfileitemp" | ||||||
|         choice [ |         choice [ | ||||||
| @ -88,7 +89,7 @@ addTransactions ts j = foldl' (flip ($)) j (map addTransaction ts) | |||||||
| -- biz.research . | -- biz.research . | ||||||
| -- inc.client1  .... .... .... .... .... .... | -- inc.client1  .... .... .... .... .... .... | ||||||
| -- @ | -- @ | ||||||
| timedotdayp :: ErroringJournalParser [Transaction] | timedotdayp :: JournalStateParser m [Transaction] | ||||||
| timedotdayp = do | timedotdayp = do | ||||||
|   ptrace " timedotdayp" |   ptrace " timedotdayp" | ||||||
|   d <- datep <* lift eolof |   d <- datep <* lift eolof | ||||||
| @ -100,7 +101,7 @@ timedotdayp = do | |||||||
| -- @ | -- @ | ||||||
| -- fos.haskell  .... .. | -- fos.haskell  .... .. | ||||||
| -- @ | -- @ | ||||||
| timedotentryp :: ErroringJournalParser Transaction | timedotentryp :: JournalStateParser m Transaction | ||||||
| timedotentryp = do | timedotentryp = do | ||||||
|   ptrace "  timedotentryp" |   ptrace "  timedotentryp" | ||||||
|   pos <- genericSourcePos <$> getPosition |   pos <- genericSourcePos <$> getPosition | ||||||
| @ -124,14 +125,14 @@ timedotentryp = do | |||||||
|         } |         } | ||||||
|   return t |   return t | ||||||
| 
 | 
 | ||||||
| timedotdurationp :: ErroringJournalParser Quantity | timedotdurationp :: JournalStateParser m Quantity | ||||||
| timedotdurationp = try timedotnumberp <|> timedotdotsp | timedotdurationp = try timedotnumberp <|> timedotdotsp | ||||||
| 
 | 
 | ||||||
| -- | Parse a duration written as a decimal number of hours (optionally followed by the letter h). | -- | Parse a duration written as a decimal number of hours (optionally followed by the letter h). | ||||||
| -- @ | -- @ | ||||||
| -- 1.5h | -- 1.5h | ||||||
| -- @ | -- @ | ||||||
| timedotnumberp :: ErroringJournalParser Quantity | timedotnumberp :: JournalStateParser m Quantity | ||||||
| timedotnumberp = do | timedotnumberp = do | ||||||
|    (q, _, _, _) <- lift numberp |    (q, _, _, _) <- lift numberp | ||||||
|    lift (many spacenonewline) |    lift (many spacenonewline) | ||||||
| @ -143,7 +144,7 @@ timedotnumberp = do | |||||||
| -- @ | -- @ | ||||||
| -- .... .. | -- .... .. | ||||||
| -- @ | -- @ | ||||||
| timedotdotsp :: ErroringJournalParser Quantity | timedotdotsp :: JournalStateParser m Quantity | ||||||
| timedotdotsp = do | timedotdotsp = do | ||||||
|   dots <- filter (not.isSpace) <$> many (oneOf (". " :: [Char])) |   dots <- filter (not.isSpace) <$> many (oneOf (". " :: [Char])) | ||||||
|   return $ (/4) $ fromIntegral $ length dots |   return $ (/4) $ fromIntegral $ length dots | ||||||
|  | |||||||
| @ -22,7 +22,7 @@ type JournalStateParser m a = StateT Journal (ParsecT Dec Text m) a | |||||||
| type JournalParser a = StateT Journal (ParsecT Dec Text Identity) a | type JournalParser a = StateT Journal (ParsecT Dec Text Identity) a | ||||||
| 
 | 
 | ||||||
| -- | A journal parser that runs in IO and can throw an error mid-parse. | -- | A journal parser that runs in IO and can throw an error mid-parse. | ||||||
| type ErroringJournalParser a = StateT Journal (ParsecT Dec Text (ExceptT String IO)) a | type ErroringJournalParser m a = StateT Journal (ParsecT Dec Text (ExceptT String m)) a | ||||||
| 
 | 
 | ||||||
| -- | Backtracking choice, use this when alternatives share a prefix. | -- | Backtracking choice, use this when alternatives share a prefix. | ||||||
| -- Consumes no input if all choices fail. | -- Consumes no input if all choices fail. | ||||||
|  | |||||||
| @ -56,3 +56,5 @@ hledger print -f personal.journal -f a.timeclock -f b.timedot | |||||||
|     (b.bb)          1.00 |     (b.bb)          1.00 | ||||||
| 
 | 
 | ||||||
| >>>=0 | >>>=0 | ||||||
|  | 
 | ||||||
|  | u | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user