@@ -69,6 +69,11 @@ module Streamly.Internal.Data.Parser.ParserD
6969 -- , suffixOf -- match any suffix of a given string
7070 -- , infixOf -- match any substring of a given string
7171
72+ -- ** Spanning
73+ , span
74+ , spanBy
75+ , spanByRolling
76+
7277 -- Second order parsers (parsers using parsers)
7378 -- * Binary Combinators
7479
@@ -162,7 +167,7 @@ import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
162167import qualified Streamly.Internal.Data.Fold.Types as FL
163168
164169import Prelude hiding
165− (any, all, take, takeWhile, sequence, concatMap, maybe, either)
170+ (any, all, take, takeWhile, sequence, concatMap, maybe, either, span)
166171import Streamly.Internal.Data.Parser.ParserD.Tee
167172import Streamly.Internal.Data.Parser.ParserD.Types
168173
@@ -724,6 +729,53 @@ eqBy cmp str = Parser step initial extract
724729 $ "eqBy: end of input, yet to match "
725730 ++ show (length xs) ++ " elements"
726731
732+--------------------------------------------------------------------------------
733+--- Spanning
734+--------------------------------------------------------------------------------
735+
736+-- | @span p f1 f2@ composes folds @f1@ and @f2@ such that @f1@ consumes the
737+-- input as long as the predicate @p@ is 'True'. @f2@ consumes the rest of the
738+-- input.
739+--
740+-- > let span_ p xs = S.parse (FL.span p FL.toList FL.toList) $ S.fromList xs
741+--
742+-- >>> span_ (< 1) [1,2,3]
743+-- > ([],[1,2,3])
744+--
745+-- >>> span_ (< 2) [1,2,3]
746+-- > ([1],[2,3])
747+--
748+-- >>> span_ (< 4) [1,2,3]
749+-- > ([1,2,3],[])
750+--
751+-- /Internal/
752+{-# INLINE span #-}
753+span :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
754+span p f1 f2 = noErrorUnsafeSplitWith (,) (takeWhile p f1) (fromFold f2)
755+
756+-- | Break the input stream into two groups, the first group takes the input as
757+-- long as the predicate applied to the first element of the stream and next
758+-- input element holds 'True', the second group takes the rest of the input.
759+--
760+-- /Internal/
761+--
762+{-# INLINE spanBy #-}
763+spanBy ::
764+ Monad m
765+ => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
766+spanBy eq f1 f2 = noErrorUnsafeSplitWith (,) (groupBy eq f1) (fromFold f2)
767+
768+-- | Like 'spanBy' but applies the predicate in a rolling fashion i.e.
769+-- predicate is applied to the previous and the next input elements.
770+--
771+-- /Internal/
772+{-# INLINE spanByRolling #-}
773+spanByRolling ::
774+ Monad m
775+ => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (b, c)
776+spanByRolling eq f1 f2 =
777+ noErrorUnsafeSplitWith (,) (groupByRolling eq f1) (fromFold f2)
778+
727779-------------------------------------------------------------------------------
728780-- nested parsers
729781-------------------------------------------------------------------------------