on master help sign in
Implement concatSequence for parser

concatSequence collects sequential parses of parsers in a serial stream using a fold. * Wrapper and internal implementation * Benchmark and tests * Documentation update

commit9d68f58a212c701952cfc185bc794ab18b314dd6 authorIshan Bhanuka <bhanuka.ishan@gmail.com> dateThu 26 May 2022, 21:03 (4 years ago) parent4fa2d54
5 files changed+111−16
benchmark/Streamly/Benchmark/Data/Parser.hs+5 benchmark/Streamly/Benchmark/Data/Parser/ParserD.hs+6 core/src/Streamly/Internal/Data/Parser.hs+13−8 core/src/Streamly/Internal/Data/Parser/ParserD.hs+72−7 test/Streamly/Test/Data/Parser/ParserD.hs+15−1
benchmark/Streamly/Benchmark/Data/Parser.hs+5
@@ -413,6 +413,10 @@ parseBreak s = do
413413 Left (_ :: SomeException) -> return ()
414414 Right (_, s1) -> parseBreak s1
415415
416+{-# INLINE concatSequence #-}
417+concatSequence :: MonadCatch m => SerialT m Int -> m ()
418+concatSequence = IP.parse $ PR.concatSequence FL.drain $ S.repeat PR.one
419+
416420-------------------------------------------------------------------------------
417421-- Benchmarks
418422-------------------------------------------------------------------------------
@@ -455,6 +459,7 @@ o_1_space_serial value =
455459 , benchIOSink value "parseMany (take all)" (parseMany value)
456460 , benchIOSink value "parseIterate (take 1)" (parseIterate 1)
457461 , benchIOSink value "parseIterate (take all)" (parseIterate value)
462+ , benchIOSink value "concatSequence" concatSequence
458463 ]
459464
460465o_1_space_filesystem :: BenchEnv -> [Benchmark]
benchmark/Streamly/Benchmark/Data/Parser/ParserD.hs+6
@@ -32,6 +32,7 @@ import qualified Streamly.Internal.Data.Parser.ParserD as PR
3232import qualified Streamly.Internal.Data.Producer as Producer
3333import qualified Streamly.Internal.Data.Producer.Source as Source
3434import qualified Streamly.Internal.Data.Stream.IsStream as IP
35+import qualified Streamly.Internal.Data.Stream.StreamD as D
3536
3637import Gauge
3738import Streamly.Prelude (SerialT, MonadAsync, IsStream)
@@ -196,6 +197,10 @@ longestAllAny value =
196197 (drainWhile (<= value))
197198 )
198199
200+{-# INLINE sequenceParser #-}
201+sequenceParser :: MonadCatch m => SerialT m Int -> m ()
202+sequenceParser = IP.parseD (PR.sequence FL.drain (D.repeat (PR.satisfy $ const True)))
203+
199204-------------------------------------------------------------------------------
200205-- Spanning
201206-------------------------------------------------------------------------------
@@ -337,6 +342,7 @@ o_1_space_serial value =
337342 , benchIOSink value "teeFst (all,any)" $ teeFstAllAny value
338343 , benchIOSink value "shortest (all,any)" $ shortestAllAny value
339344 , benchIOSink value "longest (all,any)" $ longestAllAny value
345+ , benchIOSink value "sequenceParser" sequenceParser
340346 ]
341347
342348o_1_space_serial_spanning :: Int -> [Benchmark]
core/src/Streamly/Internal/Data/Parser.hs+13−8
@@ -245,10 +245,12 @@ import Prelude hiding
245245
246246import Streamly.Internal.Data.Fold.Type (Fold(..))
247247import Streamly.Internal.Data.Parser.ParserK.Type (Parser)
248+import Streamly.Internal.Data.Stream.Serial (SerialT(getSerialT))
248249
249250import qualified Streamly.Internal.Data.Fold.Type as FL
250251import qualified Streamly.Internal.Data.Parser.ParserD as D
251252import qualified Streamly.Internal.Data.Parser.ParserK.Type as K
253+import qualified Streamly.Internal.Data.Stream.StreamD.Type as SD
252254
253255--
254256-- $setup
@@ -1193,19 +1195,22 @@ takeP i p = D.toParserK $ D.takeP i $ D.fromParserK p
11931195-- Sequential Collection
11941196-------------------------------------------------------------------------------
11951197--
1196−-- | @concatSequence f t@ collects sequential parses of parsers in the
1197−-- container @t@ using the fold @f@. Fails if the input ends or any of the
1198−-- parsers fail.
1198+-- | @concatSequence f p@ collects sequential parses of parsers in a
1199+-- serial stream @p@ using the fold @f@. Fails if the input ends or any
1200+-- of the parsers fail.
11991201--
1200−-- This is same as 'Data.Traversable.sequence' but more efficient.
1202+-- An even more efficient implementation can use ParserD type Parser in
1203+-- the SerialT stream.
12011204--
1202−-- /Unimplemented/
1205+-- /Pre-release/
12031206--
12041207{-# INLINE concatSequence #-}
12051208concatSequence ::
1206− -- Foldable t =>
1207− Fold m b c -> t (Parser m a b) -> Parser m a c
1208−concatSequence _f _p = undefined
1209+ MonadCatch m =>
1210+ Fold m b c -> SerialT m (Parser m a b) -> Parser m a c
1211+concatSequence f p =
1212+ let sp = fmap D.fromParserK $ SD.fromStreamK $ getSerialT p
1213+ in D.toParserK $ D.sequence f sp
12091214
12101215-- | Map a 'Parser' returning function on the result of a 'Parser'.
12111216--
core/src/Streamly/Internal/Data/Parser/ParserD.hs+72−7
@@ -1901,14 +1901,79 @@ sepBy
19011901-------------------------------------------------------------------------------
19021902--
19031903-- | See 'Streamly.Internal.Data.Parser.sequence'.
1904−--
1905−-- /Unimplemented/
1906−--
19071904{-# INLINE sequence #-}
1908−sequence ::
1909− -- Foldable t =>
1910− Fold m b c -> t (Parser m a b) -> Parser m a c
1911−sequence _f _p = undefined
1905+sequence :: MonadThrow m =>
1906+ Fold m b c -> D.Stream m (Parser m a b) -> Parser m a c
1907+sequence (Fold fstep finitial fextract) (D.Stream sstep sstate) =
1908+ Parser step initial extract
1909+
1910+ where
1911+
1912+ initial = do
1913+ fres <- finitial
1914+ case fres of
1915+ FL.Partial fs -> return $ IPartial (Nothing', sstate, fs)
1916+ FL.Done c -> return $ IDone c
1917+
1918+ -- state does not contain any parser
1919+ -- yield a new parser from the stream
1920+ step (Nothing', ss, fs) _ = do
1921+ sres <- sstep defState ss
1922+ case sres of
1923+ D.Yield p ss1 -> return $ Continue 1 (Just' p, ss1, fs)
1924+ D.Stop -> do
1925+ c <- fextract fs
1926+ return $ Done 1 c
1927+ D.Skip ss1 -> return $ Continue 1 (Nothing', ss1, fs)
1928+
1929+ -- state holds a parser that may or may not have been
1930+ -- initialized. pinit holds the initial parser state
1931+ -- or modified parser state respectively
1932+ step (Just' (Parser pstep pinit pextr), ss, fs) a = do
1933+ ps <- pinit
1934+ case ps of
1935+ IPartial ps1 -> do
1936+ pres <- pstep ps1 a
1937+ case pres of
1938+ Partial n ps2 ->
1939+ let newP =
1940+ Just' $ Parser pstep (return $ IPartial ps2) pextr
1941+ in return $ Partial n (newP, ss, fs)
1942+ Continue n ps2 ->
1943+ let newP =
1944+ Just' $ Parser pstep (return $ IPartial ps2) pextr
1945+ in return $ Continue n (newP, ss, fs)
1946+ Done n b -> do
1947+ fres <- fstep fs b
1948+ case fres of
1949+ FL.Partial fs1 ->
1950+ return $ Partial n (Nothing', ss, fs1)
1951+ FL.Done c -> return $ Done n c
1952+ Error msg -> return $ Error msg
1953+ IDone b -> do
1954+ fres <- fstep fs b
1955+ case fres of
1956+ FL.Partial fs1 ->
1957+ return $ Partial 1 (Nothing', ss, fs1)
1958+ FL.Done c -> return $ Done 1 c
1959+ IError err -> return $ Error err
1960+
1961+ extract (Nothing', _, fs) = fextract fs
1962+ extract (Just' (Parser _ pinit pextr), _, fs) = do
1963+ ps <- pinit
1964+ case ps of
1965+ IPartial ps1 -> do
1966+ b <- pextr ps1
1967+ fres <- fstep fs b
1968+ case fres of
1969+ FL.Partial fs1 -> fextract fs1
1970+ FL.Done c -> return c
1971+ IDone b -> do
1972+ fres <- fstep fs b
1973+ case fres of
1974+ FL.Partial fs1 -> fextract fs1
1975+ FL.Done c -> return c
1976+ IError err -> throwM $ ParseError err
19121977
19131978-------------------------------------------------------------------------------
19141979-- Alternative Collection
test/Streamly/Test/Data/Parser/ParserD.hs+15−1
@@ -2,7 +2,7 @@ module Main (main) where
22
33import Control.Exception (SomeException(..))
44import Data.Word (Word8, Word32, Word64)
5−import Streamly.Test.Common (listEquals, checkListEqual, chooseInt)
5+import Streamly.Test.Common (listEquals, checkListEqual, chooseInt, equals)
66import Test.Hspec (Spec, hspec, describe)
77import Test.Hspec.QuickCheck
88import Test.QuickCheck
@@ -18,6 +18,7 @@ import qualified Streamly.Internal.Data.Parser.ParserD as P
1818import qualified Streamly.Internal.Data.Producer.Source as Source
1919import qualified Streamly.Internal.Data.Producer as Producer
2020import qualified Streamly.Internal.Data.Stream.IsStream as S
21+import qualified Streamly.Internal.Data.Stream.StreamD as D
2122import qualified Streamly.Internal.Data.Unfold as Unfold
2223import qualified Test.Hspec as H
2324
@@ -635,6 +636,18 @@ parseUnfold = do
635636
636637 listEquals (==) xs ls
637638
639+parserSequence :: Property
640+parserSequence =
641+ forAll (vectorOf 11 (listOf (chooseAny :: Gen Int))) $ \ins ->
642+ monadicIO $ do
643+ let parsers = D.fromList
644+ $ fmap (\xs -> P.fromFold $ FL.take (length xs) FL.sum) ins
645+ let sequencedParser = P.sequence FL.sum parsers
646+ outs <-
647+ run $
648+ S.parseD sequencedParser $ S.concatMap S.fromList (S.fromList ins)
649+ equals (==) outs (sum $ map sum ins)
650+
638651-------------------------------------------------------------------------------
639652-- Test for a particular case hit during fs events testing
640653-------------------------------------------------------------------------------
@@ -720,6 +733,7 @@ main =
720733 prop "parseMany" parseMany
721734 prop "parseMany2Events" parseMany2Events
722735 prop "parseUnfold" parseUnfold
736+ prop "parserSequence" parserSequence
723737
724738 describe "test for accumulator" $ do
725739 prop "P.fromFold FL.sum = FL.sum" fromFold