on master help sign in
Add takeFramedBy parsers
commit6100419d455e260c7a17eae535a5a6a93aa536a6 authorHarendra Kumar <harendra@composewell.com> dateSat 7 May 2022, 22:04 (4 years ago) parent97bb9b9
2 files changed+129−22
core/src/Streamly/Internal/Data/Parser.hs+31−22 core/src/Streamly/Internal/Data/Parser/ParserD.hs+98
core/src/Streamly/Internal/Data/Parser.hs+31−22
@@ -125,8 +125,8 @@ module Streamly.Internal.Data.Parser
125125
126126 -- ** Framing
127127 -- , takeFramedBy
128− -- , takeFramedBy_
129− , takeFramedByEsc
128+ , takeFramedBy_
129+ , takeFramedByEsc_
130130 -- , wordByQuoted
131131 -- , wordByQuotedEsc
132132
@@ -741,34 +741,43 @@ takeEndByEsc :: MonadCatch m =>
741741takeEndByEsc isEsc isSep p =
742742 D.toParserK $ D.takeEndByEsc isEsc isSep (D.fromParserK p)
743743
744−-- | @takeFramedByEsc begin end escape@ parses a string framed using @begin@ and
745−-- @end@ as the frame begin and end marker elements and @escape@ as an escaping
746−-- element to escape the occurrence of the framing elements within the frame.
747−-- Nested frames are allowed, but nesting is removed when parsing.
744+-- | @takeFramedByEsc_ isEsc isBegin isEnd fold@ parses a token framed using a
745+-- begin and end predicate, and an escape character. The frame begin and end
746+-- characters lose their special meaning if preceded by the escape character.
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.
748751--
749752-- For example,
750753--
751−-- @
752−-- > Stream.parse (Parser.takeFramedByEsc (== '{') (== '}') (== '\\') Fold.toList) $ Stream.fromList "{hello}"
754+-- >>> p = Parser.takeFramedByEsc_ (== '\\') (== '{') (== '}') Fold.toList
755+-- >>> Stream.parse p $ Stream.fromList "{hello}"
753756-- "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}}"
759758-- "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"
760763--
761−-- > Stream.parse (Parser.takeFramedByEsc (== '{') (== '}') (== '\\') Fold.toList) $ Stream.fromList "{hello {world}"
762−-- ParseError "Unterminated '{'"
764+-- /Pre-release/
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.
763773--
764−-- @
774+-- >>> takeFramedBy_ = Parser.takeFramedByEsc_ (const False)
765775--
766−-- /Unimplemented/
767−{-# INLINE takeFramedByEsc #-}
768−takeFramedByEsc :: -- MonadCatch m =>
769− (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser m a b
770−takeFramedByEsc _begin _end _escape _p = undefined
771− -- D.toParserK . D.takeFramedByEsc begin end escape p
776+{-# INLINE takeFramedBy_ #-}
777+takeFramedBy_ :: MonadCatch m =>
778+ (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser m a b
779+takeFramedBy_ isBegin isEnd f = D.toParserK $ D.takeFramedBy_ isBegin isEnd f
780+-- takeFramedBy_ = takeFramedByEsc_ (const False)
772781
773782-------------------------------------------------------------------------------
774783-- Grouping and words
core/src/Streamly/Internal/Data/Parser/ParserD.hs+98
@@ -76,9 +76,12 @@ module Streamly.Internal.Data.Parser.ParserD
7676 , takeEndBy_
7777 , takeEndByEsc
7878 , takeStartBy
79+ , takeFramedBy_
80+ , takeFramedByEsc_
7981
8082 -- Words and grouping
8183 , wordBy
84+ -- wordFramedBy
8285 , groupBy
8386 , groupByRolling
8487 , groupByRollingEither
@@ -787,6 +790,101 @@ takeStartBy cond (Fold fstep finitial fextract) =
787790 extract (Left' s) = fextract s
788791 extract (Right' s) = fextract s
789792
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+
790888-------------------------------------------------------------------------------
791889-- Grouping and words
792890-------------------------------------------------------------------------------