{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE CPP #-} module Data.ByteString.Read.Fractional ( -- * functions fractional , double -- * raw functions , fractional10 , fractional' ) where import GHC.TypeLits.Compat import Data.Proxy.Compat import Data.ByteString.Read.Class as C -- $setup -- >>> :set -XDataKinds -XOverloadedStrings -- >>> import qualified Data.ByteString as S -- >>> import qualified Data.ByteString.Lazy as L integral :: forall proxy n r s. (Radix n, ReadFractional r, Ord (Fraction r), Num (Fraction r), Source s) => proxy n -> s -> (Fraction r, Int, Int, s) integral pn = loop 0 0 0 where pr :: Proxy r pr = Proxy loop !i !d !ad !s | C.null s = (i, d, ad, s) | not (isDigit pn (C.head s)) = (i, d, ad, s) | maybe False (i >=) (maxValue pr) = loop i d (ad + 1) (C.tail s) | otherwise = loop (i * fromIntegral (natVal pn) + (fromIntegral $ unsafeToDigit pn (C.head s) :: Fraction r)) (d+1) ad (C.tail s) {-# INLINABLE integral #-} toFractional :: (Radix b, ReadFractional r, Fractional r) => proxy b -> Fraction r -> Fraction r -> Int -> Int -> r toFractional p q r du d = fromFraction q * radix ^ du + fromFraction r / radix ^ d where radix = fromIntegral (natVal p) {-# INLINABLE toFractional #-} -- | convert bytestring into unsigned fractional using radix. -- -- this function can parse -- -- * fractional(0.1, 12224.3543) -- -- >>> fractional' (Proxy :: Proxy 36) "12z" :: Maybe (Double, S.ByteString) -- Just (1403.0,"") -- >>> fractional' (Proxy :: Proxy 2) "1012" :: Maybe (Double, L.ByteString) -- Just (5.0,"2") -- >>> fractional' (Proxy :: Proxy 10) "a12" :: Maybe (Double, S.ByteString) -- Nothing fractional' :: (Radix b, ReadFractional r, Source s) => proxy b -> s -> Maybe (r, s) fractional' pn s = case integral pn s of (_, 0, _, _) -> Nothing (q, _, d, s1) | C.null s1 -> Just (fromFraction q * fromIntegral (natVal pn) ^ d, C.empty) | C.head s1 /= dot -> Just (fromFraction q, s1) | otherwise -> case integral pn (C.tail s1) of (_, 0, _, _) -> Just (fromFraction q, s1) (r, d', _, s2) -> Just (toFractional pn q r d d', s2) where dot = 46 {-# INLINABLE fractional' #-} exponential :: forall s. Source s => s -> (Int, s) exponential s0 | C.null s0 = (0, s0) | isE (C.head s0) = sign (C.tail s0) | otherwise = (0, s0) where isE w = w == 101 || w == 69 minus = 45 plus = 43 sign s1 | C.null s1 = (0, s0) | C.head s1 == plus = expPart $ C.tail s1 | C.head s1 == minus = let (e, s) = expPart $ C.tail s1 in (-e, s) | otherwise = expPart s1 expPart s2 = case integral (Proxy :: Proxy 10) s2 :: (Fraction Double, Int, Int, s) of (_, 0, _, _) -> (0, s0) (e, _, _, s) -> (fromFraction e, s) {-# INLINABLE exponential #-} setExpPart :: Fractional f => Int -> f -> f setExpPart e f | e >= 0 = f * 10 ^ e | otherwise = f / 10 ^ abs e {-# SPECIALIZE setExpPart :: Int -> Double -> Double #-} {-# SPECIALIZE setExpPart :: Int -> Float -> Float #-} {-# INLINABLE setExpPart #-} -- | convert bytestring into unsigned fractional using radix. -- -- this function can parse -- -- * fractional(0.1, 12224.3543) -- * exponential (e1, E+2, e-123) (optional) -- -- >>> fractional10 "12.5" :: Maybe (Double, S.ByteString) -- Just (12.5,"") -- >>> fractional10 "124.1e12" :: Maybe (Double, L.ByteString) -- Just (1.241e14,"") -- >>> fractional10 "12.5e-3" :: Maybe (Double, S.ByteString) -- Just (1.25e-2,"") -- >>> fractional10 "3.11e+3" :: Maybe (Double, L.ByteString) -- Just (3110.0,"") fractional10 :: (ReadFractional r, Source s) => s -> Maybe (r, s) fractional10 s = fractional' (Proxy :: Proxy 10) s >>= \(f, s') -> let (e, s'') = exponential s' in Just (setExpPart e f, s'') {-# INLINABLE fractional10 #-} -- | convert bytestring into unsigned fractional using radix. -- -- this function can parse -- -- * oct/hexa-decimal (0o,0O,0x,0X) (optional) -- * fractional(0.1, 12224.3543) -- * exponential (e1, E+2, e-123) (10-radixed only, optional) -- -- >>> fractional "12.4" :: Maybe (Double, S.ByteString) -- Just (12.4,"") -- >>> fractional "1.23e12" :: Maybe (Double, L.ByteString) -- Just (1.23e12,"") -- >>> fractional "0o0.4" :: Maybe (Double, S.ByteString) -- Just (0.5,"") -- >>> fractional "0x3f.12" :: Maybe (Double, L.ByteString) -- Just (63.0703125,"") fractional :: (ReadFractional r, Source s) => s -> Maybe (r, s) fractional s0 | C.null s0 = Nothing | C.head s0 == zero = radix $ C.tail s0 | otherwise = fractional10 s0 where zero = 48 isX w = w == 120 || w == 88 isO w = w == 111 || w == 79 radix s1 | C.null s1 = Just (0, C.empty) | isX (C.head s1) = fractional' (Proxy :: Proxy 16) (C.tail s1) | isO (C.head s1) = fractional' (Proxy :: Proxy 8) (C.tail s1) | otherwise = fractional10 s0 {-# INLINABLE fractional #-} -- | @ -- double = fractional -- @ double :: Source s => s -> Maybe (Double, s) double = fractional