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.
138 go !_ st buf !pst = do
144 Partial 0 pst1 -> go SPEC s (List []) pst1
146 assert (n <= length (x:getList buf)) (return ())
147 let src0 = Prelude.take n (x:getList buf)
148 src = Prelude.reverse src0
149 gobuf SPEC s (List []) (List src) pst1
150 Continue 0 pst1 -> go SPEC s (List (x:getList buf)) pst1
151 Continue n pst1 -> do
152 assert (n <= length (x:getList buf)) (return ())
153 let (src0, buf1) = splitAt n (x:getList buf)
154 src = Prelude.reverse src0
155 gobuf SPEC s (List buf1) (List src) pst1
157 assert (n <= length (x:getList buf)) (return ())
158 let src0 = Prelude.take n (x:getList buf)
159 src = Prelude.reverse src0
161 return (b, unread src s1)
162 Error err -> throwM $ ParseError err
163 Skip s -> go SPEC s buf pst
164 Stop -> goStop buf pst
166 gobuf !_ s buf (List []) !pst = go SPEC s buf pst
167 gobuf !_ s buf (List (x:xs)) !pst = do
171 gobuf SPEC s (List []) (List xs) pst1
173 assert (n <= length (x:getList buf)) (return ())
174 let src0 = Prelude.take n (x:getList buf)
175 src = Prelude.reverse src0 ++ xs
176 gobuf SPEC s (List []) (List src) pst1
178 gobuf SPEC s (List (x:getList buf)) (List xs) pst1
179 Continue n pst1 -> do
180 assert (n <= length (x:getList buf)) (return ())
181 let (src0, buf1) = splitAt n (x:getList buf)
182 src = Prelude.reverse src0 ++ xs
183 gobuf SPEC s (List buf1) (List src) pst1
185 assert (n <= length (x:getList buf)) (return ())
186 let src0 = Prelude.take n (x:getList buf)
187 src = Prelude.reverse src0
189 return (b, unread src s1)
190 Error err -> throwM $ ParseError err
192 -- This is a simplified gobuf
193 goExtract !_ buf (List []) !pst = goStop buf pst
194 goExtract !_ buf (List (x:xs)) !pst = do
198 goExtract SPEC (List []) (List xs) pst1
200 assert (n <= length (x:getList buf)) (return ())
201 let src0 = Prelude.take n (x:getList buf)
202 src = Prelude.reverse src0 ++ xs
203 goExtract SPEC (List []) (List src) pst1
205 goExtract SPEC (List (x:getList buf)) (List xs) pst1
206 Continue n pst1 -> do
207 assert (n <= length (x:getList buf)) (return ())
208 let (src0, buf1) = splitAt n (x:getList buf)
209 src = Prelude.reverse src0 ++ xs
210 goExtract SPEC (List buf1) (List src) pst1
212 assert (n <= length (x:getList buf)) (return ())
213 let src0 = Prelude.take n (x:getList buf)
214 src = Prelude.reverse src0
215 return (b, unread src (source Nothing))
216 Error err -> throwM $ ParseError err
218 -- This is a simplified goExtract
219 {-# INLINE goStop #-}
223 Partial _ _ -> error "Bug: parseD: Partial in extract"
225 error "parseD: extract, Continue 0 creates infinite loop"
226 Continue n pst1 -> do
227 assert (n <= length (getList buf)) (return ())
228 let (src0, buf1) = splitAt n (getList buf)
229 src = Prelude.reverse src0
230 goExtract SPEC (List buf1) (List src) pst1
231 Done 0 b -> return (b, source Nothing)
233 assert (n <= length (getList buf)) (return ())
234 let src0 = Prelude.take n (getList buf)
235 src = Prelude.reverse src0
236 return (b, unread src (source Nothing))
237 Error err -> throwM $ ParseError err
239-- | Parse a buffered source using a parser, returning the parsed value and the
243{-# INLINE [3] parse #-}
246 => ParserK.Parser m a b
247 -> Producer m (Source s a) a
250parse = parseD . ParserD.fromParserK
252-------------------------------------------------------------------------------
254-------------------------------------------------------------------------------
256{-# INLINE parseManyD #-}
257parseManyD :: MonadThrow m =>
259 -> Producer m (Source x a) a
260 -> Producer m (Source x a) b
261parseManyD parser reader = Producer step return return
265 {-# INLINE_LATE step #-}
270 (b, s1) <- parseD parser reader src
273-- | Apply a parser repeatedly on a buffered source producer to generate a
274-- producer of parsed values.
277{-# INLINE parseMany #-}
278parseMany :: MonadThrow m =>
280 -> Producer m (Source x a) a
281 -> Producer m (Source x a) b
282parseMany parser = parseManyD (ParserD.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