2-- Module : Streamly.Internal.Data.Producer.Source
3-- Copyright : (c) 2021 Composewell Technologies
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
6-- Stability : experimental
9-- A 'Source' is a seed that can be unfolded to a stream with a buffer. Allows
10-- to 'unread' data i.e. push unused data back to the source buffer. This is
11-- useful in parsing applications with backtracking.
14module Streamly.Internal.Data.Producer.Source
36import Control.Exception (assert)
37import Control.Monad.Catch (MonadThrow, throwM)
38import GHC.Exts (SpecConstrAnnotation(..))
39import GHC.Types (SPEC(..))
40import Streamly.Internal.Data.Parser.ParserD (ParseError(..), Step(..))
41import Streamly.Internal.Data.Producer.Type (Producer(..))
42import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))
44import qualified Streamly.Internal.Data.Parser.ParserD as ParserD
45import qualified Streamly.Internal.Data.Parser.ParserK.Type as ParserK
47import Prelude hiding (read)
49-- | A seed with a buffer. It allows us to 'unread' or return some data
50-- after reading it. Useful in backtracked parsing.
52data Source a b = Source [b] (Maybe a)
54-- | Make a source from a seed value. The buffer would start as empty.
57source :: Maybe a -> Source a b
60-- | Return some unused data back to the source. The data is prepended (or
61-- consed) to the source.
64unread :: [b] -> Source a b -> Source a b
65unread xs (Source ys seed) = Source (xs ++ ys) seed
67-- | Determine if the source is empty.
68isEmpty :: Source a b -> Bool
69isEmpty (Source [] Nothing) = True
72-- | Convert a producer to a producer from a buffered source. Any buffered data
73-- is read first and then the seed is unfolded.
76{-# INLINE_NORMAL producer #-}
77producer :: Monad m => Producer m a b -> Producer m (Source a b) b
78producer (Producer step1 inject1 extract1) = Producer step inject extract
82 inject (Source [] (Just a)) = do
85 inject (Source xs a) = return $ Right (xs, a)
87 {-# INLINE_LATE step #-}
91 Yield x s1 -> Yield x (Left s1)
92 Skip s1 -> Skip (Left s1)
94 step (Right ([], Nothing)) = return Stop
95 step (Right ([], Just _)) = error "Bug: unreachable"
96 step (Right (x:[], Just a)) = do
98 return $ Yield x (Left s)
99 step (Right (x:xs, a)) = return $ Yield x (Right (xs, a))
101 extract (Left s) = Source [] . Just <$> extract1 s
102 extract (Right (xs, a)) = return $ Source xs a
104-------------------------------------------------------------------------------
106-------------------------------------------------------------------------------
108-- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need
110{-# ANN type List NoSpecConstr #-}
111newtype List a = List {getList :: [a]}
113{-# INLINE_NORMAL parseD #-}
116 => ParserD.Parser m a b
117 -> Producer m (Source s a) a
121 (ParserD.Parser pstep initial extract)
122 (Producer ustep uinject uextract)
127 ParserD.IPartial s -> do
128 state <- uinject seed
129 go SPEC state (List []) s
130 ParserD.IDone b -> return (b, seed)
131 ParserD.IError err -> throwM $ ParseError err
135 -- XXX currently we are using a dumb list based approach for backtracking
136 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
137 -- That will allow us more efficient random back and forth movement.
139 go !_ st buf !pst = do
145 Partial 0 pst1 -> go SPEC s (List []) pst1
147 assert (n <= length (x:getList buf)) (return ())
148 let src0 = Prelude.take n (x:getList buf)
149 src = Prelude.reverse src0
150 gobuf SPEC s (List []) (List src) pst1
151 Continue 0 pst1 -> go SPEC s (List (x:getList buf)) pst1
152 Continue n pst1 -> do
153 assert (n <= length (x:getList buf)) (return ())
154 let (src0, buf1) = splitAt n (x:getList buf)
155 src = Prelude.reverse src0
156 gobuf SPEC s (List buf1) (List src) pst1
158 assert (n <= length (x:getList buf)) (return ())
159 let src0 = Prelude.take n (x:getList buf)
160 src = Prelude.reverse src0
162 return (b, unread src s1)
163 Error err -> throwM $ ParseError err
164 Skip s -> go SPEC s buf pst
167 return (b, unread (reverse $ getList buf) (source Nothing))
169 gobuf !_ s buf (List []) !pst = go SPEC s buf pst
170 gobuf !_ s buf (List (x:xs)) !pst = do
174 gobuf SPEC s (List []) (List xs) pst1
176 assert (n <= length (x:getList buf)) (return ())
177 let src0 = Prelude.take n (x:getList buf)
178 src = Prelude.reverse src0 ++ xs
179 gobuf SPEC s (List []) (List src) pst1
181 gobuf SPEC s (List (x:getList buf)) (List xs) pst1
182 Continue n pst1 -> do
183 assert (n <= length (x:getList buf)) (return ())
184 let (src0, buf1) = splitAt n (x:getList buf)
185 src = Prelude.reverse src0 ++ xs
186 gobuf SPEC s (List buf1) (List src) pst1
188 assert (n <= length (x:getList buf)) (return ())
189 let src0 = Prelude.take n (x:getList buf)
190 src = Prelude.reverse src0
192 return (b, unread src s1)
193 Error err -> throwM $ ParseError err
195-- | Parse a buffered source using a parser, returning the parsed value and the
199{-# INLINE [3] parse #-}
202 => ParserK.Parser m a b
203 -> Producer m (Source s a) a
206parse = parseD . ParserK.fromParserK
208-------------------------------------------------------------------------------
210-------------------------------------------------------------------------------
212{-# INLINE parseManyD #-}
213parseManyD :: MonadThrow m =>
215 -> Producer m (Source x a) a
216 -> Producer m (Source x a) b
217parseManyD parser reader = Producer step return return
221 {-# INLINE_LATE step #-}
226 (b, s1) <- parseD parser reader src
229-- | Apply a parser repeatedly on a buffered source producer to generate a
230-- producer of parsed values.
233{-# INLINE parseMany #-}
234parseMany :: MonadThrow m =>
236 -> Producer m (Source x a) a
237 -> Producer m (Source x a) b
238parseMany parser = parseManyD (ParserK.fromParserK parser)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list