on master help sign in
Implement Data.Parser.ParserD.wordBy

- Modify Data.Parser.wordBy accordingly - Add tests for both - Add benchmarks for both

commit4d3f9b41edac9e95861dc7a4ae7ad69f74cf1c68 authorAdithya Kumar <adithya@composewell.com> dateThu 10 Dec 2020, 19:01 (6 years ago) parent9fefed3
6 files changed+97−13
benchmark/Streamly/Benchmark/Data/Parser.hs+10 benchmark/Streamly/Benchmark/Data/Parser/ParserD.hs+11 src/Streamly/Internal/Data/Parser.hs+3−7 src/Streamly/Internal/Data/Parser/ParserD.hs+37−6 test/Streamly/Test/Data/Parser.hs+18 test/Streamly/Test/Data/Parser/ParserD.hs+18
benchmark/Streamly/Benchmark/Data/Parser.hs+10
@@ -76,6 +76,14 @@ takeWhile value = IP.parse (PR.takeWhile (<= value) FL.drain)
7676groupBy :: MonadCatch m => SerialT m Int -> m ()
7777groupBy = IP.parse (PR.groupBy (<=) FL.drain)
7878
79+{-# INLINE wordBy #-}
80+wordBy :: MonadCatch m => Int -> SerialT m Int -> m ()
81+wordBy value = IP.parse (PR.wordBy (>= value) FL.drain)
82+
83+{-# INLINE manyWordByEven #-}
84+manyWordByEven :: MonadCatch m => SerialT m Int -> m ()
85+manyWordByEven = IP.parse (PR.many FL.drain (PR.wordBy (Prelude.even) FL.drain))
86+
7987{-# INLINE many #-}
8088many :: MonadCatch m => SerialT m Int -> m Int
8189many = IP.parse (PR.many FL.length (PR.satisfy (> 0)))
@@ -263,11 +271,13 @@ o_1_space_serial value =
263271 , benchIOSink value "takeWhile" $ takeWhile value
264272 , benchIOSink value "drainWhile" $ drainWhile value
265273 , benchIOSink value "groupBy" $ groupBy
274+ , benchIOSink value "wordBy" $ wordBy value
266275 , benchIOSink value "splitAp" $ splitAp value
267276 , benchIOSink value "splitApBefore" $ splitApBefore value
268277 , benchIOSink value "splitApAfter" $ splitApAfter value
269278 , benchIOSink value "splitWith" $ splitWith value
270279 , benchIOSink value "many" many
280+ , benchIOSink value "many (wordBy even)" $ manyWordByEven
271281 , benchIOSink value "some" some
272282 , benchIOSink value "manyTill" $ manyTill value
273283 , benchIOSink value "tee" $ teeAllAny value
benchmark/Streamly/Benchmark/Data/Parser/ParserD.hs+11
@@ -71,6 +71,15 @@ takeWhile value = IP.parseD (drainWhile (<= value))
7171groupBy :: MonadThrow m => SerialT m Int -> m ()
7272groupBy = IP.parseD (PR.groupBy (<=) FL.drain)
7373
74+{-# INLINE wordBy #-}
75+wordBy :: MonadThrow m => Int -> SerialT m Int -> m ()
76+wordBy value = IP.parseD (PR.wordBy (>= value) FL.drain)
77+
78+{-# INLINE manyWordByEven #-}
79+manyWordByEven :: MonadCatch m => SerialT m Int -> m ()
80+manyWordByEven =
81+ IP.parseD (PR.many FL.drain (PR.wordBy (Prelude.even) FL.drain))
82+
7483{-# INLINE many #-}
7584many :: MonadCatch m => SerialT m Int -> m Int
7685many = IP.parseD (PR.many FL.length (PR.satisfy (> 0)))
@@ -200,8 +209,10 @@ o_1_space_serial :: Int -> [Benchmark]
200209o_1_space_serial value =
201210 [ benchIOSink value "takeWhile" $ takeWhile value
202211 , benchIOSink value "groupBy" $ groupBy
212+ , benchIOSink value "wordBy" $ wordBy value
203213 , benchIOSink value "split (all,any)" $ splitAllAny value
204214 , benchIOSink value "many" many
215+ , benchIOSink value "many (wordBy even)" $ manyWordByEven
205216 , benchIOSink value "some" some
206217 , benchIOSink value "manyTill" $ manyTill value
207218 , benchIOSink value "tee (all,any)" $ teeAllAny value
src/Streamly/Internal/Data/Parser.hs+3−7
@@ -548,13 +548,9 @@ escapedFrameBy _begin _end _escape _p = undefined
548548-- S.wordsBy pred f = S.parseMany (PR.wordBy pred f)
549549-- @
550550--
551−-- /Unimplemented/
552−--
553−{-# INLINABLE wordBy #-}
554−wordBy ::
555− -- Monad m =>
556− (a -> Bool) -> Fold m a b -> Parser m a b
557−wordBy = undefined
551+{-# INLINE wordBy #-}
552+wordBy :: MonadCatch m => (a -> Bool) -> Fold m a b -> Parser m a b
553+wordBy f = K.toParserK . D.wordBy f
558554
559555-- | @groupBy cmp f $ S.fromList [a,b,c,...]@ assigns the element @a@ to the
560556-- first group, then if @a \`cmp` b@ is 'True' @b@ is also assigned to the same
src/Streamly/Internal/Data/Parser/ParserD.hs+37−6
@@ -447,15 +447,46 @@ sliceBeginWith ::
447447 (a -> Bool) -> Fold m a b -> Parser m a b
448448sliceBeginWith = undefined
449449
450+data WordByState s b = WBLeft !s | WBWord !s | WBRight !b
451+
450452-- | See 'Streamly.Internal.Data.Parser.wordBy'.
451453--
452−-- /Unimplemented/
453454--
454−{-# INLINABLE wordBy #-}
455−wordBy ::
456− -- Monad m =>
457− (a -> Bool) -> Fold m a b -> Parser m a b
458−wordBy = undefined
455+{-# INLINE wordBy #-}
456+wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser m a b
457+wordBy predicate (Fold fstep finitial fextract) = Parser step initial extract
458+
459+ where
460+
461+ {-# INLINE worder #-}
462+ worder s a = do
463+ res <- fstep s a
464+ return
465+ $ case res of
466+ FL.Partial s1 -> Partial 0 $ WBWord s1
467+ FL.Done b -> Done 0 b
468+
469+ initial = WBLeft <$> finitial
470+
471+ step (WBLeft s) a =
472+ if not (predicate a)
473+ then worder s a
474+ else return $ Partial 0 $ WBLeft s
475+ step (WBWord s) a =
476+ if not (predicate a)
477+ then worder s a
478+ else do
479+ b <- fextract s
480+ return $ Partial 0 $ WBRight b
481+ step (WBRight b) a =
482+ return
483+ $ if not (predicate a)
484+ then Done 1 b
485+ else Partial 0 $ WBRight b
486+
487+ extract (WBLeft s) = fextract s
488+ extract (WBWord s) = fextract s
489+ extract (WBRight b) = return b
459490
460491data GroupByState a s
461492 = GroupByInit !s
test/Streamly/Test/Data/Parser.hs+18
@@ -317,6 +317,23 @@ groupBy =
317317 | null lst = []
318318 | otherwise = head $ List.groupBy cmp lst
319319
320+
321+wordBy :: Property
322+wordBy =
323+ forAll (listOf (elements [' ', 's']))
324+ $ \ls ->
325+ case S.parse parser (S.fromList ls) of
326+ Right parsed -> checkListEqual parsed (words' ls)
327+ Left _ -> property False
328+
329+ where
330+
331+ predicate = (== ' ')
332+ parser = P.many FL.toList (P.wordBy predicate FL.toList)
333+ words' lst =
334+ let wrds = words lst
335+ in if wrds == [] then [""] else wrds
336+
320337-- splitWithPass :: Property
321338-- splitWithPass =
322339-- forAll (listOf (chooseInt (0, 1))) $ \ls ->
@@ -627,6 +644,7 @@ main =
627644 prop "P.takeWhile = Prelude.takeWhile" Main.takeWhile
628645 prop "P.takeWhile1 = Prelude.takeWhile if taken something, else check why failed" takeWhile1
629646 prop "P.groupBy = Prelude.head . Prelude.groupBy" groupBy
647+ prop "many (P.wordBy ' ') = words'" wordBy
630648 -- prop "" splitWithPass
631649 -- prop "" splitWithFailLeft
632650 -- prop "" splitWithFailRight
test/Streamly/Test/Data/Parser/ParserD.hs+18
@@ -323,6 +323,23 @@ sliceSepByMax =
323323 where
324324 predicate = (== 1)
325325
326+wordBy :: Property
327+wordBy =
328+ forAll (listOf (elements [' ', 's']))
329+ $ \ls ->
330+ case S.parseD parser (S.fromList ls) of
331+ Right parsed -> checkListEqual parsed (words' ls)
332+ Left _ -> property False
333+
334+ where
335+
336+ predicate = (== ' ')
337+ parser = P.many FL.toList (P.wordBy predicate FL.toList)
338+ words' lst =
339+ let wrds = words lst
340+ in if wrds == [] then [""] else wrds
341+
342+
326343splitWith :: Property
327344splitWith =
328345 forAll (listOf (chooseInt (0, 1))) $ \ls ->
@@ -657,6 +674,7 @@ main =
657674 prop "P.groupBy = Prelude.head . Prelude.groupBy" groupBy
658675 prop "P.sliceSepBy = Prelude.takeWhile (not . predicate)" sliceSepBy
659676 prop "P.sliceSepByMax = Prelude.take n (Prelude.takeWhile (not . predicate)" sliceSepByMax
677+ prop "many (P.wordBy ' ') = words'" wordBy
660678 prop "parse 0, then 1, else fail" splitWith
661679 prop "fail due to die as left parser" splitWithFailLeft
662680 prop "fail due to die as right parser" splitWithFailRight