on master help sign in
Add takeFramedBy parsers
6100419Harendra Kumar
core/src/Streamly/Internal/Data/Parser.hs+31−22
@@ -125,8 +125,8 @@ module Streamly.Internal.Data.Parser
125
125
126 -- ** Framing
126 -- ** Framing
127 -- , takeFramedBy
127 -- , takeFramedBy
128− -- , takeFramedBy_
128+ , takeFramedBy_
129− , takeFramedByEsc
129+ , takeFramedByEsc_
130 -- , wordByQuoted
130 -- , wordByQuoted
131 -- , wordByQuotedEsc
131 -- , wordByQuotedEsc
132
132
@@ -741,34 +741,43 @@ takeEndByEsc :: MonadCatch m =>
741takeEndByEsc isEsc isSep p =
741takeEndByEsc isEsc isSep p =
742 D.toParserK $ D.takeEndByEsc isEsc isSep (D.fromParserK p)
742 D.toParserK $ D.takeEndByEsc isEsc isSep (D.fromParserK p)
743
743
744−-- | @takeFramedByEsc begin end escape@ parses a string framed using @begin@ and
744+-- | @takeFramedByEsc_ isEsc isBegin isEnd fold@ parses a token framed using a
745−-- @end@ as the frame begin and end marker elements and @escape@ as an escaping
745+-- begin and end predicate, and an escape character. The frame begin and end
746−-- element to escape the occurrence of the framing elements within the frame.
746+-- characters lose their special meaning if preceded by the escape character.
747−-- Nested frames are allowed, but nesting is removed when parsing.
747+--
748+-- Nested frames are allowed if begin and end markers are different, nested
749+-- frames must be balanced unless escaped, nested frame markers are emitted as
750+-- it is.
748--
751--
749-- For example,
752-- For example,
750--
753--
751−-- @
754+-- >>> p = Parser.takeFramedByEsc_ (== '\\') (== '{') (== '}') Fold.toList
752−-- > Stream.parse (Parser.takeFramedByEsc (== '{') (== '}') (== '\\') Fold.toList) $ Stream.fromList "{hello}"
755+-- >>> Stream.parse p $ Stream.fromList "{hello}"
753-- "hello"
756-- "hello"
754−--
755−-- > Stream.parse (Parser.takeFramedByEsc (== '{') (== '}') (== '\\') Fold.toList) $ Stream.fromList "{hello {world}}"
756−-- "hello world"
757−--
758−-- > Stream.parse (Parser.takeFramedByEsc (== '{') (== '}') (== '\\') Fold.toList) $ Stream.fromList "{hello \\{world\\}}"
757+-- >>> Stream.parse p $ Stream.fromList "{hello {world}}"
759-- "hello {world}"
758-- "hello {world}"
759+-- >>> Stream.parse p $ Stream.fromList "{hello \\{world}"
760+-- "hello {world"
761+-- >>> Stream.parse p $ Stream.fromList "{hello {world}"
762+-- *** Exception: ParseError "takeFramedByEsc_: missing frame end"
760--
763--
761−-- > Stream.parse (Parser.takeFramedByEsc (== '{') (== '}') (== '\\') Fold.toList) $ Stream.fromList "{hello {world}"
764+-- /Pre-release/
762−-- ParseError "Unterminated '{'"
765+{-# INLINE takeFramedByEsc_ #-}
766+takeFramedByEsc_ :: MonadCatch m =>
767+ (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser m a b
768+takeFramedByEsc_ isEsc isBegin isEnd f =
769+ D.toParserK $ D.takeFramedByEsc_ isEsc isBegin isEnd f
770+
771+-- | @takeFramedBy_ isBegin isEnd fold@ parses a token framed by a begin and an
772+-- end predicate.
763--
773--
764−-- @
774+-- >>> takeFramedBy_ = Parser.takeFramedByEsc_ (const False)
765--
775--
766−-- /Unimplemented/
776+{-# INLINE takeFramedBy_ #-}
767−{-# INLINE takeFramedByEsc #-}
768−takeFramedByEsc :: -- MonadCatch m =>
777+takeFramedBy_ :: MonadCatch m =>
769− (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser m a b
778+ (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser m a b
770−takeFramedByEsc _begin _end _escape _p = undefined
779+takeFramedBy_ isBegin isEnd f = D.toParserK $ D.takeFramedBy_ isBegin isEnd f
771− -- D.toParserK . D.takeFramedByEsc begin end escape p
780+-- takeFramedBy_ = takeFramedByEsc_ (const False)
772
781
773-------------------------------------------------------------------------------
782-------------------------------------------------------------------------------
774-- Grouping and words
783-- Grouping and words
core/src/Streamly/Internal/Data/Parser/ParserD.hs+98
@@ -76,9 +76,12 @@ module Streamly.Internal.Data.Parser.ParserD
76 , takeEndBy_
76 , takeEndBy_
77 , takeEndByEsc
77 , takeEndByEsc
78 , takeStartBy
78 , takeStartBy
79+ , takeFramedBy_
80+ , takeFramedByEsc_
79
81
80 -- Words and grouping
82 -- Words and grouping
81 , wordBy
83 , wordBy
84+ -- wordFramedBy
82 , groupBy
85 , groupBy
83 , groupByRolling
86 , groupByRolling
84 , groupByRollingEither
87 , groupByRollingEither
@@ -787,6 +790,101 @@ takeStartBy cond (Fold fstep finitial fextract) =
787 extract (Left' s) = fextract s
790 extract (Left' s) = fextract s
788 extract (Right' s) = fextract s
791 extract (Right' s) = fextract s
789
792
793+data FramedEscState s =
794+ FrameEscInit !s | FrameEscGo !s !Int | FrameEscEsc !s !Int
795+
796+{-# INLINE takeFramedByEsc_ #-}
797+takeFramedByEsc_ :: MonadThrow m =>
798+ (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser m a b
799+takeFramedByEsc_ isEsc isBegin isEnd (Fold fstep finitial fextract) =
800+
801+ Parser step initial extract
802+
803+ where
804+
805+ initial = do
806+ res <- finitial
807+ return $
808+ case res of
809+ FL.Partial s -> IPartial (FrameEscInit s)
810+ FL.Done _ ->
811+ error "takeFramedByEsc_: fold done without input"
812+
813+ {-# INLINE process #-}
814+ process s a n = do
815+ res <- fstep s a
816+ return
817+ $ case res of
818+ FL.Partial s1 -> Continue 0 (FrameEscGo s1 n)
819+ FL.Done b -> Done 0 b
820+
821+ step (FrameEscInit s) a =
822+ if isBegin a
823+ then return $ Partial 0 (FrameEscGo s 0)
824+ else return $ Error "takeFramedByEsc_: missing frame start"
825+ step (FrameEscGo s n) a =
826+ if isEsc a
827+ then return $ Partial 0 $ FrameEscEsc s n
828+ else do
829+ if not (isEnd a)
830+ then
831+ let n1 = if isBegin a then n + 1 else n
832+ in process s a n1
833+ else
834+ if n == 0
835+ then Done 0 <$> fextract s
836+ else process s a (n - 1)
837+ step (FrameEscEsc s n) a = process s a n
838+
839+ err = throwM . ParseError
840+
841+ extract (FrameEscInit _) = err "takeFramedByEsc_: empty token"
842+ extract (FrameEscGo _ _) = err "takeFramedByEsc_: missing frame end"
843+ extract (FrameEscEsc _ _) = err "takeFramedByEsc_: trailing escape"
844+
845+data FramedState s = FrameInit !s | FrameGo !s Int
846+
847+{-# INLINE takeFramedBy_ #-}
848+takeFramedBy_ :: MonadThrow m =>
849+ (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser m a b
850+takeFramedBy_ isBegin isEnd (Fold fstep finitial fextract) =
851+
852+ Parser step initial extract
853+
854+ where
855+
856+ initial = do
857+ res <- finitial
858+ return $
859+ case res of
860+ FL.Partial s -> IPartial (FrameInit s)
861+ FL.Done _ ->
862+ error "takeFramedBy_: fold done without input"
863+
864+ {-# INLINE process #-}
865+ process s a n = do
866+ res <- fstep s a
867+ return
868+ $ case res of
869+ FL.Partial s1 -> Continue 0 (FrameGo s1 n)
870+ FL.Done b -> Done 0 b
871+
872+ step (FrameInit s) a =
873+ if isBegin a
874+ then return $ Continue 0 (FrameGo s 0)
875+ else return $ Error "takeFramedBy_: missing frame start"
876+ step (FrameGo s n) a
877+ | not (isEnd a) =
878+ let n1 = if isBegin a then n + 1 else n
879+ in process s a n1
880+ | n == 0 = Done 0 <$> fextract s
881+ | otherwise = process s a (n - 1)
882+
883+ err = throwM . ParseError
884+
885+ extract (FrameInit _) = err "takeFramedBy_: empty token"
886+ extract (FrameGo _ _) = err "takeFramedBy_: missing frame end"
887+
790-------------------------------------------------------------------------------
888-------------------------------------------------------------------------------
791-- Grouping and words
889-- Grouping and words
792-------------------------------------------------------------------------------
890-------------------------------------------------------------------------------