attoparsec 0.10.4.0 → 0.11.1.0
raw patch · 14 files changed
+202/−119 lines, 14 filesdep +scientificdep ~basedep ~deepseq
Dependencies added: scientific
Dependency ranges changed: base, deepseq
Files
- Data/Attoparsec/ByteString.hs +4/−1
- Data/Attoparsec/ByteString/Char8.hs +45/−46
- Data/Attoparsec/ByteString/FastSet.hs +1/−1
- Data/Attoparsec/ByteString/Internal.hs +13/−2
- Data/Attoparsec/ByteString/Lazy.hs +7/−6
- Data/Attoparsec/Number.hs +1/−4
- Data/Attoparsec/Text.hs +36/−43
- Data/Attoparsec/Text/FastSet.hs +1/−1
- Data/Attoparsec/Text/Internal.hs +14/−3
- Data/Attoparsec/Text/Lazy.hs +6/−5
- attoparsec.cabal +5/−3
- benchmarks/Benchmarks.hs +55/−0
- benchmarks/attoparsec-benchmarks.cabal +7/−4
- changelog +7/−0
Data/Attoparsec/ByteString.hs view
@@ -47,10 +47,13 @@ , I.word8 , I.anyWord8 , I.notWord8- , I.peekWord8 , I.satisfy , I.satisfyWith , I.skip++ -- ** Lookahead+ , I.peekWord8+ , I.peekWord8' -- ** Byte classes , I.inClass
Data/Attoparsec/ByteString/Char8.hs view
@@ -46,9 +46,12 @@ , char8 , anyChar , notChar- , peekChar , satisfy + -- ** Lookahead+ , peekChar+ , peekChar'+ -- ** Special character parsers , digit , letter_iso8859_15@@ -106,7 +109,7 @@ , I.atEnd ) where -import Control.Applicative ((*>), (<*), (<$>), (<|>))+import Control.Applicative (pure, (*>), (<*), (<$>), (<|>)) import Data.Attoparsec.ByteString.FastSet (charClass, memberChar) import Data.Attoparsec.ByteString.Internal (Parser, (<?>)) import Data.Attoparsec.Combinator@@ -114,8 +117,8 @@ import Data.Bits (Bits, (.|.), shiftL) import Data.ByteString.Internal (c2w, w2c) import Data.Int (Int8, Int16, Int32, Int64)-import Data.Ratio ((%)) import Data.String (IsString(..))+import Data.Scientific (Scientific, scientific, coefficient, base10Exponent) import Data.Word (Word8, Word16, Word32, Word64, Word) import Prelude hiding (takeWhile) import qualified Data.Attoparsec.ByteString as A@@ -223,8 +226,8 @@ anyChar = satisfy $ const True {-# INLINE anyChar #-} --- | Match any character. Returns 'Nothing' if end of input has been--- reached. Does not consume any input.+-- | Match any character, to perform lookahead. Returns 'Nothing' if+-- end of input has been reached. Does not consume any input. -- -- /Note/: Because this parser does not fail, do not use it with -- combinators such as 'many', because such parsers loop until a@@ -233,6 +236,12 @@ peekChar = (fmap w2c) `fmap` I.peekWord8 {-# INLINE peekChar #-} +-- | Match any character, to perform lookahead. Does not consume any+-- input, but will fail if end of input has been reached.+peekChar' :: Parser Char+peekChar' = w2c `fmap` I.peekWord8'+{-# INLINE peekChar' #-}+ -- | Fast predicate for matching ASCII space characters. -- -- /Note/: This predicate only gives correct answers for the ASCII@@ -471,8 +480,8 @@ {-# SPECIALIZE rational :: Parser Double #-} {-# SPECIALIZE rational :: Parser Float #-} {-# SPECIALIZE rational :: Parser Rational #-}-rational = floaty $ \real frac fracDenom -> fromRational $- real % 1 + frac % fracDenom+{-# SPECIALIZE rational :: Parser Scientific #-}+rational = scientifically realToFrac -- | Parse a rational number. --@@ -490,12 +499,7 @@ -- This function does not accept string representations of \"NaN\" or -- \"Infinity\". double :: Parser Double-double = floaty asDouble--asDouble :: Integer -> Integer -> Integer -> Double-asDouble real frac fracDenom =- fromIntegral real + fromIntegral frac / fromIntegral fracDenom-{-# INLINE asDouble #-}+double = rational -- | Parse a number, attempting to preserve both speed and precision. --@@ -507,43 +511,38 @@ -- 'rational'. -- -- This function does not accept string representations of \"NaN\" or--- \"Infinity\".+-- \" number :: Parser Number-number = floaty $ \real frac fracDenom ->- if frac == 0 && fracDenom == 0- then I real- else D (asDouble real frac fracDenom)-{-# INLINE number #-}--data T = T !Integer !Int+number = scientifically $ \s ->+ let e = base10Exponent s+ c = coefficient s+ in if e >= 0+ then I (c * 10 ^ e)+ else D (fromInteger c / 10 ^ negate e) -floaty :: Fractional a => (Integer -> Integer -> Integer -> a) -> Parser a-{-# INLINE floaty #-}-floaty f = do+{-# INLINE scientifically #-}+scientifically :: (Scientific -> a) -> Parser a+scientifically h = do let minus = 45 plus = 43 !positive <- ((== plus) <$> I.satisfy (\c -> c == minus || c == plus)) <|>- return True- real <- decimal- let tryFraction = do- let dot = 46- _ <- I.satisfy (==dot)- ds <- I.takeWhile isDigit_w8- case I.parseOnly decimal ds of- Right n -> return $ T n (B.length ds)- _ -> fail "no digits after decimal"- T fraction fracDigits <- tryFraction <|> return (T 0 0)+ pure True++ n <- decimal++ let f fracDigits = scientific (B8.foldl' step n fracDigits)+ (negate $ B8.length fracDigits)+ step a w = a * 10 + fromIntegral (w - 48)++ s <- let dot = 46 in+ (I.satisfy (==dot) *> (f <$> I.takeWhile isDigit_w8)) <|>+ pure (scientific n 0)++ let !signedCoeff | positive = coefficient s+ | otherwise = negate $ coefficient s+ let littleE = 101 bigE = 69- e w = w == littleE || w == bigE- power <- (I.satisfy e *> signed decimal) <|> return (0::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 positive- then n- else -n+ (I.satisfy (\c -> c == littleE || c == bigE) *>+ fmap (h . scientific signedCoeff . (base10Exponent s +)) (signed decimal)) <|>+ return (h $ scientific signedCoeff (base10Exponent s))
Data/Attoparsec/ByteString/FastSet.hs view
@@ -83,7 +83,7 @@ where search lo hi | hi < lo = False | otherwise =- let mid = (lo + hi) `div` 2+ let mid = (lo + hi) `quot` 2 in case compare w (U.unsafeIndex s mid) of GT -> search (mid + 1) hi LT -> search lo (mid - 1)
Data/Attoparsec/ByteString/Internal.hs view
@@ -34,7 +34,10 @@ , skip , word8 , notWord8++ -- ** Lookahead , peekWord8+ , peekWord8' -- ** Byte classes , inClass@@ -426,8 +429,8 @@ notWord8 c = satisfy (/= c) <?> "not " ++ show c {-# INLINE notWord8 #-} --- | Match any byte. Returns 'Nothing' if end of input has been--- reached. Does not consume any input.+-- | Match any byte, to perform lookahead. Returns 'Nothing' if end of+-- input has been reached. Does not consume any input. -- -- /Note/: Because this parser does not fail, do not use it with -- combinators such as 'many', because such parsers loop until a@@ -444,6 +447,14 @@ else let !w = B.unsafeHead (unI i0) in ks i0 a0 m0 (Just w) {-# INLINE peekWord8 #-}++-- | Match any byte, to perform lookahead. Does not consume any+-- input, but will fail if end of input has been reached.+peekWord8' :: Parser Word8+peekWord8' = do+ s <- ensure 1+ return $! B.unsafeHead s+{-# INLINE peekWord8' #-} -- | Match only if all input has been consumed. endOfInput :: Parser ()
Data/Attoparsec/ByteString/Lazy.hs view
@@ -7,8 +7,8 @@ -- Stability : experimental -- Portability : unknown ----- Simple, efficient combinator parsing for lazy 'ByteString'--- strings, loosely based on the Parsec library.+-- Simple, efficient combinator parsing that can consume lazy+-- 'ByteString' strings, loosely based on the Parsec library. -- -- This is essentially the same code as in the 'Data.Attoparsec' -- module, only with a 'parse' function that can consume a lazy@@ -16,10 +16,11 @@ -- more input to be fed in. Think of this as suitable for use with a -- lazily read file, e.g. via 'L.readFile' or 'L.hGetContents'. ----- Behind the scenes, strict 'B.ByteString' values are still used--- internally to store parser input and manipulate it efficiently.--- High-performance parsers such as 'string' still expect strict--- 'B.ByteString' parameters.+-- /Note:/ The various parser functions and combinators such as+-- 'string' still expect /strict/ 'B.ByteString' parameters, and+-- return strict 'B.ByteString' results. Behind the scenes, strict+-- 'B.ByteString' values are still used internally to store parser+-- input and manipulate it efficiently. module Data.Attoparsec.ByteString.Lazy (
Data/Attoparsec/Number.hs view
@@ -10,10 +10,7 @@ -- -- A simple number type, useful for parsing both exact and inexact -- quantities without losing much precision.-module Data.Attoparsec.Number- (- Number(..)- ) where+module Data.Attoparsec.Number ( Number(..) ) where import Control.DeepSeq (NFData(rnf)) import Data.Data (Data)
Data/Attoparsec/Text.hs view
@@ -52,7 +52,10 @@ , I.satisfy , I.satisfyWith , I.skip++ -- ** Lookahead , I.peekChar+ , I.peekChar' -- ** Special character parsers , digit@@ -103,14 +106,14 @@ , I.atEnd ) where -import Control.Applicative ((<$>), (*>), (<*), (<|>))+import Control.Applicative (pure, (<$>), (*>), (<*), (<|>)) import Data.Attoparsec.Combinator import Data.Attoparsec.Number (Number(..))+import Data.Scientific (Scientific, scientific, coefficient, base10Exponent) import Data.Attoparsec.Text.Internal ((<?>), Parser, Result, parse, takeWhile1) import Data.Bits (Bits, (.|.), shiftL) import Data.Char (isAlpha, isDigit, isSpace, ord) import Data.Int (Int8, Int16, Int32, Int64)-import Data.Ratio ((%)) import Data.Text (Text) import Data.Word (Word8, Word16, Word32, Word64, Word) import qualified Data.Attoparsec.Internal as I@@ -343,8 +346,8 @@ {-# SPECIALIZE rational :: Parser Double #-} {-# SPECIALIZE rational :: Parser Float #-} {-# SPECIALIZE rational :: Parser Rational #-}-rational = floaty $ \real frac fracDenom -> fromRational $- real % 1 + frac % fracDenom+{-# SPECIALIZE rational :: Parser Scientific #-}+rational = scientifically realToFrac -- | Parse a rational number. --@@ -362,12 +365,7 @@ -- This function does not accept string representations of \"NaN\" or -- \"Infinity\". double :: Parser Double-double = floaty asDouble--asDouble :: Integer -> Integer -> Integer -> Double-asDouble real frac fracDenom =- fromIntegral real + fromIntegral frac / fromIntegral fracDenom-{-# INLINE asDouble #-}+double = rational -- | Parse a number, attempting to preserve both speed and precision. --@@ -381,12 +379,35 @@ -- This function does not accept string representations of \"NaN\" or -- \"Infinity\". number :: Parser Number-number = floaty $ \real frac fracDenom ->- if frac == 0 && fracDenom == 0- then I real- else D (asDouble real frac fracDenom)-{-# INLINE number #-}+number = scientifically $ \s ->+ let e = base10Exponent s+ c = coefficient s+ in if e >= 0+ then I (c * 10 ^ e)+ else D (fromInteger c / 10 ^ negate e) +{-# INLINE scientifically #-}+scientifically :: (Scientific -> a) -> Parser a+scientifically h = do+ !positive <- ((== '+') <$> I.satisfy (\c -> c == '-' || c == '+')) <|>+ pure True++ n <- decimal++ let f fracDigits = scientific (T.foldl' step n fracDigits)+ (negate $ T.length fracDigits)+ step a c = a * 10 + fromIntegral (ord c - 48)++ s <- (I.satisfy (=='.') *> (f <$> I.takeWhile isDigit)) <|>+ pure (scientific n 0)++ let !signedCoeff | positive = coefficient s+ | otherwise = negate $ coefficient s++ (I.satisfy (\c -> c == 'e' || c == 'E') *>+ fmap (h . scientific signedCoeff . (base10Exponent s +)) (signed decimal)) <|>+ return (h $ scientific signedCoeff (base10Exponent s))+ -- | Parse a single digit, as recognised by 'isDigit'. digit :: Parser Char digit = I.satisfy isDigit <?> "digit"@@ -433,31 +454,3 @@ -- | Type-specialized version of '<*' for 'Text'. (<*.) :: Parser a -> Text -> Parser a f <*. s = f <* I.string s--data T = T !Integer !Int--floaty :: Fractional a => (Integer -> Integer -> Integer -> a) -> Parser a-{-# INLINE floaty #-}-floaty f = do- !positive <- ((== '+') <$> I.satisfy (\c -> c == '-' || c == '+')) <|>- return True- real <- decimal- let tryFraction = do- _ <- I.satisfy (=='.')- ds <- I.takeWhile isDigit- case I.parseOnly decimal ds of- Right n -> return $ T n (T.length ds)- _ -> fail "no digits after decimal"- T fraction fracDigits <- tryFraction <|> return (T 0 0)- let e c = c == 'e' || c == 'E'- power <- (I.satisfy e *> signed decimal) <|> return (0::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 positive- then n- else -n
Data/Attoparsec/Text/FastSet.hs view
@@ -50,7 +50,7 @@ where search lo hi | hi < lo = False | otherwise =- let mid = (lo + hi) `div` 2+ let mid = (lo + hi) `quot` 2 in case compare c (AB.unsafeAt a mid) of GT -> search (mid + 1) hi LT -> search lo (mid - 1)
Data/Attoparsec/Text/Internal.hs view
@@ -35,7 +35,10 @@ , skip , char , notChar++ -- ** Lookahead , peekChar+ , peekChar' -- ** Character classes , inClass@@ -90,7 +93,7 @@ fromString = string . T.pack lengthAtLeast :: T.Text -> Int -> Bool-lengthAtLeast t@(T.Text _ _ len) n = (len `div` 2) >= n || T.length t >= n+lengthAtLeast t@(T.Text _ _ len) n = (len `quot` 2) >= n || T.length t >= n {-# INLINE lengthAtLeast #-} -- | If at least @n@ characters of input are available, return the@@ -440,8 +443,8 @@ notChar c = satisfy (/= c) <?> "not " ++ show c {-# INLINE notChar #-} --- | Match any character. Returns 'Nothing' if end of input has been--- reached. Does not consume any input.+-- | Match any character, to perform lookahead. Returns 'Nothing' if+-- end of input has been reached. Does not consume any input. -- -- /Note/: Because this parser does not fail, do not use it with -- combinators such as 'many', because such parsers loop until a@@ -458,6 +461,14 @@ else let !c = unsafeHead (unI i0) in ks i0 a0 m0 (Just c) {-# INLINE peekChar #-}++-- | Match any character, to perform lookahead. Does not consume any+-- input, but will fail if end of input has been reached.+peekChar' :: Parser Char+peekChar' = do+ s <- ensure 1+ return $! unsafeHead s+{-# INLINE peekChar' #-} -- | Match only if all input has been consumed. endOfInput :: Parser ()
Data/Attoparsec/Text/Lazy.hs view
@@ -7,7 +7,7 @@ -- Stability : experimental -- Portability : unknown ----- Simple, efficient combinator parsing for lazy 'Text'+-- Simple, efficient combinator parsing that can consume lazy 'Text' -- strings, loosely based on the Parsec library. -- -- This is essentially the same code as in the 'Data.Attoparsec.Text'@@ -16,10 +16,11 @@ -- more input to be fed in. Think of this as suitable for use with a -- lazily read file, e.g. via 'L.readFile' or 'L.hGetContents'. ----- Behind the scenes, strict 'T.Text' values are still used--- internally to store parser input and manipulate it efficiently.--- High-performance parsers such as 'string' still expect strict--- 'T.Text' parameters.+-- /Note:/ The various parser functions and combinators such as+-- 'string' still expect /strict/ 'T.Text' parameters, and return+-- strict 'T.Text' results. Behind the scenes, strict 'T.Text' values+-- are still used internally to store parser input and manipulate it+-- efficiently. module Data.Attoparsec.Text.Lazy (
attoparsec.cabal view
@@ -1,12 +1,12 @@ name: attoparsec-version: 0.10.4.0+version: 0.11.1.0 license: BSD3 license-file: LICENSE category: Text, Parsing author: Bryan O'Sullivan <bos@serpentine.com> maintainer: Bryan O'Sullivan <bos@serpentine.com> stability: experimental-tested-with: GHC == 6.12.3, GHC == 7.0.3, GHC == 7.2.1, GHC == 7.4.2, GHC == 7.6.1+tested-with: GHC == 7.0, GHC == 7.2, GHC == 7.4, GHC == 7.6, GHC == 7.8 synopsis: Fast combinator parsing for bytestrings and text cabal-version: >= 1.8 homepage: https://github.com/bos/attoparsec@@ -23,6 +23,7 @@ benchmarks/Tiny.hs benchmarks/attoparsec-benchmarks.cabal benchmarks/med.txt.bz2+ changelog examples/Makefile examples/Parsec_RFC2616.hs examples/RFC2616.hs@@ -43,7 +44,8 @@ bytestring, containers, deepseq,- text >= 0.11.1.5+ text >= 0.11.1.5,+ scientific >= 0.2 exposed-modules: Data.Attoparsec Data.Attoparsec.ByteString
benchmarks/Benchmarks.hs view
@@ -6,6 +6,7 @@ import Data.Bits (unsafeShiftL) import Data.ByteString.Internal (ByteString(..)) import Data.Char+import Data.Scientific (Scientific(..)) import Data.Word (Word32) import Text.Parsec.Text () import Text.Parsec.Text.Lazy ()@@ -80,6 +81,60 @@ , bgroup "scan" [ bench "short" $ nf (AB.parse quotedString) (BC.pack "abcdefghijk\"") , bench "long" $ nf (AB.parse quotedString) b+ ]++ , let strN = "1234.56789"+ strNePos = "1234.56789e3"+ strNeNeg = "1234.56789e-3"+ in+ bgroup "numbers"+ [ let !tN = T.pack strN+ !tNePos = T.pack strNePos+ !tNeNeg = T.pack strNeNeg+ in bgroup "Text"+ [+ bgroup "no power"+ [ bench "double" $ nf (AT.parseOnly AT.double) tN+ , bench "number" $ nf (AT.parseOnly AT.number) tN+ , bench "rational" $ nf (AT.parseOnly (AT.rational :: AT.Parser Rational)) tN+ , bench "scientific" $ nf (AT.parseOnly (AT.rational :: AT.Parser Scientific)) tN+ ]+ , bgroup "positive power"+ [ bench "double" $ nf (AT.parseOnly AT.double) tNePos+ , bench "number" $ nf (AT.parseOnly AT.number) tNePos+ , bench "rational" $ nf (AT.parseOnly (AT.rational :: AT.Parser Rational)) tNePos+ , bench "scientific" $ nf (AT.parseOnly (AT.rational :: AT.Parser Scientific)) tNePos+ ]+ , bgroup "negative power"+ [ bench "double" $ nf (AT.parseOnly AT.double) tNeNeg+ , bench "number" $ nf (AT.parseOnly AT.number) tNeNeg+ , bench "rational" $ nf (AT.parseOnly (AT.rational :: AT.Parser Rational)) tNeNeg+ , bench "scientific" $ nf (AT.parseOnly (AT.rational :: AT.Parser Scientific)) tNeNeg+ ]+ ]+ , let !bN = BC.pack strN+ !bNePos = BC.pack strNePos+ !bNeNeg = BC.pack strNeNeg+ in bgroup "ByteString"+ [ bgroup "no power"+ [ bench "double" $ nf (AC.parseOnly AC.double) bN+ , bench "number" $ nf (AC.parseOnly AC.number) bN+ , bench "rational" $ nf (AC.parseOnly (AC.rational :: AC.Parser Rational)) bN+ , bench "scientific" $ nf (AC.parseOnly (AC.rational :: AC.Parser Scientific)) bN+ ]+ , bgroup "positive power"+ [ bench "double" $ nf (AC.parseOnly AC.double) bNePos+ , bench "number" $ nf (AC.parseOnly AC.number) bNePos+ , bench "rational" $ nf (AC.parseOnly (AC.rational :: AC.Parser Rational)) bNePos+ , bench "scientific" $ nf (AC.parseOnly (AC.rational :: AC.Parser Scientific)) bNePos+ ]+ , bgroup "negative power"+ [ bench "double" $ nf (AC.parseOnly AC.double) bNeNeg+ , bench "number" $ nf (AC.parseOnly AC.number) bNeNeg+ , bench "rational" $ nf (AC.parseOnly (AC.rational :: AC.Parser Rational)) bNeNeg+ , bench "scientific" $ nf (AC.parseOnly (AC.rational :: AC.Parser Scientific)) bNeNeg+ ]+ ] ] ]
benchmarks/attoparsec-benchmarks.cabal view
@@ -3,16 +3,19 @@ name: attoparsec-benchmarks version: 0-cabal-version: >=1.2+cabal-version: >=1.6 build-type: Simple executable attoparsec-benchmarks main-is: Benchmarks.hs+ hs-source-dirs: .. .+ ghc-options: -O2 build-depends:- attoparsec,- base,+ array,+ base == 4.*, bytestring, criterion >= 0.5,- deepseq == 1.1.*,+ deepseq >= 1.1, parsec >= 3.1.2,+ scientific, text
+ changelog view
@@ -0,0 +1,7 @@+0.11.1.0++ * New dependency: the scientific package. This allows us to parse+ numbers much more efficiently.++ * peekWord8', peekChar': new primitive parsers that allow+ single-character lookahead.