{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE CPP #-} module Data.ByteString.Read ( -- * functions floating , double , signed -- * classes , EffectiveDigit(..) , Base(..) -- * raw functions , floating10 , floating' ) where import Control.Arrow(first) import Control.Applicative((<$>)) import Data.ByteString.Unsafe import Data.ByteString(ByteString) import qualified Data.ByteString as S import Data.Word import GHC.TypeLits.Compat import Data.Proxy.Compat -- $setup -- >>> :set -XDataKinds -XOverloadedStrings minus :: Word8 minus = 45 {-# INLINE minus #-} plus :: Word8 plus = 43 {-# INLINE plus #-} class (Fractional a, Num (Fraction a), Ord (Fraction a)) => EffectiveDigit a where -- | data type to store fractional part of floating data Fraction a -- | maximum value of fractional part. -- -- Nothing if arbitrary-precision. -- -- @ -- Just $ fromIntegral (floatRadix t) ^ floatDigits t -- @ maxValue :: proxy a -> Maybe (Fraction a) -- | lifted fromIntegral fromFraction :: Num b => Fraction a -> b instance EffectiveDigit Float where newtype Fraction Float = FractionFloat Word32 deriving(Eq, Ord, Num) maxValue _ = let t = 0 :: Float in Just $ fromIntegral (floatRadix t) ^ floatDigits t fromFraction (FractionFloat a) = fromIntegral a {-# INLINE maxValue #-} {-# INLINE fromFraction #-} instance EffectiveDigit Double where newtype Fraction Double = FractionDouble Word64 deriving(Eq, Ord, Num) maxValue _ = let t = 0 :: Double in Just $ fromIntegral (floatRadix t) ^ floatDigits t fromFraction (FractionDouble a) = fromIntegral a {-# INLINE maxValue #-} {-# INLINE fromFraction #-} instance EffectiveDigit Rational where newtype Fraction Rational = WordRational Integer deriving(Eq, Ord, Num) maxValue _ = Nothing fromFraction (WordRational a) = fromIntegral a {-# INLINE maxValue #-} {-# INLINE fromFraction #-} class KnownNat n => Base n where -- | check input Word8 is digit charactor or not. isDigit :: proxy n -> Word8 -> Bool -- | convert digit charactor to number. -- undefined behaviour when give non-digit charactor. unsafeToDigit :: proxy n -> Word8 -> Word8 #define defineBaseUnder10(BASE, MAX)\ instance Base BASE where;\ {-# INLINE isDigit #-};\ {-# INLINE unsafeToDigit #-};\ isDigit _ = \w -> 48 <= w && w <= MAX;\ unsafeToDigit _ w = w - 48 defineBaseUnder10( 2, 49) defineBaseUnder10( 3, 50) defineBaseUnder10( 4, 51) defineBaseUnder10( 5, 52) defineBaseUnder10( 6, 53) defineBaseUnder10( 7, 54) defineBaseUnder10( 8, 55) defineBaseUnder10( 9, 56) defineBaseUnder10(10, 57) #define defineBaseOver10(BASE, MAXu, MAXl)\ instance Base BASE where;\ {-# INLINE isDigit #-};\ {-# INLINE unsafeToDigit #-};\ isDigit _ = \w -> 48 <= w && w <= 57 || 65 <= w && w <= MAXu || 97 <= w && w <= MAXl;\ unsafeToDigit _ w = if 48 <= w && w <= 57;\ then fromIntegral w - 48;\ else if 65 <= w && w <= 90;\ then fromIntegral w - 55;\ else fromIntegral w - 87 defineBaseOver10(11, 65, 97) defineBaseOver10(12, 66, 98) defineBaseOver10(13, 67, 99) defineBaseOver10(14, 68, 100) defineBaseOver10(15, 69, 101) defineBaseOver10(16, 70, 102) defineBaseOver10(17, 71, 103) defineBaseOver10(18, 72, 104) defineBaseOver10(19, 73, 105) defineBaseOver10(20, 74, 106) defineBaseOver10(21, 75, 107) defineBaseOver10(22, 76, 108) defineBaseOver10(23, 77, 109) defineBaseOver10(24, 78, 110) defineBaseOver10(25, 79, 111) defineBaseOver10(26, 80, 112) defineBaseOver10(27, 81, 113) defineBaseOver10(28, 82, 114) defineBaseOver10(29, 83, 115) defineBaseOver10(30, 84, 116) defineBaseOver10(31, 85, 117) defineBaseOver10(32, 86, 118) defineBaseOver10(33, 87, 119) defineBaseOver10(34, 88, 120) defineBaseOver10(35, 89, 121) defineBaseOver10(36, 90, 122) integral :: forall proxy n r. (Base n, EffectiveDigit r, Ord (Fraction r), Num (Fraction r)) => proxy n -> ByteString -> (Fraction r, Int, Int, ByteString) integral pn = loop 0 0 0 where pr :: Proxy r pr = Proxy loop !i !d !ad !s | S.null s = (i, d, ad, s) | not (isDigit pn (unsafeHead s)) = (i, d, ad, s) | maybe False (i >=) (maxValue pr) = loop i d (ad + 1) (unsafeTail s) | otherwise = loop (i * fromIntegral (natVal pn) + (fromIntegral $ unsafeToDigit pn (unsafeHead s) :: Fraction r)) (d+1) ad (unsafeTail s) {-# INLINABLE integral #-} toFractional :: (Base b, EffectiveDigit r, Fractional r) => proxy b -> Fraction r -> Fraction r -> Int -> Int -> r toFractional p q r du d = fromFraction q * base ^ du + fromFraction r / base ^ d where base = fromIntegral (natVal p) {-# INLINABLE toFractional #-} -- | convert bytestring into unsigned floating using radix. -- -- this function can parse -- -- * floating(0.1, 12224.3543) -- -- >>> floating' (Proxy :: Proxy 36) "12z" :: Maybe (Double, ByteString) -- Just (1403.0,"") -- >>> floating' (Proxy :: Proxy 2) "1012" :: Maybe (Double, ByteString) -- Just (5.0,"2") -- >>> floating' (Proxy :: Proxy 10) "a12" :: Maybe (Double, ByteString) -- Nothing floating' :: (Base b, EffectiveDigit r) => proxy b -> ByteString -> Maybe (r, ByteString) floating' pn s = case integral pn s of (_, 0, _, _) -> Nothing (q, _, d, "") -> Just (fromFraction q * fromIntegral (natVal pn) ^ d, "") (q, _, d, s1) | unsafeHead s1 /= dot -> Just (fromFraction q, s1) | otherwise -> case integral pn (unsafeTail s1) of (_, 0, _, _) -> Just (fromFraction q, s1) (r, d', _, s2) -> Just (toFractional pn q r d d', s2) where dot = 46 {-# INLINABLE floating' #-} exponential :: forall proxy r. (EffectiveDigit r, Ord (Fraction r), Num (Fraction r)) => proxy r -> ByteString -> (Int, ByteString) exponential _ s0 | S.null s0 = (0, s0) | isE (unsafeHead s0) = sign (unsafeTail s0) | otherwise = (0, s0) where isE w = w == 101 || w == 69 sign s1 | S.null s1 = (0, s0) | unsafeHead s1 == plus = expPart $ unsafeTail s1 | unsafeHead s1 == minus = let (e, s) = expPart $ unsafeTail s1 in (-e, s) | otherwise = expPart s1 expPart s2 = case integral (Proxy :: Proxy 10) s2 :: (Fraction r, Int, Int, ByteString) 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 floating using radix. -- -- this function can parse -- -- * floating(0.1, 12224.3543) -- * exponential (e1, E+2, e-123) (optional) -- -- >>> floating10 "12.5" :: Maybe (Double, ByteString) -- Just (12.5,"") -- >>> floating10 "124.1e12" :: Maybe (Double, ByteString) -- Just (1.241e14,"") -- >>> floating10 "12.5e-3" :: Maybe (Double, ByteString) -- Just (1.25e-2,"") -- >>> floating10 "3.11e+3" :: Maybe (Double, ByteString) -- Just (3110.0,"") floating10 :: forall r. EffectiveDigit r => ByteString -> Maybe (r, ByteString) floating10 s = floating' (Proxy :: Proxy 10) s >>= \(f, s') -> let (e, s'') = exponential (Proxy :: Proxy r) s' in Just (setExpPart e f, s'') {-# INLINABLE floating10 #-} -- | convert bytestring into unsigned floating using radix. -- -- this function can parse -- -- * oct/hexa-decimal (0o,0O,0x,0X) (optional) -- * floating(0.1, 12224.3543) -- * exponential (e1, E+2, e-123) (10-radixed only, optional) -- -- >>> floating "12.4" :: Maybe (Double, ByteString) -- Just (12.4,"") -- >>> floating "1.23e12" :: Maybe (Double, ByteString) -- Just (1.23e12,"") -- >>> floating "0o0.4" :: Maybe (Double, ByteString) -- Just (0.5,"") -- >>> floating "0x3f.12" :: Maybe (Double, ByteString) -- Just (63.0703125,"") floating :: EffectiveDigit r => ByteString -> Maybe (r, ByteString) floating s0 | S.null s0 = Nothing | unsafeHead s0 == zero = base $ unsafeTail s0 | otherwise = floating10 s0 where zero = 48 isX w = w == 120 || w == 88 isO w = w == 111 || w == 79 base s1 | S.null s1 = Just (0, "") | isX (unsafeHead s1) = floating' (Proxy :: Proxy 16) (unsafeTail s1) | isO (unsafeHead s1) = floating' (Proxy :: Proxy 8) (unsafeTail s1) | otherwise = floating10 s0 {-# INLINABLE floating #-} -- | @ -- double = floating -- @ double :: ByteString -> Maybe (Double, ByteString) double = floating -- | convert unsigned parser to signed parser. -- -- this function can parse -- -- * sign (+, -) (optional) -- -- >>> signed double "12.4" -- Just (12.4,"") -- >>> signed double "-3.21e3" -- Just (-3210.0,"") -- >>> signed double "+0x1f.4" -- Just (31.25,"") signed :: Num r => (ByteString -> Maybe (r, ByteString)) -> ByteString -> Maybe (r, ByteString) signed f s | S.null s = Nothing | unsafeHead s == minus = first negate <$> f (unsafeTail s) | unsafeHead s == plus = f (unsafeTail s) | otherwise = f s