-- | 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 :: FortranVersion
-> [String] -> String -> ByteString -> IO (ProgramFile A0)
versionedExpandedProgramFile FortranVersion
version [String]
importDirs String
path ByteString
contents = case FortranVersion
version of
  FortranVersion
Fortran77Legacy ->
    let parserF :: ByteString -> String -> IO (ProgramFile A0)
parserF ByteString
b String
s =
            Either ParseErrorSimple (ProgramFile A0) -> ProgramFile A0
forall a b. Show a => Either a b -> b
fromRight
              (Either ParseErrorSimple (ProgramFile A0) -> ProgramFile A0)
-> (ParseResult AlexInput Token (ProgramFile A0)
    -> Either ParseErrorSimple (ProgramFile A0))
-> ParseResult AlexInput Token (ProgramFile A0)
-> ProgramFile A0
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ParseResult AlexInput Token (ProgramFile A0)
-> Either ParseErrorSimple (ProgramFile A0)
forall c b a.
Show c =>
ParseResult b c a -> Either ParseErrorSimple a
fromParseResult
              (ParseResult AlexInput Token (ProgramFile A0) -> ProgramFile A0)
-> IO (ParseResult AlexInput Token (ProgramFile A0))
-> IO (ProgramFile A0)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [String]
-> ByteString
-> String
-> IO (ParseResult AlexInput Token (ProgramFile A0))
legacy77ParserWithIncludes (String -> String
takeDirectory String
path String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String]
importDirs) ByteString
b String
s
    in  ByteString -> String -> IO (ProgramFile A0)
parserF ByteString
contents String
path
  FortranVersion
_ -> String -> IO (ProgramFile A0)
forall a. HasCallStack => String -> a
error (String
"Unsupported version: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ FortranVersion -> String
forall a. Show a => a -> String
show FortranVersion
version)

-- | Obtain a 'ProgramFile' from a specific version of the parser.
versionedProgramFile
  :: FortranVersion -> String -> B.ByteString -> ProgramFile A0
versionedProgramFile :: FortranVersion -> String -> ByteString -> ProgramFile A0
versionedProgramFile FortranVersion
version String
path ByteString
contents =
  let Just Parser
parserF = FortranVersion -> [(FortranVersion, Parser)] -> Maybe Parser
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup FortranVersion
version [(FortranVersion, Parser)]
parserVersions
      pf :: ProgramFile A0
pf           = Either ParseErrorSimple (ProgramFile A0) -> ProgramFile A0
forall a b. Show a => Either a b -> b
fromRight (Either ParseErrorSimple (ProgramFile A0) -> ProgramFile A0)
-> Either ParseErrorSimple (ProgramFile A0) -> ProgramFile A0
forall a b. (a -> b) -> a -> b
$ Parser
parserF ByteString
contents String
path -- Parse contents of file
  in  ProgramFile A0
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 :: String -> ByteString -> ProgramFile A0
programFile String
path ByteString
contents =
  let version :: FortranVersion
version = String -> FortranVersion
deduceFortranVersion String
path -- suggest version from file extension
  in  FortranVersion -> String -> ByteString -> ProgramFile A0
versionedProgramFile FortranVersion
version String
path ByteString
contents