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