{-# LANGUAGE FlexibleContexts #-}
{-|

Module containing combinators to parse some indentation based
structures.

-}

module Text.Parsec.IndentParsec.Combinator
       ( blockOf
       , foldedLinesOf
       , between
       , betweenBlock
       ) where

import Text.Parsec.Prim
import Text.Parsec.IndentParsec.Prim

-- | run a given parser inside a block.
blockOf :: (Monad m, Show t, Stream s (IndentT HaskellLike m) t)
        => IndentParsecT s u m a
        -> IndentParsecT s u m a
blockOf = nest Block

-- | run a given parser inside a line fold.
foldedLinesOf :: (Monad m, Show t, Stream s (IndentT HaskellLike m) t)
              => IndentParsecT s u m a
              -> IndentParsecT s u m a
foldedLinesOf = nest LineFold


{-|

Similar to @`Text.Parsec.Combinator.between`@. However, the
@`Text.Parsec.Combinator.between`@ will not work as expected because
it will not turn off the indentation check of its input parser.

So something like

> whereClause = between lbrack rbrack bindings
> lbrack = do char '{'; spaces
> rbrack = do char '}'; spaces

will not be able to parse say

>    where {
> a = 10
> }

Use the version exported by this module instead.

-}

between :: (Monad m, Indentation i, Show t, Show i, Stream s (IndentT i m) t)
        => GenIndentParsecT i s u m open -- ^ the opening delimiter
        -> GenIndentParsecT i s u m close -- ^ the closing delimiter
        -> GenIndentParsecT i s u m a     -- ^ the contents
        -> GenIndentParsecT i s u m a

between open close p = do _ <- try open
                          a <- neglectIndent p
                          _ <- neglectIndent close
                          return a
{-|

Similar to @`between`@ but if the opening and closing delimiters are
not given, uses a block to delimit the nesting. For example, a haskell
where clause will look like

> whereClause = betweenBlock lbrack rbrack bindings
> lbrack = do char '{'; spaces
> rbrack = do char '}'; spaces

-}

betweenBlock :: (Monad m, Stream s (IndentT HaskellLike m) t, Show t)
             => IndentParsecT s u m open  -- ^ opening delimitor
             -> IndentParsecT s u m close -- ^ closing delimitor
             -> IndentParsecT s u m a -- ^ the contents parser
             -> IndentParsecT s u m a

betweenBlock  open close p = between open close p <|> blockOf p