lib: move handling of exponentials into fromRawNumber [API]
- Rationale:
- The information necessary for applying exponents to a number is more
explicitly represented in the inputs to `fromRawNumber` than in the outputs
- This way, `exponentp` may simply return an `Int`
This commit is contained in:
parent
f7fd6e6525
commit
edf9cc2366
@ -98,7 +98,6 @@ import Prelude.Compat hiding (readFile)
|
|||||||
import Control.Monad.Compat
|
import Control.Monad.Compat
|
||||||
import Control.Monad.Except (ExceptT(..), runExceptT, throwError) --, catchError)
|
import Control.Monad.Except (ExceptT(..), runExceptT, throwError) --, catchError)
|
||||||
import Control.Monad.State.Strict
|
import Control.Monad.State.Strict
|
||||||
import Data.Bifunctor
|
|
||||||
import Data.Char
|
import Data.Char
|
||||||
import Data.Data
|
import Data.Data
|
||||||
import Data.Decimal (DecimalRaw (Decimal), Decimal)
|
import Data.Decimal (DecimalRaw (Decimal), Decimal)
|
||||||
@ -570,16 +569,19 @@ rightsymbolamountp = do
|
|||||||
m <- lift multiplierp
|
m <- lift multiplierp
|
||||||
sign <- lift signp
|
sign <- lift signp
|
||||||
ambiguousRawNum <- lift rawnumberp
|
ambiguousRawNum <- lift rawnumberp
|
||||||
expMod <- lift . option id $ try exponentp
|
mExponent <- lift $ optional $ try exponentp
|
||||||
commodityspaced <- lift $ skipMany' spacenonewline
|
commodityspaced <- lift $ skipMany' spacenonewline
|
||||||
c <- lift commoditysymbolp
|
c <- lift commoditysymbolp
|
||||||
suggestedStyle <- getAmountStyle c
|
suggestedStyle <- getAmountStyle c
|
||||||
let (q0,prec0,mdec,mgrps) =
|
|
||||||
fromRawNumber $ either (disambiguateNumber suggestedStyle) id ambiguousRawNum
|
let rawNum = either (disambiguateNumber suggestedStyle) id ambiguousRawNum
|
||||||
(q, prec) = expMod (sign q0, prec0)
|
(q, prec, mdec, mgrps) <- case fromRawNumber rawNum mExponent of
|
||||||
|
Left errMsg -> fail errMsg
|
||||||
|
Right res -> pure res
|
||||||
|
|
||||||
p <- priceamountp
|
p <- priceamountp
|
||||||
let s = amountstyle{ascommodityside=R, ascommodityspaced=commodityspaced, asprecision=prec, asdecimalpoint=mdec, asdigitgroups=mgrps}
|
let s = amountstyle{ascommodityside=R, ascommodityspaced=commodityspaced, asprecision=prec, asdecimalpoint=mdec, asdigitgroups=mgrps}
|
||||||
return $ Amount c q p s m
|
return $ Amount c (sign q) p s m
|
||||||
<?> "right-symbol amount"
|
<?> "right-symbol amount"
|
||||||
|
|
||||||
nosymbolamountp :: Monad m => JournalParser m Amount
|
nosymbolamountp :: Monad m => JournalParser m Amount
|
||||||
@ -672,25 +674,21 @@ numberp suggestedStyle = do
|
|||||||
-- interspersed with periods, commas, or both
|
-- interspersed with periods, commas, or both
|
||||||
-- ptrace "numberp"
|
-- ptrace "numberp"
|
||||||
sign <- signp
|
sign <- signp
|
||||||
raw <- either (disambiguateNumber suggestedStyle) id <$> rawnumberp
|
rawNum <- either (disambiguateNumber suggestedStyle) id <$> rawnumberp
|
||||||
dbg8 "numberp suggestedStyle" suggestedStyle `seq` return ()
|
|
||||||
let (q, prec, decSep, groups) =
|
|
||||||
dbg8 "numberp quantity,precision,mdecimalpoint,mgrps"
|
|
||||||
$ fromRawNumber raw
|
|
||||||
mExp <- optional $ try $ exponentp
|
mExp <- optional $ try $ exponentp
|
||||||
case mExp of
|
dbg8 "numberp suggestedStyle" suggestedStyle `seq` return ()
|
||||||
Just expFunc
|
case dbg8 "numberp quantity,precision,mdecimalpoint,mgrps"
|
||||||
| isJust groups -> fail "groups and exponent are not mixable"
|
$ fromRawNumber rawNum mExp of
|
||||||
| otherwise -> let (q', prec') = expFunc (q, prec)
|
Left errMsg -> fail errMsg
|
||||||
in pure (sign q', prec', decSep, groups)
|
Right (q, p, d, g) -> pure (sign q, p, d, g)
|
||||||
Nothing -> pure (sign q, prec, decSep, groups)
|
|
||||||
<?> "numberp"
|
<?> "numberp"
|
||||||
|
|
||||||
exponentp :: TextParser m ((Quantity, Int) -> (Quantity, Int))
|
exponentp :: TextParser m Int
|
||||||
exponentp = do
|
exponentp = do
|
||||||
char' 'e'
|
char' 'e'
|
||||||
exp <- ($) <$> signp <*> (read <$> some digitChar)
|
sign <- signp
|
||||||
return $ bimap (* 10^^exp) (max 0 . subtract exp)
|
d <- decimal
|
||||||
|
pure $ sign d
|
||||||
<?> "exponentp"
|
<?> "exponentp"
|
||||||
|
|
||||||
-- | Interpret a raw number as a decimal number.
|
-- | Interpret a raw number as a decimal number.
|
||||||
@ -700,19 +698,29 @@ exponentp = do
|
|||||||
-- - the precision (number of digits after the decimal point)
|
-- - the precision (number of digits after the decimal point)
|
||||||
-- - the decimal point character, if any
|
-- - the decimal point character, if any
|
||||||
-- - the digit group style, if any (digit group character and sizes of digit groups)
|
-- - the digit group style, if any (digit group character and sizes of digit groups)
|
||||||
fromRawNumber :: RawNumber -> (Quantity, Int, Maybe Char, Maybe DigitGroupStyle)
|
fromRawNumber
|
||||||
fromRawNumber raw = case raw of
|
:: RawNumber
|
||||||
|
-> Maybe Int
|
||||||
|
-> Either String
|
||||||
|
(Quantity, Int, Maybe Char, Maybe DigitGroupStyle)
|
||||||
|
fromRawNumber raw mExp = case raw of
|
||||||
|
|
||||||
NoSeparators digitGrp mDecimals ->
|
NoSeparators digitGrp mDecimals ->
|
||||||
let decimalGrp = maybe mempty snd mDecimals
|
let decimalGrp = maybe mempty snd mDecimals
|
||||||
(quantity, precision) = toDecimal digitGrp decimalGrp
|
(quantity, precision) =
|
||||||
in (quantity, precision, fmap fst mDecimals, Nothing)
|
maybe id applyExp mExp $ toQuantity digitGrp decimalGrp
|
||||||
|
|
||||||
WithSeparators digitSep digitGrps mDecimals ->
|
in Right (quantity, precision, fmap fst mDecimals, Nothing)
|
||||||
|
|
||||||
|
WithSeparators digitSep digitGrps mDecimals -> do
|
||||||
let decimalGrp = maybe mempty snd mDecimals
|
let decimalGrp = maybe mempty snd mDecimals
|
||||||
(quantity, precision) = toDecimal (mconcat digitGrps) decimalGrp
|
|
||||||
digitGroupStyle = DigitGroups digitSep (groupSizes digitGrps)
|
digitGroupStyle = DigitGroups digitSep (groupSizes digitGrps)
|
||||||
in (quantity, precision, fmap fst mDecimals, Just digitGroupStyle)
|
|
||||||
|
let errMsg = "mixing digit separators with exponents is not allowed"
|
||||||
|
(quantity, precision) <- maybe Right (const $ const $ Left errMsg) mExp
|
||||||
|
$ toQuantity (mconcat digitGrps) decimalGrp
|
||||||
|
|
||||||
|
Right (quantity, precision, fmap fst mDecimals, Just digitGroupStyle)
|
||||||
|
|
||||||
where
|
where
|
||||||
-- Outputs digit group sizes from least significant to most significant
|
-- Outputs digit group sizes from least significant to most significant
|
||||||
@ -721,13 +729,17 @@ fromRawNumber raw = case raw of
|
|||||||
(a:b:cs) | a < b -> b:cs
|
(a:b:cs) | a < b -> b:cs
|
||||||
gs -> gs
|
gs -> gs
|
||||||
|
|
||||||
toDecimal :: DigitGrp -> DigitGrp -> (Decimal, Int)
|
toQuantity :: DigitGrp -> DigitGrp -> (Quantity, Int)
|
||||||
toDecimal preDecimalGrp postDecimalGrp = (quantity, precision)
|
toQuantity preDecimalGrp postDecimalGrp = (quantity, precision)
|
||||||
where
|
where
|
||||||
quantity = Decimal (fromIntegral precision)
|
quantity = Decimal (fromIntegral precision)
|
||||||
(digitGroupNumber $ preDecimalGrp <> postDecimalGrp)
|
(digitGroupNumber $ preDecimalGrp <> postDecimalGrp)
|
||||||
precision = digitGroupLength postDecimalGrp
|
precision = digitGroupLength postDecimalGrp
|
||||||
|
|
||||||
|
applyExp :: Int -> (Decimal, Int) -> (Decimal, Int)
|
||||||
|
applyExp exponent (quantity, precision) =
|
||||||
|
(quantity * 10^^exponent, max 0 (precision - exponent))
|
||||||
|
|
||||||
|
|
||||||
disambiguateNumber :: Maybe AmountStyle -> AmbiguousNumber -> RawNumber
|
disambiguateNumber :: Maybe AmountStyle -> AmbiguousNumber -> RawNumber
|
||||||
disambiguateNumber suggestedStyle (AmbiguousNumber grp1 sep grp2) =
|
disambiguateNumber suggestedStyle (AmbiguousNumber grp1 sep grp2) =
|
||||||
@ -1105,19 +1117,19 @@ bracketedpostingdatesp mdefdate = do
|
|||||||
-- default date is provided. A missing year in DATE2 will be inferred
|
-- default date is provided. A missing year in DATE2 will be inferred
|
||||||
-- from DATE.
|
-- from DATE.
|
||||||
--
|
--
|
||||||
-- >>> first parseErrorPretty $ rtp (bracketeddatetagsp Nothing) "[2016/1/2=3/4]"
|
-- >>> either (Left . parseErrorPretty) Right $ rtp (bracketeddatetagsp Nothing) "[2016/1/2=3/4]"
|
||||||
-- Right [("date",2016-01-02),("date2",2016-03-04)]
|
-- Right [("date",2016-01-02),("date2",2016-03-04)]
|
||||||
--
|
--
|
||||||
-- >>> first parseErrorPretty $ rtp (bracketeddatetagsp Nothing) "[1]"
|
-- >>> either (Left . parseErrorPretty) Right $ rtp (bracketeddatetagsp Nothing) "[1]"
|
||||||
-- Left ...not a bracketed date...
|
-- Left ...not a bracketed date...
|
||||||
--
|
--
|
||||||
-- >>> first parseErrorPretty $ rtp (bracketeddatetagsp Nothing) "[2016/1/32]"
|
-- >>> either (Left . parseErrorPretty) Right $ rtp (bracketeddatetagsp Nothing) "[2016/1/32]"
|
||||||
-- Left ...1:11:...well-formed but invalid date: 2016/1/32...
|
-- Left ...1:11:...well-formed but invalid date: 2016/1/32...
|
||||||
--
|
--
|
||||||
-- >>> first parseErrorPretty $ rtp (bracketeddatetagsp Nothing) "[1/31]"
|
-- >>> either (Left . parseErrorPretty) Right $ rtp (bracketeddatetagsp Nothing) "[1/31]"
|
||||||
-- Left ...1:6:...partial date 1/31 found, but the current year is unknown...
|
-- Left ...1:6:...partial date 1/31 found, but the current year is unknown...
|
||||||
--
|
--
|
||||||
-- >>> first parseErrorPretty $ rtp (bracketeddatetagsp Nothing) "[0123456789/-.=/-.=]"
|
-- >>> either (Left . parseErrorPretty) Right $ rtp (bracketeddatetagsp Nothing) "[0123456789/-.=/-.=]"
|
||||||
-- Left ...1:13:...expecting month or day...
|
-- Left ...1:13:...expecting month or day...
|
||||||
--
|
--
|
||||||
bracketeddatetagsp :: Maybe Day -> SimpleTextParser [(TagName, Day)]
|
bracketeddatetagsp :: Maybe Day -> SimpleTextParser [(TagName, Day)]
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user