{- | Here is a simple use of 'parsed' and standard @Streaming@ segmentation devices to parse a file in which groups of numbers are separated by blank lines. Such a problem of \'nesting streams\' is described in the @conduit@ context in <http://stackoverflow.com/questions/32957258/how-to-model-nested-streams-with-conduits/32961296 this StackOverflow question> > -- $ cat nums.txt > -- 1 > -- 2 > -- 3 > -- > -- 4 > -- 5 > -- 6 > -- > -- 7 > -- 8 We will sum the groups and stream the results to standard output: > import Streaming > import qualified Streaming.Prelude as S > import qualified Data.ByteString.Streaming.Char8 as Q > import qualified Data.Attoparsec.ByteString.Char8 as A > import qualified Data.Attoparsec.ByteString.Streaming as A > import Data.Function ((&)) > > main = Q.getContents -- raw bytes > & A.parsed lineParser -- stream of parsed `Maybe Int`s; blank lines are `Nothing` > & void -- drop any unparsed nonsense at the end > & S.split Nothing -- split on blank lines > & S.maps S.concat -- keep `Just x` values in the sub-streams (cp. catMaybes) > & S.mapped S.sum -- sum each substream > & S.print -- stream results to stdout > > lineParser = Just <$> A.scientific <* A.endOfLine <|> Nothing <$ A.endOfLine > -- $ cat nums.txt | ./atto > -- 6.0 > -- 15.0 > -- 15.0 -} module Data.Attoparsec.ByteString.Streaming (Message , parse , parsed -- , module Data.Attoparsec.ByteString ) where import qualified Data.ByteString as B import qualified Data.Attoparsec.ByteString as A import qualified Data.Attoparsec.Internal.Types as T import Data.Attoparsec.ByteString hiding (IResult(..), Result, eitherResult, maybeResult, parse, parseWith, parseTest) import Streaming hiding (concats, unfold) import Streaming.Internal (Stream (..)) import Data.ByteString.Streaming import Data.ByteString.Streaming.Internal import Data.Monoid type Message = ([String], String) {- | The result of a parse (@Either a ([String], String)@), with the unconsumed byte stream. >>> :set -XOverloadedStrings -- the string literal below is a streaming bytestring >>> (r,rest1) <- AS.parse (A.scientific <* A.many' A.space) "12.3 4.56 78.3" >>> print r Left 12.3 >>> (s,rest2) <- AS.parse (A.scientific <* A.many' A.space) rest1 >>> print s Left 4.56 >>> (t,rest3) <- AS.parse (A.scientific <* A.many' A.space) rest2 >>> print t Left 78.3 >>> Q.putStrLn rest3 -} parse :: Monad m => A.Parser a -> ByteString m x -> m (Either a Message, ByteString m x) parse parser bs = do (e,rest) <- apply parser bs return (either Right Left e, rest) {-#INLINE parse #-} apply :: Monad m => A.Parser a -> ByteString m x -> m (Either Message a, ByteString m x) apply parser = begin where begin p0 = case p0 of Go m -> m >>= begin Empty r -> step id (A.parse parser mempty) (return r) Chunk bs p1 -> if B.null bs -- attoparsec understands "" then begin p1 -- as eof. else step (chunk bs >>) (A.parse parser bs) p1 step diff res p0 = case res of T.Fail _ c m -> return (Left (c,m), diff p0) T.Done a b -> return (Right b, chunk a >> p0) T.Partial k -> do let clean p = case p of -- inspect for null chunks before Go m -> m >>= clean -- feeding attoparsec Empty r -> step diff (k mempty) (return r) Chunk bs p1 | B.null bs -> clean p1 | otherwise -> step (diff . (chunk bs >>)) (k bs) p1 clean p0 {-#INLINABLE apply #-} {-| Apply a parser repeatedly to a stream of bytes, streaming the parsed values, but ending when the parser fails.or the bytes run out. >>> S.print $ AS.parsed (A.scientific <* A.many' A.space) $ "12.3 4.56 78.9" 12.3 4.56 78.9 18.282 -} parsed :: Monad m => A.Parser a -- ^ Attoparsec parser -> ByteString m r -- ^ Raw input -> Stream (Of a) m (Either (Message, ByteString m r) r) parsed parser = begin where begin p0 = case p0 of -- inspect for null chunks before Go m -> lift m >>= begin -- feeding attoparsec Empty r -> Return (Right r) Chunk bs p1 | B.null bs -> begin p1 | otherwise -> step (chunk bs >>) (A.parse parser bs) p1 step diffP res p0 = case res of A.Fail _ c m -> Return (Left ((c,m), diffP p0)) A.Done bs a | B.null bs -> Step (a :> begin p0) | otherwise -> Step (a :> begin (chunk bs >> p0)) A.Partial k -> do x <- lift (nextChunk p0) case x of Left e -> step diffP (k mempty) (return e) Right (bs,p1) | B.null bs -> step diffP res p1 | otherwise -> step (diffP . (chunk bs >>)) (k bs) p1 {-# INLINABLE parsed #-}