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)