packages feed

fastparser 0.3.0 → 0.3.0.1

raw patch · 5 files changed

+107/−36 lines, 5 filesdep +criteriondep +fastparserdep +kan-extensionsdep ~basedep ~bytestringdep ~vector-spacePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: criterion, fastparser, kan-extensions, transformers

Dependency ranges changed: base, bytestring, vector-space

API changes (from Hackage documentation)

- ByteString.Parser.Fast: instance GHC.Base.Alternative ByteString.Parser.Fast.Parser
- ByteString.Parser.Fast: instance GHC.Base.Applicative ByteString.Parser.Fast.Parser
- ByteString.Parser.Fast: instance GHC.Base.Functor ByteString.Parser.Fast.Parser
- ByteString.Parser.Fast: instance GHC.Base.Monad ByteString.Parser.Fast.Parser
- ByteString.Parser.Fast: instance GHC.Base.MonadPlus ByteString.Parser.Fast.Parser
- ByteString.Parser.Fast: newtype Parser a
+ ByteString.Parser.Fast: instance GHC.Base.Alternative ByteString.Parser.Fast.ParserM
+ ByteString.Parser.Fast: instance GHC.Base.Applicative ByteString.Parser.Fast.ParserM
+ ByteString.Parser.Fast: instance GHC.Base.Functor ByteString.Parser.Fast.ParserM
+ ByteString.Parser.Fast: instance GHC.Base.Monad ByteString.Parser.Fast.ParserM
+ ByteString.Parser.Fast: instance GHC.Base.MonadPlus ByteString.Parser.Fast.ParserM
+ ByteString.Parser.Fast: newtype ParserM a
+ ByteString.Parser.Fast: type Parser = Codensity ParserM
- ByteString.Parser.Fast: Parser :: (forall r. ByteString -> (ParseError -> r) -> (ByteString -> a -> r) -> r) -> Parser a
+ ByteString.Parser.Fast: Parser :: (forall r. ByteString -> (ParseError -> r) -> (ByteString -> a -> r) -> r) -> ParserM a
- ByteString.Parser.Fast: [runParser] :: Parser a -> forall r. ByteString -> (ParseError -> r) -> (ByteString -> a -> r) -> r
+ ByteString.Parser.Fast: [runParser] :: ParserM a -> forall r. ByteString -> (ParseError -> r) -> (ByteString -> a -> r) -> r

Files

