attoparsec-text 0.8.0.0 → 0.8.2.0
raw patch · 7 files changed
+357/−46 lines, 7 filesbinary-addedPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Data.Attoparsec.Text: (.*>) :: Applicative f => f Text -> f a -> f a
+ Data.Attoparsec.Text: (<*.) :: Applicative f => f a -> f Text -> f a
+ Data.Attoparsec.Text: decimal :: Integral a => Parser a
+ Data.Attoparsec.Text: digit :: Parser Char
+ Data.Attoparsec.Text: double :: Parser Double
+ Data.Attoparsec.Text: endOfLine :: Parser ()
+ Data.Attoparsec.Text: hexadecimal :: Integral a => Parser a
+ Data.Attoparsec.Text: letter :: Parser Char
+ Data.Attoparsec.Text: rational :: RealFloat a => Parser a
+ Data.Attoparsec.Text: signed :: Num a => Parser a -> Parser a
+ Data.Attoparsec.Text: skipSpace :: Parser ()
+ Data.Attoparsec.Text: space :: Parser Char
Files
- Data/Attoparsec/Text.hs +83/−27
- Data/Attoparsec/Text/Internal.hs +156/−2
- README.markdown +4/−13
- attoparsec-text.cabal +15/−3
- benchmarks/Double.hs +95/−0
- benchmarks/Makefile +4/−1
- benchmarks/double_test.txt.bz2 binary
Data/Attoparsec/Text.hs view
@@ -1,6 +1,6 @@ -- | -- Module : Data.Attoparsec.Text--- Copyright : Felipe Lessa 2010, Bryan O'Sullivan 2007-2010+-- Copyright : Felipe Lessa 2010-2011, Bryan O'Sullivan 2007-2010 -- License : BSD3 -- -- Maintainer : felipe.lessa@gmail.com@@ -27,9 +27,9 @@ -- * Running parsers , parse- , feed- , parseWith , parseTest+ , parseWith+ , feed -- ** Result conversion , maybeResult@@ -48,59 +48,84 @@ , I.satisfyWith , I.skip + -- ** Special character parsers+ -- $specialcharparsers+ , I.digit+ , I.letter+ , I.space+ -- ** Character classes , I.inClass , I.notInClass -- * Efficient string handling , I.string+ , I.skipSpace , I.skipWhile , I.take , I.takeWhile , I.takeWhile1 , I.takeTill + -- * Text parsing+ , I.endOfLine++ -- * Numeric parsers+ , I.decimal+ , I.hexadecimal+ , I.signed+ , I.double+ , I.rational+ -- * State observation and manipulation functions , I.endOfInput , I.ensure++ -- * Applicative specializations+ -- $applicative+ , (<*.)+ , (.*>) ) where +import Control.Applicative (Applicative, (<*), (*>)) import Data.Attoparsec.Combinator import qualified Data.Attoparsec.Text.Internal as I import qualified Data.Text as T -- $parsec ----- Compared to Parsec 3, Attoparsec makes several tradeoffs. It is--- not intended for, or ideal for, all possible uses.+-- Compared to Parsec 3, @attoparsec-text@ makes several+-- tradeoffs. It is not intended for, or ideal for, all possible+-- uses. ----- * While Attoparsec can consume input incrementally, Parsec cannot.--- Incremental input is a huge deal for efficient and secure network--- and system programming, since it gives much more control to users--- of the library over matters such as resource usage and the I/O--- model to use.+-- * While @attoparsec-text@ can consume input incrementally,+-- Parsec cannot. Incremental input is a huge deal for+-- efficient and secure network and system programming, since+-- it gives much more control to users of the library over+-- matters such as resource usage and the I/O model to use. ----- * Much of the performance advantage of Attoparsec is gained via--- high-performance parsers such as 'I.takeWhile' and 'I.string'.--- If you use complicated combinators that return lists of--- characters, there really isn't much performance difference the--- two libraries.+-- * Much of the performance advantage of @attoparsec-text@ is+-- gained via high-performance parsers such as 'I.takeWhile'+-- and 'I.string'. If you use complicated combinators that+-- return lists of characters, there really isn't much+-- performance difference the two libraries. ----- * Unlike Parsec 3, Attoparsec does not support being used as a--- monad transformer. This is mostly a matter of the implementor--- not having needed that functionality.+-- * Unlike Parsec 3, @attoparsec-text@ does not support being+-- used as a monad transformer. This is mostly a matter of the+-- implementor not having needed that functionality. ----- * Attoparsec is specialised to deal only with strict 'T.Text'--- input. Efficiency concernts rule out both lists and lazy--- texts. The usual use for lazy texts would be to allow--- consumption of very large input without a large footprint.--- For this need, Attoparsec's incremental input provides an--- excellent substitute, with much more control over when input--- takes place.+-- * @attoparsec-text@ is specialised to deal only with strict+-- 'T.Text' input. Efficiency concernts rule out both lists+-- and lazy texts. The usual use for lazy texts would be to+-- allow consumption of very large input without a large+-- footprint. For this need, @attoparsec-text@'s incremental+-- input provides an excellent substitute, with much more+-- control over when input takes place. -- -- * Parsec parsers can produce more helpful error messages than--- Attoparsec parsers. This is a matter of focus: Attoparsec avoids--- the extra book-keeping in favour of higher performance.+-- @attoparsec-text@ parsers. This is a matter of focus:+-- @attoparsec-text@ avoids the extra book-keeping in favour of+-- higher performance. -- $performance --@@ -220,3 +245,34 @@ eitherResult (Done _ r) = Right r eitherResult (Fail _ _ msg) = Left msg eitherResult _ = Left "Result: incomplete input"++-- $specialcharparsers+--+-- Special parser for characters. Unlike the original+-- @attoparsec@ package, these parsers do work correctly for all+-- encodings. Internally "Data.Char" module is used.+++-- $applicative+--+-- We provide specializations of @\<*@ and @*\>@ as @\<*.@ and+-- @.*\>@, respectively. Together with @IsString@ instance of+-- 'I.Parser', you may write parsers applicatively more easily.+-- For example:+--+-- > paren p = "(" .*> p <*. ")"+--+-- instead of the more verbose+--+-- > paren p = string "(" *> p <* string ")"++-- | Same as @Applicative@'s @\<*@ but specialized to 'T.Text'+-- on the second argument.+(<*.) :: Applicative f => f a -> f T.Text -> f a+(<*.) = (<*)++-- | Same as @Applicative@'s @*\>@ but specialized to 'T.Text'+-- on the first argument.+(.*>) :: Applicative f => f T.Text -> f a -> f a+(.*>) = (*>)+
Data/Attoparsec/Text/Internal.hs view
@@ -1,7 +1,7 @@-{-# LANGUAGE Rank2Types, RecordWildCards #-}+{-# LANGUAGE Rank2Types, RecordWildCards, FlexibleInstances #-} -- | -- Module : Data.Attoparsec.Text.Internal--- Copyright : Felipe Lessa 2010, Bryan O'Sullivan 2007-2010+-- Copyright : Felipe Lessa 2010-2011, Bryan O'Sullivan 2007-2010 -- License : BSD3 -- -- Maintainer : felipe.lessa@gmail.com@@ -38,8 +38,14 @@ , inClass , notInClass + -- ** Special character parsers+ , digit+ , letter+ , space+ -- * Efficient string handling , skipWhile+ , skipSpace , string , stringTransform , take@@ -47,6 +53,13 @@ , takeWhile1 , takeTill + -- * Numeric parsers+ , decimal+ , hexadecimal+ , signed+ , double+ , rational+ -- * State observation and manipulation functions , endOfInput , ensure@@ -59,7 +72,10 @@ import Control.Monad (MonadPlus(..), when) import Data.Attoparsec.Combinator import Data.Attoparsec.Text.FastSet (charClass, member)+import Data.Char import Data.Monoid (Monoid(..))+import Data.Ratio ((%))+import Data.String (IsString(..)) import Prelude hiding (getChar, take, takeWhile) import qualified Data.Text as T @@ -78,6 +94,9 @@ type Failure r = S -> [String] -> String -> Result r type Success a r = S -> a -> Result r +instance IsString (Parser T.Text) where+ fromString = string . T.pack+ -- | Have we read all available input? data More = Complete | Incomplete deriving (Eq, Show)@@ -240,6 +259,7 @@ case T.uncons s of Just (h,t) | p h -> put t >> return h | otherwise -> fail "satisfy"+ Nothing -> error "Data.Attoparsec.Text.Internal.satisfy: never here" -- | The parser @skip p@ succeeds for any character for which the -- predicate @p@ returns 'True'.@@ -253,6 +273,7 @@ case T.uncons s of Just (h,t) | p h -> put t | otherwise -> fail "skip"+ Nothing -> error "Data.Attoparsec.Text.Internal.skip: never here" -- | The parser @satisfyWith f p@ transforms a character, and@@ -269,6 +290,21 @@ then put t >> return c else fail "satisfyWith" +-- | Parse a single digit.+digit :: Parser Char+digit = satisfy isDigit <?> "digit"+{-# INLINE digit #-}++-- | Parse a single letter.+letter :: Parser Char+letter = satisfy isLetter <?> "letter"+{-# INLINE letter #-}++-- | Parse a space character.+space :: Parser Char+space = satisfy isSpace <?> "space"+{-# INLINE space #-}+ -- | Consume @n@ characters of input, but succeed only if the -- predicate returns 'True'. takeWith :: Int -> (T.Text -> Bool) -> Parser T.Text@@ -322,6 +358,11 @@ put t when (T.null t) go +-- | Skip over white space.+skipSpace :: Parser ()+skipSpace = skipWhile isSpace >> return ()+{-# INLINE skipSpace #-}+ -- | Consume input as long as the predicate returns 'False' -- (i.e. until it returns 'True'), and return the consumed input. --@@ -419,6 +460,119 @@ notChar :: Char -> Parser Char notChar c = satisfy (/= c) <?> "not " ++ show c {-# INLINE notChar #-}+++-- | Parse and decode an unsigned decimal number.+decimal :: Integral a => Parser a+{-# SPECIALISE decimal :: Parser Int #-}+{-# SPECIALISE decimal :: Parser Integer #-}+decimal = T.foldl' step 0 `fmap` takeWhile1 asciiIsDigit+ where step a w = a * 10 + fromIntegral (fromEnum w - 48)++asciiIsDigit :: Char -> Bool+asciiIsDigit c = c >= '0' && c <= '9'+{-# INLINE asciiIsDigit #-}++-- | Parse and decode an unsigned hexadecimal number. The hex digits+-- @\'a\'@ through @\'f\'@ may be upper or lower case.+--+-- This parser does not accept a leading @\"0x\"@ string.+hexadecimal :: Integral a => Parser a+{-# SPECIALISE hexadecimal :: Parser Int #-}+hexadecimal = T.foldl' step 0 `fmap` takeWhile1 asciiIsHexDigit+ where step a c | c >= '0' && c <= '9' = a * 16 + fromIntegral (fromEnum c - 48)+ | otherwise = a * 16 + fromIntegral (asciiToLower c - 87)++asciiIsHexDigit :: Char -> Bool+asciiIsHexDigit c = asciiIsDigit c || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')+{-# INLINE asciiIsHexDigit #-}++asciiToLower :: Char -> Int+asciiToLower c | c >= 'A' && c <= 'Z' = fromEnum c + 32+ | otherwise = fromEnum c++-- | Parse a number with an optional leading @\'+\'@ or @\'-\'@ sign+-- character.+signed :: Num a => Parser a -> Parser a+{-# SPECIALISE signed :: Parser Int -> Parser Int #-}+signed p = (negate <$> (char '-' *> p))+ <|> (char '+' *> p)+ <|> p++-- | Parse a rational number.+--+-- This parser accepts an optional leading sign character, followed by+-- at least one decimal digit. The syntax similar to that accepted by+-- the 'read' function, with the exception that a trailing @\'.\'@ or+-- @\'e\'@ /not/ followed by a number is not consumed.+--+-- Examples with behaviour identical to 'read', if you feed an empty+-- continuation to the first result:+--+-- >rational "3" == Done 3.0 ""+-- >rational "3.1" == Done 3.1 ""+-- >rational "3e4" == Done 30000.0 ""+-- >rational "3.1e4" == Done 31000.0 ""+--+-- Examples with behaviour identical to 'read':+--+-- >rational ".3" == Fail "input does not start with a digit"+-- >rational "e3" == Fail "input does not start with a digit"+--+-- Examples of differences from 'read':+--+-- >rational "3.foo" == Done 3.0 ".foo"+-- >rational "3e" == Done 3.0 "e"+rational :: RealFloat a => Parser a+{-# SPECIALIZE rational :: Parser Double #-}+rational = floaty $ \real frac fracDenom -> fromRational $+ real % 1 + frac % fracDenom++-- | Parse a rational number.+--+-- The syntax accepted by this parser is the same as for 'rational'.+--+-- /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 :: Parser Double+double = floaty $ \real frac fracDenom ->+ fromIntegral real ++ fromIntegral frac / fromIntegral fracDenom++data T = T !Integer !Int++floaty :: RealFloat a => (Integer -> Integer -> Integer -> a) -> Parser a+{-# INLINE floaty #-}+floaty f = do+ sign <- satisfy (\c -> c == '-' || c == '+') <|> return '+'+ real <- decimal+ let tryFraction = do+ _ <- satisfy (== '.')+ ds <- takeWhile asciiIsDigit+ case (case parse decimal ds of+ Partial k -> k T.empty+ r -> r) of+ Done _ 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 <- (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 sign == '+' then n else -n++ -- | Match only if all input has been consumed. endOfInput :: Parser ()
README.markdown view
@@ -9,21 +9,12 @@ I'm happy to receive bug reports, fixes, documentation enhancements, and other improvements. -Please report bugs via the-[bitbucket issue tracker](http://bitbucket.org/bos/attoparsec/issues).--Master [Mercurial repository](http://bitbucket.org/bos/attoparsec):--* `hg clone http://bitbucket.org/bos/attoparsec`--There's also a [git mirror](http://github.com/bos/attoparsec):--* `git clone git://github.com/bos/attoparsec.git`+Master [darcs repository](http://patch-tag.com/r/felipe/attoparsec-text/home): -(You can create and contribute changes using either Mercurial or git.)+* `darcs get http://patch-tag.com/r/felipe/attoparsec-text` Authors ------- -This library is written and maintained by Bryan O'Sullivan,-<bos@serpentine.com>.+This library is written by Felipe Lessa as a port of Bryan+O'Sullivan's attoparsec.
attoparsec-text.cabal view
@@ -1,5 +1,5 @@ name: attoparsec-text-version: 0.8.0.0+version: 0.8.2.0 license: BSD3 license-file: LICENSE category: Text, Parsing@@ -9,7 +9,7 @@ tested-with: GHC == 6.12.1 synopsis: Fast combinator parsing for texts cabal-version: >= 1.6--- homepage: http://bitbucket.org/bos/attoparsec+homepage: http://patch-tag.com/r/felipe/attoparsec-text/home -- bug-reports: http://bitbucket.org/bos/attoparsec/issues build-type: Simple description:@@ -19,11 +19,23 @@ . This library is basically a translation of the original attoparsec library to use text instead of bytestrings.+ .+ Changes in version 0.8.2.0:+ .+ * Add @IsString@ instance to @Parser@.+ .+ * Add specializations of @\<*@ and @*\>@, see the docs for+ more information.+ .+ * Add @digit@, @letter@, @space@, @decimal@, @hexadecimal@,+ @signed@, @double@ and @rational@. extra-source-files: README.markdown benchmarks/Makefile+ benchmarks/Double.hs benchmarks/Tiny.hs benchmarks/med.txt.bz2+ benchmarks/double_test.txt.bz2 tests/Makefile tests/QC.hs tests/QCSupport.hs@@ -46,4 +58,4 @@ Data.Attoparsec.Text.Lazy other-modules: Data.Attoparsec.Text.Internal ghc-options: -Wall- ghc-prof-options: -auto-all+-- ghc-prof-options: -auto-all
+ benchmarks/Double.hs view
@@ -0,0 +1,95 @@+import Control.Applicative ((<|>))+import Control.Monad (forM_, MonadPlus(mzero))+import Data.Char (isDigit, isLetter)+import Data.List (foldl')+import System.Environment (getArgs)+import qualified Data.Attoparsec.Text as A+import qualified Data.Text as T+import qualified Data.Text.IO as T+import qualified Data.Text.Read as TR+import qualified Text.ParserCombinators.Parsec as P++attoparsec_text_builtin args = do+ forM_ args $ \arg -> do+ input <- T.readFile arg+ case A.parse (A.many1 (A.char '#' >> p)) input `A.feed` T.empty of+ A.Done _ xs -> print (sum xs :: Double)+ what -> print what+ where+ p = A.double++----------------------------------------------------------------------++attoparsec_text_laforge args = do+ forM_ args $ \arg -> do+ input <- T.readFile arg+ case A.parse (A.many1 (A.char '#' >> p)) input `A.feed` T.empty of+ A.Done _ xs -> print (sum xs :: Double)+ what -> print what+ where+ p = do+ i <- A.takeWhile isDigit+ f <- A.option T.empty (A.char '.' >> A.takeWhile1 isDigit)+ if (T.null i && T.null f)+ then mzero+ else return $ fromIntegral (dec i) ++ fromIntegral (dec f) / fromIntegral (10 ^ (T.length f))+ where+ dec = T.foldl' (\acc c -> acc * 10 + fromEnum c - 48) 0++attoparsec_text_laforge_discarding args = do+ forM_ args $ \arg -> do+ input <- T.readFile arg+ case A.parse (A.many1 (A.char '#' >> p)) input `A.feed` T.empty of+ A.Done _ xs -> print (length xs)+ what -> print what+ where+ p = do+ i <- A.takeWhile isDigit+ f <- A.option T.empty (A.char '.' >> A.takeWhile1 isDigit)+ return ()++----------------------------------------------------------------------++parsec_laforge args =+ forM_ args $ \arg -> do+ input <- readFile arg+ case P.parse (P.many1 (P.char '#' >> p)) "" input of+ Left err -> print err+ Right xs -> print (sum xs :: Double)+ where+ p = do+ i <- P.many P.digit+ f <- P.option "" (P.char '.' >> P.many1 P.digit)+ if (null i && null f)+ then mzero+ else return $ fromIntegral (dec i) ++ fromIntegral (dec f) / fromIntegral (10 ^ (length f))+ where+ dec = foldl' (\acc c -> acc * 10 + fromEnum c - 48) 0++parsec_laforge_discarding args =+ forM_ args $ \arg -> do+ input <- readFile arg+ case P.parse (P.many1 (P.char '#' >> p)) "" input of+ Left err -> print err+ Right xs -> print (length xs :: Int)+ where+ p = do+ i <- P.many P.digit+ f <- P.option "" (P.char '.' >> P.many1 P.digit)+ return ()++----------------------------------------------------------------------++main = do+ args <- getArgs+ case args of+ ("attoparsec_text_builtin":xs) -> attoparsec_text_builtin xs+ ("attoparsec_text_laforge":xs) -> attoparsec_text_laforge xs+ ("attoparsec_text_laforge_discarding":xs) -> attoparsec_text_laforge_discarding xs+ ("parsec_laforge":xs) -> parsec_laforge xs+ ("parsec_laforge_discarding":xs) -> parsec_laforge_discarding xs+ [] -> do+ putStrLn "Usage: ... [attoparsec_text_builtin|attoparsec_text_laforge|parsec_laforge] inputs"+ putStrLn "Usage: ... [attoparsec_text_laforge_discarding|parsec_laforge_discarding] inputs"
benchmarks/Makefile view
@@ -1,6 +1,9 @@-all: med.txt tiny+all: med.txt double_test.txt tiny double tiny: Tiny.hs+ ghc -O --make -o $@ $<++double: Double.hs ghc -O --make -o $@ $< %: %.bz2
+ benchmarks/double_test.txt.bz2 view
binary file changed (absent → 313 bytes)