module Data.Text.Read
(
Reader
, decimal
, hexadecimal
, signed
, rational
, double
) where
import Control.Monad (liftM)
import Data.Char (digitToInt, isDigit, isHexDigit, ord)
import Data.Ratio
import Data.Text as T
type Reader a = Text -> Either String (a,Text)
decimal :: Integral a => Reader a
decimal txt
| T.null h = Left "no digits in input"
| otherwise = Right (T.foldl' go 0 h, t)
where (h,t) = T.spanBy isDigit txt
go n d = (n * 10 + fromIntegral (digitToInt d))
hexadecimal :: Integral a => Reader a
hexadecimal txt
| T.toLower h == "0x" = hex t
| otherwise = hex txt
where (h,t) = T.splitAt 2 txt
signed :: Num a => Reader a -> Reader a
signed f = runP (signa (P f))
rational :: RealFloat a => Reader a
rational = floaty $ \real frac fracDenom -> fromRational $
real % 1 + frac % fracDenom
double :: Reader Double
double = floaty $ \real frac fracDenom ->
fromIntegral real +
fromIntegral frac / fromIntegral fracDenom
hex :: Integral a => Reader a
hex txt
| T.null h = Left "no digits in input"
| otherwise = Right (T.foldl' go 0 h, t)
where (h,t) = T.spanBy isHexDigit txt
go n d = (n * 16 + fromIntegral (hexDigitToInt d))
hexDigitToInt :: Char -> Int
hexDigitToInt c
| c >= '0' && c <= '9' = ord c ord '0'
| c >= 'a' && c <= 'f' = ord c (ord 'a' 10)
| c >= 'A' && c <= 'F' = ord c (ord 'A' 10)
| otherwise = error "Data.Text.Lex.hexDigitToInt: bad input"
signa :: Num a => Parser a -> Parser a
signa p = do
sign <- perhaps '+' $ char (\c -> c == '-' || c == '+')
if sign == '+' then p else negate `liftM` p
newtype Parser a = P {
runP :: Text -> Either String (a,Text)
}
instance Monad Parser where
return a = P $ \t -> Right (a,t)
m >>= k = P $ \t -> case runP m t of
Left err -> Left err
Right (a,t') -> runP (k a) t'
fail msg = P $ \_ -> Left msg
perhaps :: a -> Parser a -> Parser a
perhaps def m = P $ \t -> case runP m t of
Left _ -> Right (def,t)
r@(Right _) -> r
char :: (Char -> Bool) -> Parser Char
char p = P $ \t -> case T.uncons t of
Just (c,t') | p c -> Right (c,t')
_ -> Left "char"
data T = T !Integer !Int
floaty :: RealFloat a => (Integer -> Integer -> Integer -> a) -> Reader a
floaty f = runP $ do
sign <- perhaps '+' $ char (\c -> c == '-' || c == '+')
real <- P decimal
T fraction fracDigits <- perhaps (T 0 0) $ do
_ <- char (=='.')
digits <- P $ \t -> Right (T.length $ T.takeWhile isDigit t, t)
n <- P decimal
return $ T n digits
let e c = c == 'e' || c == 'E'
power <- perhaps 0 (char e >> signa (P decimal) :: Parser Int)
let n = if fracDigits == 0
then if power == 0
then fromIntegral real
else fromIntegral real * (10 ^^ power)
else if power == 0
then f real fraction (10 ^ fracDigits)
else f real fraction (10 ^ fracDigits) * (10 ^^ power)
return $! if sign == '+'
then n
else n