packages feed

camfort-0.905: src/Camfort/Specification/Units/Annotation.hs

{- |
Module      :  Camfort.Specification.Units.Annotation
Description :  Annotation with unit information.
Copyright   :  (c) 2017, Dominic Orchard, Andrew Rice, Mistral Contrastin, Matthew Danish
License     :  Apache-2.0

Maintainer  :  dom.orchard@gmail.com
Stability   :  experimental

Defines the 'UnitAnnotation' datatype, which is used for annotating a
'ProgramFile' with units information.
-}

{-# LANGUAGE DeriveDataTypeable    #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}

module Camfort.Specification.Units.Annotation
  (
    -- * Annotation Type
    UA
  , mkUnitAnnotation
  , prevAnnotation
  , unitBlock
  , unitConstraint
  , unitInfo
  , unitPU
  , unitSpec
    -- * Helpers
  , cleanLinks
  , getConstraint
  , getUnitInfo
  , maybeSetUnitConstraintF2
  , maybeSetUnitInfo
  , maybeSetUnitInfoF2
  , setConstraint
  , setUnitInfo
  ) where

import Data.Data (Data, Typeable)
import Data.Generics.Uniplate.Operations (transformBi)

import qualified Language.Fortran.Analysis as FA
import qualified Language.Fortran.AST      as F

import qualified Camfort.Analysis.Annotations             as Ann
import           Camfort.Analysis.CommentAnnotator
  (ASTEmbeddable(..), Linkable(..))
import qualified Camfort.Specification.Units.Environment  as E
import qualified Camfort.Specification.Units.Parser.Types as P

-- The annotation on the AST used for solving units.
data UnitAnnotation a = UnitAnnotation {
    prevAnnotation :: a,
    unitSpec       :: Maybe P.UnitStatement,
    unitConstraint :: Maybe E.Constraint,
    unitInfo       :: Maybe E.UnitInfo,
    unitBlock      :: Maybe (F.Block (FA.Analysis (UnitAnnotation a))), -- ^ linked variable declaration
    unitPU         :: Maybe (F.ProgramUnit (FA.Analysis (UnitAnnotation a))) -- ^ linked program unit
  } deriving (Data, Typeable, Show)

mkUnitAnnotation :: a -> UnitAnnotation a
mkUnitAnnotation a = UnitAnnotation a Nothing Nothing Nothing Nothing Nothing

-- Convenience name for a common annotation type.
type UA = FA.Analysis (UnitAnnotation Ann.A)

-- Instances for embedding parsed specifications into the AST
instance ASTEmbeddable UA P.UnitStatement where
  annotateWithAST ann ast =
    Ann.onPrev (\ann' -> ann' { unitSpec = Just ast }) ann

-- Link annotation comments to declaration statements
instance Linkable UA where
  link ann (b@(F.BlStatement _ _ _ F.StDeclaration {})) =
      Ann.onPrev (\ann' -> ann' { unitBlock = Just b }) ann
  link ann _ = ann
  linkPU ann pu@F.PUFunction{} =
      Ann.onPrev (\ann' -> ann' { unitPU = Just pu }) ann
  linkPU ann pu@F.PUSubroutine{} =
      Ann.onPrev (\ann' -> ann' { unitPU = Just pu }) ann
  linkPU ann _ = ann

-- | Extract the unit info from a given annotated piece of AST.
getUnitInfo :: F.Annotated f => f UA -> Maybe E.UnitInfo
getUnitInfo = unitInfo . FA.prevAnnotation . F.getAnnotation

-- | Extract the constraint from a given annotated piece of AST.
getConstraint :: F.Annotated f => f UA -> Maybe E.Constraint
getConstraint = unitConstraint . FA.prevAnnotation . F.getAnnotation

-- | Set the UnitInfo field on a piece of AST.
setUnitInfo :: F.Annotated f => E.UnitInfo -> f UA -> f UA
setUnitInfo info = F.modifyAnnotation (Ann.onPrev (\ ua -> ua { unitInfo = Just info }))

-- | Set the Constraint field on a piece of AST.
setConstraint :: F.Annotated f => E.Constraint -> f UA -> f UA
setConstraint (E.ConConj []) = id
setConstraint c              =
  F.modifyAnnotation (Ann.onPrev (\ ua -> ua { unitConstraint = Just c }))

--------------------------------------------------

-- Various helper functions for setting the UnitInfo or Constraint of a piece of AST
maybeSetUnitInfo :: F.Annotated f => Maybe E.UnitInfo -> f UA -> f UA
maybeSetUnitInfo u e  = maybe e (`setUnitInfo` e) u

maybeSetUnitInfoF2 :: F.Annotated f => (a -> b -> E.UnitInfo) -> Maybe a -> Maybe b -> f UA -> f UA
maybeSetUnitInfoF2 f u1 u2 e = maybe e (`setUnitInfo` e) (f <$> u1 <*> u2)

maybeSetUnitConstraintF2 :: F.Annotated f => (a -> b -> E.Constraint) -> Maybe a -> Maybe b -> f UA -> f UA
maybeSetUnitConstraintF2 f u1 u2 e = maybe e (`setConstraint` e) (f <$> u1 <*> u2)

cleanLinks :: F.ProgramFile UA -> F.ProgramFile UA
cleanLinks = transformBi
  (\a -> a { unitPU    = Nothing
           , unitBlock = Nothing
           , unitSpec  = Nothing
           } :: UnitAnnotation Ann.A)