+ CHANGELOG.md view
@@ -0,0 +1,8 @@+v0.3.0.1++ * Parser is now wrapped in Codensity, as the performance impact is minimal+ * Prototype benchmark suite++v0.2.0++ * Dropped the lens dependency, for microlens
+ README.md view
@@ -0,0 +1,24 @@+[![Build Status](https://travis-ci.org/bartavelle/fastparser.svg?branch=master)](https://travis-ci.org/bartavelle/fastparser)+++A very simple, backtracking, fast parser combinator library.++It is measurably faster than [attoparsec](https://hackage.haskell.org/package/attoparsec) (36% in [this use case](https://hbtvl.banquise.net/posts/2015-12-14-fastParsing03.html)), but only works on strict `ByteString`, lacks many helper functions, and is not resumable.+It also should consume a tiny bit less memory for equivalent operations.++# When NOT to use fastparser++ * When performance is not the **most** pressing concern.+ * When you need to parse anything else but strict `ByteString`.+ * When you need to use a battle-tested library. While very simple, and in constant use by me, this package is still quite experimental.+ * When you need to parse large inputs that are not easily cut into many smaller pieces that can be parsed independently.++# How to use fastparser++`fastparser` works well with small pieces, such as individual log file lines. It is recommended to use it with a coroutine library (such as [conduit](http://hackage.haskell.org/package/conduit) or [pipe](http://hackage.haskell.org/package/pipes)), so that the input could be incrementaly consumed, cut into individual records, all of which would end up parsed independently.++One such setup, with the `conduit` ecosystem, would look like:++    sourceFile "/tmp/foo" .| Data.Conduit.Binary.lines .| CL.map (parseOnly parser) .| ...++Other than that, `fastparser` is fairly close to any other parser combinators library.
+ bench/timestamps.hs view
@@ -0,0 +1,13 @@+{-# LANGUAGE OverloadedStrings #-}+module Main where++import ByteString.Parser.Fast+import Criterion+import Criterion.Main+import qualified Data.ByteString as BS++timestampBench :: BS.ByteString -> Benchmark+timestampBench b = bench (show b) $ nf ((\(Right a) -> a) . parseOnly timestamp) b++main :: IO ()+main = defaultMain [timestampBench "2016-12-27+09:58:09.9634133070+CET", timestampBench "2014-07-23+00:16:31.0000000000+CEST"]
fastparser.cabal view
@@ -1,5 +1,5 @@ name:                fastparser-version:             0.3.0+version:             0.3.0.1 synopsis:            A fast, but bare bones, bytestring parser combinators library. description:         Please see README.md homepage:            https://github.com/bartavelle/fastparser#readme@@ -10,22 +10,36 @@ copyright:           Simon Marechal category:            Parsing build-type:          Simple--- extra-source-files:+extra-source-files:  CHANGELOG.md, README.md cabal-version:       >=1.10+Tested-With:         GHC == 7.10.2, GHC == 8.0.2, GHC == 8.2.1  library   hs-source-dirs:      src   exposed-modules:     ByteString.Parser.Fast-  build-depends:       base >= 4.7 && < 5-                     , bytestring == 0.10.*-                     , thyme == 0.3.*-                     , vector-space == 0.10.*-                     , microlens == 0.4.*+  build-depends:       base              >= 4.7 && < 5+                     , bytestring+                     , thyme             == 0.3.*+                     , vector-space      >= 0.10 && < 1+                     , microlens         == 0.4.*                      , bytestring-lexing == 0.5.*-                     , containers == 0.5.*+                     , containers        == 0.5.*+                     , kan-extensions    == 5.*+                     , transformers      >= 0.4   ghc-options:         -O2 -Wall   default-language:    Haskell2010  source-repository head   type:     git   location: https://github.com/bartavelle/fastparser++benchmark timestamps+  type:                exitcode-stdio-1.0+  main-is:             timestamps.hs+  hs-source-dirs:      bench+  build-depends:       base >= 4.7 && < 5+                     , criterion+                     , fastparser+                     , bytestring+  ghc-options:         -O2 -Wall+  default-language:    Haskell2010
src/ByteString/Parser/Fast.hs view
@@ -48,7 +48,7 @@ {-# LANGUAGE OverloadedStrings #-} module ByteString.Parser.Fast   (-  Parser(..), parseOnly,+  Parser, ParserM(..), parseOnly,   -- * Error handling   ParseError(..), ErrorItem(..), ueof, ufail, parseError,   -- * Parsing numerical values@@ -76,8 +76,11 @@ import Data.Thyme import Data.Thyme.Time.Core import Lens.Micro-+import Data.Monoid import qualified Data.ByteString.Lex.Fractional as L+import Control.Monad.Codensity (Codensity, lowerCodensity)+import Control.Monad.Trans.Class (lift)+import Prelude  -- | A parser, church encoded. The arguments to the wrapped function are: --@@ -85,7 +88,7 @@ --  * A function that handles parse errors. --  * A function that handles success, taking as argument the remaining --  input and the parser result.-newtype Parser a+newtype ParserM a   = Parser {            runParser :: forall r. BS.ByteString                      -> (ParseError -> r)@@ -93,7 +96,9 @@                      -> r   } deriving Functor -instance Applicative Parser where+type Parser = Codensity ParserM++instance Applicative ParserM where     pure a = Parser $ \b _ s -> s b a     {-# INLINE pure #-}     Parser pf <*> Parser px = Parser $ \input failure success ->@@ -101,20 +106,20 @@         in  pf input failure succ'     {-# INLINE (<*>) #-} -instance Alternative Parser where+instance Alternative ParserM where     empty = Parser (\_ failure _ -> failure mempty)     {-# INLINE empty #-}     Parser a <|> Parser b = Parser $ \input failure success -> a input (\rr ->  b input (failure . mappend rr) success) success     {-# INLINE (<|>) #-} -instance Monad Parser where+instance Monad ParserM where     fail s = Parser $ \_ failure _ -> failure (ufail s)     m >>= k = Parser $ \input failure success ->         let succ' input' a = runParser (k a) input' failure success         in  runParser m input failure succ'     {-# INLINE (>>=) #-} -instance MonadPlus Parser+instance MonadPlus ParserM  data ErrorItem     = Tokens BS.ByteString@@ -154,7 +159,7 @@ -- It works well with the -- [bytestring-lexing](https://hackage.haskell.org/package/bytestring-lexing) library. wlex :: (BS.ByteString -> Maybe (a, BS.ByteString)) -> Parser a-wlex p = Parser $ \i failure success -> case p i of+wlex p = lift $ Parser $ \i failure success -> case p i of                                             Nothing -> failure mempty                                             Just (a, i') -> success i' a {-# INLINABLE wlex #-}@@ -223,25 +228,27 @@  -- | Consumes n bytes of input takeN :: Int -> Parser BS.ByteString-takeN n = Parser $ \input failure success -> if BS.length input < n-                                                 then failure ueof-                                                 else let (a,rest) = BS.splitAt n input-                                                      in  success rest a+takeN n = lift $ Parser $ \input failure success+    -> if BS.length input < n+         then failure ueof+         else let (a,rest) = BS.splitAt n input+              in  success rest a  -- | Parses any character. anyChar :: Parser Char-anyChar = Parser $ \input failure success -> if BS.null input then failure ueof else success (BS8.tail input) (BS8.head input)+anyChar = lift $ Parser $ \input failure success -> if BS.null input then failure ueof else success (BS8.tail input) (BS8.head input)  -- | Parses a specific character. char :: Char -> Parser ()-char c = Parser $ \input failure success -> if BS.null input then failure ueof else if BS8.head input == c then success (BS.tail input) () else failure (parseError (BS8.take 1 input) (BS8.singleton c))+char c = lift $ Parser $ \input failure success -> if BS.null input then failure ueof else if BS8.head input == c then success (BS.tail input) () else failure (parseError (BS8.take 1 input) (BS8.singleton c)) {-# INLINE char #-}  -- | Parses the supplied string. string :: BS.ByteString -> Parser ()-string s = Parser $ \input failure success -> if s `BS.isPrefixOf` input-                                                  then success (BS.drop (BS.length s) input) ()-                                                  else failure (parseError (BS.take (BS.length s) input) s)+string s = lift $ Parser $ \input failure success+    -> if s `BS.isPrefixOf` input+          then success (BS.drop (BS.length s) input) ()+          else failure (parseError (BS.take (BS.length s) input) s)  -- | Parses strings between double quotes. This functions handles the -- following escape sequences: \\r, \\n, \\t, \\a, \\b, \\", \\\\.@@ -277,42 +284,47 @@ {-# INLINE scientific #-}  charTakeWhile1 :: (Char -> Bool) -> Parser BS.ByteString-charTakeWhile1 prd = Parser $ \s failure success -> case BS8.span prd s of-                                                    (a,b) -> if BS.null a then failure (ParseError (S.singleton (Tokens (BS.take 1 s))) mempty) else success b a+charTakeWhile1 prd = lift $ Parser $ \s failure success ->+    case BS8.span prd s of+      (a,b) -> if BS.null a then failure (ParseError (S.singleton (Tokens (BS.take 1 s))) mempty) else success b a {-# INLINE charTakeWhile1 #-}  takeWhile1 :: (Word8 -> Bool) -> Parser BS.ByteString-takeWhile1 prd = Parser $ \s failure success -> case BS.span prd s of-                                                    (a,b) -> if BS.null a then failure (ParseError (S.singleton (Tokens (BS.take 1 s))) mempty) else success b a+takeWhile1 prd = lift $ Parser $ \s failure success ->+    case BS.span prd s of+      (a,b) -> if BS.null a then failure (ParseError (S.singleton (Tokens (BS.take 1 s))) mempty) else success b a {-# INLINE takeWhile1 #-}  -- | Consumes the input as long as the predicate remains true. charTakeWhile :: (Char -> Bool) -> Parser BS.ByteString-charTakeWhile prd = Parser $ \s _ success -> case BS8.span prd s of-                                             (a,b) -> success b a+charTakeWhile prd = lift $ Parser $ \s _ success ->+    case BS8.span prd s of+      (a,b) -> success b a {-# INLINE charTakeWhile #-}  -- | Consumes the input as long as the predicate remains true. takeWhile :: (Word8 -> Bool) -> Parser BS.ByteString-takeWhile prd = Parser $ \s _ success -> case BS.span prd s of-                                             (a,b) -> success b a+takeWhile prd = lift $ Parser $ \s _ success ->+  case BS.span prd s of+    (a,b) -> success b a {-# INLINE takeWhile #-}  -- | Discards the input as long as the predicate remains true. skipWhile :: (Word8 -> Bool) -> Parser ()-skipWhile prd = Parser $ \s _ success -> success (BS.dropWhile prd s) ()+skipWhile prd = lift $ Parser $ \s _ success -> success (BS.dropWhile prd s) () {-# INLINE skipWhile #-}  -- | Runs the parser. Will return a parse error if the parser fails -- or if the input is not completely consumed. parseOnly :: Parser a -> BS.ByteString -> Either ParseError a-parseOnly (Parser p) s = p s Left $ \b a -> if BS.null b+parseOnly prs s = case lowerCodensity prs of+                  Parser p -> p s Left $ \b a -> if BS.null b                                               then Right a                                               else Left (ParseError (S.singleton (Tokens (BS.take 1 b))) mempty)  -- | Parses the remaining input. remaining :: Parser BS.ByteString-remaining = Parser $ \input _ success -> success BS.empty input+remaining = lift $ Parser $ \input _ success -> success BS.empty input  -- | Parses days, with format YYYY-MM-DD parseYMD :: Parser Day