module Camfort.Specification.Stencils.CheckFrontend where
import Data.Data
import Data.Generics.Uniplate.Operations
import Control.Arrow
import Control.Monad.State.Strict
import Control.Monad.Reader
import Control.Monad.Writer.Strict hiding (Product)
import Camfort.Specification.Stencils.CheckBackend
import qualified Camfort.Specification.Stencils.Grammar as Gram
import Camfort.Specification.Stencils.Annotation
import Camfort.Specification.Stencils.Model
import Camfort.Specification.Stencils.InferenceFrontend hiding (LogLine)
import Camfort.Specification.Stencils.InferenceBackend
import Camfort.Specification.Stencils.Synthesis
import Camfort.Specification.Stencils.Syntax
import Camfort.Analysis.Annotations
import Camfort.Analysis.CommentAnnotator
import Camfort.Helpers
import qualified Language.Fortran.AST as F
import qualified Language.Fortran.Analysis as FA
import qualified Language.Fortran.Analysis.Types as FAT
import qualified Language.Fortran.Analysis.Renaming as FAR
import qualified Language.Fortran.Analysis.BBlocks as FAB
import qualified Language.Fortran.Analysis.DataFlow as FAD
import qualified Language.Fortran.Util.Position as FU
import Data.Graph.Inductive.Graph hiding (isEmpty)
import qualified Data.Map as M
import qualified Data.IntMap as IM
import qualified Data.Set as S
import Data.Maybe
import Data.List
import Debug.Trace
stencilChecking :: FAR.NameMap -> F.ProgramFile (FA.Analysis A) -> [String]
stencilChecking nameMap pf = snd . runWriter $
do
pf' <- annotateComments Gram.specParser pf
let bm = FAD.genBlockMap pf'
let bbm = FAB.genBBlockMap pf'
let sgr = FAB.genSuperBBGr bbm
let gr = FAB.superBBGrGraph sgr
let dm = FAD.genDefMap bm
let pprint = map (\(span, spec) -> show span ++ "\t" ++ spec)
let rd = FAD.reachingDefinitions dm gr
let flTo = FAD.genFlowsToGraph bm dm gr rd
let beMap = FAD.genBackEdgeMap (FAD.dominators gr) gr
let ivmap = FAD.genInductionVarMapByASTBlock beMap gr
let results = let ?flowsGraph = flTo in
let ?nameMap = nameMap
in descendBiM perProgramUnitCheck pf'
let a@(_, output) = evalState (runWriterT $ results) (([], Nothing), ivmap)
tell $ pprint output
type LogLine = (FU.SrcSpan, String)
type Checker a =
WriterT [LogLine]
(State ((RegionEnv, Maybe F.ProgramUnitName), FAD.InductionVarMapByASTBlock)) a
parseCommentToAST :: FA.Analysis A -> FU.SrcSpan -> Checker (FA.Analysis A)
parseCommentToAST ann span =
case stencilSpec (FA.prevAnnotation ann) of
Just (Left stencilComment) -> do
((regionEnv, _), _) <- get
let ?renv = regionEnv
in case synToAst stencilComment of
Left err -> error $ show span ++ ": " ++ err
Right ast -> return $ onPrev
(\ann -> ann {stencilSpec = Just (Right ast)}) ann
_ -> return ann
updateRegionEnv :: FA.Analysis A -> Checker ()
updateRegionEnv ann =
case stencilSpec (FA.prevAnnotation ann) of
Just (Right (Left regionEnv)) -> modify $ (((++) regionEnv) *** id) *** id
_ -> return ()
compareInferredToDeclared :: [([F.Name], Specification)] -> SpecDecls -> Bool
compareInferredToDeclared inferreds declareds =
all (\(names, dec) ->
all (\name ->
any (\inf -> eqByModel inf dec) (lookupAggregate inferreds name)
) names) declareds
perProgramUnitCheck :: (?nameMap :: FAR.NameMap, ?flowsGraph :: FAD.FlowsGraph A)
=> F.ProgramUnit (FA.Analysis A) -> Checker (F.ProgramUnit (FA.Analysis A))
perProgramUnitCheck p@(F.PUModule {}) = do
modify $ (id *** (const (Just $ FA.puName p))) *** id
descendBiM perBlockCheck p
perProgramUnitCheck p = descendBiM perBlockCheck p
perBlockCheck :: (?nameMap :: FAR.NameMap, ?flowsGraph :: FAD.FlowsGraph A)
=> F.Block (FA.Analysis A) -> Checker (F.Block (FA.Analysis A))
perBlockCheck b@(F.BlComment ann span _) = do
ann' <- parseCommentToAST ann span
updateRegionEnv ann'
let b' = F.setAnnotation ann' b
case (stencilSpec $ FA.prevAnnotation ann', stencilBlock $ FA.prevAnnotation ann') of
(Just (Right (Right specDecls)), Just block) ->
case block of
s@(F.BlStatement ann span _ (F.StExpressionAssign _ _ lhs rhs)) ->
case isArraySubscript lhs of
Just subs -> do
(_, ivmap) <- get
let realName v = v `fromMaybe` (v `M.lookup` ?nameMap)
let lhsN = maybe [] id (neighbourIndex ivmap subs)
let correctNames = map (\(names, spec) -> (map realName names, spec))
let inferred = correctNames . fst . runWriter $ genSpecifications ivmap lhsN [s]
if compareInferredToDeclared inferred specDecls
then tell [ (span, "Correct.") ]
else tell [ (span, "Not well specified:\n\t\t expecting: "
++ pprintSpecDecls specDecls
++ "\t\t inferred: " ++ pprintSpecDecls inferred) ]
return $ b'
Nothing -> return $ b'
_ -> return $ b'
(F.BlDo ann span _ mDoSpec body) -> do
return $ b'
_ -> return $ b'
_ -> return b'
perBlockCheck b@(F.BlDo ann span _ mDoSpec body) = do
mapM_ (descendBiM perBlockCheck) body
return b
perBlockCheck b = do
updateRegionEnv . F.getAnnotation $ b
mapM_ (descendBiM perBlockCheck) $ children b
return b