packages feed

camfort-0.905: src/Camfort/Specification/Hoare/Syntax.hs

{-# LANGUAGE DataKinds                 #-}
{-# LANGUAGE DeriveDataTypeable        #-}
{-# LANGUAGE DeriveGeneric             #-}
{-# LANGUAGE DeriveTraversable         #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleInstances         #-}
{-# LANGUAGE LambdaCase                #-}
{-# LANGUAGE MultiParamTypeClasses     #-}
{-# LANGUAGE NamedFieldPuns            #-}
{-# LANGUAGE PatternSynonyms           #-}
{-# LANGUAGE RankNTypes                #-}
{-# LANGUAGE StandaloneDeriving        #-}
{-# LANGUAGE TemplateHaskell           #-}
{-# LANGUAGE TupleSections             #-}
{-# LANGUAGE TypeFamilies              #-}

{-# OPTIONS_GHC -Wall #-}

{-|

Defines the syntax of invariant annotations. See "Camfort.Specification.Hoare"
for a high-level overview.

In this module, the word \'primitive\' is used to refer to untyped expression
syntax (as opposed to the typed expression syntax defined in
"Language.Fortran.Model.Op").

-}
module Camfort.Specification.Hoare.Syntax where

import           Data.Data

import           Control.Lens

import qualified Language.Fortran.AST       as F

import           Language.Expression.Pretty


-- * Syntax Types

-- | A type of primitive logical operators.
data PrimLogic a
  = PLAnd a a
  | PLOr a a
  | PLImpl a a
  | PLEquiv a a
  | PLNot a
  | PLLit Bool
  deriving (Typeable, Data, Show, Eq, Functor, Foldable, Traversable)


-- | Logical expressions over Fortran expressions.
data PrimFormula ann
  = PFExpr (F.Expression ann)
  | PFLogical (PrimLogic (PrimFormula ann))
  deriving (Typeable, Data, Show, Eq, Functor)


-- | Labels for the keyword used in @static_assert@ annotations.
data SpecKind
  = SpecPre
    -- ^ @static_assert pre(...)@
  | SpecPost
    -- ^ @static_assert post(...)@
  | SpecSeq
    -- ^ @static_assert seq(...)@
  | SpecInvariant
    -- ^ @static_assert invariant(...)@
  deriving (Show, Eq, Typeable, Data)


-- | A @static_assert@ annotation.
data Specification a =
  Specification
  { _specType    :: SpecKind
  , _specFormula :: a
  }
  deriving (Typeable, Data, Eq, Functor)


-- | A @decl_aux@ annotation.
data AuxDecl ann =
  AuxDecl
  { _adName :: F.Name
  , _adTy   :: F.TypeSpec ann
  }
  deriving (Typeable, Data, Show, Eq, Functor)


-- | A specification over untyped logical expressions.
type PrimSpec ann = Specification (PrimFormula ann)


-- | A @static_assert@ or @decl_aux@ annotation.
data SpecOrDecl ann =
    SodSpec (PrimSpec ann)
  | SodDecl (AuxDecl ann)
  deriving (Typeable, Data, Show, Eq, Functor)


instance Show a => Pretty (PrimFormula a) where pretty = show


instance (Pretty a) => Show (Specification a) where
  show Specification { _specType, _specFormula } =
    "Specification { " ++
    "_specType = " ++ show _specType ++ ", " ++
    "_specFormula = " ++ pretty _specFormula ++
    " }"

-- * Lenses

makeLenses ''Specification
makeLenses ''AuxDecl
makePrisms ''Specification
makePrisms ''SpecOrDecl


-- | Given a prism @p@ projecting a pair, @'refining' x p@ projects values from
-- the front left of the pair such that the right of the pair matches @x@.
--
-- >>> [1, 2, 3] ^? refining [] _Cons
-- Nothing
--
-- >>> [1] ^? refining [] _Cons
-- Just 1
--
refining :: (Eq r) => r -> APrism s t (a, r) (a, r) -> Prism s t a a
refining y p = clonePrism p . below (only y) . iso fst (, ())


-- | Match a @static_assert pre(...)@ annotation.
_SpecPre :: Prism' (Specification a) a
_SpecPre = refining SpecPre (_Specification . swapped)

-- | Match a @static_assert post(...)@ annotation.
_SpecPost :: Prism' (Specification a) a
_SpecPost = refining SpecPost (_Specification . swapped)

-- | Match a @static_assert seq(...)@ annotation.
_SpecSeq :: Prism' (Specification a) a
_SpecSeq = refining SpecSeq (_Specification . swapped)

-- | Match a @static_assert invariant(...)@ annotation.
_SpecInvariant :: Prism' (Specification a) a
_SpecInvariant = refining SpecInvariant (_Specification . swapped)