module Data.Textual.Fractional
(
PositionalSystem(..)
, Binary(..)
, Octal(..)
, Decimal(..)
, Hexadecimal(..)
, UpHex(..)
, LowHex(..)
, Sign(..)
, applySign
, optMinus
, optSign
, Optional(..)
, isOptional
, isRequired
, optSlash
, fraction'
, fraction
, decExpSign
, hexExpSign
, fractional'
, fractional
) where
import Data.Maybe (isJust)
import Data.Ratio ((%))
import Control.Applicative
import Text.Printer.Fractional (Optional(..), isOptional, isRequired)
import Text.Parser.Combinators ((<?>), unexpected)
import Text.Parser.Char (CharParsing)
import qualified Text.Parser.Char as PC
import Data.Textual.Integral
optSlash ∷ (Monad μ, CharParsing μ) ⇒ μ Optional
optSlash = maybe Optional (const Required) <$> optional (PC.char '/')
fraction' ∷ (PositionalSystem s, Fractional α, Monad μ, CharParsing μ)
⇒ μ Sign
→ s
→ μ Optional
→ μ α
fraction' neg s den = (<?> "fraction") $ do
n ← number' neg s <?> "numerator"
den >>= \case
Optional →
return $ fromInteger n
Required → do
d ← (<?> "denominator") $ do
d ← nonNegative s
if d == 0 then unexpected "zero denominator"
else return d
return $ fromRational $ n % d
fraction ∷ (Fractional α, Monad μ, CharParsing μ) ⇒ μ α
fraction = fraction' optMinus Decimal optSlash
decExpSign ∷ (Monad μ, CharParsing μ) ⇒ μ (Maybe Sign)
decExpSign = optional (PC.oneOf "eE") >>= \case
Nothing → return Nothing
Just _ → Just <$> optSign
hexExpSign ∷ (Monad μ, CharParsing μ) ⇒ μ (Maybe Sign)
hexExpSign = optional (PC.oneOf "pP") >>= \case
Nothing → return Nothing
Just _ → Just <$> optSign
fractional' ∷ (PositionalSystem s, Fractional α, Monad μ, CharParsing μ)
⇒ μ Sign
→ s
→ Optional
→ μ ()
→ μ (Maybe Sign)
→ μ α
fractional' neg s ip dot eneg = (<?> (systemName s ++ "-fraction")) $ do
sign ← neg <?> "sign"
(i, f, fDigits) ← do
let integral = do
i ← nonNegative s <?> "integer part"
((i, ) . isJust) <$> optional dot
(i, hasF) ← case ip of
Optional → optional dot >>= \case
Nothing → integral
Just _ → return (0, True)
Required → integral
(f, fDigits) ←
if hasF
then do
let go !ds !f = optional digit >>= \case
Just d → go (ds + 1) (f * radix + d)
Nothing → return (f, ds)
digit >>= go (1 ∷ Int) <?> "fractional part"
else
return (0, 0)
return (i, f, fDigits)
(<?> "exponent") $ eneg >>= \case
Nothing | f == 0 → return $ fromInteger $ applySign sign i
| otherwise → return $ fromRational
$ applySign sign
$ fromInteger i + f % radix ^ fDigits
Just esign → do
e ← nnBounded Decimal
return $ applySign sign $ case esign of
NonNegative → case e fDigits of
e₁ | e₁ >= 0 → fromInteger $ i * radix ^ e + f * radix ^ e₁
| otherwise → fromRational
$ fromInteger (i * radix ^ e)
+ i % radix ^ negate e₁
NonPositive → fromRational
$ i % (radix ^ e) + f % radix ^ (fDigits + e)
where
radix = radixIn s
digit = digitIn s
fractional ∷ (Monad μ, Fractional α, CharParsing μ) ⇒ μ α
fractional = fractional' optMinus Decimal Required
(PC.char '.' *> pure ()) decExpSign