@@ -59,8 +59,9 @@ module Streamly.Internal.Data.Parser.Types
62−import Control.Exception (assert, Exception(..))
63import Control.Applicative (Alternative(..))
62import Control.Applicative (Alternative(..))
63+import Control.Exception (assert, Exception(..))
64+import Control.Monad (MonadPlus(..))
64import Control.Monad.Catch (MonadCatch, try, throwM, MonadThrow)
65import Control.Monad.Catch (MonadCatch, try, throwM, MonadThrow)
66import Fusion.Plugin.Types (Fuse(..))
67import Fusion.Plugin.Types (Fuse(..))
@@ -454,7 +455,7 @@ dieM err =
454-- Note: The implementation of '<|>' is not lazy in the second
455-- Note: The implementation of '<|>' is not lazy in the second
455-- argument. The following code will fail:
456-- argument. The following code will fail:
457−-- >>> S.parse (PR.satisfy (> 0) `<|>` undefined) $ S.fromList [1..10]
458+-- >>> S.parse (PR.satisfy (> 0) <|> undefined) $ S.fromList [1..10]
459instance MonadCatch m => Alternative (Parser m a) where
460instance MonadCatch m => Alternative (Parser m a) where
460 {-# INLINE empty #-}
461 {-# INLINE empty #-}
@@ -468,3 +469,100 @@ instance MonadCatch m => Alternative (Parser m a) where
469 {-# INLINE some #-}
470 {-# INLINE some #-}
470 some = splitSome toList
471 some = splitSome toList
473+{-# ANN type ConcatParseState Fuse #-}
474+data ConcatParseState sl p = ConcatParseL sl | ConcatParseR p
476+-- Note: The monad instance has quadratic performance complexity. It works fine
477+-- for small number of compositions but for a scalable implementation we need a
480+-- | Monad composition can be used for lookbehind parsers, we can make the
481+-- future parses depend on the previously parsed values.
483+-- If we have to parse "a9" or "9a" but not "99" or "aa" we can use the
487+-- backtracking :: MonadCatch m => PR.Parser m Char String
489+-- sequence [PR.satisfy isDigit, PR.satisfy isAlpha]
491+-- sequence [PR.satisfy isAlpha, PR.satisfy isDigit]
494+-- We know that if the first parse resulted in a digit at the first place then
495+-- the second parse is going to fail. However, we waste that information and
496+-- parse the first character again in the second parse only to know that it is
497+-- not an alphabetic char. By using lookbehind in a 'Monad' composition we can
498+-- avoid redundant work:
501+-- data DigitOrAlpha = Digit Char | Alpha Char
503+-- lookbehind :: MonadCatch m => PR.Parser m Char String
505+-- x1 \<- Digit '<$>' PR.satisfy isDigit
506+-- '<|>' Alpha '<$>' PR.satisfy isAlpha
508+-- -- Note: the parse depends on what we parsed already
510+-- Digit _ -> PR.satisfy isAlpha
511+-- Alpha _ -> PR.satisfy isDigit
513+-- return $ case x1 of
518+instance Monad m => Monad (Parser m a) where
519+ {-# INLINE return #-}
522+ -- (>>=) :: Parser m a b -> (b -> Parser m a c) -> Parser m a c
523+ {-# INLINE (>>=) #-}
524+ (Parser stepL initialL extractL) >>= func = Parser step initial extract
528+ initial = ConcatParseL <$> initialL
530+ step (ConcatParseL st) a = do
533+ Yield _ s -> Skip 0 (ConcatParseL s)
534+ Skip n s -> Skip n (ConcatParseL s)
535+ Stop n b -> Skip n (ConcatParseR (func b))
536+ Error err -> Error err
538+ step (ConcatParseR (Parser stepR initialR extractR)) a = do
543+ Yield n (ConcatParseR (Parser stepR (return s) extractR))
545+ Skip n (ConcatParseR (Parser stepR (return s) extractR))
546+ Stop n b -> Stop n b
547+ Error err -> Error err
549+ extract (ConcatParseR (Parser _ initialR extractR)) =
550+ initialR >>= extractR
552+ extract (ConcatParseL sL) = extractL sL >>= f . func
556+ f (Parser _ initialR extractR) = initialR >>= extractR
558+-- | 'mzero' is same as 'empty', it aborts the parser. 'mplus' is same as
559+-- '<|>', it selects the first succeeding parser.
563+instance MonadCatch m => MonadPlus (Parser m a) where
564+ {-# INLINE mzero #-}
567+ {-# INLINE mplus #-}