on master help sign in
Add Parser(D).groupByRolling
commit401f504fc76078ba070cc65eb0fd95af47edbcb3 authorAdithya Kumar <adithya@composewell.com> dateSun 24 Jan 2021, 10:49 (6 years ago) parent02e22d4
2 files changed+66
src/Streamly/Internal/Data/Parser.hs+33 src/Streamly/Internal/Data/Parser/ParserD.hs+33
src/Streamly/Internal/Data/Parser.hs+33
@@ -92,6 +92,7 @@ module Streamly.Internal.Data.Parser
9292 , escapedFrameBy
9393 , wordBy
9494 , groupBy
95+ , groupByRolling
9596 , eqBy
9697 -- | Unimplemented
9798 --
@@ -595,6 +596,38 @@ wordBy f = K.toParserK . D.wordBy f
595596groupBy :: MonadCatch m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
596597groupBy cmp = K.toParserK . D.groupBy cmp
597598
599+-- | Unlike 'groupBy' this combinator performs a rolling comparison of two
600+-- successive elements in the input stream. Assuming the input stream to the
601+-- parser is @[a,b,c,...]@ and the comparison function is @cmp@, the parser
602+-- first assigns the element @a@ to the first group, then if @a \`cmp` b@ is
603+-- 'True' @b@ is also assigned to the same group. If @b \`cmp` c@ is 'True'
604+-- then @c@ is also assigned to the same group and so on. When the comparison
605+-- fails the parser is terminated. Each group is folded using the 'Fold' @f@ and
606+-- the result of the fold is the result of the parser.
607+--
608+-- * Stops - when the comparison fails.
609+-- * Fails - never.
610+--
611+-- > runGroupsByRolling eq =
612+-- > Stream.toList
613+-- > . Stream.parseMany (groupByRolling eq Fold.toList)
614+-- > . Stream.fromList
615+--
616+-- >>> runGroupsByRolling (<) []
617+-- []
618+--
619+-- >>> runGroupsByRolling (<) [1]
620+-- [[1]]
621+--
622+-- >>> runGroupsByRolling (<) [3, 5, 4, 1, 2, 0]
623+-- [[3, 5], [4], [1, 2], [0]]
624+--
625+-- /Internal/
626+--
627+{-# INLINABLE groupByRolling #-}
628+groupByRolling :: MonadCatch m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
629+groupByRolling eq = K.toParserK . D.groupByRolling eq
630+
598631-- | Match the given sequence of elements using the given comparison function.
599632--
600633-- >>> S.parse $ S.eqBy (==) "string" $ S.fromList "string"
src/Streamly/Internal/Data/Parser/ParserD.hs+33
@@ -63,6 +63,7 @@ module Streamly.Internal.Data.Parser.ParserD
6363 --
6464 , wordBy
6565 , groupBy
66+ , groupByRolling
6667 , eqBy
6768 -- , prefixOf -- match any prefix of a given string
6869 -- , suffixOf -- match any suffix of a given string
@@ -624,6 +625,38 @@ groupBy cmp (Fold fstep finitial fextract) = Parser step initial extract
624625 extract (GroupByInit s) = fextract s
625626 extract (GroupByGrouping _ s) = fextract s
626627
628+-- | See 'Streamly.Internal.Data.Parser.groupByRolling'.
629+--
630+{-# INLINE groupByRolling #-}
631+groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
632+groupByRolling eq (Fold fstep finitial fextract) = Parser step initial extract
633+
634+ where
635+
636+ {-# INLINE grouper #-}
637+ grouper s a = do
638+ res <- fstep s a
639+ return
640+ $ case res of
641+ FL.Done b -> Done 0 b
642+ FL.Partial s1 -> Partial 0 (GroupByGrouping a s1)
643+
644+ initial = do
645+ res <- finitial
646+ return
647+ $ case res of
648+ FL.Partial s -> GroupByInit s
649+ FL.Done _ -> error "groupByRolling: Done in initial not implemented"
650+
651+ step (GroupByInit s) a = grouper s a
652+ step (GroupByGrouping a0 s) a =
653+ if eq a0 a
654+ then grouper s a
655+ else Done 1 <$> fextract s
656+
657+ extract (GroupByInit s) = fextract s
658+ extract (GroupByGrouping _ s) = fextract s
659+
627660-- XXX use an Unfold instead of a list?
628661-- XXX custom combinators for matching list, array and stream?
629662--