on 4676d68 help sign in
1342 lines 43 kB history raw
1{-# OPTIONS_GHC -Wno-orphans #-}
3-- |
4-- Module : Streamly.Internal.Data.Parser
5-- Copyright : (c) 2020 Composewell Technologies
6-- License : BSD-3-Clause
7-- Maintainer : streamly@composewell.com
8-- Stability : pre-release
9-- Portability : GHC
10--
11-- Fast backtracking parsers with stream fusion and native streaming
12-- capability.
13--
14-- 'Applicative' and 'Control.Applicative.Alternative' type class based
15-- combinators from the
16-- <http://hackage.haskell.org/package/parser-combinators parser-combinators>
17-- package can also be used with the 'Parser' type. However, there are two
18-- important differences between @parser-combinators@ and the equivalent ones
19-- provided in this module in terms of performance:
20--
21-- 1) @parser-combinators@ use plain Haskell lists to collect the results, in a
22-- strict Monad like IO, the results are necessarily buffered before they can
23-- be consumed. This may not perform optimally in streaming applications
24-- processing large amounts of data. Equivalent combinators in this module can
25-- consume the results of parsing using a 'Fold', thus providing a scalability
26-- and a composable consumer.
27--
28-- 2) Several combinators in this module can be many times faster because of
29-- stream fusion. For example, 'Streamly.Internal.Data.Parser.many' combinator
30-- in this module is much faster than the 'Control.Applicative.many' combinator
31-- of 'Control.Applicative.Alternative' type class.
32--
33-- = Errors
34--
35-- Failing parsers in this module throw the 'D.ParseError' exception.
36--
37-- = Naming
38--
39-- As far as possible, try that the names of the combinators in this module are
40-- consistent with:
41--
42-- * <https://hackage.haskell.org/package/base/docs/Text-ParserCombinators-ReadP.html base/Text.ParserCombinators.ReadP>
43-- * <http://hackage.haskell.org/package/parser-combinators parser-combinators>
44-- * <http://hackage.haskell.org/package/megaparsec megaparsec>
45-- * <http://hackage.haskell.org/package/attoparsec attoparsec>
46-- * <http://hackage.haskell.org/package/parsec parsec>
48module Streamly.Internal.Data.Parser
49 (
50 K.Parser (..)
51 , D.ParseError (..)
52 , D.Step (..)
54 -- * Downgrade to Fold
55 , toFold
57 -- First order parsers
58 -- * Accumulators
59 , fromFold
60 , fromPure
61 , fromEffect
62 , die
63 , dieM
65 -- * Map on input
66 , lmap
67 , lmapM
68 , filter
70 -- * Element parsers
71 , peek
72 , one
73 , next
74 , element
75 , except
76 , oneOf
77 , noneOf
78 , eof
79 , satisfy
80 , maybe
81 , either
83 -- * Sequence parsers
84 --
85 -- | Parsers chained in series, if one parser terminates the composition
86 -- terminates.
88 -- | Grab a sequence of input elements without inspecting them
89 , takeBetween
90 -- , take -- takeBetween 0 n
91 , takeEQ -- takeBetween n n
92 , takeGE -- takeBetween n maxBound
93 , takeP
95 -- Grab a sequence of input elements by inspecting them
96 , eqBy
97 , list
98 , lookAhead
99 , takeWhileP
100 , takeWhile
101 -- $takeWhile
102 , takeWhile1
103 , dropWhile
105 -- Separators
106 , sliceSepByP
107 , sliceBeginWith
108 , sliceSepWith
110 -- Quoting and Escaping
111 , escapedSliceSepBy
112 , escapedFrameBy
114 -- Words and grouping
115 , wordBy
116 , groupBy
117 , groupByRolling
118 , groupByRollingEither
120 -- | Unimplemented
121 --
122 -- @
123 -- , prefixOf -- match any prefix of a given string
124 -- , suffixOf -- match any suffix of a given string
125 -- , infixOf -- match any substring of a given string
126 -- @
128 -- Second order parsers (parsers using parsers)
129 -- * Binary Combinators
131 -- ** Sequential Applicative
132 , serialWith
133 , split_
135 -- ** Parallel Applicatives
136 , teeWith
137 , teeWithFst
138 , teeWithMin
139 -- , teeTill -- like manyTill but parallel
141 -- ** Sequential Interleaving
142 -- Use two folds, run a primary parser, its rejected values go to the
143 -- secondary parser.
144 , deintercalate
146 -- ** Sequential Alternative
147 , alt
149 -- ** Parallel Alternatives
150 , shortest
151 , longest
152 -- , fastest
154 -- * N-ary Combinators
155 -- ** Sequential Collection
156 , concatSequence
157 , concatMap
159 -- ** Sequential Repetition
160 , count
161 , countBetween
163 , manyP
164 , many
165 , some
166 , manyTillP
167 , manyTill
168 , manyThen
170 -- ** Special cases
171 -- | TODO: traditional implmentations of these may be of limited use. For
172 -- example, consider parsing lines separated by @\\r\\n@. The main parser
173 -- will have to detect and exclude the sequence @\\r\\n@ anyway so that we
174 -- can apply the "sep" parser.
175 --
176 -- We can instead implement these as special cases of deintercalate.
177 --
178 -- @
179 -- , endBy
180 , sepBy1
181 , sepBy
182 -- , sepEndBy
183 -- , beginBy
184 -- , sepBeginBy
185 -- , sepAroundBy
186 -- @
188 -- * Distribution
189 --
190 -- | A simple and stupid impl would be to just convert the stream to an
191 -- array and give the array reference to all consumers. The array can be
192 -- grown on demand by any consumer and truncated when nonbody needs it.
194 -- ** Distribute to collection
195 -- ** Distribute to repetition
197 -- ** Interleaved collection
198 -- |
199 --
200 -- 1. Round robin
201 -- 2. Priority based
202 , roundRobin
204 -- ** Collection of Alternatives
205 -- | Unimplemented
206 --
207 -- @
208 -- , shortestN
209 -- , longestN
210 -- , fastestN -- first N successful in time
211 -- , choiceN -- first N successful in position
212 -- @
213 , choice -- first successful in position
215 -- ** Repeated Alternatives
216 , retryMaxTotal
217 , retryMaxSuccessive
218 , retry
219 )
220where
222import Control.Applicative ((<|>))
223import Control.Monad.Catch (MonadCatch, MonadThrow)
224import Data.Functor (($>))
225import Prelude hiding
226 ( any, all, dropWhile, take, takeWhile, sequence, concatMap, maybe, either
227 , filter )
229import Streamly.Internal.Data.Fold.Type (Fold(..))
230import Streamly.Internal.Data.Parser.ParserK.Type (Parser)
232import qualified Streamly.Internal.Data.Fold.Type as FL
233import qualified Streamly.Internal.Data.Parser.ParserD as D
234import qualified Streamly.Internal.Data.Parser.ParserK.Type as K
236--
237-- $setup
238-- >>> :m
239-- >>> :set -package streamly
240-- >>> import Prelude hiding (any, all, dropWhile, take, takeWhile, sequence, concatMap, maybe, either, filter)
241-- >>> import qualified Streamly.Prelude as Stream
242-- >>> import qualified Streamly.Internal.Data.Stream.IsStream as Stream (parse, parseMany)
243-- >>> import qualified Streamly.Data.Fold as Fold
244-- >>> import qualified Streamly.Internal.Data.Parser as Parser
246-------------------------------------------------------------------------------
247-- Downgrade a parser to a Fold
248-------------------------------------------------------------------------------
249--
250-- | Make a 'Fold' from a 'Parser'. The fold just throws an exception if the
251-- parser fails or tries to backtrack.
252--
253-- This can be useful in combinators that accept a Fold and we know that a
254-- Parser cannot fail or failure exception is acceptable as there is no way to
255-- recover.
256--
257-- /Pre-release/
258--
259{-# INLINE toFold #-}
260toFold :: MonadThrow m => Parser m a b -> Fold m a b
261toFold p = D.toFold $ D.fromParserK p
263-------------------------------------------------------------------------------
264-- Upgrade folds to parses
265-------------------------------------------------------------------------------
266--
267-- | Make a 'Parser' from a 'Fold'.
268--
269-- /Pre-release/
270--
271{-# INLINE fromFold #-}
272fromFold :: MonadCatch m => Fold m a b -> Parser m a b
273fromFold = D.toParserK . D.fromFold
275-------------------------------------------------------------------------------
276-- Terminating but not failing folds
277-------------------------------------------------------------------------------
278--
279-- This is the dual of stream "fromPure".
280--
281-- | A parser that always yields a pure value without consuming any input.
282--
283-- /Pre-release/
284--
285{-# INLINE [3] fromPure #-}
286fromPure :: MonadCatch m => b -> Parser m a b
287fromPure = D.toParserK . D.fromPure
288{-# RULES "fromPure fallback to CPS" [2]
289 forall a. D.toParserK (D.fromPure a) = K.fromPure a #-}
291-- This is the dual of stream "fromEffect".
292--
293-- | A parser that always yields the result of an effectful action without
294-- consuming any input.
295--
296-- /Pre-release/
297--
298{-# INLINE fromEffect #-}
299fromEffect :: MonadCatch m => m b -> Parser m a b
300fromEffect = K.fromEffect -- D.toParserK . D.fromEffect
302-- This is the dual of "nil".
303--
304-- | A parser that always fails with an error message without consuming
305-- any input.
306--
307-- /Pre-release/
308--
309{-# INLINE [3] die #-}
310die :: MonadCatch m => String -> Parser m a b
311die = D.toParserK . D.die
312{-# RULES "die fallback to CPS" [2]
313 forall a. D.toParserK (D.die a) = K.die a #-}
315-- This is the dual of "nilM".
316--
317-- | A parser that always fails with an effectful error message and without
318-- consuming any input.
319--
320-- /Pre-release/
321--
322{-# INLINE dieM #-}
323dieM :: MonadCatch m => m String -> Parser m a b
324dieM = D.toParserK . D.dieM
326-------------------------------------------------------------------------------
327-- Map on input
328-------------------------------------------------------------------------------
330-- | @lmap f parser@ maps the function @f@ on the input of the parser.
331--
332-- >>> Stream.parse (Parser.lmap (\x -> x * x) (Parser.fromFold Fold.sum)) (Stream.enumerateFromTo 1 100)
333-- 338350
334--
335-- > lmap = Parser.lmapM return
336--
337-- /Internal/
338{-# INLINE lmap #-}
339lmap :: MonadCatch m => (a -> b) -> Parser m b r -> Parser m a r
340lmap f p = D.toParserK $ D.lmap f $ D.fromParserK p
342-- | @lmapM f parser@ maps the monadic function @f@ on the input of the parser.
343--
344-- /Internal/
345{-# INLINE lmapM #-}
346lmapM :: MonadCatch m => (a -> m b) -> Parser m b r -> Parser m a r
347lmapM f p = D.toParserK $ D.lmapM f $ D.fromParserK p
349-- | Include only those elements that pass a predicate.
350--
351-- >>> Stream.parse (Parser.filter (> 5) (Parser.fromFold Fold.sum)) $ Stream.fromList [1..10]
352-- 40
353--
354-- /Internal/
355{-# INLINE filter #-}
356filter :: MonadCatch m => (a -> Bool) -> Parser m a b -> Parser m a b
357filter f p = D.toParserK $ D.filter f $ D.fromParserK p
359-------------------------------------------------------------------------------
360-- Failing Parsers
361-------------------------------------------------------------------------------
363-- | Peek the head element of a stream, without consuming it. Fails if it
364-- encounters end of input.
365--
366-- >>> Stream.parse ((,) <$> Parser.peek <*> Parser.satisfy (> 0)) $ Stream.fromList [1]
367-- (1,1)
368--
369-- @
370-- peek = lookAhead (satisfy True)
371-- @
372--
373-- /Pre-release/
374--
375{-# INLINE peek #-}
376peek :: MonadCatch m => Parser m a a
377peek = D.toParserK D.peek
379-- | Succeeds if we are at the end of input, fails otherwise.
380--
381-- >>> Stream.parse ((,) <$> Parser.satisfy (> 0) <*> Parser.eof) $ Stream.fromList [1]
382-- (1,())
383--
384-- /Pre-release/
385--
386{-# INLINE eof #-}
387eof :: MonadCatch m => Parser m a ()
388eof = D.toParserK D.eof
390-- | Returns the next element if it passes the predicate, fails otherwise.
391--
392-- >>> Stream.parse (Parser.satisfy (== 1)) $ Stream.fromList [1,0,1]
393-- 1
394--
395-- /Pre-release/
396--
397{-# INLINE satisfy #-}
398satisfy :: MonadCatch m => (a -> Bool) -> Parser m a a
399satisfy = D.toParserK . D.satisfy
401-- | Consume one element from the head of the stream. Fails if it encounters
402-- end of input.
403--
404-- >>> one = Parser.satisfy $ const True
405--
406-- /Pre-release/
407--
408{-# INLINE one #-}
409one :: MonadCatch m => Parser m a a
410one = satisfy $ const True
412-- | Match a specific element.
413--
414-- >>> element x = Parser.satisfy (== x)
415--
416{-# INLINE element #-}
417element :: (MonadCatch m, Eq a) => a -> Parser m a a
418element x = satisfy (== x)
420-- | Match anything other than the supplied element.
421--
422-- >>> except x = Parser.satisfy (/= x)
423--
424{-# INLINE except #-}
425except :: (MonadCatch m, Eq a) => a -> Parser m a a
426except x = satisfy (/= x)
428-- | Match any one of the elements in the supplied list.
429--
430-- >>> oneOf xs = Parser.satisfy (`elem` xs)
431--
432-- When performance matters a pattern matching predicate could be more
433-- efficient than a list:
434--
435-- @
436-- let p x =
437-- case x of
438-- 'a' -> True
439-- 'e' -> True
440-- _ -> False
441-- in satisfy p
442-- @
443--
444-- GHC may use a binary search instead of linear search in the list.
445-- Alternatively, you can also use an array instead of list for storage and
446-- search.
447--
448{-# INLINE oneOf #-}
449oneOf :: (MonadCatch m, Eq a) => [a] -> Parser m a a
450oneOf xs = satisfy (`elem` xs)
452-- | See performance notes in 'oneOf'.
453--
454-- >>> noneOf xs = Parser.satisfy (`notElem` xs)
455--
456{-# INLINE noneOf #-}
457noneOf :: (MonadCatch m, Eq a) => [a] -> Parser m a a
458noneOf xs = satisfy (`notElem` xs)
460-- | Return the next element of the input. Returns 'Nothing'
461-- on end of input. Also known as 'head'.
462--
463-- /Pre-release/
464--
465{-# INLINE next #-}
466next :: MonadCatch m => Parser m a (Maybe a)
467next = D.toParserK D.next
469-- | Map a 'Maybe' returning function on the next element in the stream. The
470-- parser fails if the function returns 'Nothing' otherwise returns the 'Just'
471-- value.
472--
473-- /Pre-release/
474--
475{-# INLINE maybe #-}
476maybe :: MonadCatch m => (a -> Maybe b) -> Parser m a b
477maybe = D.toParserK . D.maybe
479-- | Map an 'Either' returning function on the next element in the stream. If
480-- the function returns 'Left err', the parser fails with the error message
481-- @err@ otherwise returns the 'Right' value.
482--
483-- /Pre-release/
484--
485{-# INLINE either #-}
486either :: MonadCatch m => (a -> Either String b) -> Parser m a b
487either = D.toParserK . D.either
489-------------------------------------------------------------------------------
490-- Taking elements
491-------------------------------------------------------------------------------
493-- | @takeBetween m n@ takes a minimum of @m@ and a maximum of @n@ input
494-- elements and folds them using the supplied fold.
495--
496-- Stops after @n@ elements.
497-- Fails if the stream ends before @m@ elements could be taken.
498--
499-- Examples: -
500--
501-- @
502-- >>> :{
503-- takeBetween' low high ls = Stream.parse prsr (Stream.fromList ls)
504-- where prsr = Parser.takeBetween low high Fold.toList
505-- :}
506--
507-- @
508--
509-- >>> takeBetween' 2 4 [1, 2, 3, 4, 5]
510-- [1,2,3,4]
511--
512-- >>> takeBetween' 2 4 [1, 2]
513-- [1,2]
514--
515-- >>> takeBetween' 2 4 [1]
516-- *** Exception: ParseError "takeBetween: Expecting alteast 2 elements, got 1"
517--
518-- >>> takeBetween' 0 0 [1, 2]
519-- []
520--
521-- >>> takeBetween' 0 1 []
522-- []
523--
524-- @takeBetween@ is the most general take operation, other take operations can
525-- be defined in terms of takeBetween. For example:
526--
527-- @
528-- take = takeBetween 0 n -- equivalent of take
529-- take1 = takeBetween 1 n -- equivalent of takeLE1
530-- takeEQ = takeBetween n n
531-- takeGE = takeBetween n maxBound
532-- @
533--
534-- /Pre-release/
535--
536{-# INLINE takeBetween #-}
537takeBetween :: MonadCatch m =>
538 Int -> Int -> Fold m a b -> Parser m a b
539takeBetween m n = D.toParserK . D.takeBetween m n
541-- | Stops after taking exactly @n@ input elements.
542--
543-- * Stops - after consuming @n@ elements.
544-- * Fails - if the stream or the collecting fold ends before it can collect
545-- exactly @n@ elements.
546--
547-- >>> Stream.parse (Parser.takeEQ 4 Fold.toList) $ Stream.fromList [1,0,1]
548-- *** Exception: ParseError "takeEQ: Expecting exactly 4 elements, input terminated on 3"
549--
550-- /Pre-release/
551--
552{-# INLINE takeEQ #-}
553takeEQ :: MonadCatch m => Int -> Fold m a b -> Parser m a b
554takeEQ n = D.toParserK . D.takeEQ n
556-- | Take at least @n@ input elements, but can collect more.
557--
558-- * Stops - when the collecting fold stops.
559-- * Fails - if the stream or the collecting fold ends before producing @n@
560-- elements.
561--
562-- >>> Stream.parse (Parser.takeGE 4 Fold.toList) $ Stream.fromList [1,0,1]
563-- *** Exception: ParseError "takeGE: Expecting at least 4 elements, input terminated on 3"
564--
565-- >>> Stream.parse (Parser.takeGE 4 Fold.toList) $ Stream.fromList [1,0,1,0,1]
566-- [1,0,1,0,1]
567--
568-- /Pre-release/
569--
570{-# INLINE takeGE #-}
571takeGE :: MonadCatch m => Int -> Fold m a b -> Parser m a b
572takeGE n = D.toParserK . D.takeGE n
574-------------------------------------------------------------------------------
575-- Take until a condition
576-------------------------------------------------------------------------------
578-- $takeWhile
579-- Note: This is called @takeWhileP@ and @munch@ in some parser libraries.
581-- | Like 'takeWhile' but uses a 'Parser' instead of a 'Fold' to collect the
582-- input. The combinator stops when the condition fails or if the collecting
583-- parser stops.
584--
585-- This is a generalized version of takeWhile, for example 'takeWhile1' can be
586-- implemented in terms of this:
587--
588-- @
589-- takeWhile1 cond p = takeWhile cond (takeBetween 1 maxBound p)
590-- @
591--
592-- Stops: when the condition fails or the collecting parser stops.
593-- Fails: when the collecting parser fails.
594--
595-- /Unimplemented/
596--
597{-# INLINE takeWhileP #-}
598takeWhileP :: -- MonadCatch m =>
599 (a -> Bool) -> Parser m a b -> Parser m a b
600takeWhileP _cond = undefined -- D.toParserK . D.takeWhileP cond
602-- | Collect stream elements until an element fails the predicate. The element
603-- on which the predicate fails is returned back to the input stream.
604--
605-- * Stops - when the predicate fails or the collecting fold stops.
606-- * Fails - never.
607--
608-- >>> Stream.parse (Parser.takeWhile (== 0) Fold.toList) $ Stream.fromList [0,0,1,0,1]
609-- [0,0]
610--
611-- We can implement a @breakOn@ using 'takeWhile':
612--
613-- @
614-- breakOn p = takeWhile (not p)
615-- @
616--
617-- /Pre-release/
618--
619{-# INLINE takeWhile #-}
620takeWhile :: MonadCatch m => (a -> Bool) -> Fold m a b -> Parser m a b
621takeWhile cond = D.toParserK . D.takeWhile cond
623-- | Like 'takeWhile' but takes at least one element otherwise fails.
624--
625-- /Pre-release/
626--
627{-# INLINE takeWhile1 #-}
628takeWhile1 :: MonadCatch m => (a -> Bool) -> Fold m a b -> Parser m a b
629takeWhile1 cond = D.toParserK . D.takeWhile1 cond
631-- | Drain the input as long as the predicate succeeds, running the effects and
632-- discarding the results.
633--
634-- This is also called @skipWhile@ in some parsing libraries.
635--
636-- /Pre-release/
637--
638{-# INLINE dropWhile #-}
639dropWhile :: MonadCatch m => (a -> Bool) -> Parser m a ()
640dropWhile p = takeWhile p FL.drain
642-------------------------------------------------------------------------------
643-- Separators
644-------------------------------------------------------------------------------
646-- | @sliceSepByP cond parser@ parses a slice of the input using @parser@ until
647-- @cond@ succeeds or the parser stops.
648--
649-- This is a generalized slicing parser which can be used to implement other
650-- parsers e.g.:
651--
652-- @
653-- sliceSepByMax cond n p = sliceSepByP cond (take n p)
654-- sliceSepByBetween cond m n p = sliceSepByP cond (takeBetween m n p)
655-- @
656--
657-- /Pre-release/
658--
659{-# INLINE sliceSepByP #-}
660sliceSepByP ::
661 MonadCatch m =>
662 (a -> Bool) -> Parser m a b -> Parser m a b
663sliceSepByP cond = D.toParserK . D.sliceSepByP cond . D.fromParserK
665-- | Like 'sliceSepBy' but does not drop the separator element, instead
666-- separator is emitted as a separate element in the output.
667--
668-- /Unimplemented/
669{-# INLINE sliceSepWith #-}
670sliceSepWith :: -- MonadCatch m =>
671 (a -> Bool) -> Fold m a b -> Parser m a b
672sliceSepWith _cond = undefined -- D.toParserK . D.sliceSepBy cond
674-- | Collect stream elements until an elements passes the predicate, return the
675-- last element on which the predicate succeeded back to the input stream. If
676-- the predicate succeeds on the first element itself then the parser does not
677-- terminate there. The succeeding element in the leading position
678-- is treated as a prefix separator which is kept in the output segment.
679--
680-- * Stops - when the predicate succeeds in non-leading position.
681-- * Fails - never.
682--
683-- S.splitWithPrefix pred f = S.parseMany (PR.sliceBeginWith pred f)
684--
685-- Examples: -
686--
687-- >>> :{
688-- sliceBeginWithOdd ls = Stream.parse prsr (Stream.fromList ls)
689-- where prsr = Parser.sliceBeginWith odd Fold.toList
690-- :}
691--
692--
693-- >>> sliceBeginWithOdd [2, 4, 6, 3]
694-- *** Exception: sliceBeginWith : slice begins with an element which fails the predicate
695-- ...
696--
697-- >>> sliceBeginWithOdd [3, 5, 7, 4]
698-- [3]
699--
700-- >>> sliceBeginWithOdd [3, 4, 6, 8, 5]
701-- [3,4,6,8]
702--
703-- >>> sliceBeginWithOdd []
704-- []
705--
706-- /Pre-release/
707--
708{-# INLINE sliceBeginWith #-}
709sliceBeginWith ::
710 MonadCatch m =>
711 (a -> Bool) -> Fold m a b -> Parser m a b
712sliceBeginWith cond = D.toParserK . D.sliceBeginWith cond
714-------------------------------------------------------------------------------
715-- Quoting and Escaping
716-------------------------------------------------------------------------------
718-- | Like 'sliceSepBy' but the separator elements can be escaped using an
719-- escape char determined by the second predicate.
720--
721-- /Unimplemented/
722{-# INLINE escapedSliceSepBy #-}
723escapedSliceSepBy :: -- MonadCatch m =>
724 (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser m a b
725escapedSliceSepBy _cond _esc = undefined
726 -- D.toParserK . D.escapedSliceSepBy cond esc
728-- | @escapedFrameBy begin end escape@ parses a string framed using @begin@ and
729-- @end@ as the frame begin and end marker elements and @escape@ as an escaping
730-- element to escape the occurrence of the framing elements within the frame.
731-- Nested frames are allowed, but nesting is removed when parsing.
732--
733-- For example,
734--
735-- @
736-- > Stream.parse (Parser.escapedFrameBy (== '{') (== '}') (== '\\') Fold.toList) $ Stream.fromList "{hello}"
737-- "hello"
738--
739-- > Stream.parse (Parser.escapedFrameBy (== '{') (== '}') (== '\\') Fold.toList) $ Stream.fromList "{hello {world}}"
740-- "hello world"
741--
742-- > Stream.parse (Parser.escapedFrameBy (== '{') (== '}') (== '\\') Fold.toList) $ Stream.fromList "{hello \\{world\\}}"
743-- "hello {world}"
744--
745-- > Stream.parse (Parser.escapedFrameBy (== '{') (== '}') (== '\\') Fold.toList) $ Stream.fromList "{hello {world}"
746-- ParseError "Unterminated '{'"
747--
748-- @
749--
750-- /Unimplemented/
751{-# INLINE escapedFrameBy #-}
752escapedFrameBy :: -- MonadCatch m =>
753 (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser m a b
754escapedFrameBy _begin _end _escape _p = undefined
755 -- D.toParserK . D.frameBy begin end escape p
757-------------------------------------------------------------------------------
758-- Grouping and words
759-------------------------------------------------------------------------------
761-- Note we can also get words using something like:
762-- sepBy FL.toList (takeWhile (not . p) Fold.toList) (dropWhile p)
763--
764-- But that won't be as efficient and ergonomic.
765--
766-- | Like 'splitOn' but strips leading, trailing, and repeated separators.
767-- Therefore, @".a..b."@ having '.' as the separator would be parsed as
768-- @["a","b"]@. In other words, its like parsing words from whitespace
769-- separated text.
770--
771-- * Stops - when it finds a word separator after a non-word element
772-- * Fails - never.
773--
774-- @
775-- S.wordsBy pred f = S.parseMany (PR.wordBy pred f)
776-- @
777--
778{-# INLINE wordBy #-}
779wordBy :: MonadCatch m => (a -> Bool) -> Fold m a b -> Parser m a b
780wordBy f = D.toParserK . D.wordBy f
782-- | Given an input stream @[a,b,c,...]@ and a comparison function @cmp@, the
783-- parser assigns the element @a@ to the first group, then if @a \`cmp` b@ is
784-- 'True' @b@ is also assigned to the same group. If @a \`cmp` c@ is 'True'
785-- then @c@ is also assigned to the same group and so on. When the comparison
786-- fails the parser is terminated. Each group is folded using the 'Fold' @f@ and
787-- the result of the fold is the result of the parser.
788--
789-- * Stops - when the comparison fails.
790-- * Fails - never.
791--
792-- >>> :{
793-- runGroupsBy eq =
794-- Stream.toList
795-- . Stream.parseMany (Parser.groupBy eq Fold.toList)
796-- . Stream.fromList
797-- :}
798--
799-- >>> runGroupsBy (<) []
800-- []
801--
802-- >>> runGroupsBy (<) [1]
803-- [[1]]
804--
805-- >>> runGroupsBy (<) [3, 5, 4, 1, 2, 0]
806-- [[3,5,4],[1,2],[0]]
807--
808-- /Pre-release/
809--
810{-# INLINE groupBy #-}
811groupBy :: MonadCatch m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
812groupBy eq = D.toParserK . D.groupBy eq
814-- | Unlike 'groupBy' this combinator performs a rolling comparison of two
815-- successive elements in the input stream. Assuming the input stream
816-- is @[a,b,c,...]@ and the comparison function is @cmp@, the parser
817-- first assigns the element @a@ to the first group, then if @a \`cmp` b@ is
818-- 'True' @b@ is also assigned to the same group. If @b \`cmp` c@ is 'True'
819-- then @c@ is also assigned to the same group and so on. When the comparison
820-- fails the parser is terminated. Each group is folded using the 'Fold' @f@ and
821-- the result of the fold is the result of the parser.
822--
823-- * Stops - when the comparison fails.
824-- * Fails - never.
825--
826-- >>> :{
827-- runGroupsByRolling eq =
828-- Stream.toList
829-- . Stream.parseMany (Parser.groupByRolling eq Fold.toList)
830-- . Stream.fromList
831-- :}
832--
833-- >>> runGroupsByRolling (<) []
834-- []
835--
836-- >>> runGroupsByRolling (<) [1]
837-- [[1]]
838--
839-- >>> runGroupsByRolling (<) [3, 5, 4, 1, 2, 0]
840-- [[3,5],[4],[1,2],[0]]
841--
842-- /Pre-release/
843--
844{-# INLINE groupByRolling #-}
845groupByRolling :: MonadCatch m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
846groupByRolling eq = D.toParserK . D.groupByRolling eq
848-- | Like 'groupByRolling', but if the predicate is 'True' then collects using
849-- the first fold as long as the predicate holds 'True', if the predicate is
850-- 'False' collects using the second fold as long as it remains 'False'.
851-- Returns 'Left' for the first case and 'Right' for the second case.
852--
853-- For example, if we want to detect sorted sequences in a stream, both
854-- ascending and descending cases we can use 'groupByRollingEither (<=)
855-- Fold.toList Fold.toList'.
856--
857-- /Unimplemented/
858{-# INLINE groupByRollingEither #-}
859groupByRollingEither :: MonadCatch m =>
860 (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser m a (Either b c)
861groupByRollingEither eq f1 = D.toParserK . D.groupByRollingEither eq f1
863-- XXX Use a stream instead of a list so that we can use any container type.
865-- | Match the given sequence of elements using the given comparison function.
866--
867-- >>> Stream.parse (Parser.eqBy (==) "string") $ Stream.fromList "string"
868--
869-- >>> Stream.parse (Parser.eqBy (==) "mismatch") $ Stream.fromList "match"
870-- *** Exception: ParseError "eqBy: failed, yet to match 7 elements"
871--
872-- /Pre-release/
873--
874{-# INLINE eqBy #-}
875eqBy :: MonadCatch m => (a -> a -> Bool) -> [a] -> Parser m a ()
876eqBy cmp = D.toParserK . D.eqBy cmp
878-- | Match the input sequence with the supplied list and return it if
879-- successful.
880--
881-- /Pre-release/
882{-# INLINE list #-}
883list :: (MonadCatch m, Eq a) => [a] -> Parser m a [a]
884list xs = eqBy (==) xs $> xs
886-------------------------------------------------------------------------------
887-- nested parsers
888-------------------------------------------------------------------------------
890-- | Sequential parser application. Apply two parsers sequentially to an input
891-- stream. The input is provided to the first parser, when it is done the
892-- remaining input is provided to the second parser. If both the parsers
893-- succeed their outputs are combined using the supplied function. The
894-- operation fails if any of the parsers fail.
895--
896-- Note: This is a parsing dual of appending streams using
897-- 'Streamly.Prelude.serial', it splits the streams using two parsers and zips
898-- the results.
899--
900-- This implementation is strict in the second argument, therefore, the
901-- following will fail:
902--
903-- >>> Stream.parse (Parser.serialWith const (Parser.satisfy (> 0)) undefined) $ Stream.fromList [1]
904-- *** Exception: Prelude.undefined
905-- ...
906--
907-- Compare with 'Applicative' instance method '<*>'. This implementation allows
908-- stream fusion but has quadratic complexity. This can fuse with other
909-- operations and can be faster than 'Applicative' instance for small number
910-- (less than 8) of compositions.
911--
912-- Many combinators can be expressed using @serialWith@ and other parser
913-- primitives. Some common idioms are described below,
914--
915-- @
916-- span :: (a -> Bool) -> Fold m a b -> Fold m a b -> Parser m a b
917-- span pred f1 f2 = serialWith (,) ('takeWhile' pred f1) ('fromFold' f2)
918-- @
919--
920-- @
921-- spanBy :: (a -> a -> Bool) -> Fold m a b -> Fold m a b -> Parser m a b
922-- spanBy eq f1 f2 = serialWith (,) ('groupBy' eq f1) ('fromFold' f2)
923-- @
924--
925-- @
926-- spanByRolling :: (a -> a -> Bool) -> Fold m a b -> Fold m a b -> Parser m a b
927-- spanByRolling eq f1 f2 = serialWith (,) ('groupByRolling' eq f1) ('fromFold' f2)
928-- @
929--
930-- /Pre-release/
931--
932{-# INLINE serialWith #-}
933serialWith :: MonadCatch m
934 => (a -> b -> c) -> Parser m x a -> Parser m x b -> Parser m x c
935serialWith f p1 p2 =
936 D.toParserK $ D.serialWith f (D.fromParserK p1) (D.fromParserK p2)
938-- | Sequential parser application ignoring the output of the first parser.
939-- Apply two parsers sequentially to an input stream. The input is provided to
940-- the first parser, when it is done the remaining input is provided to the
941-- second parser. The output of the parser is the output of the second parser.
942-- The operation fails if any of the parsers fail.
943--
944-- This implementation is strict in the second argument, therefore, the
945-- following will fail:
946--
947-- >>> Stream.parse (Parser.split_ (Parser.satisfy (> 0)) undefined) $ Stream.fromList [1]
948-- *** Exception: Prelude.undefined
949-- ...
950--
951-- Compare with 'Applicative' instance method '*>'. This implementation allows
952-- stream fusion but has quadratic complexity. This can fuse with other
953-- operations, and can be faster than 'Applicative' instance for small
954-- number (less than 8) of compositions.
955--
956-- /Pre-release/
957--
958{-# INLINE split_ #-}
959split_ :: MonadCatch m => Parser m x a -> Parser m x b -> Parser m x b
960split_ p1 p2 = D.toParserK $ D.split_ (D.fromParserK p1) (D.fromParserK p2)
962-- | @teeWith f p1 p2@ distributes its input to both @p1@ and @p2@ until both
963-- of them succeed or anyone of them fails and combines their output using @f@.
964-- The parser succeeds if both the parsers succeed.
965--
966-- /Pre-release/
967--
968{-# INLINE teeWith #-}
969teeWith :: MonadCatch m
970 => (a -> b -> c) -> Parser m x a -> Parser m x b -> Parser m x c
971teeWith f p1 p2 =
972 D.toParserK $ D.teeWith f (D.fromParserK p1) (D.fromParserK p2)
974-- | Like 'teeWith' but ends parsing and zips the results, if available,
975-- whenever the first parser ends.
976--
977-- /Pre-release/
978--
979{-# INLINE teeWithFst #-}
980teeWithFst :: MonadCatch m
981 => (a -> b -> c) -> Parser m x a -> Parser m x b -> Parser m x c
982teeWithFst f p1 p2 =
983 D.toParserK $ D.teeWithFst f (D.fromParserK p1) (D.fromParserK p2)
985-- | Like 'teeWith' but ends parsing and zips the results, if available,
986-- whenever any of the parsers ends or fails.
987--
988-- /Unimplemented/
989--
990{-# INLINE teeWithMin #-}
991teeWithMin :: MonadCatch m
992 => (a -> b -> c) -> Parser m x a -> Parser m x b -> Parser m x c
993teeWithMin f p1 p2 =
994 D.toParserK $ D.teeWithMin f (D.fromParserK p1) (D.fromParserK p2)
996-- | Sequential alternative. Apply the input to the first parser and return the
997-- result if the parser succeeds. If the first parser fails then backtrack and
998-- apply the same input to the second parser and return the result.
999--
1000-- Note: This implementation is not lazy in the second argument. The following
1001-- will fail:
1003-- >>> Stream.parse (Parser.satisfy (> 0) `Parser.alt` undefined) $ Stream.fromList [1..10]
1004-- 1
1006-- Compare with 'Alternative' instance method '<|>'. This implementation allows
1007-- stream fusion but has quadratic complexity. This can fuse with other
1008-- operations and can be much faster than 'Alternative' instance for small
1009-- number (less than 8) of alternatives.
1011-- /Pre-release/
1013{-# INLINE alt #-}
1014alt :: MonadCatch m => Parser m x a -> Parser m x a -> Parser m x a
1015alt p1 p2 = D.toParserK $ D.alt (D.fromParserK p1) (D.fromParserK p2)
1017-- | Shortest alternative. Apply both parsers in parallel but choose the result
1018-- from the one which consumed least input i.e. take the shortest succeeding
1019-- parse.
1021-- /Pre-release/
1023{-# INLINE shortest #-}
1024shortest :: MonadCatch m
1025 => Parser m x a -> Parser m x a -> Parser m x a
1026shortest p1 p2 = D.toParserK $ D.shortest (D.fromParserK p1) (D.fromParserK p2)
1028-- | Longest alternative. Apply both parsers in parallel but choose the result
1029-- from the one which consumed more input i.e. take the longest succeeding
1030-- parse.
1032-- /Pre-release/
1034{-# INLINE longest #-}
1035longest :: MonadCatch m
1036 => Parser m x a -> Parser m x a -> Parser m x a
1037longest p1 p2 = D.toParserK $ D.longest (D.fromParserK p1) (D.fromParserK p2)
1039-- | Run a parser without consuming the input.
1041-- /Pre-release/
1043{-# INLINE lookAhead #-}
1044lookAhead :: MonadCatch m => Parser m a b -> Parser m a b
1045lookAhead p = D.toParserK $ D.lookAhead $ D.fromParserK p
1047-- | Takes at-most @n@ input elements.
1049-- * Stops - when the collecting parser stops.
1050-- * Fails - when the collecting parser fails.
1052-- >>> Stream.parse (Parser.takeP 4 (Parser.takeEQ 2 Fold.toList)) $ Stream.fromList [1, 2, 3, 4, 5]
1053-- [1,2]
1055-- >>> Stream.parse (Parser.takeP 4 (Parser.takeEQ 5 Fold.toList)) $ Stream.fromList [1, 2, 3, 4, 5]
1056-- *** Exception: ParseError "takeEQ: Expecting exactly 5 elements, input terminated on 4"
1058-- /Internal/
1059{-# INLINE takeP #-}
1060takeP :: MonadCatch m => Int -> Parser m a b -> Parser m a b
1061takeP i p = D.toParserK $ D.takeP i $ D.fromParserK p
1063-------------------------------------------------------------------------------
1064-- Sequential Collection
1065-------------------------------------------------------------------------------
1067-- | @concatSequence f t@ collects sequential parses of parsers in the
1068-- container @t@ using the fold @f@. Fails if the input ends or any of the
1069-- parsers fail.
1071-- This is same as 'Data.Traversable.sequence' but more efficient.
1073-- /Unimplemented/
1075{-# INLINE concatSequence #-}
1076concatSequence ::
1077 -- Foldable t =>
1078 Fold m b c -> t (Parser m a b) -> Parser m a c
1079concatSequence _f _p = undefined
1081-- | Map a 'Parser' returning function on the result of a 'Parser'.
1083-- Compare with 'Monad' instance method '>>='. This implementation allows
1084-- stream fusion but has quadratic complexity. This can fuse with other
1085-- operations and can be much faster than 'Monad' instance for small number
1086-- (less than 8) of compositions.
1088-- /Pre-release/
1090{-# INLINE concatMap #-}
1091concatMap :: MonadCatch m
1092 => (b -> Parser m a c) -> Parser m a b -> Parser m a c
1093concatMap f p = D.toParserK $ D.concatMap (D.fromParserK . f) (D.fromParserK p)
1095-------------------------------------------------------------------------------
1096-- Alternative Collection
1097-------------------------------------------------------------------------------
1099-- | @choice parsers@ applies the @parsers@ in order and returns the first
1100-- successful parse.
1102-- This is same as 'asum' but more efficient.
1104-- /Broken/
1106{-# INLINE choice #-}
1107choice ::
1108 (Functor t, Foldable t, MonadCatch m) => t (Parser m a b) -> Parser m a b
1109choice ps = D.toParserK $ D.choice $ D.fromParserK <$> ps
1111-------------------------------------------------------------------------------
1112-- Sequential Repetition
1113-------------------------------------------------------------------------------
1115-- $many
1116-- TODO "many" is essentially a Fold because it cannot fail. So it can be
1117-- downgraded to a Fold. Or we can make the return type a Fold instead and
1118-- upgrade that to a parser when needed.
1120-- | Like 'many' but uses a 'Parser' instead of a 'Fold' to collect the
1121-- results. Parsing stops or fails if the collecting parser stops or fails.
1123-- /Unimplemented/
1125{-# INLINE manyP #-}
1126manyP :: -- MonadCatch m =>
1127 Parser m a b -> Parser m b c -> Parser m a c
1128manyP _p _f = undefined -- D.toParserK $ D.manyP (D.fromParserK p) f
1130-- | Collect zero or more parses. Apply the supplied parser repeatedly on the
1131-- input stream and push the parse results to a downstream fold.
1133-- Stops: when the downstream fold stops or the parser fails.
1134-- Fails: never, produces zero or more results.
1136-- Compare with 'Control.Applicative.many'.
1138-- /Pre-release/
1140{-# INLINE many #-}
1141many :: MonadCatch m => Parser m a b -> Fold m b c -> Parser m a c
1142many p f = D.toParserK $ D.many (D.fromParserK p) f
1143-- many = countBetween 0 maxBound
1145-- Note: many1 would perhaps be a better name for this and consistent with
1146-- other names like takeWhile1. But we retain the name "some" for
1147-- compatibility.
1149-- | Collect one or more parses. Apply the supplied parser repeatedly on the
1150-- input stream and push the parse results to a downstream fold.
1152-- Stops: when the downstream fold stops or the parser fails.
1153-- Fails: if it stops without producing a single result.
1155-- @some fld parser = manyP (takeGE 1 fld) parser@
1157-- Compare with 'Control.Applicative.some'.
1159-- /Pre-release/
1161{-# INLINE some #-}
1162some :: MonadCatch m => Parser m a b -> Fold m b c -> Parser m a c
1163some p f = D.toParserK $ D.some (D.fromParserK p) f
1164-- some p f = manyP p (takeGE 1 f)
1165-- many = countBetween 1 maxBound
1167-- | @countBetween m n f p@ collects between @m@ and @n@ sequential parses of
1168-- parser @p@ using the fold @f@. Stop after collecting @n@ results. Fails if
1169-- the input ends or the parser fails before @m@ results are collected.
1171-- /Unimplemented/
1173{-# INLINE countBetween #-}
1174countBetween ::
1175 -- MonadCatch m =>
1176 Int -> Int -> Parser m a b -> Fold m b c -> Parser m a c
1177countBetween _m _n _p = undefined
1178-- countBetween m n p f = manyP (takeBetween m n f) p
1180-- | @count n f p@ collects exactly @n@ sequential parses of parser @p@ using
1181-- the fold @f@. Fails if the input ends or the parser fails before @n@
1182-- results are collected.
1184-- /Unimplemented/
1186{-# INLINE count #-}
1187count ::
1188 -- MonadCatch m =>
1189 Int -> Parser m a b -> Fold m b c -> Parser m a c
1190count n = countBetween n n
1191-- count n p f = manyP (takeEQ n f) p
1193-- | Like 'manyTill' but uses a 'Parser' to collect the results instead of a
1194-- 'Fold'. Parsing stops or fails if the collecting parser stops or fails.
1196-- We can implemnent parsers like the following using 'manyTillP':
1198-- @
1199-- countBetweenTill m n f p = manyTillP (takeBetween m n f) p
1200-- @
1202-- /Unimplemented/
1204{-# INLINE manyTillP #-}
1205manyTillP :: -- MonadCatch m =>
1206 Parser m a b -> Parser m a x -> Parser m b c -> Parser m a c
1207manyTillP _p1 _p2 _f = undefined
1208 -- D.toParserK $ D.manyTillP (D.fromParserK p1) (D.fromParserK p2) f
1210-- | @manyTill f collect test@ tries the parser @test@ on the input, if @test@
1211-- fails it backtracks and tries @collect@, after @collect@ succeeds @test@ is
1212-- tried again and so on. The parser stops when @test@ succeeds. The output of
1213-- @test@ is discarded and the output of @collect@ is accumulated by the
1214-- supplied fold. The parser fails if @collect@ fails.
1216-- Stops when the fold @f@ stops.
1218-- /Pre-release/
1220{-# INLINE manyTill #-}
1221manyTill :: MonadCatch m
1222 => Parser m a b -> Parser m a x -> Fold m b c -> Parser m a c
1223manyTill p1 p2 f =
1224 D.toParserK $ D.manyTill f (D.fromParserK p1) (D.fromParserK p2)
1226-- | @manyThen f collect recover@ repeats the parser @collect@ on the input and
1227-- collects the output in the supplied fold. If the the parser @collect@ fails,
1228-- parser @recover@ is run until it stops and then we start repeating the
1229-- parser @collect@ again. The parser fails if the recovery parser fails.
1231-- For example, this can be used to find a key frame in a video stream after an
1232-- error.
1234-- /Unimplemented/
1236{-# INLINE manyThen #-}
1237manyThen :: -- (Foldable t, MonadCatch m) =>
1238 Parser m a b -> Parser m a x -> Fold m b c -> Parser m a c
1239manyThen _parser _recover _f = undefined
1241-------------------------------------------------------------------------------
1242-- Interleaving
1243-------------------------------------------------------------------------------
1245-- To deinterleave we can chain two parsers one behind the other. The input is
1246-- given to the first parser and the input definitively rejected by the first
1247-- parser is given to the second parser.
1249-- We can either have the parsers themselves buffer the input or use the shared
1250-- global buffer to hold it until none of the parsers need it. When the first
1251-- parser returns Skip (i.e. rewind) we let the second parser consume the
1252-- rejected input and when it is done we move the cursor forward to the first
1253-- parser again. This will require a "move forward" command as well.
1255-- To implement grep we can use three parsers, one to find the pattern, one
1256-- to store the context behind the pattern and one to store the context in
1257-- front of the pattern. When a match occurs we need to emit the accumulator of
1258-- all the three parsers. One parser can count the line numbers to provide the
1259-- line number info.
1261-- | Apply two parsers alternately to an input stream. The input stream is
1262-- considered an interleaving of two patterns. The two parsers represent the
1263-- two patterns.
1265-- This undoes a "gintercalate" of two streams.
1267-- /Unimplemented/
1269{-# INLINE deintercalate #-}
1270deintercalate ::
1271 -- Monad m =>
1272 Fold m a y -> Parser m x a
1273 -> Fold m b z -> Parser m x b
1274 -> Parser m x (y, z)
1275deintercalate = undefined
1277-- | Parse items separated by a separator parsed by the supplied parser. At
1278-- least one item must be present for the parser to succeed.
1279{-# INLINE sepBy1 #-}
1280sepBy1 :: MonadCatch m =>
1281 Fold m b c -> Parser m a b -> Parser m a x -> Parser m a c
1282sepBy1 sink p sep = do
1283 x <- p
1284 f <- fromEffect $ FL.initialize sink
1285 f1 <- fromEffect $ FL.snoc f x
1286 many (sep >> p) f1
1288-- | sepBy1 or empty, does not fail.
1289{-# INLINE sepBy #-}
1290sepBy :: (MonadCatch m, Monoid c) =>
1291 Fold m b c -> Parser m a b -> Parser m a x -> Parser m a c
1292sepBy sink p sep = sepBy1 sink p sep <|> return mempty
1294-------------------------------------------------------------------------------
1295-- Interleaving a collection of parsers
1296-------------------------------------------------------------------------------
1298-- | Apply a collection of parsers to an input stream in a round robin fashion.
1299-- Each parser is applied until it stops and then we repeat starting with the
1300-- the first parser again.
1302-- /Unimplemented/
1304{-# INLINE roundRobin #-}
1305roundRobin :: -- (Foldable t, MonadCatch m) =>
1306 t (Parser m a b) -> Fold m b c -> Parser m a c
1307roundRobin _ps _f = undefined
1309-------------------------------------------------------------------------------
1310-- Repeated Alternatives
1311-------------------------------------------------------------------------------
1313-- | Keep trying a parser up to a maximum of @n@ failures. When the parser
1314-- fails the input consumed till now is dropped and the new instance is tried
1315-- on the fresh input.
1317-- /Unimplemented/
1319{-# INLINE retryMaxTotal #-}
1320retryMaxTotal :: -- (MonadCatch m) =>
1321 Int -> Parser m a b -> Fold m b c -> Parser m a c
1322retryMaxTotal _n _p _f = undefined
1324-- | Like 'retryMaxTotal' but aborts after @n@ successive failures.
1326-- /Unimplemented/
1328{-# INLINE retryMaxSuccessive #-}
1329retryMaxSuccessive :: -- (MonadCatch m) =>
1330 Int -> Parser m a b -> Fold m b c -> Parser m a c
1331retryMaxSuccessive _n _p _f = undefined
1333-- | Keep trying a parser until it succeeds. When the parser fails the input
1334-- consumed till now is dropped and the new instance is tried on the fresh
1335-- input.
1337-- /Unimplemented/
1339{-# INLINE retry #-}
1340retry :: -- (MonadCatch m) =>
1341 Parser m a b -> Parser m a b
1342retry _p = undefined