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)
|
-- right, optional unit or total price, and optional (ignored)
|
||||||
-- ledger-style balance assertion or fixed lot price declaration.
|
-- ledger-style balance assertion or fixed lot price declaration.
|
||||||
amountp :: JournalParser m Amount
|
amountp :: JournalParser m Amount
|
||||||
amountp = do
|
amountp = label "amount" $ do
|
||||||
amount <- amountwithoutpricep
|
amount <- amountwithoutpricep
|
||||||
price <- priceamountp
|
price <- priceamountp
|
||||||
pure $ amount { aprice = price }
|
pure $ amount { aprice = price }
|
||||||
|
|
||||||
amountwithoutpricep :: JournalParser m Amount
|
amountwithoutpricep :: JournalParser m Amount
|
||||||
amountwithoutpricep =
|
amountwithoutpricep = do
|
||||||
try leftsymbolamountp <|> try rightsymbolamountp <|> nosymbolamountp
|
(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
|
#ifdef TESTS
|
||||||
test_amountp = do
|
test_amountp = do
|
||||||
@ -569,50 +639,6 @@ skipMany' p = go False
|
|||||||
then go True
|
then go True
|
||||||
else pure isNull
|
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 :: TextParser m CommoditySymbol
|
||||||
commoditysymbolp =
|
commoditysymbolp =
|
||||||
quotedcommoditysymbolp <|> simplecommoditysymbolp <?> "commodity symbol"
|
quotedcommoditysymbolp <|> simplecommoditysymbolp <?> "commodity symbol"
|
||||||
|
|||||||
@ -636,7 +636,8 @@ postingp mTransactionYear = do
|
|||||||
account <- modifiedaccountnamep
|
account <- modifiedaccountnamep
|
||||||
return (status, account)
|
return (status, account)
|
||||||
let (ptype, account') = (accountNamePostingType account, textUnbracket account)
|
let (ptype, account') = (accountNamePostingType account, textUnbracket account)
|
||||||
amount <- spaceandamountormissingp
|
lift (skipMany spacenonewline)
|
||||||
|
amount <- option missingmixedamt $ Mixed . (:[]) <$> amountp
|
||||||
massertion <- partialbalanceassertionp
|
massertion <- partialbalanceassertionp
|
||||||
_ <- fixedlotpricep
|
_ <- fixedlotpricep
|
||||||
lift (skipMany spacenonewline)
|
lift (skipMany spacenonewline)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user