{-# LANGUAGE ExistentialQuantification,
             ScopedTypeVariables,
             FlexibleInstances #-}

-- | This module contains the additional data types, instance definitions and functions to run parsers in an interleaved way.
--   If all the interleaved parsers recognise a single connected piece of the input text this incorporates the permutation parsers.
--   For some examples see the module "Text.ParserCombinators.UU.Demo.MergeAndPermute".

module Control.Applicative.Interleaved   
(   -- * Classes
    Splittable (..),
    -- * Types
    Gram (..),
    Alt (..),
    -- * Functions
    mkG,
    mkP,
    (<<||>),
    (<||>),
    sepBy,
    gmList,
   -- * Modules
    module Control.Applicative,
    module Data.Monoid
  ) where

-- import Text.ParserCombinators.UU.Core
import Control.Applicative
import Data.Monoid


infixl 4  <||>
infixl 4  <<||> 

-- * The data type `Gram`
-- | Since we want to get access to the individual parsers which recognise a consecutive 
--   piece of the input text we define a new data type, which lifts the underlying parsers 
--   to the grammatical level, so they can be transformed, manipulated, and run in a piecewise way.
--   `Gram` is defined in such a way that we can always access the first parsers to be ran from such a structure.
--   We require that all the `Alt`s do not recognise the empty string.
--   These should be covered by the `Maybe` in the `Gram` constructor.

data Gram f a =             Gram   [Alt f a]    (Maybe a) 
data Alt  f a =  forall b . Seq    (f (b -> a)) (Gram f b) 
              |  forall b.  Bind   (f b)        (b -> Gram f a)

-- * The requirement that we can split of a possible empty part
class Splittable f where
  getNonPure    :: f a  -> Maybe (f  a)       
  getPure       :: f a  -> Maybe     a

-- * Grammars can be used as a monoid using the <||> combinator to combine them and (.) for composing  results

instance Functor f => Monoid (Gram f (r -> r)) where
  mappend p q = (.) <$> p <||> q
  mempty      = empty

instance (Show a) => Show (Gram f a) where
  show (Gram l ma) = "Gram " ++ show  (length l) ++ " " ++ show ma 

-- | The function `mkGram` splits a simple parser into the possibly empty part and the non-empty part.
--   The non-empty part recognises a consecutive part of the input.
--   Here we use the functions `getOneP` and `getZeroP` which are provided in the uu-parsinglib package,
--   but they could easily be provided by other packages too.



mkG::  (Splittable f, Functor f) => f a -> Gram f a
mkG p =  Gram   (maybe [] (\p -> [(const <$> p) `Seq` pure ()]) (getNonPure p)) 
                (getPure p)


-- * Class instances for Gram
-- | We define instances for the data type `Gram` for `Functor`, `Applicative`,  `Alternative` and `ExtAlternative`
instance Functor f => Functor (Gram f) where
  fmap f (Gram alts e) = Gram (map (f <$>) alts) (f <$> e)

instance Functor f => Functor (Alt f) where
  fmap a2c (fb2a `Seq`  gb)   = ((a2c.) <$> fb2a) `Seq`  gb
  fmap a2c (fb   `Bind` b2ga) = fb                `Bind` (\b -> fmap a2c (b2ga b))

-- |  The function `<<||>` is a special version of `<||>`, which only starts a new instance of its right operand when the left operand cannot proceed.
--   This is used in the function 'pmMany', where we want to merge as many instances of its argument, but no more than that.

(<<||>):: Functor f => Gram f (b->a) -> Gram f b -> Gram f a
gb2a@(Gram lb2a eb2a) <<||> ~gb@(Gram _ eb)
   = Gram ( map (`fwdby` gb) lb2a)   (eb2a <*> eb)
     where (fc2b2a `Seq`   gc)      `fwdby` gb = (uncurry <$> fc2b2a) `Seq` ((,)  <$> gc <||> gb)
           (fc     `Bind`  c2gb2a)  `fwdby` gb =  fc `Bind` (\ c ->  c2gb2a c <||> gb)  


-- | The function `<||>` is the merging equivalent of `<*>`. Instead of running its two arguments consecutively, 
--   the input is split into parts which serve as input for the left operand and parts which are served to the right operand. 

fb2a <||> fb = fb2a <<||> fb <|> flip ($) <$> fb <<||> fb2a

-- | The left hand side operand is gradually transformed so we get access to its first component
instance Functor f => Applicative (Gram f) where
  pure a = Gram [] (Just a)
  Gram lb2a mb2a  <*> ~gb@(Gram lb mb) 
    =   Gram  (map (`fwdby` gb) lb2a ++ [b2a <$> fb | Just b2a <- [mb2a], fb <- lb]) (mb2a <*> mb)
        where (fc2b2a `Seq`  gc) `fwdby` gb = (uncurry <$> fc2b2a)  `Seq`  ((,) <$> gc <*> gb)
              (fc `Bind` c2gb2a) `fwdby` gb = fc  `Bind` (\b -> c2gb2a b               <*> gb)


instance Functor f => Alternative (Gram f) where
  empty                     = Gram [] Nothing
  Gram ps pe <|> Gram qs qe = Gram (ps++qs) (pe <|> qe)


-- * `Gram` is a `Monad`
instance  Functor f =>  Monad (Gram f) where
  return a = Gram [] (Just a)
  Gram lb mb >>= b2g_a = 
     let bindto :: Functor f => Alt f b -> (b -> Gram f a) -> Alt f a
         (f_c2b `Seq`  g_c)   `bindto` b2g_a = f_c2b `Bind` \ c2b  -> c2b <$> g_c   >>= b2g_a
         (f_c   `Bind` c2g_b) `bindto` b2g_a = f_c   `Bind` \ c    -> c2g_b c       >>= b2g_a
         la = map (`bindto` b2g_a) lb
     in case mb of
        Nothing -> Gram la Nothing
        Just b  -> let Gram lra ma =  b2g_a b
                   in  Gram (la ++ lra) ma
 


-- | 'mkParser' converts a `Gram`mar back into a parser, which can subsequenly be run.
mkP :: (Monad f, Applicative f, Alternative f) => Gram f a -> f a
mkP (Gram l_a m_a) = foldr  (<|>) (maybe empty pure m_a) 
                                  (map mkP_Alt l_a)
   where  mkP_Alt (f_b2a  `Seq`  g_b  )   = f_b2a  <*>    mkP g_b
          mkP_Alt (f_b    `Bind` b2g_a)   = f_b    >>=   (mkP . b2g_a)


-- | `sepBy` is like `mkP`, with the additional feature that we require separators between the components. Probably only useful in the permuting case.
sepBy :: (Monad f, Applicative f, Alternative f) =>  Gram f a -> f b -> f a
sepBy  g sep = mkP (insertSep sep g)

insertSep :: (Applicative f) => f b -> Gram f a -> Gram f a
insertSep sep (Gram na ea :: Gram f a) = Gram (map insertSepInAlt na) ea
   where insertSepInAlt (fb2a `Seq` gb   ) = fb2a `Seq` prefixSepInGram gb
         insertSepInAlt (fc   `Bind` c2ga) = fc `Bind` (insertSep sep . c2ga)
         prefixSepInGram (Gram na ne) = Gram (map prefixSepInAlt na) ne
         prefixSepInAlt ::  Alt f b -> Alt f b
         prefixSepInAlt (fb2a `Seq` gb)   = (sep *> fb2a) `Seq` prefixSepInGram  gb

-- | Run a sufficient number of  @p@'s in a merged fashion, but no more than necessary!!
gmList :: Functor f => Gram f a -> Gram f [a]
gmList p = let pm = ( (:) <$> p <<||> pm ) <|> pure [] in pm