mirror of
https://github.com/simonmichael/hledger.git
synced 2024-11-09 21:22:26 +03:00
lib: refactor amount parsers to minimize backtracking
- inline `spaceamountormissingp` into `postingp` - combine `rightsymbolamountp` and `nosymbolamountp` - the multiplier symbol '*' for an amount must now always preceed a sign '-' [breaking change] - make amount parser labels more generic to simplify error messages
This commit is contained in:
parent
89b1fd7de3
commit
e3a755b5b1
@ -517,14 +517,84 @@ test_spaceandamountormissingp = do
|
||||
-- right, optional unit or total price, and optional (ignored)
|
||||
-- ledger-style balance assertion or fixed lot price declaration.
|
||||
amountp :: JournalParser m Amount
|
||||
amountp = do
|
||||
amountp = label "amount" $ do
|
||||
amount <- amountwithoutpricep
|
||||
price <- priceamountp
|
||||
pure $ amount { aprice = price }
|
||||
|
||||
amountwithoutpricep :: JournalParser m Amount
|
||||
amountwithoutpricep =
|
||||
try leftsymbolamountp <|> try rightsymbolamountp <|> nosymbolamountp
|
||||
amountwithoutpricep = do
|
||||
(mult, sign) <- lift $ (,) <$> multiplierp <*> signp
|
||||
leftsymbolamountp mult sign <|> rightornosymbolamountp mult sign
|
||||
|
||||
where
|
||||
|
||||
leftsymbolamountp :: Bool -> (Decimal -> Decimal) -> JournalParser m Amount
|
||||
leftsymbolamountp mult sign = label "amount" $ do
|
||||
c <- lift commoditysymbolp
|
||||
suggestedStyle <- getAmountStyle c
|
||||
|
||||
commodityspaced <- lift $ skipMany' spacenonewline
|
||||
|
||||
sign2 <- lift $ signp
|
||||
posBeforeNum <- getPosition
|
||||
ambiguousRawNum <- lift rawnumberp
|
||||
mExponent <- lift $ optional $ try exponentp
|
||||
posAfterNum <- getPosition
|
||||
let numRegion = (posBeforeNum, posAfterNum)
|
||||
|
||||
(q,prec,mdec,mgrps) <- lift $
|
||||
interpretNumber numRegion suggestedStyle ambiguousRawNum mExponent
|
||||
let s = amountstyle{ascommodityside=L, ascommodityspaced=commodityspaced, asprecision=prec, asdecimalpoint=mdec, asdigitgroups=mgrps}
|
||||
return $ Amount c (sign (sign2 q)) NoPrice s mult
|
||||
|
||||
rightornosymbolamountp
|
||||
:: Bool -> (Decimal -> Decimal) -> JournalParser m Amount
|
||||
rightornosymbolamountp mult sign = label "amount" $ do
|
||||
posBeforeNum <- getPosition
|
||||
ambiguousRawNum <- lift rawnumberp
|
||||
mExponent <- lift $ optional $ try exponentp
|
||||
posAfterNum <- getPosition
|
||||
let numRegion = (posBeforeNum, posAfterNum)
|
||||
|
||||
mSpaceAndCommodity <- lift $ optional $ try $
|
||||
(,) <$> skipMany' spacenonewline <*> commoditysymbolp
|
||||
|
||||
case mSpaceAndCommodity of
|
||||
Just (commodityspaced, c) -> do
|
||||
suggestedStyle <- getAmountStyle c
|
||||
(q,prec,mdec,mgrps) <- lift $
|
||||
interpretNumber numRegion suggestedStyle ambiguousRawNum mExponent
|
||||
|
||||
let s = amountstyle{ascommodityside=R, ascommodityspaced=commodityspaced, asprecision=prec, asdecimalpoint=mdec, asdigitgroups=mgrps}
|
||||
return $ Amount c (sign q) NoPrice s mult
|
||||
|
||||
Nothing -> do
|
||||
suggestedStyle <- getDefaultAmountStyle
|
||||
(q,prec,mdec,mgrps) <- lift $
|
||||
interpretNumber numRegion suggestedStyle ambiguousRawNum mExponent
|
||||
|
||||
-- apply the most recently seen default commodity and style to this commodityless amount
|
||||
defcs <- getDefaultCommodityAndStyle
|
||||
let (c,s) = case defcs of
|
||||
Just (defc,defs) -> (defc, defs{asprecision=max (asprecision defs) prec})
|
||||
Nothing -> ("", amountstyle{asprecision=prec, asdecimalpoint=mdec, asdigitgroups=mgrps})
|
||||
return $ Amount c (sign q) NoPrice s mult
|
||||
|
||||
-- For reducing code duplication. Doesn't parse anything. Has the type
|
||||
-- of a parser only in order to throw parse errors (for convenience).
|
||||
interpretNumber
|
||||
:: (SourcePos, SourcePos)
|
||||
-> Maybe AmountStyle
|
||||
-> Either AmbiguousNumber RawNumber
|
||||
-> Maybe Int
|
||||
-> TextParser m (Quantity, Int, Maybe Char, Maybe DigitGroupStyle)
|
||||
interpretNumber posRegion suggestedStyle ambiguousNum mExp =
|
||||
let rawNum = either (disambiguateNumber suggestedStyle) id ambiguousNum
|
||||
in case fromRawNumber rawNum mExp of
|
||||
Left errMsg -> uncurry parseErrorAtRegion posRegion errMsg
|
||||
Right res -> pure res
|
||||
|
||||
|
||||
#ifdef TESTS
|
||||
test_amountp = do
|
||||
@ -569,50 +639,6 @@ skipMany' p = go False
|
||||
then go True
|
||||
else pure isNull
|
||||
|
||||
leftsymbolamountp :: JournalParser m Amount
|
||||
leftsymbolamountp = do
|
||||
sign <- lift signp
|
||||
m <- lift multiplierp
|
||||
c <- lift commoditysymbolp
|
||||
suggestedStyle <- getAmountStyle c
|
||||
commodityspaced <- lift $ skipMany' spacenonewline
|
||||
(q,prec,mdec,mgrps) <- lift $ numberp suggestedStyle
|
||||
let s = amountstyle{ascommodityside=L, ascommodityspaced=commodityspaced, asprecision=prec, asdecimalpoint=mdec, asdigitgroups=mgrps}
|
||||
return $ Amount c (sign q) NoPrice s m
|
||||
<?> "left-symbol amount"
|
||||
|
||||
rightsymbolamountp :: JournalParser m Amount
|
||||
rightsymbolamountp = do
|
||||
m <- lift multiplierp
|
||||
sign <- lift signp
|
||||
ambiguousRawNum <- lift rawnumberp
|
||||
mExponent <- lift $ optional $ try exponentp
|
||||
commodityspaced <- lift $ skipMany' spacenonewline
|
||||
c <- lift commoditysymbolp
|
||||
suggestedStyle <- getAmountStyle c
|
||||
|
||||
let rawNum = either (disambiguateNumber suggestedStyle) id ambiguousRawNum
|
||||
(q, prec, mdec, mgrps) <- case fromRawNumber rawNum mExponent of
|
||||
Left errMsg -> fail errMsg
|
||||
Right res -> pure res
|
||||
|
||||
let s = amountstyle{ascommodityside=R, ascommodityspaced=commodityspaced, asprecision=prec, asdecimalpoint=mdec, asdigitgroups=mgrps}
|
||||
return $ Amount c (sign q) NoPrice s m
|
||||
<?> "right-symbol amount"
|
||||
|
||||
nosymbolamountp :: JournalParser m Amount
|
||||
nosymbolamountp = do
|
||||
m <- lift multiplierp
|
||||
suggestedStyle <- getDefaultAmountStyle
|
||||
(q,prec,mdec,mgrps) <- lift $ numberp suggestedStyle
|
||||
-- apply the most recently seen default commodity and style to this commodityless amount
|
||||
defcs <- getDefaultCommodityAndStyle
|
||||
let (c,s) = case defcs of
|
||||
Just (defc,defs) -> (defc, defs{asprecision=max (asprecision defs) prec})
|
||||
Nothing -> ("", amountstyle{asprecision=prec, asdecimalpoint=mdec, asdigitgroups=mgrps})
|
||||
return $ Amount c q NoPrice s m
|
||||
<?> "no-symbol amount"
|
||||
|
||||
commoditysymbolp :: TextParser m CommoditySymbol
|
||||
commoditysymbolp =
|
||||
quotedcommoditysymbolp <|> simplecommoditysymbolp <?> "commodity symbol"
|
||||
|
@ -636,7 +636,8 @@ postingp mTransactionYear = do
|
||||
account <- modifiedaccountnamep
|
||||
return (status, account)
|
||||
let (ptype, account') = (accountNamePostingType account, textUnbracket account)
|
||||
amount <- spaceandamountormissingp
|
||||
lift (skipMany spacenonewline)
|
||||
amount <- option missingmixedamt $ Mixed . (:[]) <$> amountp
|
||||
massertion <- partialbalanceassertionp
|
||||
_ <- fixedlotpricep
|
||||
lift (skipMany spacenonewline)
|
||||
|
Loading…
Reference in New Issue
Block a user