mirror of
https://github.com/simonmichael/hledger.git
synced 2024-09-18 17:57:11 +03:00
dev: lib: clarify some amount parsers; describe Ledger lot notation
amountpwithmultiplier -> amountp' amountpnolotpricesp -> amountnobasisp amountwithoutpricep -> simpleamountp priceamountp -> costp
This commit is contained in:
parent
04d5813a41
commit
aa54c3273a
@ -76,9 +76,9 @@ module Hledger.Read.Common (
|
||||
-- ** amounts
|
||||
spaceandamountormissingp,
|
||||
amountp,
|
||||
amountpwithmultiplier,
|
||||
amountp',
|
||||
commoditysymbolp,
|
||||
priceamountp,
|
||||
costp,
|
||||
balanceassertionp,
|
||||
lotpricep,
|
||||
numberp,
|
||||
@ -675,21 +675,42 @@ singlespacep = spacenonewline *> notFollowedBy spacenonewline
|
||||
|
||||
--- *** amounts
|
||||
|
||||
-- | Parse whitespace then an amount, with an optional left or right
|
||||
-- currency symbol and optional price, or return the special
|
||||
-- "missing" marker amount.
|
||||
-- | Parse whitespace then an amount, or return the special "missing" marker amount.
|
||||
spaceandamountormissingp :: JournalParser m MixedAmount
|
||||
spaceandamountormissingp =
|
||||
option missingmixedamt $ try $ do
|
||||
lift $ skipNonNewlineSpaces1
|
||||
mixedAmount <$> amountp
|
||||
|
||||
-- | Parse a single-commodity amount, with optional symbol on the left
|
||||
-- or right, followed by, in any order: an optional transaction price,
|
||||
-- an optional ledger-style lot price, and/or an optional ledger-style
|
||||
-- lot date. A lot price and lot date will be ignored.
|
||||
-- | Parse a single-commodity amount, applying the default commodity if there is no commodity symbol;
|
||||
-- optionally followed by, in any order:
|
||||
-- a Ledger-style cost and/or one or more parts of a Ledger-style cost basis:
|
||||
-- lot cost, lot date, and/or lot note (we loosely call this triple the lot's cost basis).
|
||||
-- The cost basis makes it a lot rather than just an amount. The cost basis info is discarded for now.
|
||||
-- The main amount's sign is significant; here are the possibilities and their interpretation:
|
||||
-- @
|
||||
--
|
||||
-- To parse the amount's quantity (number) we need to know which character
|
||||
-- AMT -- acquiring an amount
|
||||
-- AMT COST -- acquiring an amount at some cost
|
||||
-- AMT COST COSTBASIS -- acquiring a lot at some cost, saving its cost basis
|
||||
-- AMT COSTBASIS COST -- like the above
|
||||
-- AMT COSTBASIS -- like the above with cost same as the cost basis
|
||||
--
|
||||
-- -AMT -- releasing an amount
|
||||
-- -AMT SELLPRICE -- releasing an amount at some selling price
|
||||
-- -AMT SELLPRICE COSTBASISSEL -- releasing a lot at some selling price, selecting it by its cost basis
|
||||
-- -AMT COSTBASISSEL SELLPRICE -- like the above
|
||||
-- -AMT COSTBASISSEL -- like the above with selling price same as the selected lot's cost basis amount
|
||||
--
|
||||
-- COST/SELLPRICE can be @ UNITAMT, @@ TOTALAMT, (@) UNITAMT, or (@@) TOTALAMT. The () are ignored.
|
||||
-- COSTBASIS is one or more of {LOTCOST}, [LOTDATE], (LOTNOTE), in any order, with LOTCOST defaulting to COST.
|
||||
-- COSTBASISSEL is one or more of {LOTCOST}, [LOTDATE], (LOTNOTE), in any order.
|
||||
-- {LOTCOST} can be {UNITAMT}, {{TOTALAMT}}, {=UNITAMT}, or {{=TOTALAMT}}. The = is ignored.
|
||||
-- Rule of thumb: curly braces, parentheses, and/or square brackets in an amount means a Ledger-style cost basis is involved.
|
||||
--
|
||||
-- @
|
||||
--
|
||||
-- To parse an amount's numeric quantity we need to know which character
|
||||
-- represents a decimal mark. We find it in one of three ways:
|
||||
--
|
||||
-- 1. If a decimal mark has been set explicitly in the journal parse state,
|
||||
@ -700,38 +721,45 @@ spaceandamountormissingp =
|
||||
--
|
||||
-- 3. Otherwise we will parse any valid decimal mark appearing in the
|
||||
-- number, as long as the number appears well formed.
|
||||
-- (This means we handle files with any supported decimal mark without configuration,
|
||||
-- but it also allows different decimal marks in different amounts,
|
||||
-- which is a bit too loose. There's an open issue.)
|
||||
--
|
||||
-- Note 3 is the default zero-config case; it means we automatically handle
|
||||
-- files with any supported decimal mark, but it also allows different decimal marks
|
||||
-- in different amounts, which is a bit too loose. There's an open issue.
|
||||
amountp :: JournalParser m Amount
|
||||
amountp = amountpwithmultiplier False
|
||||
amountp = amountp' False
|
||||
|
||||
amountpwithmultiplier :: Bool -> JournalParser m Amount
|
||||
amountpwithmultiplier mult = label "amount" $ do
|
||||
-- An amount with optional cost and/or cost basis, as described above.
|
||||
-- A flag indicates whether we are parsing a multiplier amount;
|
||||
-- if not, a commodity-less amount will have the default commodity applied to it.
|
||||
amountp' :: Bool -> JournalParser m Amount
|
||||
amountp' mult = label "amount" $ do
|
||||
let spaces = lift $ skipNonNewlineSpaces
|
||||
amt <- amountwithoutpricep mult <* spaces
|
||||
amt <- simpleamountp mult <* spaces
|
||||
(mprice, _elotprice, _elotdate) <- runPermutation $
|
||||
(,,) <$> toPermutationWithDefault Nothing (Just <$> priceamountp amt <* spaces)
|
||||
(,,) <$> toPermutationWithDefault Nothing (Just <$> costp amt <* spaces)
|
||||
<*> toPermutationWithDefault Nothing (Just <$> lotpricep <* spaces)
|
||||
<*> toPermutationWithDefault Nothing (Just <$> lotdatep <* spaces)
|
||||
pure $ amt { aprice = mprice }
|
||||
|
||||
amountpnolotpricesp :: JournalParser m Amount
|
||||
amountpnolotpricesp = label "amount" $ do
|
||||
-- An amount with optional cost, but no cost basis.
|
||||
amountnobasisp :: JournalParser m Amount
|
||||
amountnobasisp = label "amount" $ do
|
||||
let spaces = lift $ skipNonNewlineSpaces
|
||||
amt <- amountwithoutpricep False
|
||||
amt <- simpleamountp False
|
||||
spaces
|
||||
mprice <- optional $ priceamountp amt <* spaces
|
||||
mprice <- optional $ costp amt <* spaces
|
||||
pure $ amt { aprice = mprice }
|
||||
|
||||
amountwithoutpricep :: Bool -> JournalParser m Amount
|
||||
amountwithoutpricep mult = do
|
||||
-- An amount with no cost or cost basis.
|
||||
-- A flag indicates whether we are parsing a multiplier amount;
|
||||
-- if not, a commodity-less amount will have the default commodity applied to it.
|
||||
simpleamountp :: Bool -> JournalParser m Amount
|
||||
simpleamountp mult = do
|
||||
sign <- lift signp
|
||||
leftsymbolamountp sign <|> rightornosymbolamountp sign
|
||||
|
||||
where
|
||||
|
||||
-- An amount with commodity symbol on the left.
|
||||
leftsymbolamountp :: (Decimal -> Decimal) -> JournalParser m Amount
|
||||
leftsymbolamountp sign = label "amount" $ do
|
||||
c <- lift commoditysymbolp
|
||||
@ -750,6 +778,9 @@ amountwithoutpricep mult = do
|
||||
let s = amountstyle{ascommodityside=L, ascommodityspaced=commodityspaced, asprecision=prec, asdecimalpoint=mdec, asdigitgroups=mgrps}
|
||||
return nullamt{acommodity=c, aquantity=sign (sign2 q), astyle=s, aprice=Nothing}
|
||||
|
||||
-- An amount with commodity symbol on the right or no commodity symbol.
|
||||
-- A no-symbol amount will have the default commodity applied to it
|
||||
-- unless we are parsing a multiplier amount (*AMT).
|
||||
rightornosymbolamountp :: (Decimal -> Decimal) -> JournalParser m Amount
|
||||
rightornosymbolamountp sign = label "amount" $ do
|
||||
offBeforeNum <- getOffset
|
||||
@ -837,8 +868,10 @@ quotedcommoditysymbolp =
|
||||
simplecommoditysymbolp :: TextParser m CommoditySymbol
|
||||
simplecommoditysymbolp = takeWhile1P Nothing (not . isNonsimpleCommodityChar)
|
||||
|
||||
priceamountp :: Amount -> JournalParser m AmountPrice
|
||||
priceamountp baseAmt = label "transaction price" $ do
|
||||
-- | Ledger-style cost notation:
|
||||
-- @ UNITAMT, @@ TOTALAMT, (@) UNITAMT, or (@@) TOTALAMT. The () are ignored.
|
||||
costp :: Amount -> JournalParser m AmountPrice
|
||||
costp baseAmt = label "transaction price" $ do
|
||||
-- https://www.ledger-cli.org/3.0/doc/ledger3.html#Virtual-posting-costs
|
||||
parenthesised <- option False $ char '(' >> pure True
|
||||
char '@'
|
||||
@ -846,7 +879,7 @@ priceamountp baseAmt = label "transaction price" $ do
|
||||
when parenthesised $ void $ char ')'
|
||||
|
||||
lift skipNonNewlineSpaces
|
||||
priceAmount <- amountwithoutpricep False -- <?> "unpriced amount (specifying a price)"
|
||||
priceAmount <- simpleamountp False -- <?> "unpriced amount (specifying a price)"
|
||||
|
||||
let amtsign' = signum $ aquantity baseAmt
|
||||
amtsign = if amtsign' == 0 then 1 else amtsign'
|
||||
@ -863,9 +896,9 @@ balanceassertionp = do
|
||||
istotal <- fmap isJust $ optional $ try $ char '='
|
||||
isinclusive <- fmap isJust $ optional $ try $ char '*'
|
||||
lift skipNonNewlineSpaces
|
||||
-- this amount can have a price; balance assertions ignore it,
|
||||
-- but balance assignments will use it
|
||||
a <- amountpnolotpricesp <?> "amount (for a balance assertion or assignment)"
|
||||
-- this amount can have a cost, but not a cost basis.
|
||||
-- balance assertions ignore it, but balance assignments will use it
|
||||
a <- amountnobasisp <?> "amount (for a balance assertion or assignment)"
|
||||
return BalanceAssertion
|
||||
{ baamount = a
|
||||
, batotal = istotal
|
||||
@ -883,7 +916,7 @@ lotpricep = label "ledger-style lot price" $ do
|
||||
doublebrace <- option False $ char '{' >> pure True
|
||||
_fixed <- fmap isJust $ optional $ lift skipNonNewlineSpaces >> char '='
|
||||
lift skipNonNewlineSpaces
|
||||
_a <- amountwithoutpricep False
|
||||
_a <- simpleamountp False
|
||||
lift skipNonNewlineSpaces
|
||||
char '}'
|
||||
when (doublebrace) $ void $ char '}'
|
||||
|
@ -821,7 +821,7 @@ postingphelper isPostingRule mTransactionYear = do
|
||||
let (ptype, account') = (accountNamePostingType account, textUnbracket account)
|
||||
lift skipNonNewlineSpaces
|
||||
mult <- if isPostingRule then multiplierp else pure False
|
||||
amt <- optional $ amountpwithmultiplier mult
|
||||
amt <- optional $ amountp' mult
|
||||
lift skipNonNewlineSpaces
|
||||
massertion <- optional balanceassertionp
|
||||
lift skipNonNewlineSpaces
|
||||
|
Loading…
Reference in New Issue
Block a user