lib: organise JournalReader a bit
This commit is contained in:
		
							parent
							
								
									50aeb90596
								
							
						
					
					
						commit
						9946e7df88
					
				@ -1,6 +1,9 @@
 | 
				
			|||||||
-- {-# OPTIONS_GHC -F -pgmF htfpp #-}
 | 
					-- * doc
 | 
				
			||||||
{-# LANGUAGE CPP, RecordWildCards, NoMonoLocalBinds, ScopedTypeVariables #-}
 | 
					-- lines beginning "-- *" are collapsible orgstruct nodes. Emacs users:
 | 
				
			||||||
{-# LANGUAGE FlexibleContexts #-}
 | 
					-- (add-hook 'haskell-mode-hook
 | 
				
			||||||
 | 
					--   (lambda () (set-variable 'orgstruct-heading-prefix-regexp "-- " t))
 | 
				
			||||||
 | 
					--   'orgstruct-mode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{-|
 | 
					{-|
 | 
				
			||||||
 | 
					
 | 
				
			||||||
A reader for hledger's journal file format
 | 
					A reader for hledger's journal file format
 | 
				
			||||||
@ -17,12 +20,19 @@ reader should handle many ledger files as well. Example:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
-}
 | 
					-}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- * module
 | 
				
			||||||
 | 
					-- {-# OPTIONS_GHC -F -pgmF htfpp #-}
 | 
				
			||||||
 | 
					{-# LANGUAGE CPP, RecordWildCards, NoMonoLocalBinds, ScopedTypeVariables, FlexibleContexts #-}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module Hledger.Read.JournalReader (
 | 
					module Hledger.Read.JournalReader (
 | 
				
			||||||
  -- * Reader
 | 
					  -- * Reader
 | 
				
			||||||
  reader,
 | 
					  reader,
 | 
				
			||||||
  -- * Parsers used elsewhere
 | 
					
 | 
				
			||||||
  parseAndFinaliseJournal,
 | 
					  -- * Parsing utils
 | 
				
			||||||
  genericSourcePos,
 | 
					  genericSourcePos,
 | 
				
			||||||
 | 
					  parseAndFinaliseJournal,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  -- * Parsers used elsewhere
 | 
				
			||||||
  getParentAccount,
 | 
					  getParentAccount,
 | 
				
			||||||
  journalp,
 | 
					  journalp,
 | 
				
			||||||
  directivep,
 | 
					  directivep,
 | 
				
			||||||
@ -51,6 +61,7 @@ module Hledger.Read.JournalReader (
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
where
 | 
					where
 | 
				
			||||||
 | 
					-- * imports
 | 
				
			||||||
import Prelude ()
 | 
					import Prelude ()
 | 
				
			||||||
import Prelude.Compat hiding (readFile)
 | 
					import Prelude.Compat hiding (readFile)
 | 
				
			||||||
import qualified Control.Exception as C
 | 
					import qualified Control.Exception as C
 | 
				
			||||||
@ -77,7 +88,7 @@ import Hledger.Data
 | 
				
			|||||||
import Hledger.Utils
 | 
					import Hledger.Utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- standard reader exports
 | 
					-- * reader
 | 
				
			||||||
 | 
					
 | 
				
			||||||
reader :: Reader
 | 
					reader :: Reader
 | 
				
			||||||
reader = Reader format detect parse
 | 
					reader = Reader format detect parse
 | 
				
			||||||
@ -96,7 +107,7 @@ detect f s
 | 
				
			|||||||
parse :: Maybe FilePath -> Bool -> FilePath -> String -> ExceptT String IO Journal
 | 
					parse :: Maybe FilePath -> Bool -> FilePath -> String -> ExceptT String IO Journal
 | 
				
			||||||
parse _ = parseAndFinaliseJournal journalp
 | 
					parse _ = parseAndFinaliseJournal journalp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- parsing utils
 | 
					-- * parsing utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
genericSourcePos :: SourcePos -> GenericSourcePos
 | 
					genericSourcePos :: SourcePos -> GenericSourcePos
 | 
				
			||||||
genericSourcePos p = GenericSourcePos (sourceName p) (sourceLine p) (sourceColumn p)
 | 
					genericSourcePos p = GenericSourcePos (sourceName p) (sourceLine p) (sourceColumn p)
 | 
				
			||||||
@ -110,7 +121,7 @@ combineJournalUpdates us = foldl' (flip (.)) id <$> sequence us
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
-- cf http://neilmitchell.blogspot.co.uk/2015/09/detecting-space-leaks.html
 | 
					-- cf http://neilmitchell.blogspot.co.uk/2015/09/detecting-space-leaks.html
 | 
				
			||||||
-- $ ./devprof +RTS -K576K -xc
 | 
					-- $ ./devprof +RTS -K576K -xc
 | 
				
			||||||
-- *** Exception (reporting due to +RTS -xc): (THUNK_STATIC), stack trace: 
 | 
					-- Exception (reporting due to +RTS -xc): (THUNK_STATIC), stack trace:
 | 
				
			||||||
--   Hledger.Read.JournalReader.combineJournalUpdates.\,
 | 
					--   Hledger.Read.JournalReader.combineJournalUpdates.\,
 | 
				
			||||||
--   called from Hledger.Read.JournalReader.combineJournalUpdates,
 | 
					--   called from Hledger.Read.JournalReader.combineJournalUpdates,
 | 
				
			||||||
--   called from Hledger.Read.JournalReader.fixedlotprice,
 | 
					--   called from Hledger.Read.JournalReader.fixedlotprice,
 | 
				
			||||||
@ -222,7 +233,8 @@ getIndex = liftM ctxTransactionIndex getState
 | 
				
			|||||||
setIndex :: Stream [Char] m Char => Integer -> ParsecT [Char] JournalContext m ()
 | 
					setIndex :: Stream [Char] m Char => Integer -> ParsecT [Char] JournalContext m ()
 | 
				
			||||||
setIndex i = modifyState (\ctx -> ctx{ctxTransactionIndex=i})
 | 
					setIndex i = modifyState (\ctx -> ctx{ctxTransactionIndex=i})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- parsers
 | 
					-- * parsers
 | 
				
			||||||
 | 
					-- ** journal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- | Top-level journal parser. Returns a single composite, I/O performing,
 | 
					-- | Top-level journal parser. Returns a single composite, I/O performing,
 | 
				
			||||||
-- error-raising "JournalUpdate" (and final "JournalContext") which can be
 | 
					-- error-raising "JournalUpdate" (and final "JournalContext") which can be
 | 
				
			||||||
@ -246,6 +258,8 @@ journalp = do
 | 
				
			|||||||
                           , multilinecommentp >> return (return id)
 | 
					                           , multilinecommentp >> return (return id)
 | 
				
			||||||
                           ] <?> "journal transaction or directive"
 | 
					                           ] <?> "journal transaction or directive"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- ** directives
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- cf http://ledger-cli.org/3.0/doc/ledger3.html#Command-Directives
 | 
					-- cf http://ledger-cli.org/3.0/doc/ledger3.html#Command-Directives
 | 
				
			||||||
directivep :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate
 | 
					directivep :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate
 | 
				
			||||||
directivep = do
 | 
					directivep = do
 | 
				
			||||||
@ -428,6 +442,8 @@ commodityconversiondirectivep = do
 | 
				
			|||||||
  restofline
 | 
					  restofline
 | 
				
			||||||
  return $ return id
 | 
					  return $ return id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- ** transactions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
modifiertransactionp :: ParsecT [Char] JournalContext (ExceptT String IO) ModifierTransaction
 | 
					modifiertransactionp :: ParsecT [Char] JournalContext (ExceptT String IO) ModifierTransaction
 | 
				
			||||||
modifiertransactionp = do
 | 
					modifiertransactionp = do
 | 
				
			||||||
  char '=' <?> "modifier transaction"
 | 
					  char '=' <?> "modifier transaction"
 | 
				
			||||||
@ -462,8 +478,6 @@ transactionp = do
 | 
				
			|||||||
  setIndex i'
 | 
					  setIndex i'
 | 
				
			||||||
  return $ txnTieKnot $ Transaction i' sourcepos date edate status code description comment tags postings ""
 | 
					  return $ txnTieKnot $ Transaction i' sourcepos date edate status code description comment tags postings ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
descriptionp = many (noneOf ";\n")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef TESTS
 | 
					#ifdef TESTS
 | 
				
			||||||
test_transactionp = do
 | 
					test_transactionp = do
 | 
				
			||||||
    let s `gives` t = do
 | 
					    let s `gives` t = do
 | 
				
			||||||
@ -557,6 +571,22 @@ test_transactionp = do
 | 
				
			|||||||
    assertEqual 2 (let Right t = p in length $ tpostings t)
 | 
					    assertEqual 2 (let Right t = p in length $ tpostings t)
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					statusp :: Stream [Char] m Char => ParsecT [Char] JournalContext m ClearedStatus
 | 
				
			||||||
 | 
					statusp =
 | 
				
			||||||
 | 
					  choice'
 | 
				
			||||||
 | 
					    [ many spacenonewline >> char '*' >> return Cleared
 | 
				
			||||||
 | 
					    , many spacenonewline >> char '!' >> return Pending
 | 
				
			||||||
 | 
					    , return Uncleared
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					    <?> "cleared status"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					codep :: Stream [Char] m Char => ParsecT [Char] JournalContext m String
 | 
				
			||||||
 | 
					codep = try (do { many1 spacenonewline; char '(' <?> "codep"; code <- anyChar `manyTill` char ')'; return code } ) <|> return ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					descriptionp = many (noneOf ";\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- ** dates
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- | Parse a date in YYYY/MM/DD format.
 | 
					-- | Parse a date in YYYY/MM/DD format.
 | 
				
			||||||
-- Hyphen (-) and period (.) are also allowed as separators.
 | 
					-- Hyphen (-) and period (.) are also allowed as separators.
 | 
				
			||||||
-- The year may be omitted if a default year has been set.
 | 
					-- The year may be omitted if a default year has been set.
 | 
				
			||||||
@ -632,17 +662,7 @@ secondarydatep primarydate = do
 | 
				
			|||||||
  edate <- withDefaultYear primarydate datep
 | 
					  edate <- withDefaultYear primarydate datep
 | 
				
			||||||
  return edate
 | 
					  return edate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
statusp :: Stream [Char] m Char => ParsecT [Char] JournalContext m ClearedStatus
 | 
					-- ** postings
 | 
				
			||||||
statusp =
 | 
					 | 
				
			||||||
  choice'
 | 
					 | 
				
			||||||
    [ many spacenonewline >> char '*' >> return Cleared
 | 
					 | 
				
			||||||
    , many spacenonewline >> char '!' >> return Pending
 | 
					 | 
				
			||||||
    , return Uncleared
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
    <?> "cleared status"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
codep :: Stream [Char] m Char => ParsecT [Char] JournalContext m String
 | 
					 | 
				
			||||||
codep = try (do { many1 spacenonewline; char '(' <?> "codep"; code <- anyChar `manyTill` char ')'; return code } ) <|> return ""
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Parse the following whitespace-beginning lines as postings, posting tags, and/or comments.
 | 
					-- Parse the following whitespace-beginning lines as postings, posting tags, and/or comments.
 | 
				
			||||||
postingsp :: Stream [Char] m Char => ParsecT [Char] JournalContext m [Posting]
 | 
					postingsp :: Stream [Char] m Char => ParsecT [Char] JournalContext m [Posting]
 | 
				
			||||||
@ -669,7 +689,7 @@ postingp = do
 | 
				
			|||||||
  ctx <- getState
 | 
					  ctx <- getState
 | 
				
			||||||
  comment <- try followingcommentp <|> (newline >> return "")
 | 
					  comment <- try followingcommentp <|> (newline >> return "")
 | 
				
			||||||
  let tags = tagsInComment comment
 | 
					  let tags = tagsInComment comment
 | 
				
			||||||
  -- oh boy
 | 
					  -- parse any dates specified with tags here for good parse errors
 | 
				
			||||||
  date <- case dateValueFromTags tags of
 | 
					  date <- case dateValueFromTags tags of
 | 
				
			||||||
        Nothing -> return Nothing
 | 
					        Nothing -> return Nothing
 | 
				
			||||||
        Just v -> case runParser (datep <* eof) ctx "" v of
 | 
					        Just v -> case runParser (datep <* eof) ctx "" v of
 | 
				
			||||||
@ -739,6 +759,8 @@ test_postingp = do
 | 
				
			|||||||
    -- assertEqual (Just nullmixedamt) (pbalanceassertion p)
 | 
					    -- assertEqual (Just nullmixedamt) (pbalanceassertion p)
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- ** 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 :: Stream [Char] m Char => ParsecT [Char] JournalContext m AccountName
 | 
					modifiedaccountnamep :: Stream [Char] m Char => ParsecT [Char] JournalContext m AccountName
 | 
				
			||||||
modifiedaccountnamep = do
 | 
					modifiedaccountnamep = do
 | 
				
			||||||
@ -773,6 +795,8 @@ accountnamep = do
 | 
				
			|||||||
-- accountnamechar = notFollowedBy (oneOf "()[]") >> nonspace
 | 
					-- accountnamechar = notFollowedBy (oneOf "()[]") >> nonspace
 | 
				
			||||||
--     <?> "account name character (non-bracket, non-parenthesis, non-whitespace)"
 | 
					--     <?> "account name character (non-bracket, non-parenthesis, non-whitespace)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- ** amounts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- | 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.
 | 
				
			||||||
@ -1016,7 +1040,7 @@ numberp = do
 | 
				
			|||||||
--       assertFails ".1,"
 | 
					--       assertFails ".1,"
 | 
				
			||||||
--       assertFails ",1."
 | 
					--       assertFails ",1."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- comment parsers
 | 
					-- ** comments
 | 
				
			||||||
 | 
					
 | 
				
			||||||
multilinecommentp :: Stream [Char] m Char => ParsecT [Char] JournalContext m ()
 | 
					multilinecommentp :: Stream [Char] m Char => ParsecT [Char] JournalContext m ()
 | 
				
			||||||
multilinecommentp = do
 | 
					multilinecommentp = do
 | 
				
			||||||
@ -1057,6 +1081,8 @@ commentStartingWithp cs = do
 | 
				
			|||||||
  optional newline
 | 
					  optional newline
 | 
				
			||||||
  return l
 | 
					  return l
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- ** tags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tagsInComment :: String -> [Tag]
 | 
					tagsInComment :: String -> [Tag]
 | 
				
			||||||
tagsInComment c = concatMap tagsInCommentLine $ lines c'
 | 
					tagsInComment c = concatMap tagsInCommentLine $ lines c'
 | 
				
			||||||
  where
 | 
					  where
 | 
				
			||||||
@ -1116,6 +1142,8 @@ dateValueFromTags  ts = maybe Nothing (Just . snd) $ find ((=="date") . fst) ts
 | 
				
			|||||||
date2ValueFromTags ts = maybe Nothing (Just . snd) $ find ((=="date2") . fst) ts
 | 
					date2ValueFromTags ts = maybe Nothing (Just . snd) $ find ((=="date2") . fst) ts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- * tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tests_Hledger_Read_JournalReader = TestList $ concat [
 | 
					tests_Hledger_Read_JournalReader = TestList $ concat [
 | 
				
			||||||
    -- test_numberp
 | 
					    -- test_numberp
 | 
				
			||||||
 ]
 | 
					 ]
 | 
				
			||||||
@ -1213,4 +1241,3 @@ tests_Hledger_Read_JournalReader = TestList $ concat [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 ]]
 | 
					 ]]
 | 
				
			||||||
-}
 | 
					-}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user