packages feed

fortran-src 0.3.0 → 0.4.0

raw patch · 18 files changed

+521/−154 lines, 18 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Language.Fortran.AST: instance (Control.DeepSeq.NFData a, Control.DeepSeq.NFData (t a)) => Control.DeepSeq.NFData (Language.Fortran.AST.AList t a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData Language.Fortran.AST.BaseType
+ Language.Fortran.AST: instance Control.DeepSeq.NFData Language.Fortran.AST.BinaryOp
+ Language.Fortran.AST: instance Control.DeepSeq.NFData Language.Fortran.AST.CharacterLen
+ Language.Fortran.AST: instance Control.DeepSeq.NFData Language.Fortran.AST.Intent
+ Language.Fortran.AST: instance Control.DeepSeq.NFData Language.Fortran.AST.MetaInfo
+ Language.Fortran.AST: instance Control.DeepSeq.NFData Language.Fortran.AST.ModuleNature
+ Language.Fortran.AST: instance Control.DeepSeq.NFData Language.Fortran.AST.Only
+ Language.Fortran.AST: instance Control.DeepSeq.NFData Language.Fortran.AST.ProgramUnitName
+ Language.Fortran.AST: instance Control.DeepSeq.NFData Language.Fortran.AST.UnaryOp
+ Language.Fortran.AST: instance Control.DeepSeq.NFData Language.Fortran.ParserMonad.FortranVersion
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.AllocOpt a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.Argument a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.Attribute a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.Block a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.Comment a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.CommonGroup a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.ControlPair a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.DataGroup a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.Declarator a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.DimensionDeclarator a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.DoSpecification a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.Expression a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.FlushSpec a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.ForallHeader a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.FormatItem a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.ImpElement a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.ImpList a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.Index a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.Namelist a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.Prefix a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.ProcDecl a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.ProcInterface a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.ProgramFile a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.ProgramUnit a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.Selector a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.Statement a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.StructureItem a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.Suffix a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.TypeSpec a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.UnionMap a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.Use a)
+ Language.Fortran.AST: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Language.Fortran.AST.Value a)
+ Language.Fortran.Analysis.DataFlow: instance Control.DeepSeq.NFData Language.Fortran.Analysis.DataFlow.IEFlow
+ Language.Fortran.Analysis.DataFlow: instance Control.DeepSeq.NFData Language.Fortran.Analysis.DataFlow.InductionExpr
+ Language.Fortran.Analysis.ModGraph: MOFSMod :: FilePath -> ModOrigin
+ Language.Fortran.Analysis.ModGraph: MOFile :: FilePath -> ModOrigin
+ Language.Fortran.Analysis.ModGraph: ModGraph :: Map String (Node, Maybe ModOrigin) -> Gr String () -> Int -> ModGraph
+ Language.Fortran.Analysis.ModGraph: [mgGraph] :: ModGraph -> Gr String ()
+ Language.Fortran.Analysis.ModGraph: [mgModNodeMap] :: ModGraph -> Map String (Node, Maybe ModOrigin)
+ Language.Fortran.Analysis.ModGraph: [mgNumNodes] :: ModGraph -> Int
+ Language.Fortran.Analysis.ModGraph: data ModGraph
+ Language.Fortran.Analysis.ModGraph: data ModOrigin
+ Language.Fortran.Analysis.ModGraph: delModNodes :: [Node] -> ModGraph -> ModGraph
+ Language.Fortran.Analysis.ModGraph: genModGraph :: Maybe FortranVersion -> [FilePath] -> [FilePath] -> IO ModGraph
+ Language.Fortran.Analysis.ModGraph: instance Data.Data.Data Language.Fortran.Analysis.ModGraph.ModGraph
+ Language.Fortran.Analysis.ModGraph: instance Data.Data.Data Language.Fortran.Analysis.ModGraph.ModOrigin
+ Language.Fortran.Analysis.ModGraph: instance GHC.Classes.Eq Language.Fortran.Analysis.ModGraph.ModGraph
+ Language.Fortran.Analysis.ModGraph: instance GHC.Classes.Eq Language.Fortran.Analysis.ModGraph.ModOrigin
+ Language.Fortran.Analysis.ModGraph: instance GHC.Classes.Ord Language.Fortran.Analysis.ModGraph.ModOrigin
+ Language.Fortran.Analysis.ModGraph: instance GHC.Show.Show Language.Fortran.Analysis.ModGraph.ModOrigin
+ Language.Fortran.Analysis.ModGraph: modGraphToDOT :: ModGraph -> String
+ Language.Fortran.Analysis.ModGraph: takeNextMods :: ModGraph -> [(Node, Maybe ModOrigin)]
+ Language.Fortran.Util.ModFile: CompileFile :: TimestampStatus
+ Language.Fortran.Util.ModFile: ModFileExists :: FilePath -> TimestampStatus
+ Language.Fortran.Util.ModFile: NoSuchFile :: TimestampStatus
+ Language.Fortran.Util.ModFile: checkTimestamps :: FilePath -> IO TimestampStatus
+ Language.Fortran.Util.ModFile: data TimestampStatus
+ Language.Fortran.Util.Position: instance Control.DeepSeq.NFData Language.Fortran.Util.Position.Position
+ Language.Fortran.Util.Position: instance Control.DeepSeq.NFData Language.Fortran.Util.Position.SrcSpan
- Language.Fortran.AST: StructStructure :: a -> SrcSpan -> Maybe String -> AList StructureItem a -> StructureItem a
+ Language.Fortran.AST: StructStructure :: a -> SrcSpan -> Maybe String -> String -> AList StructureItem a -> StructureItem a
- Language.Fortran.Analysis.DataFlow: IELinear :: Name -> Int -> Int -> InductionExpr
+ Language.Fortran.Analysis.DataFlow: IELinear :: !Name -> !Int -> !Int -> InductionExpr
- Language.Fortran.Analysis.DataFlow: dataFlowSolver :: Ord t => BBGr a -> (Node -> InOut t) -> OrderF a -> (OutF t -> InF t) -> (InF t -> OutF t) -> InOutMap t
+ Language.Fortran.Analysis.DataFlow: dataFlowSolver :: (NFData t, Ord t) => BBGr a -> (Node -> InOut t) -> OrderF a -> (OutF t -> InF t) -> (InF t -> OutF t) -> InOutMap t
- Language.Fortran.Util.ModFile: decodeModFile :: ByteString -> Either String ModFile
+ Language.Fortran.Util.ModFile: decodeModFile :: ByteString -> Either String [ModFile]
- Language.Fortran.Util.ModFile: encodeModFile :: ModFile -> ByteString
+ Language.Fortran.Util.ModFile: encodeModFile :: [ModFile] -> ByteString

Files

