journal: parse and store commodity formats

This commit is contained in:
Simon Michael 2016-05-07 09:54:01 -07:00
parent 207922a023
commit 2c0ef877eb
3 changed files with 58 additions and 6 deletions

View File

@ -127,6 +127,7 @@ nulljournal :: Journal
nulljournal = Journal { jmodifiertxns = [] nulljournal = Journal { jmodifiertxns = []
, jperiodictxns = [] , jperiodictxns = []
, jtxns = [] , jtxns = []
, jcommodities = M.fromList []
, open_timeclock_entries = [] , open_timeclock_entries = []
, jmarketprices = [] , jmarketprices = []
, final_comment_lines = [] , final_comment_lines = []

View File

@ -114,7 +114,9 @@ type CommoditySymbol = String
data Commodity = Commodity { data Commodity = Commodity {
csymbol :: CommoditySymbol, csymbol :: CommoditySymbol,
cformat :: Maybe AmountStyle cformat :: Maybe AmountStyle
} -- deriving (Eq,Ord,Typeable,Data,Generic) } deriving (Show,Eq,Data,Generic) --,Ord,Typeable,Data,Generic)
instance NFData Commodity
data Amount = Amount { data Amount = Amount {
acommodity :: CommoditySymbol, acommodity :: CommoditySymbol,
@ -257,6 +259,8 @@ data Journal = Journal {
jmodifiertxns :: [ModifierTransaction], jmodifiertxns :: [ModifierTransaction],
jperiodictxns :: [PeriodicTransaction], jperiodictxns :: [PeriodicTransaction],
jtxns :: [Transaction], jtxns :: [Transaction],
jcommoditystyles :: M.Map CommoditySymbol AmountStyle, -- ^ commodities and formats inferred from journal amounts
jcommodities :: M.Map CommoditySymbol Commodity, -- ^ commodities and formats defined by commodity directives
open_timeclock_entries :: [TimeclockEntry], open_timeclock_entries :: [TimeclockEntry],
jmarketprices :: [MarketPrice], jmarketprices :: [MarketPrice],
final_comment_lines :: String, -- ^ any trailing comments from the journal file final_comment_lines :: String, -- ^ any trailing comments from the journal file
@ -265,8 +269,7 @@ data Journal = Journal {
-- any included journal files. The main file is -- any included journal files. The main file is
-- first followed by any included files in the -- first followed by any included files in the
-- order encountered. -- order encountered.
filereadtime :: ClockTime, -- ^ when this journal was last read from its file(s) filereadtime :: ClockTime -- ^ when this journal was last read from its file(s)
jcommoditystyles :: M.Map CommoditySymbol AmountStyle -- ^ how to display amounts in each commodity
} deriving (Eq, Typeable, Data, Generic) } deriving (Eq, Typeable, Data, Generic)
instance NFData Journal instance NFData Journal

View File

@ -25,7 +25,7 @@ reader should handle many ledger files as well. Example:
-- {-# OPTIONS_GHC -F -pgmF htfpp #-} -- {-# OPTIONS_GHC -F -pgmF htfpp #-}
{-# LANGUAGE CPP, RecordWildCards, NoMonoLocalBinds, ScopedTypeVariables, FlexibleContexts, TupleSections #-} {-# LANGUAGE CPP, RecordWildCards, NamedFieldPuns, NoMonoLocalBinds, ScopedTypeVariables, FlexibleContexts, TupleSections #-}
module Hledger.Read.JournalReader ( module Hledger.Read.JournalReader (
@ -83,6 +83,7 @@ import Data.Char (isNumber)
import Data.Functor.Identity import Data.Functor.Identity
import Data.List.Compat import Data.List.Compat
import Data.List.Split (wordsBy) import Data.List.Split (wordsBy)
import qualified Data.Map.Strict as M
import Data.Maybe import Data.Maybe
import Data.Time.Calendar import Data.Time.Calendar
import Data.Time.LocalTime import Data.Time.LocalTime
@ -307,6 +308,7 @@ directivep = do
,endaliasesdirectivep ,endaliasesdirectivep
,accountdirectivep ,accountdirectivep
,applyaccountdirectivep ,applyaccountdirectivep
,commoditydirectivep
,endapplyaccountdirectivep ,endapplyaccountdirectivep
,tagdirectivep ,tagdirectivep
,endtagdirectivep ,endtagdirectivep
@ -350,17 +352,58 @@ journalAddFile :: (FilePath,String) -> Journal -> Journal
journalAddFile f j@Journal{files=fs} = j{files=fs++[f]} journalAddFile f j@Journal{files=fs} = j{files=fs++[f]}
-- NOTE: first encountered file to left, to avoid a reverse -- NOTE: first encountered file to left, to avoid a reverse
indentedlinep = many1 spacenonewline >> (rstrip <$> restofline)
accountdirectivep :: ErroringJournalParser JournalUpdate accountdirectivep :: ErroringJournalParser JournalUpdate
accountdirectivep = do accountdirectivep = do
string "account" string "account"
many1 spacenonewline many1 spacenonewline
acct <- accountnamep acct <- accountnamep
newline newline
let indentedline = many1 spacenonewline >> restofline _ <- many indentedlinep
many indentedline
pushAccount acct pushAccount acct
return $ ExceptT $ return $ Right id return $ ExceptT $ return $ Right id
-- | Terminate parsing entirely, returning the given error message
-- with the current parse position prepended.
parserError :: String -> ErroringJournalParser a
parserError s = do
pos <- getPosition
parserErrorAt pos s
-- | Terminate parsing entirely, returning the given error message
-- with the given parse position prepended.
parserErrorAt :: SourcePos -> String -> ErroringJournalParser a
parserErrorAt pos s = do
throwError $ show pos ++ ":\n" ++ s
-- | Parse a commodity directive, containing 0 or more format subdirectives.
commoditydirectivep :: ErroringJournalParser JournalUpdate
commoditydirectivep = do
string "commodity"
many1 spacenonewline
sym <- commoditysymbolp
_ <- followingcommentp
mformat <- lastMay <$> many (indented $ formatdirectivep sym)
let comm = Commodity{csymbol=sym, cformat=mformat}
return $ ExceptT $ return $ Right $ \j -> j{jcommodities=M.insert sym comm $ jcommodities j}
indented = (many1 spacenonewline >>)
-- | Parse a format (sub)directive, throwing a parse error if its
-- symbol does not match the one given.
formatdirectivep :: CommoditySymbol -> ErroringJournalParser AmountStyle
formatdirectivep expectedsym = do
string "format"
many1 spacenonewline
pos <- getPosition
Amount{acommodity,astyle} <- amountp
_ <- followingcommentp
if acommodity==expectedsym
then return astyle
else parserErrorAt pos $
printf "commodity directive symbol \"%s\" and format directive symbol \"%s\" should be the same" expectedsym acommodity
applyaccountdirectivep :: ErroringJournalParser JournalUpdate applyaccountdirectivep :: ErroringJournalParser JournalUpdate
applyaccountdirectivep = do applyaccountdirectivep = do
string "apply" >> many1 spacenonewline >> string "account" string "apply" >> many1 spacenonewline >> string "account"
@ -370,6 +413,11 @@ applyaccountdirectivep = do
pushParentAccount parent pushParentAccount parent
return $ ExceptT $ return $ Right id return $ ExceptT $ return $ Right id
data Commodity2 = Commodity2 {
csymbol :: String,
cformat :: Maybe AmountStyle
} -- deriving (Eq,Ord,Typeable,Data,Generic)
endapplyaccountdirectivep :: ErroringJournalParser JournalUpdate endapplyaccountdirectivep :: ErroringJournalParser JournalUpdate
endapplyaccountdirectivep = do endapplyaccountdirectivep = do
string "end" >> many1 spacenonewline >> string "apply" >> many1 spacenonewline >> string "account" string "end" >> many1 spacenonewline >> string "apply" >> many1 spacenonewline >> string "account"