fortran-src-extras (empty) → 0.1.0
raw patch · 15 files changed
+801/−0 lines, 15 filesdep +GenericPrettydep +aesondep +basesetup-changed
Dependencies added: GenericPretty, aeson, base, binary, bytestring, containers, directory, filepath, fortran-src, fortran-src-extras, hspec, optparse-applicative, silently, text, uniplate
Files
- CHANGELOG.md +9/−0
- LICENSE +13/−0
- README.md +15/−0
- Setup.hs +2/−0
- fortran-src-extras.cabal +75/−0
- src/Language/Fortran/Extras.hs +218/−0
- src/Language/Fortran/Extras/Analysis.hs +85/−0
- src/Language/Fortran/Extras/Encoding.hs +42/−0
- src/Language/Fortran/Extras/ModFiles.hs +50/−0
- src/Language/Fortran/Extras/ProgramFile.hs +51/−0
- src/Language/Fortran/Extras/RunOptions.hs +104/−0
- src/Language/Fortran/Extras/Test.hs +55/−0
- test/AnalysisSpec.hs +53/−0
- test/ExtrasSpec.hs +28/−0
- test/Spec.hs +1/−0
+ CHANGELOG.md view
@@ -0,0 +1,9 @@+## 0.1.0+Initial release.++### Major changes from original package+ * Use `Language.Fortran.Util.Position.getSpan` instead of `extractExpSrcSpan`,+ `extractBlockSrcSpan`. AST nodes storing location info are instances of the+ `Spanned` typeclass which allows easy `SrcSpan` extraction.+ * `Encoding.pprint` now `Encoding.pprint77l`+ * Rewriter has moved: `Language.Fortran.Rewriter` (in fortran-src package)
+ LICENSE view
@@ -0,0 +1,13 @@+Copyright (c) 2021 CamFort contributors++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++ http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.
+ README.md view
@@ -0,0 +1,15 @@+# fortran-src-extras+Various utility functions and orphan instances which may be useful when using+fortran-src.++## Contributors+Thanks to the original package authors:++ * Jason Xu <jxu116@bloomberg.net>+ * Anthony Burzillo <aburzillo@bloomberg.net>+ * Daniel Beer <dbeer1@bloomberg.net>+ * Aiden Jeffrey <ajeffrey@bloomberg.net>+ * Raoul Hidalgo Charman <rhidalgochar@bloomberg.net>+ * Lukasz Kolodziejczyk <lkolodziejc1@bloomberg.net>+ * Eric Seidel <eseidel13@bloomberg.net>+ * Darius Makovsky <bapophis@bloomberg.net>
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ fortran-src-extras.cabal view
@@ -0,0 +1,75 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.33.0.+--+-- see: https://github.com/sol/hpack+--+-- hash: 58acdfe9c45070482fedea0466e7e18d8b2fcb95dbe956761a628ca1a3067696++name: fortran-src-extras+version: 0.1.0+synopsis: Common functions and utils for fortran-src.+description: Various utility functions and orphan instances which may be useful when using fortran-src.+category: Language+homepage: https://github.com/camfort/fortran-src-extras#readme+bug-reports: https://github.com/camfort/fortran-src-extras/issues+maintainer: thefirstmuffinman@gmail.com+license: Apache-2.0+license-file: LICENSE+build-type: Simple+extra-source-files:+ README.md+ CHANGELOG.md++source-repository head+ type: git+ location: https://github.com/camfort/fortran-src-extras++library+ exposed-modules:+ Language.Fortran.Extras+ Language.Fortran.Extras.Analysis+ Language.Fortran.Extras.Encoding+ Language.Fortran.Extras.ModFiles+ Language.Fortran.Extras.ProgramFile+ Language.Fortran.Extras.RunOptions+ Language.Fortran.Extras.Test+ other-modules:+ Paths_fortran_src_extras+ hs-source-dirs:+ src+ build-depends:+ GenericPretty >=1.2.1+ , aeson >=1.2.3.0+ , base >=4.7 && <5+ , binary >=0.8.5.1+ , bytestring >=0.10.8.1+ , containers >=0.5.0.0+ , directory >=1.3.0.2+ , filepath >=1.4.1.2+ , fortran-src >=0.4.2+ , optparse-applicative >=0.14+ , text >=1.2.2.2+ , uniplate >=1.6.10+ default-language: Haskell2010++test-suite spec+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ other-modules:+ AnalysisSpec+ ExtrasSpec+ Paths_fortran_src_extras+ hs-source-dirs:+ test+ ghc-options: -threaded -rtsopts -with-rtsopts=-N+ build-tool-depends:+ hspec-discover:hspec-discover+ build-depends:+ base >=4.7 && <5+ , fortran-src >=0.4.2+ , fortran-src-extras+ , hspec >=2.2 && <3+ , silently >=1.2 && <1.3+ , uniplate >=1.6.10+ default-language: Haskell2010
+ src/Language/Fortran/Extras.hs view
@@ -0,0 +1,218 @@+module Language.Fortran.Extras where++import Control.Exception ( try+ , SomeException+ )+import Data.Data ( Data )+import Data.List ( find )+import Data.Maybe ( fromMaybe+ , mapMaybe+ )+import Data.Generics.Uniplate.Data ( universeBi )+import Language.Fortran.AST ( A0+ , Block+ , ProgramFile+ , Statement+ , ProgramUnit(..)+ , ProgramUnitName(..)+ )+import Language.Fortran.Analysis ( Analysis+ , puSrcName+ )+import Language.Fortran.Version ( FortranVersion(..) )+import System.Exit ( ExitCode(..)+ , exitWith+ )+import System.IO ( hPutStr+ , hPutStrLn+ , stderr+ )+import Options.Applicative+import qualified Language.Fortran.Extras.ProgramFile+ as P+import qualified Language.Fortran.Extras.Analysis+ as A+import Language.Fortran.Extras.ModFiles+ ( decodeModFiles )+import Language.Fortran.Extras.RunOptions+ ( unwrapFortranSrcOptions+ , getFortranSrcRunOptions+ , getRunOptions+ , FortranSrcRunOptions(..)+ , RunOptions(..)+ )++-- | Get a list of all 'Block's in a 'ProgramFile'+allB :: Data a => ProgramFile a -> [Block a]+allB = universeBi++-- | Get a list of all 'Statement's in a 'ProgramFile'+allS :: Data a => ProgramFile a -> [Statement a]+allS = universeBi++-- | Get a list of all 'ProgramUnit's in a 'ProgramFile'+allPU :: Data a => ProgramFile a -> [ProgramUnit a]+allPU = universeBi++-- | Get a list of all 'Block's in a 'ProgramUnit'+allPUB :: Data a => ProgramUnit a -> [Block a]+allPUB = universeBi++-- | Get a list of all 'Statement's in a 'ProgramUnit'+allPUS :: Data a => ProgramUnit a -> [Statement a]+allPUS = universeBi++-- | Given a 'ProgramFile' find a 'ProgramUnit' with a particular 'ProgramUnitName'+findPU'+ :: Data a+ => ProgramUnitName+ -> ProgramFile (Analysis a)+ -> Maybe (ProgramUnit (Analysis a))+findPU' n = find (\pu -> puSrcName pu == n) . allPU++-- | Given a 'ProgramFile' find a 'ProgramUnit' with a particular name+findPU+ :: Data a+ => String+ -> ProgramFile (Analysis a)+ -> Maybe (ProgramUnit (Analysis a))+findPU n = findPU' $ Named n++-- | Get a 'ProgramFile' from version and path specified in 'FortranSrcRunOptions'+programFile :: FortranSrcRunOptions -> IO (ProgramFile A0)+programFile options = do+ (pfPath, pfContents, pfIncludes, fVersion) <- unwrapFortranSrcOptions options+ case fVersion of+ Fortran77Legacy ->+ P.versionedExpandedProgramFile fVersion pfIncludes pfPath pfContents+ _ -> return $ P.versionedProgramFile fVersion pfPath pfContents++-- | Get a 'ProgramFile' with 'Analysis' from version and path specified+-- in 'FortranSrcRunOptions'+programAnalysis :: FortranSrcRunOptions -> IO (ProgramFile (Analysis A0))+programAnalysis options = do+ (pfPath, pfContents, pfIncludes, fVersion) <- unwrapFortranSrcOptions options+ case fVersion of+ Fortran77Legacy ->+ A.versionedExpandedProgramAnalysis fVersion pfIncludes pfPath pfContents+ _ -> if null pfIncludes+ then return $ A.versionedProgramAnalysis fVersion pfPath pfContents+ else do+ pfMods <- decodeModFiles pfIncludes+ return $ A.versionedProgramAnalysisWithMods fVersion+ pfMods+ pfPath+ pfContents++-- | Parse arguments and return 'ProgramFile'+--+-- This function has the purpose of being the general entry-point for the `fortran-src-tools` tools.+-- It handles parsing the common arguments and then returns a 'ProgramFile' that the tool can then use to+-- do further processing.+-- +-- This function takes in two arguments, namely:+--+-- * A description of the program that shows up when the program is invoked incorrectly+-- @+-- $ some-fortran-tool+-- Missing: (-v|--fortranVersion VERSION) PATH+-- +-- Usage: vars (-v|--fortranVersion VERSION) [-I|--include DIRECTORY] PATH+-- THIS IS WHERE THE DESCRIPTION GOES+-- @+--+-- * A header that is shown when the user passes the `--help` argument to the tool (note that this does+-- not show up when the program is invoked incorrectly)+-- @+-- $ some-fortran-tool --help+-- THIS IS WHERE THE HEADER GOES+-- +-- Usage: vars (-v|--fortranVersion VERSION) [-I|--include DIRECTORY] PATH+-- THIS IS WHERE THE DESCRIPTION GOES+-- +-- Available options:+-- -h,--help Show this help text+-- -v,--fortranVersion VERSION+-- Fortran version to use, format:+-- Fortran[66/77/BigIron/77Legacy/77Extended/90]+-- -I,--include DIRECTORY Directory to include files from+-- @+getProgramFile :: String -> String -> IO (ProgramFile A0)+getProgramFile programDescription programHeader = do+ options <- getFortranSrcRunOptions programDescription programHeader+ programFile options++-- | Parse arguments and return a 'ProgramFile' with 'Analysis'+--+-- This function takes the same arguments as 'getProgramFile', however it will return an 'Analysis' object+-- within the 'ProgramFile' monad. If any `-I DIR` arguments were specified when invoking the tool, this+-- function will ensure that any module files located in this directory are loaded and incorporated into the+-- analysis.+getProgramAnalysis :: String -> String -> IO (ProgramFile (Analysis A0))+getProgramAnalysis programDescription programHeader = do+ options <- getFortranSrcRunOptions programDescription programHeader+ programAnalysis options++-- | Helper to print out exceptions with the name of the file being processed+errorHandler :: String -> Either SomeException () -> IO ()+errorHandler filename (Left e) = do+ hPutStrLn stderr $ "Caught exception in file: " ++ filename+ hPutStr stderr . unlines . map (" " ++) . lines $ show e+ exitWith $ ExitFailure 2+errorHandler _ (Right _) = return ()++-- | Given a program description, a program header, and a handler that+-- takes a 'ProgramFile', this function generates the 'ProgramFile'+-- and passes it to the handler, while catching any exceptions that+-- occur within either the parsing of the 'ProgramFile' itself+-- or while the handler is processing+withProgramFile :: String -> String -> (ProgramFile A0 -> IO ()) -> IO ()+withProgramFile programDescription programHeader handler = do+ options <- getFortranSrcRunOptions programDescription programHeader+ results <- try $ programFile options >>= handler+ errorHandler (path options) results++-- | Given a program description, a program header, and a handler that+-- takes a 'ProgramFile', this function generates the 'ProgramFile'+-- annotated with 'Analysis' and passes it to the handler,+-- while catching any exceptions that occur within either the parsing+-- of the 'ProgramFile' itself or while the handler is processing+withProgramAnalysis+ :: String -> String -> (ProgramFile (Analysis A0) -> IO ()) -> IO ()+withProgramAnalysis programDescription programHeader handler = do+ options <- getFortranSrcRunOptions programDescription programHeader+ results <- try $ programAnalysis options >>= handler+ errorHandler (path options) results++-- | Given a program description, a program header, a parser for+-- tool CLI options, and a handler that takes tool CLI options object,+-- and a 'ProgramFile', this function generates tool CLI options object,+-- and a 'ProgramFile' annotated with 'Analysis', and passes them to the+-- handler, while catching any exceptions that occurs during handler+-- processing+withToolOptionsAndProgramAnalysis+ :: String+ -> String+ -> Parser a+ -> (a -> ProgramFile (Analysis A0) -> IO ())+ -> IO ()+withToolOptionsAndProgramAnalysis programDescription programHeader toolOptsParser handler+ = do+ options <- getRunOptions programDescription programHeader toolOptsParser+ let (fortranSrcOptions, toolOptions) =+ (fortranSrcOpts options, toolOpts options)+ results <- try $ programAnalysis fortranSrcOptions >>= handler toolOptions+ errorHandler (path fortranSrcOptions) results++-- | Given a 'ProgramUnit' return a pair of the name of the unit as well as the unit itself+-- only if the 'ProgramUnit' is a 'PUMain', 'PUSubroutine', or a 'PUFunction'+namedProgramUnit :: Data a => ProgramUnit a -> Maybe (String, ProgramUnit a)+namedProgramUnit pu@(PUMain _ _ mn _ _) = Just (fromMaybe "MAIN" mn, pu)+namedProgramUnit pu@(PUSubroutine _ _ _ n _ _ _) = Just (n, pu)+namedProgramUnit pu@(PUFunction _ _ _ _ n _ _ _ _) = Just (n, pu)+namedProgramUnit _ = Nothing++-- | Given a 'ProgramFile' return all of the named 'ProgramUnits' within the file, i.e.+-- 'PUMain's, 'PUSubroutine's, or 'PUFunction's+namedProgramUnits :: Data a => ProgramFile a -> [(String, ProgramUnit a)]+namedProgramUnits = mapMaybe namedProgramUnit . allPU
+ src/Language/Fortran/Extras/Analysis.hs view
@@ -0,0 +1,85 @@+-- | This module exposes functions obtaining both explicitly versioned+-- and implicitly versionied analyses of source code.+module Language.Fortran.Extras.Analysis+ ( versionedExpandedProgramAnalysis+ , versionedProgramAnalysis+ , versionedProgramAnalysisWithMods+ , programAnalysis+ , programAnalysisWithMods+ )+where++import qualified Data.ByteString.Char8 as B+import Language.Fortran.AST ( A0+ , ProgramFile+ )+import Language.Fortran.Analysis ( Analysis+ , initAnalysis+ )+import Language.Fortran.Analysis.Types+ ( analyseTypes+ , analyseTypesWithEnv+ )+import Language.Fortran.Version ( FortranVersion(..) )+import Language.Fortran.Util.ModFile ( combinedTypeEnv+ , ModFiles+ )++import Language.Fortran.Extras.ProgramFile+ ( programFile+ , versionedProgramFile+ , versionedExpandedProgramFile+ )++-- | Obtain the analysis of the 'ProgramFile'.+programAnalysis' :: ProgramFile A0 -> ProgramFile (Analysis A0)+programAnalysis' = fst . analyseTypes . initAnalysis++-- | Obtain the analysis of the 'ProgramFile' and modules.+programAnalysisWithMods'+ :: ModFiles -> ProgramFile A0 -> ProgramFile (Analysis A0)+programAnalysisWithMods' mods =+ let tenv = combinedTypeEnv mods+ in fst . analyseTypesWithEnv tenv . initAnalysis++-- | Obtain the analysis of source code with imports expanded using+-- a specific version of the parser.+versionedExpandedProgramAnalysis+ :: FortranVersion+ -> [String]+ -> String+ -> B.ByteString+ -> IO (ProgramFile (Analysis A0))+versionedExpandedProgramAnalysis version importDirs path contents = do+ pf <- versionedExpandedProgramFile version importDirs path contents+ return $ programAnalysis' pf++-- | Obtain the analysis of source code using an explicit version of+-- the parser.+versionedProgramAnalysis+ :: FortranVersion -> String -> B.ByteString -> ProgramFile (Analysis A0)+versionedProgramAnalysis version path contents =+ programAnalysis' $ versionedProgramFile version path contents++-- | Obtain the analysis of source code and module files using an+-- explicit version of the parser.+versionedProgramAnalysisWithMods+ :: FortranVersion+ -> ModFiles+ -> String+ -> B.ByteString+ -> ProgramFile (Analysis A0)+versionedProgramAnalysisWithMods version mods path contents =+ programAnalysisWithMods' mods $ versionedProgramFile version path contents++-- | Obtain the analysis of source code using an implicit version of+-- the parser.+programAnalysis :: String -> B.ByteString -> ProgramFile (Analysis A0)+programAnalysis path contents = programAnalysis' $ programFile path contents++-- | Obtain the analysis of source code and module files using an+-- implicit version of the parser.+programAnalysisWithMods+ :: ModFiles -> String -> B.ByteString -> ProgramFile (Analysis A0)+programAnalysisWithMods mods path contents =+ programAnalysisWithMods' mods $ programFile path contents
+ src/Language/Fortran/Extras/Encoding.hs view
@@ -0,0 +1,42 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}++-- | Utils and Aeson orphan instances for common types in the AST.+module Language.Fortran.Extras.Encoding where++import Data.Aeson ( ToJSON+ , FromJSON+ , encode+ )+import Language.Fortran.AST ( BaseType+ , CharacterLen+ )+import Language.Fortran.Version ( FortranVersion(..) )+import Language.Fortran.PrettyPrint ( IndentablePretty+ , pprintAndRender+ )+import Language.Fortran.Util.Position ( Position+ , SrcSpan+ )+import Data.ByteString.Lazy ( ByteString )++-- | Provide a wrapper for the 'Data.Aeson.encode' function to allow+-- indirect use in modules importing+-- 'Language.Fortran.Extras.Encoding'.+commonEncode :: ToJSON a => a -> ByteString+commonEncode = encode++instance ToJSON Position+instance FromJSON Position++instance ToJSON SrcSpan+instance FromJSON SrcSpan++instance ToJSON CharacterLen+instance FromJSON CharacterLen++instance ToJSON BaseType+instance FromJSON BaseType++-- | Render some AST element to a 'String' using F77 legacy mode.+pprint77l :: IndentablePretty a => a -> String+pprint77l s = pprintAndRender Fortran77Legacy s Nothing
+ src/Language/Fortran/Extras/ModFiles.hs view
@@ -0,0 +1,50 @@+-- | This module provides functions for handling FORTRAN source modules.+module Language.Fortran.Extras.ModFiles+ ( isModFile+ , decodeModFiles+ )+where++import Control.Monad ( foldM+ , forM+ )+import Data.Binary ( decodeFileOrFail )+import Language.Fortran.Util.ModFile ( emptyModFile+ , emptyModFiles+ , ModFiles+ , modFileSuffix+ )+import Language.Fortran.Util.Files ( rGetDirContents )+import System.FilePath ( takeExtension+ , (</>)+ )++-- | Return TRUE iff the file extension indicates a module file.+isModFile :: String -> Bool+isModFile = (== modFileSuffix) . takeExtension++-- | Read suspected module files from a list of files and obtain the+-- 'Language.Fortran.Util.ModFiles' object populated by their contents.+--+-- TODO: almost equal to Language.Fortran.Analysis.ModGraph.decodeModFiles+decodeModFiles :: [FilePath] -> IO ModFiles+decodeModFiles = foldM+ (\modFiles d -> do+ modFileNames <- filter isModFile `fmap` rGetDirContents d+ addedModFiles <- forM modFileNames $ \modFileName -> do+ eResult <- decodeFileOrFail (d </> modFileName)+ case eResult of+ Left (offset, msg) -> do+ putStrLn+ $ modFileName+ ++ ": Error at offset "+ ++ show offset+ ++ ": "+ ++ msg+ return emptyModFile+ Right modFile -> do+ putStrLn $ modFileName ++ ": successfully parsed precompiled file."+ return modFile+ return $ addedModFiles ++ modFiles+ )+ emptyModFiles
+ src/Language/Fortran/Extras/ProgramFile.hs view
@@ -0,0 +1,51 @@+-- | This module exposes functions obtaining+-- 'Language.Fortran.AST.ProgramFile' from a valid file name.+module Language.Fortran.Extras.ProgramFile where++import qualified Data.ByteString.Char8 as B+import Language.Fortran.AST ( A0+ , ProgramFile+ )+import Language.Fortran.Version ( deduceFortranVersion+ , FortranVersion(..)+ )+import Language.Fortran.Parser.Any ( parserVersions+ )+import Language.Fortran.ParserMonad ( fromParseResult+ , fromRight+ )+import Language.Fortran.Parser.Fortran77+ ( legacy77ParserWithIncludes )+import System.FilePath ( takeDirectory )++-- | Obtain a 'ProgramFile' from a specific version of the parser with include+-- statements expanded.+--+-- TODO: cover all FortranVersions, instead of just Fortran77Legacy+versionedExpandedProgramFile+ :: FortranVersion -> [String] -> String -> B.ByteString -> IO (ProgramFile A0)+versionedExpandedProgramFile version importDirs path contents = case version of+ Fortran77Legacy ->+ let parserF b s =+ fromRight+ . fromParseResult+ <$> legacy77ParserWithIncludes (takeDirectory path : importDirs) b s+ in parserF contents path+ _ -> error ("Unsupported version: " ++ show version)++-- | Obtain a 'ProgramFile' from a specific version of the parser.+versionedProgramFile+ :: FortranVersion -> String -> B.ByteString -> ProgramFile A0+versionedProgramFile version path contents =+ let Just parserF = lookup version parserVersions+ pf = fromRight $ parserF contents path -- Parse contents of file+ in pf++-- | Obtain a 'ProgramFile' from a parser version deduced by inspection+-- of the file extension.+--+-- For example "foo.f90" will deduce the 'Fortran90' Parser version.+programFile :: String -> B.ByteString -> ProgramFile A0+programFile path contents =+ let version = deduceFortranVersion path -- suggest version from file extension+ in versionedProgramFile version path contents
+ src/Language/Fortran/Extras/RunOptions.hs view
@@ -0,0 +1,104 @@+{-# LANGUAGE FlexibleInstances #-}++-- | This module is responsible for handling CLI options+module Language.Fortran.Extras.RunOptions+ ( FortranSrcRunOptions(..)+ , getFortranSrcRunOptions+ , unwrapFortranSrcOptions+ , RunOptions(..)+ , getRunOptions+ )+where++import qualified Data.ByteString.Char8 as B+import Data.Char ( toLower )+import Data.List ( isInfixOf )+import Data.Semigroup ( (<>) )+import Language.Fortran.Version ( FortranVersion(..)+ , selectFortranVersion+ )+import Options.Applicative++import Language.Fortran.Util.Files+ ( flexReadFile )++-- | Holds fortran-src specific CLI options.+-- This includes the version of the parser, included files and the path+-- of the source+data FortranSrcRunOptions = FortranSrcRunOptions+ { version :: !FortranVersion+ , includes :: ![String]+ , path :: !String+ } deriving Show++-- | Provided version, includes and path strings, this functon maps them to+-- 'FortranSrcRunOptions'+--+-- Note that this will throw an exception on an unrecognized version string.+toFortranSrcOptions :: String -> [String] -> String -> FortranSrcRunOptions+toFortranSrcOptions verStr =+ let (Just ver) = selectFortranVersion verStr+ in FortranSrcRunOptions ver++-- | Definition of parser for 'FortranSrcRunOptions'+fortranSrcRunOptionsParser :: Parser FortranSrcRunOptions+fortranSrcRunOptionsParser =+ toFortranSrcOptions+ <$> strOption+ ( short 'v'+ <> long "fortranVersion"+ <> metavar "VERSION"+ <> help+ "Fortran version to use, format: Fortran[66/77/77l/77e/90/95/03/08]"+ )+ <*> many+ (strOption+ (short 'I' <> long "include" <> metavar "DIRECTORY" <> help+ "Directory to include files from"+ )+ )+ <*> argument str (metavar "PATH")++-- | Given description and header, decorate fortran-src options parser with+-- info details+fortranSrcRunOptionsInfo :: String -> String -> ParserInfo FortranSrcRunOptions+fortranSrcRunOptionsInfo programDescription headerDescription = info+ (helper <*> fortranSrcRunOptionsParser)+ (fullDesc <> progDesc programDescription <> header headerDescription)++-- | Given description and header, execute fortran-src options parser+-- and get the 'FortranSrcRunOptions'+getFortranSrcRunOptions :: String -> String -> IO FortranSrcRunOptions+getFortranSrcRunOptions programDescription headerDescription =+ execParser $ fortranSrcRunOptionsInfo programDescription headerDescription++-- | Obtain path, contents, include dirs and 'FortranVersion'+-- from 'FortranSrcRunOptions'+unwrapFortranSrcOptions+ :: FortranSrcRunOptions -> IO (String, B.ByteString, [String], FortranVersion)+unwrapFortranSrcOptions options = do+ let p = path options+ c <- flexReadFile p+ let i = includes options+ v = version options+ return (p, c, i, v)++-- | Holds 'FortranSrcRunOptions' and additional tool specific CLI options+data RunOptions a = RunOptions+ { fortranSrcOpts :: FortranSrcRunOptions+ , toolOpts :: a+ } deriving Show++-- | Given decription, header and tool options parser, decorate options+-- with info details+runOptionsInfo :: String -> String -> Parser a -> ParserInfo (RunOptions a)+runOptionsInfo programDescription headerDescription toolOptsParser = info+ (helper <*> liftA2 RunOptions fortranSrcRunOptionsParser toolOptsParser)+ (fullDesc <> progDesc programDescription <> header headerDescription)++-- | Given description, header and tool options parser, execute options parser+-- and get the 'RunOptions'+getRunOptions :: String -> String -> Parser a -> IO (RunOptions a)+getRunOptions programDescription headerDescription toolOptsParser =+ execParser+ $ runOptionsInfo programDescription headerDescription toolOptsParser
+ src/Language/Fortran/Extras/Test.hs view
@@ -0,0 +1,55 @@+{-# LANGUAGE OverloadedStrings #-}+module Language.Fortran.Extras.Test where++import qualified Data.ByteString.Lazy.Char8 as BC+import Language.Fortran.Analysis ( Analysis )+import Language.Fortran.AST ( A0+ , ProgramFile+ )+import Language.Fortran.Version ( FortranVersion(..) )++import Language.Fortran.Extras.Analysis+ ( versionedExpandedProgramAnalysis+ )+import Language.Fortran.Extras.ProgramFile+ ( versionedExpandedProgramFile )+import Language.Fortran.Util.Files+ ( flexReadFile )++getTestProgramFile :: String -> IO (ProgramFile A0)+getTestProgramFile p = do+ cts <- flexReadFile p+ versionedExpandedProgramFile Fortran77Legacy [] p cts++getTestProgramFileIncludes :: String -> [String] -> IO (ProgramFile A0)+getTestProgramFileIncludes p incls = do+ cts <- flexReadFile p+ versionedExpandedProgramFile Fortran77Legacy incls p cts++getTestProgramAnalysis :: String -> IO (ProgramFile (Analysis A0))+getTestProgramAnalysis p = do+ cts <- flexReadFile p+ versionedExpandedProgramAnalysis Fortran77Legacy [] p cts++getTestProgramAnalysisIncludes+ :: String -> [String] -> IO (ProgramFile (Analysis A0))+getTestProgramAnalysisIncludes p incls = do+ cts <- flexReadFile p+ versionedExpandedProgramAnalysis Fortran77Legacy incls p cts++-- | Utility function to compare file content+compareFile :: FilePath -> FilePath -> IO Bool+compareFile expected actual = do+ c1 <- BC.readFile expected+ c2 <- BC.readFile actual+ compareByteString c1 c2++compareByteString :: BC.ByteString -> BC.ByteString -> IO Bool+compareByteString expected actual = if expected == actual+ then return True+ else do+ BC.putStrLn "<<<<<<< EXPECTED"+ BC.putStrLn expected+ BC.putStrLn ">>>>>>> ACTUAL"+ BC.putStrLn actual+ return False
+ test/AnalysisSpec.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE TypeApplications #-}++module AnalysisSpec+ ( spec+ )+where++import Test.Hspec+import Data.Maybe ( isJust )+import Language.Fortran.AST ( A0+ , ProgramFile+ , Expression(..)+ , Statement(..)+ , Value(..)+ )+import Language.Fortran.Analysis ( Analysis )+import Language.Fortran.Version ( FortranVersion(..) )++import Language.Fortran.Extras.Analysis+ ( versionedProgramAnalysis+ , versionedExpandedProgramAnalysis+ )+import Language.Fortran.Util.Files+ ( flexReadFile )+import Data.Generics.Uniplate.Data ( universeBi )++wasIncluded :: ProgramFile (Analysis A0) -> String -> Bool+wasIncluded pa filename = any+ isJust+ [ included+ | StInclude _ _ (ExpValue _ _ (ValString incFilename)) included <- (universeBi pa :: [Statement (Analysis A0)])+ , incFilename == filename+ ]++spec :: Spec+spec = describe "ProgramAnalysis" $ do+ it "versionedProgramAnalysis - no includes expansion" $ do+ let path = "test/input_data/analysis/relative_include/include.f"+ pa <- versionedProgramAnalysis Fortran77Legacy path <$> flexReadFile path+ wasIncluded pa "includee.f" `shouldBe` False++ it "versionedExpandedProgramAnalysis - with expanded includes" $ do+ let path = "test/input_data/analysis/with_include_path/include.f"+ idirs = ["test/input_data/analysis/with_include_path/includes"]+ contents <- flexReadFile path+ pa <- versionedExpandedProgramAnalysis Fortran77Legacy idirs path contents+ wasIncluded pa "includee.f" `shouldBe` True++ it "versionedExpandedProgramAnalysis - with expanded relative includes" $ do+ let path = "test/input_data/analysis/relative_include/include.f"+ contents <- flexReadFile path+ pa <- versionedExpandedProgramAnalysis Fortran77Legacy [] path contents+ wasIncluded pa "includee.f" `shouldBe` True
+ test/ExtrasSpec.hs view
@@ -0,0 +1,28 @@+module ExtrasSpec where++import Test.Hspec++import Control.Exception+import System.Exit ( ExitCode )+import System.IO ( stderr+ , stdout+ )+import System.IO.Silently ( hCapture_ )++import Language.Fortran.Extras+ ( errorHandler )++spec :: Spec+spec = describe "errorHandler" $ do+ let+ doErr s = do+ _ <-+ try $ errorHandler "foo" . Left . toException $ AssertionFailed s :: IO+ (Either ExitCode ())+ return ()+ it "Prints to stderr" $ do+ out <- hCapture_ [stderr] $ doErr "stderr"+ null out `shouldBe` False+ it "Doesn't print to stdout" $ do+ out <- hCapture_ [stdout] $ doErr "stdout"+ null out `shouldBe` True
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}