@@ -63,6 +63,7 @@ module Data.Attoparsec.Char8
6363 , isHorizontalSpace
6464
6565 -- * Numeric parsers
66+ , hexNumber
6667 --, int
6768 --, integer
6869 --, double
@@ -77,16 +78,20 @@ import Data.Attoparsec.FastSet (charClass, memberChar)
7778import Data.Attoparsec.Internal (Parser, (<?>))
7879import Data.ByteString.Internal (c2w, w2c)
7980-- import Data.ByteString.Lex.Double (readDouble)
80−import Data.Char (toLower)
8181import Data.Word (Word8)
8282import Prelude hiding (takeWhile)
8383import qualified Data.Attoparsec as A
8484import qualified Data.Attoparsec.Internal as I
8585import qualified Data.ByteString.Char8 as B
86+import qualified Data.ByteString as B8
87+
88+toLower :: Word8 -> Word8
89+toLower w | w >= 65 && w <= 90 = w + 32
90+ | otherwise = w
8691
8792-- | Satisfy a literal string, ignoring case.
8893stringCI :: B.ByteString -> Parser B.ByteString
89−stringCI = I.stringTransform (B.map toLower)
94+stringCI = I.stringTransform (B8.map toLower)
9095{-# INLINE stringCI #-}
9196
9297takeWhile1 :: (Char -> Bool) -> Parser B.ByteString
@@ -203,3 +208,14 @@ integer = numeric "Integer" B.readInteger
203208double :: Parser Double
204209double = numeric "Double" readDouble
205210-}
211+
212+hexNumber :: Integral a => Parser a
213+{-# SPECIALISE hexNumber :: Parser Int #-}
214+hexNumber = fromHex `fmap` I.takeWhile1 isHexDigit
215+ where isHexDigit w = (w >= 48 && w <= 57) || (x >= 97 && x <= 102)
216+ where x = toLower w
217+ fromHex = B8.foldl' step 0
218+ step a w | w >= 48 && w <= 57 = a * 16 + fromIntegral (w - 48)
219+ | x >= 97 && x <= 102 = a * 16 + fromIntegral (x - 87)
220+ | otherwise = error "impossible"
221+ where x = toLower w