Commits

Bryan O'Sullivan  committed 47563ac

Common up shared code and improve performance 40%

I have absolutely no idea where the performance improvement comes from.

  • Participants
  • Parent commits 9dbc019

Comments (0)

Files changed (1)

File Data/Text/Lex.hs

 -- the remaining text.
 type Lexer a = Text -> Either String (a,Text)
 
--- | Read a decimal number.
+-- | Read a decimal integer.
+--
+-- This function does not handle leading sign characters.  If you need
+-- to handle signed input, use @'signed' 'decimal'@.
 decimal :: Integral a => Lexer a
 {-# SPECIALIZE decimal :: Lexer Int #-}
 {-# SPECIALIZE decimal :: Lexer Integer #-}
 
 -- | Read a hexadecimal number, with optional leading @\"0x\"@.  This
 -- function is case insensitive.
+--
+-- This function does not handle leading sign characters.  If you need
+-- to handle signed input, use @'signed' 'hexadecimal'@.
 hexadecimal :: Integral a => Lexer a
 {-# SPECIALIZE hex :: Lexer Int #-}
 {-# SPECIALIZE hex :: Lexer Integer #-}
     | otherwise           = hex txt
  where (h,t) = T.splitAt 2 txt
 
+-- | Read a leading sign character (@\'-\'@ or @\'+\'@) and apply it
+-- to the result of applying the given reader.
+signed :: Num a => Lexer a -> Lexer a
+{-# INLINE signed #-}
+signed f = runP (signa (P f))
+
+-- | Read a rational number.
+--
+-- This function accepts an optional leading sign character.
+rational :: RealFloat a => Lexer a
+{-# SPECIALIZE rational :: Lexer Double #-}
+rational = floaty $ \real frac fracDenom -> fromRational $
+                     real % 1 + frac % fracDenom
+
+-- | Read a rational number.
+--
+-- This function accepts an optional leading sign character.
+--
+-- /Note/: This function is almost ten times faster than 'rational',
+-- but is slightly less accurate.
+--
+-- The 'Double' type supports about 16 decimal places of accuracy.
+-- For 94.2% of numbers, this function and 'rational' give identical
+-- results, but for the remaining 5.8%, this function loses precision
+-- around the 15th decimal place.  For 0.001% of numbers, this
+-- function will lose precision at the 13th or 14th decimal place.
+double :: Lexer Double
+double = floaty $ \real frac fracDenom ->
+                   fromIntegral real +
+                   fromIntegral frac / fromIntegral fracDenom
+
 hex :: Integral a => Lexer a
 {-# SPECIALIZE hex :: Lexer Int #-}
 {-# SPECIALIZE hex :: Lexer Integer #-}
     | c >= 'A' && c <= 'F' = ord c - (ord 'A' - 10)
     | otherwise            = error "Data.Text.Lex.hexDigitToInt: bad input"
 
--- | Read a leading sign character (@\'-\'@ or @\'+\'@) and apply it
--- to the result of applying the given reader.
-signed :: Num a => Lexer a -> Lexer a
-{-# INLINE signed #-}
-signed f = runP (signa (P f))
-
 signa :: Num a => Parser a -> Parser a
 {-# SPECIALIZE signa :: Parser Int -> Parser Int #-}
 {-# SPECIALIZE signa :: Parser Integer -> Parser Integer #-}
                      Just (c,t') | p c -> Right (c,t')
                      _                 -> Left "char"
 
--- | Read a rational number.
-rational :: RealFloat a => Lexer a
-{-# SPECIALIZE rational :: Lexer Double #-}
-rational = runP $ do
+floaty :: RealFloat a => (Integer -> Integer -> Integer -> a) -> Lexer a
+{-# INLINE floaty #-}
+floaty f = runP $ do
   real <- signa (P decimal)
   (fraction,fracDigits) <- perhaps (0,0) $ do
     _ <- char (=='.')
     n <- P decimal
     return (n, digits)
   power <- perhaps 0 (char (`elem` "eE") >> signa (P decimal) :: Parser Int)
-  return $! if fraction == 0
-            then if power == 0
-                 then fromIntegral real
-                 else fromIntegral real * (10 ^^ power)
-            else fromRational $ if power == 0
-                 then real % 1 + fraction % (10 ^ fracDigits)
-                 else (real % 1 + fraction % (10 ^ fracDigits)) * (10 ^^ power)
-
--- | Read a rational number.  This function is about ten times faster
--- than 'rational', but is slightly less accurate.
---
--- The 'Double' type supports about 16 decimal places of accuracy.
--- For 94.2% of numbers, this function and 'rational' give identical
--- results, but for the remaining 5.8%, this function loses precision
--- around the 15th decimal place.  For 0.001% of numbers, this
--- function will lose precision at the 13th or 14th decimal place.
-double :: Lexer Double
-double = runP $ do
-  real <- signa (P decimal) :: Parser Integer
-  (fraction,fracDigits) <- perhaps (0,0) $ do
-    _ <- char (=='.')
-    digits <- P $ \t -> Right (T.length $ T.takeWhile isDigit t, t)
-    n <- P decimal :: Parser Integer
-    return (n, digits)
-  power <- perhaps 0 (char (`elem` "eE") >> signa (P decimal) :: Parser Int)
-  return $! if fraction == 0
+  return $! if fracDigits == 0
             then if power == 0
                  then fromIntegral real
                  else fromIntegral real * (10 ^^ power)
             else if power == 0
-                 then fromIntegral real + fromIntegral fraction /
-                      (10 ^ fracDigits)
-                 else (fromIntegral real + fromIntegral fraction /
-                       (10 ^ fracDigits)) * (10 ^^ power)
+                 then f real fraction (10 ^ fracDigits)
+                 else f real fraction (10 ^ fracDigits) * (10 ^^ power)