Skip to content

Commit 73a5ce7

Browse files
Create a NonFailing parsers module
1 parent 6dd122a commit 73a5ce7

File tree

5 files changed

+219
-176
lines changed

5 files changed

+219
-176
lines changed

core/src/Streamly/Internal/Data/Array/Stream/Fold/Foreign.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
7070
import qualified Streamly.Internal.Data.Array.Unboxed as Array
7171
import qualified Streamly.Internal.Data.Fold as Fold
7272
import qualified Streamly.Internal.Data.Parser.ParserD as ParserD
73-
import qualified Streamly.Internal.Data.Parser.ParserD.Type as ParserD
73+
import qualified Streamly.Internal.Data.Parser.ParserD.NonFailing as ParserD
7474
import qualified Streamly.Internal.Data.Parser as Parser
7575

7676
import Prelude hiding (concatMap, take)

core/src/Streamly/Internal/Data/Parser/ParserD.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
214214
import Prelude hiding
215215
(any, all, take, takeWhile, sequence, concatMap, maybe, either, span
216216
, zip, filter)
217+
import Streamly.Internal.Data.Parser.ParserD.NonFailing
217218
import Streamly.Internal.Data.Parser.ParserD.Tee
218219
import Streamly.Internal.Data.Parser.ParserD.Type
219220

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
-- |
2+
-- Module : Streamly.Internal.Data.Parser.ParserD.NonFailing
3+
-- Copyright : (c) 2020 Composewell Technologies
4+
-- License : BSD-3-Clause
5+
-- Maintainer : [email protected]
6+
-- Stability : experimental
7+
-- Portability : GHC
8+
--
9+
-- Parsers that can backtrack but never fail. Because they cannot fail they
10+
-- cannot have an alternative instance. This enables us to write more
11+
-- efficient sequential parsers, because we do not need buffering for the
12+
-- failure case.
13+
--
14+
-- These parsers lie between parsers that can fail and folds. They are more
15+
-- powerful than folds because they add the backtracking capability to folds.
16+
-- However, they are less powerful than parsers that can fail.
17+
18+
module Streamly.Internal.Data.Parser.ParserD.NonFailing
19+
(
20+
noErrorUnsafeSplit_
21+
, noErrorUnsafeSplitWith
22+
, noErrorUnsafeConcatMap
23+
)
24+
where
25+
26+
import Control.Monad.Catch (throwM, MonadThrow)
27+
import Streamly.Internal.Data.Parser.ParserD.Type
28+
( Initial(..), Step(..), Parser(..), SeqParseState(..), SeqAState(..)
29+
, ConcatParseState(..), ParseError(..)
30+
)
31+
32+
import Prelude hiding (concatMap, filter)
33+
--
34+
-- $setup
35+
-- >>> :m
36+
-- >>> :set -package streamly
37+
-- >>> import Control.Applicative ((<|>))
38+
-- >>> import Prelude hiding (concatMap)
39+
-- >>> import qualified Streamly.Prelude as Stream
40+
-- >>> import qualified Streamly.Internal.Data.Stream.IsStream as Stream (parse)
41+
-- >>> import qualified Streamly.Internal.Data.Parser as Parser
42+
-- >>> import qualified Streamly.Internal.Data.Parser.ParserD as ParserD
43+
44+
-- | Works correctly only if the first parser is guaranteed to never fail.
45+
{-# INLINE noErrorUnsafeSplitWith #-}
46+
noErrorUnsafeSplitWith :: Monad m
47+
=> (a -> b -> c) -> Parser m x a -> Parser m x b -> Parser m x c
48+
noErrorUnsafeSplitWith func (Parser stepL initialL extractL)
49+
(Parser stepR initialR extractR) =
50+
Parser step initial extract
51+
52+
where
53+
54+
initial = do
55+
resL <- initialL
56+
case resL of
57+
IPartial sl -> return $ IPartial $ SeqParseL sl
58+
IDone bl -> do
59+
resR <- initialR
60+
return $ case resR of
61+
IPartial sr -> IPartial $ SeqParseR (func bl) sr
62+
IDone br -> IDone (func bl br)
63+
IError err -> IError err
64+
IError err -> return $ IError err
65+
66+
-- Note: For the composed parse to terminate, the left parser has to be
67+
-- a terminating parser returning a Done at some point.
68+
step (SeqParseL st) a = do
69+
r <- stepL st a
70+
case r of
71+
-- Assume that the first parser can never fail, therefore we do not
72+
-- need to keep the input for backtracking.
73+
Partial n s -> return $ Partial n (SeqParseL s)
74+
Continue n s -> return $ Continue n (SeqParseL s)
75+
Done n b -> do
76+
res <- initialR
77+
return
78+
$ case res of
79+
IPartial sr -> Partial n $ SeqParseR (func b) sr
80+
IDone br -> Done n (func b br)
81+
IError err -> Error err
82+
Error err -> return $ Error err
83+
84+
step (SeqParseR f st) a = do
85+
r <- stepR st a
86+
return $ case r of
87+
Partial n s -> Partial n (SeqParseR f s)
88+
Continue n s -> Continue n (SeqParseR f s)
89+
Done n b -> Done n (f b)
90+
Error err -> Error err
91+
92+
extract (SeqParseR f sR) = fmap f (extractR sR)
93+
extract (SeqParseL sL) = do
94+
rL <- extractL sL
95+
res <- initialR
96+
case res of
97+
IPartial sR -> do
98+
rR <- extractR sR
99+
return $ func rL rR
100+
IDone rR -> return $ func rL rR
101+
IError err -> error $ "noErrorUnsafeSplitWith: cannot use a "
102+
++ "failing parser. Parser failed with: " ++ err
103+
104+
{-# INLINE noErrorUnsafeSplit_ #-}
105+
noErrorUnsafeSplit_ :: MonadThrow m => Parser m x a -> Parser m x b -> Parser m x b
106+
noErrorUnsafeSplit_ (Parser stepL initialL extractL) (Parser stepR initialR extractR) =
107+
Parser step initial extract
108+
109+
where
110+
111+
initial = do
112+
resL <- initialL
113+
case resL of
114+
IPartial sl -> return $ IPartial $ SeqAL sl
115+
IDone _ -> do
116+
resR <- initialR
117+
return $ case resR of
118+
IPartial sr -> IPartial $ SeqAR sr
119+
IDone br -> IDone br
120+
IError err -> IError err
121+
IError err -> return $ IError err
122+
123+
-- Note: For the composed parse to terminate, the left parser has to be
124+
-- a terminating parser returning a Done at some point.
125+
step (SeqAL st) a = do
126+
-- Important: Please do not use Applicative here. Applicative somehow
127+
-- caused the next action to run many times in the "tar" parsing code,
128+
-- not sure why though.
129+
resL <- stepL st a
130+
case resL of
131+
Partial n s -> return $ Partial n (SeqAL s)
132+
Continue n s -> return $ Continue n (SeqAL s)
133+
Done n _ -> do
134+
initR <- initialR
135+
return $ case initR of
136+
IPartial s -> Partial n (SeqAR s)
137+
IDone b -> Done n b
138+
IError err -> Error err
139+
Error err -> return $ Error err
140+
141+
step (SeqAR st) a =
142+
(\case
143+
Partial n s -> Partial n (SeqAR s)
144+
Continue n s -> Continue n (SeqAR s)
145+
Done n b -> Done n b
146+
Error err -> Error err) <$> stepR st a
147+
148+
extract (SeqAR sR) = extractR sR
149+
extract (SeqAL sL) = do
150+
_ <- extractL sL
151+
res <- initialR
152+
case res of
153+
IPartial sR -> extractR sR
154+
IDone rR -> return rR
155+
IError err -> throwM $ ParseError err
156+
157+
{-# INLINE noErrorUnsafeConcatMap #-}
158+
noErrorUnsafeConcatMap :: MonadThrow m =>
159+
(b -> Parser m a c) -> Parser m a b -> Parser m a c
160+
noErrorUnsafeConcatMap func (Parser stepL initialL extractL) =
161+
Parser step initial extract
162+
163+
where
164+
165+
{-# INLINE initializeR #-}
166+
initializeR (Parser stepR initialR extractR) = do
167+
resR <- initialR
168+
return $ case resR of
169+
IPartial sr -> IPartial $ ConcatParseR stepR sr extractR
170+
IDone br -> IDone br
171+
IError err -> IError err
172+
173+
initial = do
174+
res <- initialL
175+
case res of
176+
IPartial s -> return $ IPartial $ ConcatParseL s
177+
IDone b -> initializeR (func b)
178+
IError err -> return $ IError err
179+
180+
{-# INLINE initializeRL #-}
181+
initializeRL n (Parser stepR initialR extractR) = do
182+
resR <- initialR
183+
return $ case resR of
184+
IPartial sr -> Partial n $ ConcatParseR stepR sr extractR
185+
IDone br -> Done n br
186+
IError err -> Error err
187+
188+
step (ConcatParseL st) a = do
189+
r <- stepL st a
190+
case r of
191+
Partial n s -> return $ Partial n (ConcatParseL s)
192+
Continue n s -> return $ Continue n (ConcatParseL s)
193+
Done n b -> initializeRL n (func b)
194+
Error err -> return $ Error err
195+
196+
step (ConcatParseR stepR st extractR) a = do
197+
r <- stepR st a
198+
return $ case r of
199+
Partial n s -> Partial n $ ConcatParseR stepR s extractR
200+
Continue n s -> Continue n $ ConcatParseR stepR s extractR
201+
Done n b -> Done n b
202+
Error err -> Error err
203+
204+
{-# INLINE extractP #-}
205+
extractP (Parser _ initialR extractR) = do
206+
res <- initialR
207+
case res of
208+
IPartial s -> extractR s
209+
IDone b -> return b
210+
IError err -> throwM $ ParseError err
211+
212+
extract (ConcatParseR _ s extractR) = extractR s
213+
extract (ConcatParseL sL) = extractL sL >>= extractP . func

0 commit comments

Comments
 (0)