packages feed

camfort-1.0.1: src/Language/Fortran/Model/Op/Meta.hs

{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE EmptyCase             #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE GADTs                 #-}
{-# LANGUAGE LambdaCase            #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds             #-}
{-# LANGUAGE RankNTypes            #-}
{-# LANGUAGE UndecidableInstances  #-}

{-# OPTIONS_GHC -Wall #-}

{-|
For expressions over normal Fortran values that are not representable in
Fortran.

- Immutable array update ('MopWriteArr')
- Immutable data update ('MopWriteData')
- Explicit coercions ('MopCoercePrim')
-}

module Language.Fortran.Model.Op.Meta (MetaOp(..)) where

import           Data.Functor.Compose

import           Data.Vinyl                          (Rec, RMap, RApply, rmap, (<<*>>))
import           Data.Vinyl.Functor                  (Lift (..))
import           Data.Vinyl.Lens                     (RElem, rput)

import           Data.Singletons.TypeLits

import qualified Data.SBV.Dynamic                    as SBV

import           Language.Expression
import           Language.Expression.Pretty

import           Language.Fortran.Model.Op.Core.Eval
import           Language.Fortran.Model.Op.Eval
import           Language.Fortran.Model.Repr
import           Language.Fortran.Model.Types


data MetaOp t a where
  MopWriteArr
    :: D (Array i v)
    -> t (Array i v)
    -> t i
    -> t v
    -> MetaOp t (Array i v)

  {-|

  In @'MopWriteData' recD fSymb valD recVal valVal@:

  * @recD@ is the type of the record we're writing to.
  * @fSymb@ is the name of the field we're writing to.
  * @valD@ is the type of the value we're writing to.
  * @recVal@ is the original value of the record.
  * @valVal@ is the new value of the field to write to.
  -}
  MopWriteData
    :: RElem '(fname, a) fields i
    => D (Record rname fields)
    -> SSymbol fname
    -> D a
    -> t (Record rname fields)
    -> t a
    -> MetaOp t (Record rname fields)

  MopCoercePrim
    :: Prim p k b
    -> t (PrimS a)
    -> MetaOp t (PrimS b)


instance HFunctor MetaOp where
instance HTraversable MetaOp where
  htraverse f = \case
    MopWriteArr d x y z -> MopWriteArr d <$> f x <*> f y <*> f z
    MopWriteData a b c x y -> MopWriteData a b c <$> f x <*> f y
    MopCoercePrim p x -> MopCoercePrim p <$> f x


instance (MonadEvalFortran r m) => HFoldableAt (Compose m CoreRepr) MetaOp where
  hfoldMap = implHfoldMapCompose $ \case
    MopWriteArr _ arr ix val -> pure $ writeArray arr ix val
    MopWriteData _ fname _ rec val -> pure $ writeDataAt fname rec val
    MopCoercePrim p x -> coercePrim p x


instance (MonadEvalFortran r m) => HFoldableAt (Compose m HighRepr) MetaOp where
  hfoldMap = implHfoldMapCompose $ fmap HRCore . hfoldA .
    hmap (\case HRCore x -> x
                HRHigh _ -> error "impossible")


instance Pretty2 MetaOp where
  prettys2Prec p = \case
    MopWriteArr _ arr i v ->
      -- e.g. @myArrayVar[9 <- "new value"]@
      showParen (p > 9) $ prettys1Prec 10 arr .
                          showString "[" . prettys1Prec 0 i .
                          showString " <- " . prettys1Prec 0 v .
                          showString "]"
    MopWriteData _ fname _ r v ->
      showParen (p > 9) $ prettys1Prec 10 r .
      showString "{" .
      showString (withKnownSymbol fname (symbolVal fname)) .
      showString " <- " .
      prettys1Prec 0 v .
      showString "}"

    -- TODO: Consider adding visual evidence of coercion
    MopCoercePrim _ x -> prettys1Prec p x


--------------------------------------------------------------------------------
--  Write array
--------------------------------------------------------------------------------

rzip3With
  :: (RMap xs, RApply xs)
  => (forall x. f x -> g x -> h x -> i x)
  -> Rec f xs
  -> Rec g xs
  -> Rec h xs
  -> Rec i xs
rzip3With f x y z = rmap (Lift . (Lift .) . f) x <<*>> y <<*>> z

writeArray' :: CoreRepr i -> D (Array i v) -> ArrRepr i v -> CoreRepr v -> ArrRepr i v
writeArray' ixRep (DArray ixIndex@(Index _) valAV) arrRep valRep =
  case ixRep of
    CRPrim _ ixVal -> case (valAV, arrRep, valRep) of
      (ArrPrim _, ARPrim arr, CRPrim _ valVal) -> ARPrim (SBV.writeSArr arr ixVal valVal)
      (ArrData _ fieldsAV, ARData fieldsAR, CRData _ fieldsRep) ->
        ARData (rzip3With (zip3FieldsWith (writeArray' ixRep . DArray ixIndex))
                fieldsAV
                fieldsAR
                fieldsRep)


writeArray :: CoreRepr (Array i v) -> CoreRepr i -> CoreRepr v -> CoreRepr (Array i v)
writeArray (CRArray arrD arrRep) ixRep valRep =
  CRArray arrD (writeArray' ixRep arrD arrRep valRep)

--------------------------------------------------------------------------------
--  Write Data
--------------------------------------------------------------------------------

writeDataAt
  :: RElem '(fname, a) fields i
  => SSymbol fname
  -> CoreRepr (Record rname fields)
  -> CoreRepr a
  -> CoreRepr (Record rname fields)
writeDataAt fieldSymbol (CRData d dataRec) valRep =
  CRData d $ rput (Field fieldSymbol valRep) dataRec

--------------------------------------------------------------------------------
--  Coerce primitives
--------------------------------------------------------------------------------

coercePrim
  :: (MonadEvalFortran r m)
  => Prim p k b
  -> CoreRepr (PrimS a)
  -> m (CoreRepr (PrimS b))
coercePrim prim2 (CRPrim _ v) = CRPrim (DPrim prim2) <$> coercePrimSVal prim2 v