on master help sign in
Bring lazy Read up to speed with strict.
376f921Bryan O'Sullivan
Data/Text/Lazy/Read.hs+63−32
@@ -22,18 +22,24 @@ module Data.Text.Lazy.Read
22 ) where
22 ) where
23
23
24import Control.Monad (liftM)
24import Control.Monad (liftM)
25−import Data.Char (digitToInt, isDigit, isHexDigit, ord)
25+import Data.Char (isDigit, isHexDigit, ord)
26−import Data.Ratio
26+import Data.Ratio ((%))
27import Data.Text.Lazy as T
27import Data.Text.Lazy as T
28
28
29−-- | Read some text, and if the read succeeds, return its value and
29+-- | Read some text. If the read succeeds, return its value and the
30−-- the remaining text.
30+-- remaining text, otherwise an error message.
31type Reader a = Text -> Either String (a,Text)
31type Reader a = Text -> Either String (a,Text)
32
32
33−-- | Read a decimal integer.
33+-- | Read a decimal integer. The input must begin with at least one
34+-- decimal digit, and is consumed until a non-digit or end of string
35+-- is reached.
34--
36--
35-- This function does not handle leading sign characters. If you need
37-- This function does not handle leading sign characters. If you need
36-- to handle signed input, use @'signed' 'decimal'@.
38-- to handle signed input, use @'signed' 'decimal'@.
39+--
40+-- /Note/: For fixed-width integer types, this function does not
41+-- attempt to detect overflow, so a sufficiently long input may give
42+-- incorrect results.
37decimal :: Integral a => Reader a
43decimal :: Integral a => Reader a
38{-# SPECIALIZE decimal :: Reader Int #-}
44{-# SPECIALIZE decimal :: Reader Int #-}
39{-# SPECIALIZE decimal :: Reader Integer #-}
45{-# SPECIALIZE decimal :: Reader Integer #-}
@@ -43,28 +49,69 @@ decimal txt
43 where (h,t) = T.spanBy isDigit txt
49 where (h,t) = T.spanBy isDigit txt
44 go n d = (n * 10 + fromIntegral (digitToInt d))
50 go n d = (n * 10 + fromIntegral (digitToInt d))
45
51
46−-- | Read a hexadecimal number, with optional leading @\"0x\"@. This
52+-- | Read a hexadecimal integer, consisting of an optional leading
53+-- @\"0x\"@ followed by at least one decimal digit. Input is consumed
54+-- until a non-hex-digit or end of string is reached. This function
47−-- function is case insensitive.
55+-- is case insensitive.
48--
56--
49-- This function does not handle leading sign characters. If you need
57-- This function does not handle leading sign characters. If you need
50-- to handle signed input, use @'signed' 'hexadecimal'@.
58-- to handle signed input, use @'signed' 'hexadecimal'@.
59+--
60+-- /Note/: For fixed-width integer types, this function does not
61+-- attempt to detect overflow, so a sufficiently long input may give
62+-- incorrect results.
51hexadecimal :: Integral a => Reader a
63hexadecimal :: Integral a => Reader a
52−{-# SPECIALIZE hex :: Reader Int #-}
64+{-# SPECIALIZE hexadecimal :: Reader Int #-}
53−{-# SPECIALIZE hex :: Reader Integer #-}
65+{-# SPECIALIZE hexadecimal :: Reader Integer #-}
54hexadecimal txt
66hexadecimal txt
55− | T.toLower h == "0x" = hex t
67+ | h == "0x" || h == "0X" = hex t
56− | otherwise = hex txt
68+ | otherwise = hex txt
57 where (h,t) = T.splitAt 2 txt
69 where (h,t) = T.splitAt 2 txt
58
70
71+hex :: Integral a => Reader a
72+{-# SPECIALIZE hex :: Reader Int #-}
73+{-# SPECIALIZE hex :: Reader Integer #-}
74+hex txt
75+ | T.null h = Left "input does not start with a hexadecimal digit"
76+ | otherwise = Right (T.foldl' go 0 h, t)
77+ where (h,t) = T.spanBy isHexDigit txt
78+ go n d = (n * 16 + fromIntegral (hexDigitToInt d))
79+
80+hexDigitToInt :: Char -> Int
81+hexDigitToInt c
82+ | c >= '0' && c <= '9' = ord c - ord '0'
83+ | c >= 'a' && c <= 'f' = ord c - (ord 'a' - 10)
84+ | otherwise = ord c - (ord 'A' - 10)
85+
86+digitToInt :: Char -> Int
87+digitToInt c = ord c - ord '0'
88+
59−-- | Read a leading sign character (@\'-\'@ or @\'+\'@) and apply it
89+-- | Read an optional leading sign character (@\'-\'@ or @\'+\'@) and
60−-- to the result of applying the given reader.
90+-- apply it to the result of applying the given reader.
61signed :: Num a => Reader a -> Reader a
91signed :: Num a => Reader a -> Reader a
62{-# INLINE signed #-}
92{-# INLINE signed #-}
63signed f = runP (signa (P f))
93signed f = runP (signa (P f))
64
94
65-- | Read a rational number.
95-- | Read a rational number.
66--
96--
67−-- This function accepts an optional leading sign character.
97+-- This function accepts an optional leading sign character, followed
98+-- by at least one decimal digit. The syntax similar to that accepted
99+-- by the 'read' function, with the exception that a trailing @\'.\'@
100+-- or @\'e\'@ /not/ followed by a number is not consumed.
101+--
102+-- Examples:
103+--
104+-- >rational "3" == Right (3.0, "")
105+-- >rational "3.1" == Right (3.1, "")
106+-- >rational "3e4" == Right (30000.0, "")
107+-- >rational "3.1e4" == Right (31000.0, "")
108+-- >rational ".3" == Left "input does not start with a digit"
109+-- >rational "e3" == Left "input does not start with a digit"
110+--
111+-- Examples of differences from 'read':
112+--
113+-- >rational "3.foo" == Right (3.0, ".foo")
114+-- >rational "3e" == Right (3.0, "e")
68rational :: RealFloat a => Reader a
115rational :: RealFloat a => Reader a
69{-# SPECIALIZE rational :: Reader Double #-}
116{-# SPECIALIZE rational :: Reader Double #-}
70rational = floaty $ \real frac fracDenom -> fromRational $
117rational = floaty $ \real frac fracDenom -> fromRational $
@@ -72,7 +119,7 @@ rational = floaty $ \real frac fracDenom -> fromRational $
72
119
73-- | Read a rational number.
120-- | Read a rational number.
74--
121--
75−-- This function accepts an optional leading sign character.
122+-- The syntax accepted by this function is the same as for 'rational'.
76--
123--
77-- /Note/: This function is almost ten times faster than 'rational',
124-- /Note/: This function is almost ten times faster than 'rational',
78-- but is slightly less accurate.
125-- but is slightly less accurate.
@@ -87,22 +134,6 @@ double = floaty $ \real frac fracDenom ->
87 fromIntegral real +
134 fromIntegral real +
88 fromIntegral frac / fromIntegral fracDenom
135 fromIntegral frac / fromIntegral fracDenom
89
136
90−hex :: Integral a => Reader a
91−{-# SPECIALIZE hex :: Reader Int #-}
92−{-# SPECIALIZE hex :: Reader Integer #-}
93−hex txt
94− | T.null h = Left "input does not start with a hexadecimal digit"
95− | otherwise = Right (T.foldl' go 0 h, t)
96− where (h,t) = T.spanBy isHexDigit txt
97− go n d = (n * 16 + fromIntegral (hexDigitToInt d))
98−
99−hexDigitToInt :: Char -> Int
100−hexDigitToInt c
101− | c >= '0' && c <= '9' = ord c - ord '0'
102− | c >= 'a' && c <= 'f' = ord c - (ord 'a' - 10)
103− | c >= 'A' && c <= 'F' = ord c - (ord 'A' - 10)
104− | otherwise = error "Data.Text.Lex.hexDigitToInt: bad input"
105−
106signa :: Num a => Parser a -> Parser a
137signa :: Num a => Parser a -> Parser a
107{-# SPECIALIZE signa :: Parser Int -> Parser Int #-}
138{-# SPECIALIZE signa :: Parser Int -> Parser Int #-}
108{-# SPECIALIZE signa :: Parser Integer -> Parser Integer #-}
139{-# SPECIALIZE signa :: Parser Integer -> Parser Integer #-}
@@ -111,7 +142,7 @@ signa p = do
111 if sign == '+' then p else negate `liftM` p
142 if sign == '+' then p else negate `liftM` p
112
143
113newtype Parser a = P {
144newtype Parser a = P {
114− runP :: Text -> Either String (a,Text)
145+ runP :: Reader a
115 }
146 }
116
147
117instance Monad Parser where
148instance Monad Parser where
Data/Text/Read.hs+1−1
@@ -49,7 +49,7 @@ decimal txt
49 where (h,t) = T.spanBy isDigit txt
49 where (h,t) = T.spanBy isDigit txt
50 go n d = (n * 10 + fromIntegral (digitToInt d))
50 go n d = (n * 10 + fromIntegral (digitToInt d))
51
51
52−-- | Read a hexadecimal number, consisting of an optional leading
52+-- | Read a hexadecimal integer, consisting of an optional leading
53-- @\"0x\"@ followed by at least one decimal digit. Input is consumed
53-- @\"0x\"@ followed by at least one decimal digit. Input is consumed
54-- until a non-hex-digit or end of string is reached. This function
54-- until a non-hex-digit or end of string is reached. This function
55-- is case insensitive.
55-- is case insensitive.