{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE Rank2Types #-}
{- |
Apply operations on symbolic arrays to physical ones.

This is an approach with no pre-defined direction of type dependencies.
-}
module LLVM.DSL.Render.Run (
   T(Cons, decons),
   postmapPlain,
   premapDSL,
   Creator,
   run,
   (*->),
   ) where

import qualified LLVM.DSL.Render.Argument as Arg
import qualified LLVM.DSL.Expression as Expr
import LLVM.DSL.Render.Argument (Creator)
import LLVM.DSL.Expression (Exp)

import qualified LLVM.Extra.Multi.Value.Marshal as Marshal

import Prelude2010
import Prelude ()



{-
Type order of 'f' and 'fdsl' is consistent with 'run',
but inconsistent with 'Arg.T'.
-}
newtype T m p fdsl f =
   Cons {decons :: (Exp p -> fdsl) -> m (Creator p -> f)}

{-
We could turn this into an 'Functor'/'fmap' instance,
however this is less descriptive and
would require to keep the current type parameter order.
-}
postmapPlain :: Functor m => (f -> g) -> T m p fdsl f -> T m p fdsl g
postmapPlain f build = Cons $ fmap (f .) . decons build

premapDSL :: (gdsl -> fdsl) -> T m p fdsl f -> T m p gdsl f
premapDSL f build = Cons $ decons build . fmap f


-- ToDo: duplicate of Argument
primitiveCreator :: a -> Creator a
primitiveCreator a = return (a, return ())

run :: (Functor m) => T m () fdsl f -> fdsl -> m f
run (Cons build) f = fmap ($ primitiveCreator ()) $ build $ const f


-- precedence like Applicative.<*>, but different associativity
infixr 4 *->

(*->) ::
   (Functor m) =>
   Arg.T a adsl ->
   (forall al. Marshal.C al => T m (p, al) fdsl f) ->
   T m p (adsl -> fdsl) (a -> f)
(*->) arg build = Cons $ \f ->
   case arg of
      Arg.Cons pass createA ->
         fmap
            (\g createP av ->
               g (do (p,finalP) <- createP
                     (pa,finalA) <- createA av
                     return ((p,pa), finalA >> finalP)))
            (decons build (Expr.uncurry $ \p -> f p . pass))