packages feed

camfort-1.0: src/Language/Fortran/Model/Repr.hs

{-# LANGUAGE DataKinds                  #-}
{-# LANGUAGE DeriveDataTypeable         #-}
{-# LANGUAGE EmptyCase                  #-}
{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE FunctionalDependencies     #-}
{-# LANGUAGE GADTs                      #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE KindSignatures             #-}
{-# LANGUAGE LambdaCase                 #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE PolyKinds                  #-}
{-# LANGUAGE RankNTypes                 #-}
{-# LANGUAGE ScopedTypeVariables        #-}

{-# OPTIONS_GHC -Wall #-}


{-|

Symbolic representations of Fortran values, for symbolic reasoning.

There is a distinction between core representations ('CoreRepr') and high-level
representations ('HighRepr'). 'CoreRepr' represents any @a@ such that @'D' a@
exists; i.e. anything with a corresponding Fortran type. 'HighRepr' is a
superset of 'CoreRepr'. It represents Fortran types, and also higher-level types
that facilitate reasoning. There is more information about this distinction in
"Language.Fortran.Model.Op".

-}
module Language.Fortran.Model.Repr where

import           Data.Int                        (Int16, Int32, Int64, Int8)
import           Data.Word                       (Word8)

import           Data.Functor.Compose

import           Data.SBV
import           Data.SBV.Dynamic
import           Data.SBV.Internals              (SBV (..))

import           Data.Vinyl                      hiding (Field)

import           Language.Expression
import           Language.Expression.Prop

import           Language.Fortran.Model.Repr.Prim
import           Language.Fortran.Model.Types

--------------------------------------------------------------------------------
-- * Core Fortran Representations

{-|

Symbolic representations of Fortran values, using "Data.SBV.Dynamic".

-}
data CoreRepr a where
  CRPrim
    :: D (PrimS a)
    -> SVal
    -> CoreRepr (PrimS a)

  CRArray
    :: D (Array i a)
    -> ArrRepr i a
    -> CoreRepr (Array i a)

  CRData
    :: D (Record name fs)
    -> Rec (Field CoreRepr) fs
    -> CoreRepr (Record name fs)

{-|

Symbolic respresentations of Fortran arrays. SBV arrays can only contain basic
values, so in order to represent arrays of derived data types, we use multiple
flat arrays, one for each basic field. Nested derived data types are recursively
expanded.

Arrays of arrays are not yet supported.

-}
data ArrRepr i a where
  -- | A primitive type is represented by a flat 'SArr'.
  ARPrim :: SArr -> ArrRepr i (PrimS a)

  -- | A derived data type is represented by a record of 'ArrRepr's over the
  -- record's fields.
  ARData :: Rec (Field (ArrRepr i)) fs -> ArrRepr i (Record name fs)


--------------------------------------------------------------------------------
-- * High-level data representations

{-|

Symbolic representations of Fortran values plus types in the higher-level
meta-language (see "Language.Fortran.Op" for more information).

-}
data HighRepr a where
  HRCore :: CoreRepr a -> HighRepr a
  HRHigh :: SBV a -> HighRepr a

instance HFoldableAt HighRepr LogicOp where
  hfoldMap = implHfoldMap $ \case
    LogLit x     -> HRHigh (fromBool x)
    LogNot x     -> HRHigh . sNot . getHrBool $ x
    LogAnd x y   -> appBinop (.&&) x y
    LogOr x y    -> appBinop (.||) x y
    LogImpl x y  -> appBinop (.=>) x y
    LogEquiv x y -> appBinop (.<=>) x y

    where
      appBinop (g :: SBool -> SBool -> SBool) x y =
        HRHigh $ g (getHrBool x) (getHrBool y)
      getHrBool :: HighRepr Bool -> SBool
      getHrBool (HRHigh x) = x
      getHrBool (HRCore x) = case x of -- I.e. absurd

instance (Monad m) => HFoldableAt (Compose m HighRepr) LogicOp where
  hfoldMap = implHfoldMapCompose (pure . hfold)

--------------------------------------------------------------------------------
-- *  Lifting Fortran types to high-level representations

-- | Provides conversions between symbolic representations of core Fortran
-- values and their corresponding high-level types.
class (SymVal a) => LiftD b a | b -> a where
  -- | Go from a core value to the corresponding high-level value.
  liftD :: b -> a
  -- | Go from a symbolic core value to the corresponding symbolic high-level
  -- value.
  liftDRepr :: PrimReprHandlers -> HighRepr b -> HighRepr a

liftDInt :: PrimReprHandlers -> HighRepr (PrimS a) -> HighRepr Integer
liftDInt _ (HRCore (CRPrim _ x)) = HRHigh (SBV (svFromIntegral KUnbounded x))
liftDInt _ _ = error "impossible"


liftDReal :: PrimReprHandlers -> HighRepr (PrimS a) -> HighRepr AlgReal
liftDReal env (HRCore (CRPrim (DPrim prim) x)) =
  HRHigh $ case primSBVKind prim env of
              KFloat -> fromSFloat sRTZ (SBV x)
              KDouble -> fromSDouble sRTZ (SBV x)
              KReal -> SBV x
              k -> error $ "liftDReal: can't convert something of kind " ++
                   show k ++ " to a real"
liftDReal _ _ = error "impossible"

liftDBool :: PrimReprHandlers -> HighRepr (PrimS a) -> HighRepr Bool
liftDBool _ (HRCore (CRPrim _ x)) = HRHigh (SBV (x `svGreaterThan` svFalse))
liftDBool _ _ = error "impossible"


instance LiftD (PrimS Int8) Integer where
  liftD = fromIntegral . getPrimS
  liftDRepr = liftDInt
instance LiftD (PrimS Int16) Integer where
  liftD = fromIntegral . getPrimS
  liftDRepr = liftDInt
instance LiftD (PrimS Int32) Integer where
  liftD = fromIntegral . getPrimS
  liftDRepr = liftDInt
instance LiftD (PrimS Int64) Integer where
  liftD = fromIntegral . getPrimS
  liftDRepr = liftDInt

instance LiftD (PrimS Float) AlgReal where
  liftD = realToFrac . getPrimS
  liftDRepr = liftDReal
instance LiftD (PrimS Double) AlgReal where
  liftD = realToFrac . getPrimS
  liftDRepr = liftDReal

instance LiftD (PrimS Bool8) Bool where
  liftD = (> 0) . getBool8 . getPrimS
  liftDRepr = liftDBool
instance LiftD (PrimS Bool16) Bool where
  liftD = (> 0) . getBool16 . getPrimS
  liftDRepr = liftDBool
instance LiftD (PrimS Bool32) Bool where
  liftD = (> 0) . getBool32 . getPrimS
  liftDRepr = liftDBool
instance LiftD (PrimS Bool64) Bool where
  liftD = (> 0) . getBool64 . getPrimS
  liftDRepr = liftDBool

instance LiftD (PrimS Char8) Word8 where
  liftD = getChar8 . getPrimS
  liftDRepr _ (HRCore (CRPrim _ x)) = HRHigh (sFromIntegral (SBV x :: SBV Word8))
  liftDRepr _ _ =
    error "liftDRepr: a 'PrimS Char8' has a non-primitive representation"

--------------------------------------------------------------------------------
-- * Combinators

-- | Any type that has a core representation has a corresponding Fortran type.
coreReprD :: CoreRepr a -> D a
coreReprD = \case
  CRPrim d _  -> d
  CRArray d _ -> d
  CRData d _  -> d