packages feed

camfort-0.615: Transformation/CommonBlockElimToCalls.hs

module Transformation.CommonBlockElimToCalls where

import Control.Monad
import Control.Monad.State.Lazy

import Data.Generics.Uniplate.Operations

import Data.List

import Language.Fortran
import Language.Haskell.Syntax (SrcLoc(..))

import Helpers
import Traverse
import Analysis.Annotations
import Analysis.Syntax
import Analysis.Types
import Transformation.Syntax
import Transformation.CommonBlockElim

{- This is somewhat experimental and incomplete -}

-- Top-level functions for eliminating common blocks in a set of files
commonElimToCalls :: Directory -> [(Filename, Program A)] -> (Report, [(Filename, Program A)])

-- Eliminates common blocks in a program directory (and convert to calls)
commonElimToCalls d ps = let (ps', (r, cg)) = runState (analyseCommons ps) ("", [])
                             (r', ps'') = mapM (introduceCalls cg) ps'
                         in (r ++ r', ps'')

{-Extending calls version-}
introduceCalls :: [TLCommon A] -> (Filename, Program A) -> (Report, (Filename, Program A))
introduceCalls cenv (fname, ps) = do ps' <- mapM (transformBiM commonElim) ps
                                     -- ps'' <- mapM (transformBiM commonElim'') ps'
                                     return (fname, ps')

              where commonElim s@(Sub a sp mbt (SubName a' moduleName) (Arg p arg asp) b) = 
                        
                         let commons = lookups moduleName (lookups fname cenv) 
                             sortedC = sortBy cmpTConBNames commons
                             tArgs = extendArgs (nonNullArgs arg) asp (concatMap snd sortedC)
                             ra = p { refactored = Just (fst sp) }
                             arg' = Arg unitAnnotation (ASeq unitAnnotation arg tArgs) asp
                             a' = a -- { pRefactored = Just sp }
                             r = fname ++ (show $ srcLineCol $ snd asp) ++ ": changed common variables to parameters\n"
                         in do b' <- transformBiM (extendCalls fname moduleName cenv) b
                               (r, Sub a' sp mbt (SubName a' moduleName) arg' b')

                    commonElim s = --case (getSubName s) of
                                   --    Just n -> transformBiM (extendCalls fname n cenv) s
                                   --    Nothing -> 
                                                  transformBiM r s 
                                                    where r :: ProgUnit A -> (Report, ProgUnit A)
                                                          r p = case getSubName p of
                                                                  Just n -> transformBiM (extendCalls fname n cenv) p
                                                                  Nothing -> return p


extendCalls :: String -> String -> [TLCommon A] -> Fortran A -> (Report, Fortran A)
extendCalls fname localSub cenv f@(Call p sp v@(Var _ _ ((VarName _ n, _):_)) (ArgList ap arglist)) =
        let commons = lookups n (map snd cenv)
            targetCommonNames = map fst (sortBy cmpTConBNames commons)

            localCommons = lookups localSub (lookups fname cenv)
            localCommons' = sortBy cmpTConBNames localCommons

            p' = p { refactored = Just $ toCol0 $ fst sp }
            ap' = ap { refactored = Just $ fst sp } 

            arglist' = toArgList p' sp (select targetCommonNames localCommons')
            r = fname  ++ (show $ srcLineCol $ fst sp) ++ ": call, added common variables as parameters\n"
        in (r, Call p' sp v (ArgList ap' $ ESeq p' sp arglist arglist'))
        
      --       Nothing -> error "Source has less commons than the target!"
extendCalls _ _ _ f = return f
                                      

toArgList :: A -> SrcSpan -> [(Variable, Type A)] -> Expr A
toArgList p sp [] = NullExpr p sp
toArgList p sp ((v, _):xs) = ESeq p sp (Var p sp [(VarName p v, [])]) (toArgList p sp xs)

select :: [Maybe String] -> [TCommon A] -> [(Variable, Type A)]
select [] _ = []
select x [] = error $ "Source has less commons than the target!" ++ show x
select a@(x:xs) b@((y, e):yes) | x == y = e ++ select xs yes
                               | otherwise = select xs yes

nonNullArgs (ASeq _ _ _) = True
nonNullArgs (ArgName _ _) = True
nonNullArgs (NullArg _) = False


extendArgs nonNullArgs sp' args = if nonNullArgs then 
                                     let p' = unitAnnotation { refactored = Just $ snd sp' }
                                     in ASeq p' (ArgName p' "") (extendArgs' sp' args)
                                  else extendArgs' sp' args
                                 

extendArgs'  _ [] = NullArg unitAnnotation
extendArgs' sp' ((v, t):vts) = 
    let p' = unitAnnotation { refactored = Just $ fst sp' }
    in ASeq p' (ArgName p' v) (extendArgs' sp' vts)

{- blockExtendDecls (Block a s i sp ds f) ds' = Block a s i sp (DSeq unitAnnotation ds ds') f
              
 extendArgs _ [] = (NullDecl unitAnnotation, NullArg unitAnnotation)
 extendArgs sp' ((v, t):vts) = 
     let p' = unitAnnotation { refactored = Just $ toCol0 $ fst sp' }
         dec = Decl p' [(Var p' sp' [(VarName p' v, [])], NullExpr p' sp')] t
         arg = ArgName p' v
         (decs, args) = extendArgs sp' vts
     in (DSeq p' dec decs, ASeq p' arg args)
-}




{-
 collectTCommons :: [Program Annotation] -> State (TCommons Annotation) [Program Annotation]
 collectTCommons p = transformBiM collectTCommons' p    
(transformBiM collectTCommons)
-}


collectCommons :: Filename -> String -> Block A -> State (Report, [TLCommon A]) (Block A)
collectCommons fname pname b = 
    let tenv = typeEnv b
                    
        commons' :: Decl A -> State (Report, [TLCommon A]) (Decl A)
        commons' f@(Common a sp cname exprs) = 
            do let r' = fname ++ (show $ srcLineCol $ fst sp) ++ ": removed common declaration\n"
               (r, env) <- get
               put (r ++ r', (fname, (pname, (cname, typeCommonExprs exprs))):env)
               return $ (NullDecl (a { refactored = (Just $ fst sp) }) sp)
        commons' f = return f

        typeCommonExprs :: [Expr Annotation] -> [(Variable, Type Annotation)]
        typeCommonExprs [] = []
        typeCommonExprs ((Var _ sp [(VarName _ v, _)]):es) = 
            case (tenvLookup v tenv) of
                 Just t -> (v, t) : (typeCommonExprs es)
                 Nothing -> error $ "Variable " ++ (show v) ++ " is of an unknown type at: " ++ show sp
        typeCommonExprs (e:_) = error $ "Not expecting a non-variable expression in expression at: " ++ show (srcSpan e)

    in transformBiM commons' b                           

{-
-- Turn common blocks into type defs

 commonToTypeDefs :: String -> [(String, [Program Annotation])] -> IO Report
 commonToTypeDefs d = 
     let name = d ++ "Types"
         unitSrcLoc = SrcLoc (name ++ ".f90") 0 0
         decls = undefined
         mod = Module () (unitSrcLoc, unitSrcLoc) (SubName () name) [] ImplicitNode decls []
     in let ?variant = DefaultPP in writeFile (d ++ "/" ++ name ++ ".f90") (outputF mod)

 
 commonToTypeDefs' :: String -> (String, [Program Annotation]) -> [Decls]
 commonToTypeDefs' = undefined -- DerivedTypeDef p 
-}