{-# LANGUAGE OverloadedStrings, UnboxedTuples, CPP #-}
{-# LANGUAGE Trustworthy #-}
module Data.Text.Read
(
Reader
, decimal
, hexadecimal
, signed
, rational
, double
) where
import Control.Monad (liftM)
import Data.Char (ord)
import Data.Int (Int8, Int16, Int32, Int64)
import Data.Ratio ((%))
import Data.Text as T
import Data.Text.Internal as T (Text(..))
import Data.Text.Array as A
import Data.Text.Internal.Private (spanAscii_)
import Data.Text.Internal.Read
import Data.Word (Word, Word8, Word16, Word32, Word64)
type Reader a = Text -> Either String (a, Text)
type Parser a = IParser Text a
decimal :: Integral a => Reader a
{-# SPECIALIZE decimal :: Reader Int #-}
{-# SPECIALIZE decimal :: Reader Int8 #-}
{-# SPECIALIZE decimal :: Reader Int16 #-}
{-# SPECIALIZE decimal :: Reader Int32 #-}
{-# SPECIALIZE decimal :: Reader Int64 #-}
{-# SPECIALIZE decimal :: Reader Integer #-}
{-# SPECIALIZE decimal :: Reader Data.Word.Word #-}
{-# SPECIALIZE decimal :: Reader Word8 #-}
{-# SPECIALIZE decimal :: Reader Word16 #-}
{-# SPECIALIZE decimal :: Reader Word32 #-}
{-# SPECIALIZE decimal :: Reader Word64 #-}
decimal :: forall a. Integral a => Reader a
decimal Text
txt
| Text -> Bool
T.null Text
h = forall a b. a -> Either a b
Left String
"input does not start with a digit"
| Bool
otherwise = forall a b. b -> Either a b
Right (forall a. (a -> Char -> a) -> a -> Text -> a
T.foldl' forall {a}. Num a => a -> Char -> a
go a
0 Text
h, Text
t)
where (# Text
h,Text
t #) = (Word8 -> Bool) -> Text -> (# Text, Text #)
spanAscii_ (\Word8
w -> Word8
w forall a. Num a => a -> a -> a
- Char -> Word8
ord8 Char
'0' forall a. Ord a => a -> a -> Bool
< Word8
10) Text
txt
go :: a -> Char -> a
go a
n Char
d = (a
n forall a. Num a => a -> a -> a
* a
10 forall a. Num a => a -> a -> a
+ forall a b. (Integral a, Num b) => a -> b
fromIntegral (Char -> Int
digitToInt Char
d))
hexadecimal :: Integral a => Reader a
{-# SPECIALIZE hexadecimal :: Reader Int #-}
{-# SPECIALIZE hexadecimal :: Reader Int8 #-}
{-# SPECIALIZE hexadecimal :: Reader Int16 #-}
{-# SPECIALIZE hexadecimal :: Reader Int32 #-}
{-# SPECIALIZE hexadecimal :: Reader Int64 #-}
{-# SPECIALIZE hexadecimal :: Reader Integer #-}
{-# SPECIALIZE hexadecimal :: Reader Word #-}
{-# SPECIALIZE hexadecimal :: Reader Word8 #-}
{-# SPECIALIZE hexadecimal :: Reader Word16 #-}
{-# SPECIALIZE hexadecimal :: Reader Word32 #-}
{-# SPECIALIZE hexadecimal :: Reader Word64 #-}
hexadecimal :: forall a. Integral a => Reader a
hexadecimal Text
txt
| Text
h forall a. Eq a => a -> a -> Bool
== Text
"0x" Bool -> Bool -> Bool
|| Text
h forall a. Eq a => a -> a -> Bool
== Text
"0X" = forall a. Integral a => Reader a
hex Text
t
| Bool
otherwise = forall a. Integral a => Reader a
hex Text
txt
where (Text
h,Text
t) = Int -> Text -> (Text, Text)
T.splitAt Int
2 Text
txt
hex :: Integral a => Reader a
{-# SPECIALIZE hex :: Reader Int #-}
{-# SPECIALIZE hex :: Reader Int8 #-}
{-# SPECIALIZE hex :: Reader Int16 #-}
{-# SPECIALIZE hex :: Reader Int32 #-}
{-# SPECIALIZE hex :: Reader Int64 #-}
{-# SPECIALIZE hex :: Reader Integer #-}
{-# SPECIALIZE hex :: Reader Word #-}
{-# SPECIALIZE hex :: Reader Word8 #-}
{-# SPECIALIZE hex :: Reader Word16 #-}
{-# SPECIALIZE hex :: Reader Word32 #-}
{-# SPECIALIZE hex :: Reader Word64 #-}
hex :: forall a. Integral a => Reader a
hex Text
txt
| Text -> Bool
T.null Text
h = forall a b. a -> Either a b
Left String
"input does not start with a hexadecimal digit"
| Bool
otherwise = forall a b. b -> Either a b
Right (forall a. (a -> Char -> a) -> a -> Text -> a
T.foldl' forall {a}. Num a => a -> Char -> a
go a
0 Text
h, Text
t)
where (# Text
h,Text
t #) = (Word8 -> Bool) -> Text -> (# Text, Text #)
spanAscii_ (\Word8
w -> Word8
w forall a. Num a => a -> a -> a
- Char -> Word8
ord8 Char
'0' forall a. Ord a => a -> a -> Bool
< Word8
10 Bool -> Bool -> Bool
|| Word8
w forall a. Num a => a -> a -> a
- Char -> Word8
ord8 Char
'A' forall a. Ord a => a -> a -> Bool
< Word8
6 Bool -> Bool -> Bool
|| Word8
w forall a. Num a => a -> a -> a
- Char -> Word8
ord8 Char
'a' forall a. Ord a => a -> a -> Bool
< Word8
6) Text
txt
go :: a -> Char -> a
go a
n Char
d = (a
n forall a. Num a => a -> a -> a
* a
16 forall a. Num a => a -> a -> a
+ forall a b. (Integral a, Num b) => a -> b
fromIntegral (Char -> Int
hexDigitToInt Char
d))
signed :: Num a => Reader a -> Reader a
{-# INLINE signed #-}
signed :: forall a. Num a => Reader a -> Reader a
signed Reader a
f = forall t a. IParser t a -> IReader t a
runP (forall a. Num a => Parser a -> Parser a
signa (forall t a. IReader t a -> IParser t a
P Reader a
f))
rational :: Fractional a => Reader a
{-# SPECIALIZE rational :: Reader Double #-}
rational :: forall a. Fractional a => Reader a
rational = forall a.
Fractional a =>
(Integer -> Integer -> Integer -> a) -> Reader a
floaty forall a b. (a -> b) -> a -> b
$ \Integer
real Integer
frac Integer
fracDenom -> forall a. Fractional a => Rational -> a
fromRational forall a b. (a -> b) -> a -> b
$
Integer
real forall a. Integral a => a -> a -> Ratio a
% Integer
1 forall a. Num a => a -> a -> a
+ Integer
frac forall a. Integral a => a -> a -> Ratio a
% Integer
fracDenom
double :: Reader Double
double :: Reader Double
double = forall a.
Fractional a =>
(Integer -> Integer -> Integer -> a) -> Reader a
floaty forall a b. (a -> b) -> a -> b
$ \Integer
real Integer
frac Integer
fracDenom ->
forall a. Num a => Integer -> a
fromInteger Integer
real forall a. Num a => a -> a -> a
+
forall a. Num a => Integer -> a
fromInteger Integer
frac forall a. Fractional a => a -> a -> a
/ forall a. Num a => Integer -> a
fromInteger Integer
fracDenom
signa :: Num a => Parser a -> Parser a
{-# SPECIALIZE signa :: Parser Int -> Parser Int #-}
{-# SPECIALIZE signa :: Parser Int8 -> Parser Int8 #-}
{-# SPECIALIZE signa :: Parser Int16 -> Parser Int16 #-}
{-# SPECIALIZE signa :: Parser Int32 -> Parser Int32 #-}
{-# SPECIALIZE signa :: Parser Int64 -> Parser Int64 #-}
{-# SPECIALIZE signa :: Parser Integer -> Parser Integer #-}
signa :: forall a. Num a => Parser a -> Parser a
signa Parser a
p = do
Word8
sign <- forall a t. a -> IParser t a -> IParser t a
perhaps (Char -> Word8
ord8 Char
'+') forall a b. (a -> b) -> a -> b
$ (Word8 -> Bool) -> IParser Text Word8
charAscii (\Word8
c -> Word8
c forall a. Eq a => a -> a -> Bool
== Char -> Word8
ord8 Char
'-' Bool -> Bool -> Bool
|| Word8
c forall a. Eq a => a -> a -> Bool
== Char -> Word8
ord8 Char
'+')
if Word8
sign forall a. Eq a => a -> a -> Bool
== Char -> Word8
ord8 Char
'+' then Parser a
p else forall a. Num a => a -> a
negate forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
`liftM` Parser a
p
charAscii :: (Word8 -> Bool) -> Parser Word8
charAscii :: (Word8 -> Bool) -> IParser Text Word8
charAscii Word8 -> Bool
p = forall t a. IReader t a -> IParser t a
P forall a b. (a -> b) -> a -> b
$ \(Text Array
arr Int
off Int
len) -> let c :: Word8
c = Array -> Int -> Word8
A.unsafeIndex Array
arr Int
off in
if Int
len forall a. Ord a => a -> a -> Bool
> Int
0 Bool -> Bool -> Bool
&& Word8 -> Bool
p Word8
c
then forall a b. b -> Either a b
Right (Word8
c, Array -> Int -> Int -> Text
Text Array
arr (Int
off forall a. Num a => a -> a -> a
+ Int
1) (Int
len forall a. Num a => a -> a -> a
- Int
1))
else forall a b. a -> Either a b
Left String
"character does not match"
floaty :: Fractional a => (Integer -> Integer -> Integer -> a) -> Reader a
{-# INLINE floaty #-}
floaty :: forall a.
Fractional a =>
(Integer -> Integer -> Integer -> a) -> Reader a
floaty Integer -> Integer -> Integer -> a
f = forall t a. IParser t a -> IReader t a
runP forall a b. (a -> b) -> a -> b
$ do
Word8
sign <- forall a t. a -> IParser t a -> IParser t a
perhaps (Char -> Word8
ord8 Char
'+') forall a b. (a -> b) -> a -> b
$ (Word8 -> Bool) -> IParser Text Word8
charAscii (\Word8
c -> Word8
c forall a. Eq a => a -> a -> Bool
== Char -> Word8
ord8 Char
'-' Bool -> Bool -> Bool
|| Word8
c forall a. Eq a => a -> a -> Bool
== Char -> Word8
ord8 Char
'+')
Integer
real <- forall t a. IReader t a -> IParser t a
P forall a. Integral a => Reader a
decimal
T Integer
fraction Int
fracDigits <- forall a t. a -> IParser t a -> IParser t a
perhaps (Integer -> Int -> T
T Integer
0 Int
0) forall a b. (a -> b) -> a -> b
$ do
Word8
_ <- (Word8 -> Bool) -> IParser Text Word8
charAscii (forall a. Eq a => a -> a -> Bool
== Char -> Word8
ord8 Char
'.')
Int
digits <- forall t a. IReader t a -> IParser t a
P forall a b. (a -> b) -> a -> b
$ \Text
t -> forall a b. b -> Either a b
Right (let (# Text
hd, Text
_ #) = (Word8 -> Bool) -> Text -> (# Text, Text #)
spanAscii_ (\Word8
w -> Word8
w forall a. Num a => a -> a -> a
- Char -> Word8
ord8 Char
'0' forall a. Ord a => a -> a -> Bool
< Word8
10) Text
t in Text -> Int
T.length Text
hd, Text
t)
Integer
n <- forall t a. IReader t a -> IParser t a
P forall a. Integral a => Reader a
decimal
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Integer -> Int -> T
T Integer
n Int
digits
let e :: Word8 -> Bool
e Word8
c = Word8
c forall a. Eq a => a -> a -> Bool
== Char -> Word8
ord8 Char
'e' Bool -> Bool -> Bool
|| Word8
c forall a. Eq a => a -> a -> Bool
== Char -> Word8
ord8 Char
'E'
Int
power <- forall a t. a -> IParser t a -> IParser t a
perhaps Int
0 ((Word8 -> Bool) -> IParser Text Word8
charAscii Word8 -> Bool
e forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall a. Num a => Parser a -> Parser a
signa (forall t a. IReader t a -> IParser t a
P forall a. Integral a => Reader a
decimal) :: Parser Int)
let n :: a
n = if Int
fracDigits forall a. Eq a => a -> a -> Bool
== Int
0
then if Int
power forall a. Eq a => a -> a -> Bool
== Int
0
then forall a. Num a => Integer -> a
fromInteger Integer
real
else forall a. Num a => Integer -> a
fromInteger Integer
real forall a. Num a => a -> a -> a
* (a
10 forall a b. (Fractional a, Integral b) => a -> b -> a
^^ Int
power)
else if Int
power forall a. Eq a => a -> a -> Bool
== Int
0
then Integer -> Integer -> Integer -> a
f Integer
real Integer
fraction (Integer
10 forall a b. (Num a, Integral b) => a -> b -> a
^ Int
fracDigits)
else Integer -> Integer -> Integer -> a
f Integer
real Integer
fraction (Integer
10 forall a b. (Num a, Integral b) => a -> b -> a
^ Int
fracDigits) forall a. Num a => a -> a -> a
* (a
10 forall a b. (Fractional a, Integral b) => a -> b -> a
^^ Int
power)
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$! if Word8
sign forall a. Eq a => a -> a -> Bool
== Char -> Word8
ord8 Char
'+'
then a
n
else -a
n
ord8 :: Char -> Word8
ord8 :: Char -> Word8
ord8 = forall a b. (Integral a, Num b) => a -> b
fromIntegral forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Int
ord