{-|
MonadPlus utilities.
For instance, they can be applied with parsing libraries.
-}
module VectorBuilder.MonadPlus
where

import VectorBuilder.Prelude
import Data.Vector (Vector)
import qualified VectorBuilder.Builder as A
import qualified VectorBuilder.Vector as B
import qualified Data.Vector.Generic as C


{-# INLINABLE many #-}
many :: (MonadPlus m, C.Vector vector element) => m element -> m (vector element)
many m =
  liftM B.build (manyBuilder m)

{-# INLINABLE manyBuilder #-}
manyBuilder :: (MonadPlus m) => m element -> m (A.Builder element)
manyBuilder m =
  loop mempty
  where
    loop !builder =
      mplus
        (do
          !element <- m
          loop (builder <> A.singleton element))
        (return builder)

{-# INLINABLE many1 #-}
many1 :: (MonadPlus m, C.Vector vector element) => m element -> m (vector element)
many1 m =
  do
    firstElement <- m
    builder <- manyBuilder m
    return (B.build (A.singleton firstElement <> builder))

{-# INLINABLE sepBy #-}
sepBy :: (MonadPlus m, C.Vector vector element) => m element -> m separator -> m (vector element)
sepBy elementM separatorM =
  mplus (sepBy1 elementM separatorM) (return C.empty)

{-# INLINABLE sepBy1 #-}
sepBy1 :: (MonadPlus m, C.Vector vector element) => m element -> m separator -> m (vector element)
sepBy1 elementM separatorM =
  do
    firstElement <- elementM
    builder <- loop (A.singleton firstElement)
    return (B.build builder)
  where
    loop builder =
      mplus
        (do
          separatorM
          !element <- elementM
          loop (builder <> A.singleton element))
        (return builder)