CHANGELOG.md view
@@ -1,3 +1,14 @@+### 0.4.0 (August 29, 2019)++* ModGraph: parse Fortran files and assemble them into a dependency graph in order to construct automated 'build' plans for analysis and summarisation (e.g. with --make-mods option).+* Change name of compilation to summarisation. Remains as '-c' option.+* Allow multiple files and directories to be specified on command line.+* Search includedir recursively for fsmod files.+* Change format of fsmod-files so that they can contain [ModFile] since multiple Fortran files can be summarised into a single mod file.+* Introduce strictness and NFData dependencies across the board.+* Use Pipes to process large amounts of files in order to control memory usage and more efficiently process things.+* Parsing rules for StructStructures (thanks Raoul Charman)+ ### 0.3.0 (June 13, 2019)  * Add partial Fortran2003 support.
LICENSE view
@@ -1,6 +1,6 @@ Copyright (c) 2015-2019: Mistral Contrastin, Matthew Danish, Dominic Orchard and Andrew Rice -Additional thanks for contributions from: Anthony Burzillo, Azeem Bande-Ali, Ben Moon, Bradley Hardy, Eric Seidel, Harry Clarke, Jason Xu, Lukasz Kolodziejczyk, TravelTissues and Vaibhav Yenamandra+Additional thanks for contributions from: Anthony Burzillo, Azeem Bande-Ali, Ben Moon, Bradley Hardy, Eric Seidel, Harry Clarke, Jason Xu, Lukasz Kolodziejczyk, Raoul Charman, TravelTissues and Vaibhav Yenamandra  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
fortran-src.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: f33857aa248d66c7f4c20fdc906e32ee466aeca57820d7fd2c0874092659737a+-- hash: 7ecba9dfd6edf736093629bbf1b7aa27d0e4d21260c2e931eaabda5aa5ff2c82  name:           fortran-src-version:        0.3.0+version:        0.4.0 synopsis:       Parser and anlyses for Fortran standards 66, 77, 90 and 95. description:    Provides lexing, parsing, and basic analyses of Fortran code covering standards: FORTRAN 66, FORTRAN 77, Fortran 90, and Fortran 95 and some legacy extensions. Includes data flow and basic block analysis, a renamer, and type analysis. For example usage, see the 'camfort' project, which uses fortran-src as its front end. category:       Language@@ -29,6 +29,7 @@   exposed-modules:       Language.Fortran.Analysis       Language.Fortran.Analysis.Renaming+      Language.Fortran.Analysis.ModGraph       Language.Fortran.Analysis.Types       Language.Fortran.Analysis.BBlocks       Language.Fortran.Analysis.DataFlow@@ -65,6 +66,7 @@     , binary >=0.8.3.0 && <0.9     , bytestring >=0.10 && <0.11     , containers >=0.5 && <0.7+    , deepseq     , directory >=1.2 && <2     , fgl >=5 && <6     , filepath >=1.4 && <2@@ -89,6 +91,7 @@     , binary >=0.8.3.0 && <0.9     , bytestring >=0.10 && <0.11     , containers >=0.5 && <0.7+    , deepseq     , directory >=1.2 && <2     , fgl >=5 && <6     , filepath >=1.4 && <2
src/Language/Fortran/AST.hs view
@@ -16,6 +16,7 @@ import Data.Typeable () import Data.Binary import GHC.Generics (Generic)+import Control.DeepSeq import Text.PrettyPrint.GenericPretty import Language.Fortran.ParserMonad (FortranVersion(..)) @@ -460,7 +461,7 @@ data StructureItem a =     StructFields a SrcSpan (TypeSpec a) (Maybe (AList Attribute a)) (AList Declarator a)   | StructUnion a SrcSpan (AList UnionMap a)-  | StructStructure a SrcSpan (Maybe String) (AList StructureItem a)+  | StructStructure a SrcSpan (Maybe String) String (AList StructureItem a)   deriving (Eq, Show, Data, Typeable, Generic, Functor)  data UnionMap a =@@ -858,6 +859,7 @@   deriving (Ord, Eq, Show, Data, Typeable, Generic)  instance Binary ProgramUnitName+instance NFData ProgramUnitName  class Named a where   getName :: a -> ProgramUnitName@@ -979,3 +981,46 @@ nonExecutableStatementBlock v (BlStatement _ _ _ s) = nonExecutableStatement v s nonExecutableStatementBlock _ BlInterface{} = True nonExecutableStatementBlock _ _ = False++instance (NFData a, NFData (t a)) => NFData (AList t a)+instance NFData a => NFData (ProgramFile a)+instance NFData a => NFData (ProgramUnit a)+instance NFData a => NFData (Block a)+instance NFData a => NFData (Expression a)+instance NFData a => NFData (TypeSpec a)+instance NFData a => NFData (Index a)+instance NFData a => NFData (Value a)+instance NFData a => NFData (Comment a)+instance NFData a => NFData (Statement a)+instance NFData a => NFData (ProcDecl a)+instance NFData a => NFData (ProcInterface a)+instance NFData a => NFData (DoSpecification a)+instance NFData a => NFData (Selector a)+instance NFData a => NFData (ForallHeader a)+instance NFData a => NFData (Argument a)+instance NFData a => NFData (Use a)+instance NFData a => NFData (Attribute a)+instance NFData a => NFData (CommonGroup a)+instance NFData a => NFData (ControlPair a)+instance NFData a => NFData (AllocOpt a)+instance NFData a => NFData (DataGroup a)+instance NFData a => NFData (DimensionDeclarator a)+instance NFData a => NFData (Declarator a)+instance NFData a => NFData (FormatItem a)+instance NFData a => NFData (FlushSpec a)+instance NFData a => NFData (ImpElement a)+instance NFData a => NFData (ImpList a)+instance NFData a => NFData (Namelist a)+instance NFData a => NFData (Prefix a)+instance NFData a => NFData (Suffix a)+instance NFData a => NFData (StructureItem a)+instance NFData a => NFData (UnionMap a)+instance NFData MetaInfo+instance NFData FortranVersion+instance NFData CharacterLen+instance NFData BaseType+instance NFData UnaryOp+instance NFData BinaryOp+instance NFData Only+instance NFData ModuleNature+instance NFData Intent
src/Language/Fortran/Analysis.hs view
@@ -15,7 +15,6 @@ where  import Prelude hiding (exp)-import Control.Monad (void) import Language.Fortran.Util.Position (SrcSpan) import Data.Generics.Uniplate.Data import Data.Data@@ -316,21 +315,21 @@      -- Match and give the varname for LHS of statement     match' v@(ExpValue _ _ ValVariable{}) = varName v-    match' (ExpSubscript _ _ v@(ExpValue _ _ ValVariable{}) _) = varName v-    match' (ExpDataRef _ _ v _) = match' v-    match' e = error $ "An unexpected LHS to an expression assign: " ++ show (void (const ()) e)+    match' (ExpSubscript _ _ e _)         = match' e+    match' (ExpDataRef _ _ v _)           = match' v+    match' e                              = error $ "An unexpected LHS to an expression assign: " ++ show (fmap (const ()) e)      -- Match and give the varname of LHSes which occur in subroutine calls-    match'' v@(ExpValue _ _ ValVariable{})                      = [varName v]-    match'' (ExpSubscript _ _ v@(ExpValue _ _ ValVariable{}) _) = [varName v]-    match'' (ExpDataRef _ _ v _)                                = match'' v-    match'' e                                                   = onExprs e+    match'' v@(ExpValue _ _ ValVariable{}) = [varName v]+    match'' (ExpSubscript _ _ e _)         = match'' e+    match'' (ExpDataRef _ _ v _)           = match'' v+    match'' e                              = onExprs e     -- Match and give the varname of LHSes which occur in function calls-    match v@(ExpValue _ _ ValVariable{})                      = [varName v]-    match (ExpSubscript _ _ v@(ExpValue _ _ ValVariable{}) _) = [varName v]-    match (ExpDataRef _ _ e _)                                = match e-    match _                                                   = []+    match v@(ExpValue _ _ ValVariable{}) = [varName v]+    match (ExpSubscript _ _ e _)         = match e+    match (ExpDataRef _ _ e _)           = match e+    match e                              = onExprs e  -- | Set of expressions used -- not defined -- by an AST-block. blockRhsExprs :: Data a => Block a -> [Expression a]
src/Language/Fortran/Analysis/DataFlow.hs view
@@ -1,6 +1,6 @@ -- | Dataflow analysis to be applied once basic block analysis is complete. -{-# LANGUAGE FlexibleContexts, PatternGuards, ScopedTypeVariables, TupleSections, DeriveGeneric, DeriveDataTypeable #-}+{-# LANGUAGE FlexibleContexts, PatternGuards, ScopedTypeVariables, TupleSections, DeriveGeneric, DeriveDataTypeable, BangPatterns #-} module Language.Fortran.Analysis.DataFlow   ( dominators, iDominators, DomMap, IDomMap   , postOrder, revPostOrder, preOrder, revPreOrder, OrderF@@ -25,7 +25,9 @@ import Data.Generics.Uniplate.Data import GHC.Generics import Data.Data-import Control.Monad.State.Lazy+import qualified Control.Monad.State.Lazy as Lazy+import Control.Monad.State.Strict+import Control.DeepSeq import Control.Arrow ((&&&)) import Text.PrettyPrint.GenericPretty (Out) import Language.Fortran.Parser.Utils@@ -34,6 +36,7 @@ import Language.Fortran.AST import qualified Data.Map as M import qualified Data.IntMap.Lazy as IM+import qualified Data.IntMap.Strict as IMS import qualified Data.Set as S import qualified Data.IntSet as IS import Data.Graph.Inductive hiding (trc, dom, order, inn, out, rc)@@ -48,7 +51,7 @@ type BBNodeSet = IS.IntSet type ASTBlockNodeMap = IM.IntMap type ASTBlockNodeSet = IS.IntSet-type ASTExprNodeMap = IM.IntMap+type ASTExprNodeMap = IMS.IntMap type ASTExprNodeSet = IS.IntSet  -- | DomMap : node -> dominators of node@@ -115,19 +118,22 @@ -- | OutF, a function that returns the out-dataflow for a given node type OutF t     = Node -> t --- | Apply the iterative dataflow analysis method.-dataFlowSolver :: Ord t => BBGr a            -- ^ basic block graph-                        -> (Node -> InOut t) -- ^ initialisation for in and out dataflows-                        -> OrderF a          -- ^ ordering function-                        -> (OutF t -> InF t) -- ^ compute the in-flow given an out-flow function-                        -> (InF t -> OutF t) -- ^ compute the out-flow given an in-flow function-                        -> InOutMap t        -- ^ final dataflow for each node-dataFlowSolver gr initF order inF outF = converge (==) $ iterate step initM+-- | Apply the iterative dataflow analysis method. Forces evaluation+-- of intermediate data structures at each step.+dataFlowSolver :: (NFData t, Ord t)+               => BBGr a            -- ^ basic block graph+               -> (Node -> InOut t) -- ^ initialisation for in and out dataflows+               -> OrderF a          -- ^ ordering function+               -> (OutF t -> InF t) -- ^ compute the in-flow given an out-flow function+               -> (InF t -> OutF t) -- ^ compute the out-flow given an in-flow function+               -> InOutMap t        -- ^ final dataflow for each node+dataFlowSolver gr initF order inF outF = converge (==) $ iterate' step initM   where-    ordNodes = order gr-    initM    = IM.fromList [ (n, initF n) | n <- ordNodes ]-    step m   = IM.fromList [ (n, (inF (snd . get' m) n, outF (fst . get' m) n)) | n <- ordNodes ]-    get' m n  = fromJustMsg ("dataFlowSolver: get " ++ show n) $ IM.lookup n m+    ordNodes     = order gr+    initM        = IM.fromList [ (n, initF n) | n <- ordNodes ]+    step !m      = IM.fromList [ (n, (inF (snd . get' m) n, outF (fst . get' m) n)) | n <- ordNodes ]+    get' m n     = fromJustMsg ("dataFlowSolver: get " ++ show n) $ IM.lookup n m+    iterate' f x = x `deepseq` x : iterate' f (f x)  -- Similar to above but return a list of states instead of just the final one. --dataFlowSolver' :: Ord t => BBGr a            -- ^ basic block graph@@ -514,69 +520,80 @@ -- It's a 'lattice' but will leave it ungeneralised for the moment. data InductionExpr   = IETop                 -- not enough info-  | IELinear Name Int Int -- Basic induction var 'Name' * coefficient + offset+  | IELinear !Name !Int !Int -- Basic induction var 'Name' * coefficient + offset   | IEBottom              -- too difficult   deriving (Show, Eq, Ord, Typeable, Generic, Data)-+instance NFData InductionExpr type DerivedInductionMap = ASTExprNodeMap InductionExpr -data IEFlow = IEFlow { ieFlowVars :: M.Map Name InductionExpr, ieFlowExprs :: DerivedInductionMap }+data IEFlow = IEFlow { ieFlowVars :: M.Map Name InductionExpr, ieFlowExprs :: !DerivedInductionMap }   deriving (Show, Eq, Ord, Typeable, Generic, Data)+instance NFData IEFlow  ieFlowInsertVar :: Name -> InductionExpr -> IEFlow -> IEFlow ieFlowInsertVar v ie flow = flow { ieFlowVars = M.insert v ie (ieFlowVars flow) }  ieFlowInsertExpr :: ASTExprNode -> InductionExpr -> IEFlow -> IEFlow-ieFlowInsertExpr i ie flow = flow { ieFlowExprs = IM.insert i ie (ieFlowExprs flow) }+ieFlowInsertExpr i ie flow = flow { ieFlowExprs = IMS.insert i ie (ieFlowExprs flow) }  emptyIEFlow :: IEFlow-emptyIEFlow = IEFlow M.empty IM.empty+emptyIEFlow = IEFlow M.empty IMS.empty  joinIEFlows :: [IEFlow] -> IEFlow joinIEFlows flows = IEFlow flowV flowE   where     flowV = M.unionsWith joinInductionExprs (map ieFlowVars flows)-    flowE = IM.unionsWith joinInductionExprs (map ieFlowExprs flows)+    flowE = IMS.unionsWith joinInductionExprs (map ieFlowExprs flows)  -- | For every expression in a loop, try to derive its relationship to -- a basic induction variable. genDerivedInductionMap :: forall a. Data a => BackEdgeMap -> BBGr (Analysis a) -> DerivedInductionMap-genDerivedInductionMap bedges gr = ieFlowExprs . joinIEFlows . map snd . IM.elems . IM.filterWithKey inLoop $ inOutMaps+genDerivedInductionMap bedges gr = ieFlowExprs . joinIEFlows . map snd . IMS.elems . IMS.filterWithKey inLoop $ inOutMaps   where     bivMap = basicInductionVars bedges gr -- basic indvars indexed by loop header node     loopNodeSet = IS.unions (loopNodes bedges $ bbgrGr gr) -- set of nodes within a loop     inLoop i _ = i `IS.member` loopNodeSet      step :: IEFlow -> Block (Analysis a) -> IEFlow-    step flow b = case b of+    step !flow b = case b of       BlStatement _ _ _ (StExpressionAssign _ _ lv@(ExpValue _ _ (ValVariable _)) rhs)-        | _ <- insLabel (getAnnotation rhs)-        , flow''   <- ieFlowInsertVar (varName lv) (derivedInductionExpr flow' rhs) flow' -> stepExpr flow'' lv+        | _ <- insLabel (getAnnotation rhs), flow'' <- ieFlowInsertVar (varName lv) (derivedInductionExprMemo flow' rhs) flow'+        -> stepExpr flow'' lv       _ -> flow'       where-        flow' = foldl' stepExpr flow (universeBi b)+        -- flow' = foldl' stepExpr flow (universeBi b)+        flow' = execState (trans (\ e -> derivedInductionExprM e >> pure e) b) flow -- monadic version+        trans = transformBiM :: (Expression (Analysis a) -> State IEFlow (Expression (Analysis a))) -> Block (Analysis a) -> State IEFlow (Block (Analysis a)) +     stepExpr :: IEFlow -> Expression (Analysis a) -> IEFlow-    stepExpr flow e = ieFlowInsertExpr label ie flow+    stepExpr !flow e = ieFlowInsertExpr label ie flow       where         ie = derivedInductionExpr flow e         label = fromJustMsg "stepExpr" $ insLabel (getAnnotation e)      out :: InF IEFlow -> OutF IEFlow-    out inF node = foldl' step flow (fromJustMsg ("analyseDerivedIE out(" ++ show node ++ ")") $ lab (bbgrGr gr) node)+    out inF node = flow'       where         flow = joinIEFlows [fst (initF node), inF node]+        flow' = foldl' step flow (fromJustMsg ("analyseDerivedIE out(" ++ show node ++ ")") $ lab (bbgrGr gr) node)      inn :: OutF IEFlow -> InF IEFlow     inn outF node = joinIEFlows [ outF p | p <- pre (bbgrGr gr) node ]      initF :: Node -> InOut IEFlow-    initF node = case IM.lookup node bivMap of-                   Just set -> (IEFlow (M.fromList [ (n, IELinear n 1 0) | n <- S.toList set ]) IM.empty, emptyIEFlow)+    initF node = case IMS.lookup node bivMap of+                   Just set -> (IEFlow (M.fromList [ (n, IELinear n 1 0) | n <- S.toList set ]) IMS.empty, emptyIEFlow)                    Nothing  -> (emptyIEFlow, emptyIEFlow)      inOutMaps = dataFlowSolver gr initF revPostOrder inn out +derivedInductionExprMemo :: Data a => IEFlow -> Expression (Analysis a) -> InductionExpr+derivedInductionExprMemo flow e+  | Just label <- insLabel (getAnnotation e)+  , Just iexpr <- IMS.lookup label (ieFlowExprs flow) = iexpr+  | otherwise = derivedInductionExpr flow e+ -- Compute the relationship between the given expression and a basic -- induction variable, if possible. derivedInductionExpr :: Data a => IEFlow -> Expression (Analysis a) -> InductionExpr@@ -591,6 +608,25 @@   where     derive = derivedInductionExpr flow +-- Monadic version using State.+derivedInductionExprM :: Data a => Expression (Analysis a) -> State IEFlow InductionExpr+derivedInductionExprM e = do+  flow <- get+  let derive e' | Just label <- insLabel (getAnnotation e')+                , Just iexpr <- IMS.lookup label (ieFlowExprs flow) = pure iexpr+                | otherwise = derivedInductionExprM e'+  ie <- case e of+        v@(ExpValue _ _ (ValVariable _))   -> pure . fromMaybe IETop $ M.lookup (varName v) (ieFlowVars flow)+        ExpValue _ _ (ValInteger str)+          | Just i <- readInteger str      -> pure $ IELinear "" 0 (fromIntegral i)+        ExpBinary _ _ Addition e1 e2       -> addInductionExprs <$> derive e1 <*> derive e2+        ExpBinary _ _ Subtraction e1 e2    -> addInductionExprs <$> derive e1 <*> (negInductionExpr <$> derive e2)+        ExpBinary _ _ Multiplication e1 e2 -> mulInductionExprs <$> derive e1 <*> derive e2+        _                                  -> pure $ IETop -- unsure+  let Just label = insLabel (getAnnotation e)+  put $ ieFlowInsertExpr label ie flow+  pure ie+ -- Combine two induction variable relationships through addition. addInductionExprs :: InductionExpr -> InductionExpr -> InductionExpr addInductionExprs (IELinear ln lc lo) (IELinear rn rc ro)@@ -701,7 +737,7 @@  -- | Create a call map showing the structure of the program. genCallMap :: Data a => ProgramFile (Analysis a) -> CallMap-genCallMap pf = flip execState M.empty $ do+genCallMap pf = flip Lazy.execState M.empty $ do   let uP = universeBi :: Data a => ProgramFile a -> [ProgramUnit a]   forM_ (uP pf) $ \ pu -> do     let n = puName pu
+ src/Language/Fortran/Analysis/ModGraph.hs view
@@ -0,0 +1,158 @@+{-# LANGUAGE DeriveDataTypeable, ScopedTypeVariables, PatternGuards, TupleSections #-}++-- | Generate a module use-graph.+module Language.Fortran.Analysis.ModGraph+  (genModGraph, ModGraph(..), ModOrigin(..), modGraphToDOT, takeNextMods, delModNodes)+where++import Prelude hiding (mod)+import Control.Monad+import Control.Monad.State.Strict+import Data.Data+import Data.Generics.Uniplate.Data+import Data.Graph.Inductive hiding (version)+import Data.Graph.Inductive.PatriciaTree (Gr)+import Data.Maybe+import Data.Text.Encoding (encodeUtf8, decodeUtf8With)+import Data.Text.Encoding.Error (replace)+import Language.Fortran.AST hiding (setName)+import Language.Fortran.Parser.Any+import Language.Fortran.ParserMonad (FortranVersion(..), fromRight)+import Language.Fortran.Util.ModFile+import qualified Data.ByteString.Char8 as B+import qualified Data.ByteString.Lazy.Char8 as LB+import qualified Data.Map as M+import System.IO+import System.Directory+import System.FilePath++--------------------------------------------------++data ModOrigin = MOFile FilePath | MOFSMod FilePath+  deriving (Eq, Data, Show)++instance Ord ModOrigin where+  MOFSMod _ <= MOFSMod _ = True+  a <= b = a == b++data ModGraph = ModGraph { mgModNodeMap :: M.Map String (Node, Maybe ModOrigin)+                         , mgGraph      :: Gr String ()+                         , mgNumNodes   :: Int }+  deriving (Eq, Data)++modGraph0 :: ModGraph+modGraph0 = ModGraph M.empty empty 0++type ModGrapher a = StateT ModGraph IO a++maybeAddModName :: String -> Maybe ModOrigin -> ModGrapher Node+maybeAddModName modName org = do+  mg@ModGraph { mgModNodeMap = mnmap, mgGraph = gr, mgNumNodes = numNodes } <- get+  case M.lookup modName mnmap of+    Just (i, org') | org <= org' -> pure i+                   | otherwise   -> do+                       let mnmap' = M.insert modName (i, org) mnmap+                       put $ mg { mgModNodeMap = mnmap' }+                       pure i+    Nothing -> do+      let i = numNodes + 1+      let mnmap' = M.insert modName (i, org) mnmap+      let gr' = insNode (i, modName) gr+      put $ mg { mgModNodeMap = mnmap', mgGraph = gr', mgNumNodes = i }+      pure i++addModDep :: String -> String -> ModGrapher ()+addModDep modName depName = do+  i <- maybeAddModName modName Nothing+  j <- maybeAddModName depName Nothing+  mg@ModGraph { mgGraph = gr } <- get+  put $ mg { mgGraph = insEdge (i, j, ()) gr }++genModGraph :: Maybe FortranVersion -> [FilePath] -> [FilePath] -> IO ModGraph+genModGraph mversion includeDirs paths = do+  let perModule path pu@(PUModule _ _ modName _ _) = do+        _ <- maybeAddModName modName (Just $ MOFile path)+        let uses = [ usedName | StUse _ _ (ExpValue _ _ (ValVariable usedName)) _ _ _ <-+                                universeBi pu :: [Statement ()] ]+        forM_ uses $ \ usedName -> do+          _ <- maybeAddModName usedName Nothing+          addModDep modName usedName+      perModule path pu | Named puName <- getName pu = do+        _ <- maybeAddModName puName (Just $ MOFile path)+        let uses = [ usedName | StUse _ _ (ExpValue _ _ (ValVariable usedName)) _ _ _ <-+                                universeBi pu :: [Statement ()] ]+        forM_ uses $ \ usedName -> do+          _ <- maybeAddModName usedName Nothing+          addModDep puName usedName+      perModule _ _ = pure ()+  let iter :: FilePath -> ModGrapher ()+      iter path = do+        contents <- liftIO $ flexReadFile path+        let version = fromMaybe (deduceVersion path) mversion+        let (Just parserF0) = lookup version parserWithModFilesVersions+        let parserF m b s = fromRight (parserF0 m b s)+        fileMods <- liftIO $ decodeModFiles includeDirs+        let mods = map snd fileMods+        forM_ fileMods $ \ (fileName, mod) -> do+          forM_ [ name | Named name <- M.keys (combinedModuleMap [mod]) ] $ \ name -> do+            _ <- maybeAddModName name . Just $ MOFSMod fileName+            pure ()+        let pf = parserF mods contents path+        mapM_ (perModule path) (childrenBi pf :: [ProgramUnit ()])+        pure ()+  execStateT (mapM_ iter paths) modGraph0++modGraphToDOT :: ModGraph -> String+modGraphToDOT ModGraph { mgGraph = gr } = unlines dot+  where+    dot = [ "strict digraph {\n"+          , "node [shape=box,fontname=\"Courier New\"]\n" ] +++          concatMap (\ (i, name) ->+                        [ "n" ++ show i ++ "[label=\"" ++ name ++ "\"]\n"+                        , "n" ++ show i ++ " -> {" ] +++                        [ " n" ++ show j | j <- suc gr i ] +++                        ["}\n"])+                    (labNodes gr) +++          [ "}\n" ]++takeNextMods :: ModGraph -> [(Node, Maybe ModOrigin)]+takeNextMods ModGraph { mgModNodeMap = mnmap, mgGraph = gr } = noDepFiles+  where+    noDeps = [ (i, modName) | (i, modName) <- labNodes gr, null (suc gr i) ]+    noDepFiles = [ (i, mo) | (i, modName) <- noDeps+                           , (_, mo) <- maybeToList (M.lookup modName mnmap) ]++delModNodes :: [Node] -> ModGraph -> ModGraph+delModNodes ns mg@ModGraph { mgGraph = gr } = mg'+  where+    mg' = mg { mgGraph = delNodes ns gr }++--------------------------------------------------++flexReadFile :: String -> IO B.ByteString+flexReadFile = fmap (encodeUtf8 . decodeUtf8With (replace ' ')) . B.readFile++decodeModFiles :: [String] -> IO [(FilePath, ModFile)]+decodeModFiles = foldM (\ modFiles d -> do+      -- Figure out the camfort mod files and parse them.+      modFileNames <- filter isModFile `fmap` getDirContents d+      addedModFiles <- fmap concat . forM modFileNames $ \ modFileName -> do+        contents <- LB.readFile (d </> modFileName)+        case decodeModFile contents of+          Left msg -> do+            hPutStrLn stderr $ modFileName ++ ": Error: " ++ msg+            return [(modFileName, emptyModFile)]+          Right mods -> do+            hPutStrLn stderr $ modFileName ++ ": successfully parsed precompiled file."+            return $ map (modFileName,) mods+      return $ addedModFiles ++ modFiles+    ) []++isModFile :: FilePath -> Bool+isModFile = (== modFileSuffix) . takeExtension++-- List files in dir+getDirContents :: String -> IO [String]+getDirContents d = do+  d' <- canonicalizePath d+  map (d' </>) `fmap` listDirectory d'
src/Language/Fortran/Parser/Fortran2003.y view
@@ -236,6 +236,7 @@  PROGRAM_INNER :: { ProgramFile A0 } : PROGRAM_UNITS { ProgramFile (MetaInfo { miVersion = Fortran2003, miFilename = "" }) (reverse $1) }+| {- empty -}   { ProgramFile (MetaInfo { miVersion = Fortran2003, miFilename = "" }) [] }  PROGRAM_UNITS :: { [ ProgramUnit A0 ] } : PROGRAM_UNITS PROGRAM_UNIT MAYBE_NEWLINE { $2 : $1 }
src/Language/Fortran/Parser/Fortran66.y view
@@ -111,6 +111,7 @@ PROGRAM_INNER :: { ProgramFile A0 } PROGRAM_INNER : PROGRAM_UNITS BLOCKS { ProgramFile (MetaInfo { miVersion = Fortran66, miFilename = "" })  (reverse $1 ++ convCmts (reverse $2)) }+| {- empty -}   { ProgramFile (MetaInfo { miVersion = Fortran66, miFilename = "" }) [] }  PROGRAM_UNITS :: { [ ProgramUnit A0 ] } PROGRAM_UNITS
src/Language/Fortran/Parser/Fortran77.y view
@@ -196,6 +196,7 @@ PROGRAM_INNER :: { ProgramFile A0 } PROGRAM_INNER : PROGRAM_UNITS { ProgramFile (MetaInfo { miVersion = Fortran77, miFilename = "" }) (reverse $1) }+| {- empty -}   { ProgramFile (MetaInfo { miVersion = Fortran77, miFilename = "" }) [] }  PROGRAM_UNITS :: { [ ProgramUnit A0 ] } PROGRAM_UNITS@@ -363,7 +364,6 @@ FORMAT_ID : FORMAT_ID '/' '/' FORMAT_ID %prec CONCAT { ExpBinary () (getTransSpan $1 $4) Concatenation $1 $4 } | INTEGER_LITERAL               { $1 }-| STRING                        { $1 } -- There should be FUNCTION_CALL here but as far as the parser is concerned it is same as SUBSCRIPT, -- hence putting it here would cause a reduce/reduce conflict. | SUBSCRIPT                     { $1 }@@ -431,7 +431,6 @@ | '(' CI_EXPRESSION ')' { setSpan (getTransSpan $1 $3) $2 } | INTEGER_LITERAL               { $1 } | LOGICAL_LITERAL               { $1 }-| STRING                        { $1 } -- There should be FUNCTION_CALL here but as far as the parser is concerned it is same as SUBSCRIPT, -- hence putting it here would cause a reduce/reduce conflict. | SUBSCRIPT                     { $1 }@@ -526,6 +525,8 @@     in StructFields () s t attrs decls } | union NEWLINE UNION_MAPS endunion NEWLINE   { StructUnion () (getTransSpan $1 $5) (fromReverseList $3) }+| structure MAYBE_NAME NAME NEWLINE STRUCTURE_DECLARATIONS endstructure NEWLINE+  { StructStructure () (getTransSpan $1 $7) $2 $3 (fromReverseList $5) }  UNION_MAPS :: { [ UnionMap A0 ] } UNION_MAPS@@ -805,7 +806,6 @@ | NUMERIC_LITERAL                   { $1 } | '(' EXPRESSION ',' EXPRESSION ')' { ExpValue () (getTransSpan $1 $5) (ValComplex $2 $4) } | LOGICAL_LITERAL                   { $1 }-| STRING                            { $1 } | HOLLERITH                         { $1 } -- There should be FUNCTION_CALL here but as far as the parser is concerned it is same as SUBSCRIPT, -- hence putting it here would cause a reduce/reduce conflict.@@ -860,7 +860,6 @@ | NUMERIC_LITERAL               { $1 } | '(' CONSTANT_EXPRESSION ',' CONSTANT_EXPRESSION ')' { ExpValue () (getTransSpan $1 $5) (ValComplex $2 $4)} | LOGICAL_LITERAL               { $1 }-| string                        { let (TString s cs) = $1 in ExpValue () s (ValString cs) } | SUBSCRIPT                    { $1 } | HOLLERITH                    { $1 } | '(/' EXPRESSION_LIST '/)' {@@ -900,6 +899,7 @@ | SUBSCRIPT '(' INDICIES ')'   { ExpSubscript () (getTransSpan $1 $4) $1 (fromReverseList $3) } | VARIABLE { $1 }+| STRING { $1 }  INDICIES :: { [ Index A0 ] } : INDICIES ',' INDEX { $3 : $1 }
src/Language/Fortran/Parser/Fortran90.y view
@@ -211,6 +211,7 @@  PROGRAM_INNER :: { ProgramFile A0 } : PROGRAM_UNITS { ProgramFile (MetaInfo { miVersion = Fortran90, miFilename = "" }) (reverse $1) }+| {- empty -}   { ProgramFile (MetaInfo { miVersion = Fortran90, miFilename = "" }) [] }  PROGRAM_UNITS :: { [ ProgramUnit A0 ] } : PROGRAM_UNITS PROGRAM_UNIT MAYBE_NEWLINE { $2 : $1 }
src/Language/Fortran/Parser/Fortran95.y view
@@ -215,6 +215,7 @@  PROGRAM_INNER :: { ProgramFile A0 } : PROGRAM_UNITS { ProgramFile (MetaInfo { miVersion = Fortran95, miFilename = "" }) (reverse $1) }+| {- empty -}   { ProgramFile (MetaInfo { miVersion = Fortran95, miFilename = "" }) [] }  PROGRAM_UNITS :: { [ ProgramUnit A0 ] } : PROGRAM_UNITS PROGRAM_UNIT MAYBE_NEWLINE { $2 : $1 }
src/Language/Fortran/PrettyPrint.hs view
@@ -974,7 +974,7 @@     "union" <> newline <>     foldl' (\doc item -> doc <> pprint v item (incIndentation i) <> newline) empty (aStrip maps) <>     "end union"-  pprint v (StructStructure a s mName items) _ = pprint' v (StStructure a s mName items)+  pprint v (StructStructure a s mName _ items) _ = pprint' v (StStructure a s mName items)  instance IndentablePretty (UnionMap a) where   pprint v (UnionMap _ _ items) i =
src/Language/Fortran/Util/ModFile.hs view
@@ -26,15 +26,18 @@ modules. The 'ModuleMap' stores information important to the renamer. The other data is up to you. +Note that the encoder and decoder work on lists of ModFile so that one+fsmod-file may contain information about multiple Fortran files.+ One typical usage might look like:  > let modFile1 = genModFile programFile > let modFile2 = alterModFileData (const (Just ...)) "mydata" modFile1-> let bytes    = encodeModFile modFile2+> let bytes    = encodeModFile [modFile2] > ... > case decodeModFile bytes of >   Left error -> print error->   Right modFile3 -> ...+>   Right modFile3:otherModuleFiles -> ... >     where >       moduleMap = combinedModuleMap (modFile3:otherModuleFiles) >       myData    = lookupModFileData "mydata" modFile3@@ -48,26 +51,27 @@   , genModFile, regenModFile, encodeModFile, decodeModFile   , StringMap, DeclMap, ParamVarMap, DeclContext(..), extractModuleMap, extractDeclMap   , moduleFilename, combinedStringMap, combinedDeclMap, combinedModuleMap, combinedTypeEnv, combinedParamVarMap-  , genUniqNameToFilenameMap )+  , genUniqNameToFilenameMap+  , TimestampStatus(..), checkTimestamps ) where +import Control.Monad.State+import Data.Binary (Binary, encode, decodeOrFail)+import qualified Data.ByteString.Lazy.Char8 as LB import Data.Data-import Data.Maybe import Data.Generics.Uniplate.Operations import qualified Data.Map.Strict as M-import Data.Binary (Binary, encode, decodeOrFail)-import Control.Monad.State+import Data.Maybe import GHC.Generics (Generic)--- import qualified Data.ByteString.Char8 as B-import qualified Data.ByteString.Lazy.Char8 as LB--import qualified Language.Fortran.Util.Position as P import qualified Language.Fortran.AST as F import qualified Language.Fortran.Analysis as FA+import qualified Language.Fortran.Analysis.BBlocks as FAB+import qualified Language.Fortran.Analysis.DataFlow as FAD import qualified Language.Fortran.Analysis.Renaming as FAR import qualified Language.Fortran.Analysis.Types as FAT-import qualified Language.Fortran.Analysis.DataFlow as FAD-import qualified Language.Fortran.Analysis.BBlocks as FAB+import qualified Language.Fortran.Util.Position as P+import System.Directory+import System.FilePath  -------------------------------------------------- @@ -153,19 +157,23 @@ -- alterModFileDataF :: Functor f => (Maybe B.ByteString -> f (Maybe B.ByteString)) -> String -> ModFile -> f ModFile -- alterModFileDataF f k mf = (\ od -> mf { mfOtherData = od }) <$> M.alterF f k (mfOtherData mf) --- | Convert ModFile to a strict ByteString for writing to file.-encodeModFile :: ModFile -> LB.ByteString-encodeModFile mf = encode mf' { mfStringMap = sm }+-- | Convert ModFiles to a strict ByteString for writing to file.+encodeModFile :: [ModFile] -> LB.ByteString+encodeModFile = encode . map each   where-    (mf', sm) = extractStringMap (mf { mfStringMap = M.empty })+    each mf = mf' { mfStringMap = sm }+      where+        (mf', sm) = extractStringMap (mf { mfStringMap = M.empty }) --- | Convert a strict ByteString to a ModFile, if possible. Revert the+-- | Convert a strict ByteString to ModFiles, if possible. Revert the -- String aliases according to the StringMap.-decodeModFile :: LB.ByteString -> Either String ModFile+decodeModFile :: LB.ByteString -> Either String [ModFile] decodeModFile bs = case decodeOrFail bs of-  Left (_, _, s)   -> Left s-  Right (_, _, mf) -> Right (revertStringMap sm mf { mfStringMap = M.empty }) { mfStringMap = sm }-    where sm = mfStringMap mf+  Left (_, _, s)    -> Left s+  Right (_, _, mfs) -> Right (map each mfs)+    where+      each mf = (revertStringMap sm mf { mfStringMap = M.empty }) { mfStringMap = sm }+        where sm = mfStringMap mf  -- | Extract the combined module map from a set of ModFiles. Useful -- for parsing a Fortran file in a large context of other modules.@@ -297,3 +305,23 @@           , st@F.StParameter {}                               <- universeBi bs  :: [F.Statement (FA.Analysis a)]           , (F.DeclVariable _ _ v _ _)                        <- universeBi st  :: [F.Declarator (FA.Analysis a)]           , Just con                                          <- [FA.constExp (F.getAnnotation v)] ]++-- | Status of mod-file compared to Fortran file.+data TimestampStatus = NoSuchFile | CompileFile | ModFileExists FilePath++-- | Compare the source file timestamp to the fsmod file timestamp, if+-- it exists.+checkTimestamps :: FilePath -> IO TimestampStatus+checkTimestamps path = do+  pathExists <- doesFileExist path+  modExists <- doesFileExist $ path -<.> modFileSuffix+  case (pathExists, modExists) of+    (False, _)    -> pure NoSuchFile+    (True, False) -> pure CompileFile+    (True, True)  -> do+      let modPath = path -<.> modFileSuffix+      pathModTime <- getModificationTime path+      modModTime  <- getModificationTime modPath+      if pathModTime < modModTime+        then pure $ ModFileExists modPath+        else pure CompileFile
src/Language/Fortran/Util/Position.hs view
@@ -9,6 +9,8 @@ import Text.PrettyPrint.GenericPretty import Text.PrettyPrint import Data.Binary+import GHC.Generics (Generic)+import Control.DeepSeq  import Language.Fortran.Util.SecondParameter @@ -24,6 +26,7 @@   } deriving (Eq, Ord, Data, Typeable, Generic)  instance Binary Position+instance NFData Position  instance Show Position where   show (Position _ c l _ _) = show l ++ ':' : show c@@ -53,7 +56,7 @@ data SrcSpan = SrcSpan Position Position deriving (Eq, Ord, Typeable, Data, Generic)  instance Binary SrcSpan-+instance NFData SrcSpan instance Show SrcSpan where   show (SrcSpan s1 s2)= '(' : show s1 ++ ")-(" ++ show s2 ++ ")" 
src/Main.hs view
@@ -3,7 +3,7 @@  module Main where -import Prelude hiding (readFile)+import Prelude hiding (readFile, mod) import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy.Char8 as LB import Data.Text.Encoding (encodeUtf8, decodeUtf8With)@@ -38,6 +38,7 @@ import Language.Fortran.Analysis import Language.Fortran.AST import Language.Fortran.Analysis.Types+import Language.Fortran.Analysis.ModGraph import Language.Fortran.Analysis.BBlocks import Language.Fortran.Analysis.DataFlow import Language.Fortran.Analysis.Renaming@@ -56,7 +57,52 @@   args <- getArgs   (opts, parsedArgs) <- compileArgs args   case (parsedArgs, action opts) of-    ([path], actionOpt) -> do+    (paths, ShowMakeGraph) -> do+      paths' <- expandDirs paths+      mg <- genModGraph (fortranVersion opts) (includeDirs opts) paths'+      putStrLn $ modGraphToDOT mg+    -- make: construct a build-dep graph and follow it+    (paths, Make) -> do+      let mvers = fortranVersion opts+      paths' <- expandDirs paths+      -- Build the graph of module dependencies+      mg0 <- genModGraph mvers (includeDirs opts) paths'+      -- Start the list of mods with those from the command line+      mods0 <- decodeModFiles $ includeDirs opts+      -- Loop through the dependency graph until it is empty+      let loop mg mods+            | nxt <- takeNextMods mg+            , not (null nxt) = do+                let fnPaths = [ fn | (_, Just (MOFile fn)) <- nxt ]+                newMods <- fmap concat . forM fnPaths $ \ fnPath -> do+                  tsStatus <- checkTimestamps fnPath+                  case tsStatus of+                    NoSuchFile -> do+                      putStr $ "Does not exist: " ++ fnPath+                      pure [emptyModFile]+                    ModFileExists modPath -> do+                      putStrLn $ "Loading mod file " ++ modPath ++ "."+                      decodeOneModFile modPath+                    CompileFile -> do+                      putStr $ "Summarising " ++ fnPath ++ "..."+                      mod <- compileFileToMod mvers mods fnPath Nothing+                      putStrLn "done"+                      pure [mod]++                let ns  = map fst nxt+                let mg' = delModNodes ns mg+                loop mg' $ newMods ++ mods+          loop _ mods = pure mods++      allMods <- loop mg0 mods0+      case outputFile opts of+        Nothing -> pure ()+        Just f  -> LB.writeFile f $ encodeModFile allMods++    (paths, Compile) -> do+      mods <- decodeModFiles $ includeDirs opts+      mapM_ (\ p -> compileFileToMod (fortranVersion opts) mods p (outputFile opts)) paths+    (path:_, actionOpt) -> do       contents <- flexReadFile path       let version = fromMaybe (deduceVersion path) (fortranVersion opts)       let (Just parserF0) = lookup version parserWithModFilesVersions@@ -76,7 +122,6 @@             where pf' = analyseParameterVars pvm . analyseBBlocks . analyseRenamesWithModuleMap mmap . initAnalysis $ pf                   bbm = genBBlockMap pf'                   sgr = genSuperBBGr bbm-      let runCompile = encodeModFile . genModFile . fst . analyseTypesWithEnv tenv . analyseRenamesWithModuleMap mmap . initAnalysis       let findBlockPU pf astBlockId = listToMaybe             [ pu | pu <- universeBi pf :: [ProgramUnit (Analysis A0)]                  , bbgr <- maybeToList (bBlocks (getAnnotation pu))@@ -95,22 +140,19 @@         BBlocks    -> putStrLn . runBBlocks $ parserF mods contents path         SuperGraph -> putStrLn . runSuperGraph $ parserF mods contents path         Reprint    -> putStrLn . render . flip (pprint version) (Just 0) $ parserF mods contents path-        Compile    -> do-          let bytes = runCompile $ parserF mods contents path-          let fspath = path <.> modFileSuffix-          LB.writeFile fspath bytes         DumpModFile -> do           let path' = if modFileSuffix `isSuffixOf` path then path else path <.> modFileSuffix           contents' <- LB.readFile path'           case decodeModFile contents' of-            Left msg -> putStrLn $ "Error: " ++ msg-            Right mf -> putStrLn $ "Filename: " ++ moduleFilename mf ++-                                   "\n\nStringMap:\n" ++ showStringMap (combinedStringMap [mf]) ++-                                   "\n\nModuleMap:\n" ++ showModuleMap (combinedModuleMap [mf]) ++-                                   "\n\nDeclMap:\n" ++ showGenericMap (combinedDeclMap [mf]) ++-                                   "\n\nTypeEnv:\n" ++ showTypes (combinedTypeEnv [mf]) ++-                                   "\n\nParamVarMap:\n" ++ showGenericMap (combinedParamVarMap [mf]) ++-                                   "\n\nOther Data Labels: " ++ show (getLabelsModFileData mf)+            Left msg  -> putStrLn $ "Error: " ++ msg+            Right mfs -> forM_ mfs $ \ mf ->+              putStrLn $ "Filename: " ++ moduleFilename mf +++                       "\n\nStringMap:\n" ++ showStringMap (combinedStringMap [mf]) +++                       "\n\nModuleMap:\n" ++ showModuleMap (combinedModuleMap [mf]) +++                       "\n\nDeclMap:\n" ++ showGenericMap (combinedDeclMap [mf]) +++                       "\n\nTypeEnv:\n" ++ showTypes (combinedTypeEnv [mf]) +++                       "\n\nParamVarMap:\n" ++ showGenericMap (combinedParamVarMap [mf]) +++                       "\n\nOther Data Labels: " ++ show (getLabelsModFileData mf)         ShowFlows isFrom isSuper astBlockId -> do           let pf = analyseParameterVars pvm .                    analyseBBlocks .@@ -156,8 +198,57 @@                 let suffix | null nodeIDs = ""                            | otherwise    = B.replicate (maxLen - B.length line + 1) ' ' <> "!" <> nodeStr                 B.putStrLn $ line <> suffix+        _ -> fail $ usageInfo programName options     _ -> fail $ usageInfo programName options ++-- | Expand all paths that are directories into a list of Fortran+-- files from a recursive directory listing.+expandDirs :: [FilePath] -> IO [FilePath]+expandDirs = fmap concat . mapM each+  where+    each path = do+      isDir <- doesDirectoryExist path+      if isDir+        then listFortranFiles path+        else pure [path]++-- | Get a list of Fortran files under the given directory.+listFortranFiles :: FilePath -> IO [FilePath]+listFortranFiles dir = filter isFortran <$> listDirectoryRecursively dir+  where+    -- | True if the file has a valid fortran extension.+    isFortran :: FilePath -> Bool+    isFortran x = map toLower (takeExtension x) `elem` exts+      where exts = [".f", ".f90", ".f77", ".f03"]++listDirectoryRecursively :: FilePath -> IO [FilePath]+listDirectoryRecursively dir = listDirectoryRec dir ""+  where+    listDirectoryRec :: FilePath -> FilePath -> IO [FilePath]+    listDirectoryRec d f = do+      let fullPath = d </> f+      isDir <- doesDirectoryExist fullPath+      if isDir+      then do+        conts <- listDirectory fullPath+        concat <$> mapM (listDirectoryRec fullPath) conts+      else pure [fullPath]++compileFileToMod :: Maybe FortranVersion -> ModFiles -> FilePath -> Maybe FilePath -> IO ModFile+compileFileToMod mvers mods path moutfile = do+  contents <- flexReadFile path+  let version = fromMaybe (deduceVersion path) mvers+  let (Just parserF0) = lookup version parserWithModFilesVersions+  let parserF m b s = fromRight (parserF0 m b s)+  let mmap = combinedModuleMap mods+  let tenv = combinedTypeEnv mods+  let runCompile = genModFile . fst . analyseTypesWithEnv tenv . analyseRenamesWithModuleMap mmap . initAnalysis+  let mod = runCompile $ parserF mods contents path+  let fspath = path -<.> modFileSuffix `fromMaybe` moutfile+  LB.writeFile fspath $ encodeModFile [mod]+  return mod+ -- List files in dir recursively rGetDirContents :: String -> IO [String] rGetDirContents d = canonicalizePath d >>= \d' -> go [d'] d'@@ -181,21 +272,23 @@   map (d' </>) `fmap` listDirectory d'  decodeModFiles :: [String] -> IO ModFiles-decodeModFiles = foldM (\ modFiles d -> do-      -- Figure out the camfort mod files and parse them.-      modFileNames <- filter isModFile `fmap` getDirContents d-      addedModFiles <- forM modFileNames $ \ modFileName -> do-        contents <- LB.readFile (d </> modFileName)-        case decodeModFile contents of-          Left msg -> do-            hPutStrLn stderr $ modFileName ++ ": Error: " ++ msg-            return emptyModFile-          Right modFile -> do-            hPutStrLn stderr $ modFileName ++ ": successfully parsed precompiled file."-            return modFile-      return $ addedModFiles ++ modFiles-    ) emptyModFiles+decodeModFiles = flip foldM emptyModFiles $ \ modFiles d -> do+  -- Figure out the camfort mod files and parse them.+  modFileNames <- filter isModFile `fmap` getDirContents d+  addedModFiles <- concat <$> mapM (decodeOneModFile . (d </>)) modFileNames+  return $ addedModFiles ++ modFiles +decodeOneModFile :: FilePath -> IO ModFiles+decodeOneModFile path = do+  contents <- LB.readFile path+  case decodeModFile contents of+    Left msg -> do+      hPutStrLn stderr $ path ++ ": Error: " ++ msg+      return []+    Right modFiles -> do+      hPutStrLn stderr $ path ++ ": successfully parsed summary file."+      return modFiles+ isModFile :: FilePath -> Bool isModFile = (== modFileSuffix) . takeExtension @@ -262,7 +355,7 @@  data Action   = Lex | Parse | Typecheck | Rename | BBlocks | SuperGraph | Reprint | DumpModFile | Compile-  | ShowFlows Bool Bool Int | ShowBlocks (Maybe Int)+  | ShowFlows Bool Bool Int | ShowBlocks (Maybe Int) | ShowMakeGraph | Make   deriving Eq  instance Read Action where@@ -280,10 +373,11 @@   { fortranVersion  :: Maybe FortranVersion   , action          :: Action   , outputFormat    :: OutputFormat+  , outputFile      :: Maybe FilePath   , includeDirs     :: [String] }  initOptions :: Options-initOptions = Options Nothing Parse Default []+initOptions = Options Nothing Parse Default Nothing []  options :: [OptDescr (Options -> Options)] options =@@ -328,10 +422,22 @@       (ReqArg (\ d opts -> opts { includeDirs = d:includeDirs opts }) "DIR")       "directory to search for precompiled 'mod files'"   , Option ['c']-      ["compile"]+      ["summarise", "compile-mod"]       (NoArg $ \ opts -> opts { action = Compile })-      "compile an .fsmod file from the input"+      "build an .fsmod file from the input"+  , Option ['o']+      ["output-file"]+      (ReqArg (\ f opts -> opts { outputFile = Just f }) "FILE")+      "name of output file (e.g. name of generated fsmod file)"   , Option []+      ["make-mods", "make"]+      (NoArg $ \ opts -> opts { action = Make })+      "determine dependency order of modules and automatically build .fsmod files"+  , Option []+      ["show-make-graph"]+      (NoArg $ \ opts -> opts { action = ShowMakeGraph })+      "dump a graph showing the build structure of modules"+  , Option []       ["show-block-numbers"]       (OptArg (\a opts -> opts { action = ShowBlocks (a >>= readMaybe) }               ) "LINE-NUM")@@ -358,7 +464,7 @@     (o, n, []) -> return (foldl (flip id) initOptions o, n)     (_, _, errors) -> ioError $ userError $ concat errors ++ usageInfo header options   where-    header = "Usage: " ++ programName ++ " [OPTION...] <file>"+    header = "Usage: " ++ programName ++ " [OPTION...] <file...>"  instance {-# OVERLAPPING #-} Show [ FixedForm.Token ] where   show = unlines . lines'
test/Language/Fortran/Parser/Fortran77/ParserSpec.hs view
@@ -135,6 +135,11 @@             exp = ExpSubscript () u (varGen "a") (AList () u [ range ])         eParser "a(5:)" `shouldBe'` exp +      it "parses literal string subscript" $ do+        let range = IxRange () u (Just $ intGen 1) (Just $ intGen 2) Nothing+            exp = ExpSubscript () u (strGen "abc") (AList () u [ range ])+        eParser "'abc'(1:2)" `shouldBe'` exp+     describe "GOTO" $ do       it "parses computed GOTO with integer expression" $ do         let exp = ExpBinary () u Multiplication (intGen 42) (intGen 24)@@ -229,6 +234,20 @@                     AList () u [DeclVariable () u (varGen "r") Nothing Nothing]]                  ]             st = StStructure () u (Just "foo") $ AList () u [StructUnion () u $ AList () u ds]+        resetSrcSpan (slParser src) `shouldBe` st++      it "parses nested structure blocks" $ do+        let src = init+                $ unlines [ "      structure /foo/"+                          , "        structure /bar/ baz"+                          , "          integer qux"+                          , "        end structure"+                          , "      end structure"]+            var = DeclVariable () u (varGen "qux") Nothing Nothing+            innerst = StructStructure () u (Just "bar") ("baz")+              $ AList () u [StructFields () u (TypeSpec () u TypeInteger Nothing) Nothing+                $ AList () u [var]]+            st = StStructure () u (Just "foo") $ AList () u [innerst]         resetSrcSpan (slParser src) `shouldBe` st        it "parses character declarations with unspecfied lengths" $ do
test/Language/Fortran/Transformation/GroupingSpec.hs view
@@ -4,7 +4,7 @@ import Test.Hspec hiding (Selector) import TestUtil import Control.Exception (evaluate)-import Control.DeepSeq (force, NFData)+import Control.DeepSeq (force) import Data.ByteString.Char8 (ByteString, pack)  import Language.Fortran.Transformer@@ -20,51 +20,6 @@ groupDo = transform [ GroupLabeledDo ] groupForall :: ProgramFile () -> ProgramFile () groupForall = transform [ GroupForall ]--instance NFData MetaInfo-instance NFData FortranVersion-instance NFData SrcSpan-instance NFData Position-instance NFData CharacterLen-instance NFData BaseType-instance NFData UnaryOp-instance NFData BinaryOp-instance NFData Only-instance NFData ModuleNature-instance NFData Intent-instance (NFData a, NFData (t a)) => NFData (AList t a)-instance NFData a => NFData (ProgramFile a)-instance NFData a => NFData (ProgramUnit a)-instance NFData a => NFData (Block a)-instance NFData a => NFData (Expression a)-instance NFData a => NFData (TypeSpec a)-instance NFData a => NFData (Index a)-instance NFData a => NFData (Value a)-instance NFData a => NFData (Comment a)-instance NFData a => NFData (Statement a)-instance NFData a => NFData (ProcDecl a)-instance NFData a => NFData (ProcInterface a)-instance NFData a => NFData (DoSpecification a)-instance NFData a => NFData (Selector a)-instance NFData a => NFData (ForallHeader a)-instance NFData a => NFData (Argument a)-instance NFData a => NFData (Use a)-instance NFData a => NFData (Attribute a)-instance NFData a => NFData (CommonGroup a)-instance NFData a => NFData (ControlPair a)-instance NFData a => NFData (AllocOpt a)-instance NFData a => NFData (DataGroup a)-instance NFData a => NFData (DimensionDeclarator a)-instance NFData a => NFData (Declarator a)-instance NFData a => NFData (FormatItem a)-instance NFData a => NFData (FlushSpec a)-instance NFData a => NFData (ImpElement a)-instance NFData a => NFData (ImpList a)-instance NFData a => NFData (Namelist a)-instance NFData a => NFData (Prefix a)-instance NFData a => NFData (Suffix a)-instance NFData a => NFData (StructureItem a)-instance NFData a => NFData (UnionMap a)  spec :: Spec spec = do