@@ -13,15 +13,21 @@ combinators are exposed to assist in manually configuring parsers.
1313module Language.Fortran.Parser
1414 (
1515 -- * Main parsers (ProgramFile, with transformation)
16- f66 , f77 , f90 , f95 , f2003
16+ byVer , byVerWithMods
17+ , f66 , f77 , f77e , f77l , f90 , f95 , f2003
1718
1819 -- * Main parsers without post-parse transformation
19- , f66NoTransform , f77NoTransform , f90NoTransform , f95NoTransform , f2003NoTransform
20+ , f66NoTransform , f77NoTransform , f77eNoTransform , f77lNoTransform
21+ , f90NoTransform , f95NoTransform , f2003NoTransform
2022
21- -- * Parser former combinators
23+ -- * Various combinators
2224 , transformAs , defaultTransformation
2325 , StateInit , ParserMaker , makeParser , makeParserFixed , makeParserFree
2426 , initParseStateFixed , initParseStateFree
27+
28+ -- * F77 with inlined includes
29+ -- $f77includes
30+ , f77lIncludes
2531 ) where
2632
2733import Language.Fortran.AST
@@ -36,6 +42,7 @@ import qualified Language.Fortran.Parser.Fixed.Lexer as Fixed
3642import qualified Language.Fortran.Parser.Free.Lexer as Free
3743import Language.Fortran.Version
3844import Language.Fortran.Util.Position
45+ import Language.Fortran.Util.ModFile
3946import Language.Fortran.Transformation.Monad
4047import qualified Language.Fortran.Transformation.Grouping as Trans
4148import qualified Language.Fortran.Transformation.Disambiguation.Function as Trans
@@ -44,24 +51,71 @@ import qualified Language.Fortran.Transformation.Disambiguation.Intrinsic as Tra
4451import qualified Data.ByteString.Char8 as B
4552import Data.Data
4653
54+ import Control.Monad.State
55+ import qualified Data.Map as Map
56+ import Data.Map ( Map )
57+ import Data.Generics.Uniplate.Operations ( descendBiM )
58+ import Control.Exception ( throwIO )
59+ import System.FilePath ( (</>) )
60+ import System.Directory ( doesFileExist )
61+
4762-- | Our common Fortran parser type takes a filename and input, and returns
4863-- either a normalized error (tokens are printed) or an untransformed
4964-- 'ProgramFile'.
5065type Parser a = String -> B. ByteString -> Either ParseErrorSimple a
5166
5267--------------------------------------------------------------------------------
5368
54- f66 , f77 , f90 , f95 , f2003 :: Parser (ProgramFile A0 )
55- f66 = transformAs Fortran66 f66NoTransform
56- f77 = transformAs Fortran77 f77NoTransform
57- f90 = transformAs Fortran90 f90NoTransform
58- f95 = transformAs Fortran95 f95NoTransform
59- f2003 = transformAs Fortran2003 f2003NoTransform
60-
61- f66NoTransform, f77NoTransform, f90NoTransform, f95NoTransform, f2003NoTransform
69+ byVer :: FortranVersion -> Parser (ProgramFile A0 )
70+ byVer = \ case
71+ Fortran66 -> f66
72+ Fortran77 -> f77
73+ Fortran77Extended -> f77e
74+ Fortran77Legacy -> f77l
75+ Fortran90 -> f90
76+ Fortran95 -> f95
77+ Fortran2003 -> f2003
78+ v -> error $ " Language.Fortran.Parser.byVer: "
79+ <> " no parser available for requested version: "
80+ <> show v
81+
82+ byVerWithMods :: ModFiles -> FortranVersion -> Parser (ProgramFile A0 )
83+ byVerWithMods mods = \ case
84+ Fortran66 -> f66Mods mods
85+ Fortran77 -> f77Mods mods
86+ Fortran77Extended -> f77eMods mods
87+ Fortran77Legacy -> f77lMods mods
88+ Fortran90 -> f90Mods mods
89+ Fortran95 -> f95Mods mods
90+ Fortran2003 -> f2003Mods mods
91+ v -> error $ " Language.Fortran.Parser.byVerWithMods: no parser available for requested version: " <> show v
92+
93+ f66 , f77 , f77e , f77l , f90 , f95 , f2003 :: Parser (ProgramFile A0 )
94+ f66 = f66Mods []
95+ f77 = f77Mods []
96+ f77e = f77eMods []
97+ f77l = f77lMods []
98+ f90 = f90Mods []
99+ f95 = f95Mods []
100+ f2003 = f2003Mods []
101+
102+ f66Mods, f77Mods, f77eMods, f77lMods, f90Mods, f95Mods, f2003Mods
103+ :: ModFiles -> Parser (ProgramFile A0 )
104+ f66Mods = transformAs Fortran66 f66NoTransform
105+ f77Mods = transformAs Fortran77 f77NoTransform
106+ f77eMods = transformAs Fortran77Extended f77NoTransform
107+ f77lMods = transformAs Fortran77Legacy f77NoTransform
108+ f90Mods = transformAs Fortran90 f90NoTransform
109+ f95Mods = transformAs Fortran95 f95NoTransform
110+ f2003Mods = transformAs Fortran2003 f2003NoTransform
111+
112+ f66NoTransform, f77NoTransform, f77eNoTransform, f77lNoTransform,
113+ f90NoTransform, f95NoTransform, f2003NoTransform
62114 :: Parser (ProgramFile A0 )
63115f66NoTransform = makeParserFixed F66. programParser Fortran66
64116f77NoTransform = makeParserFixed F77. programParser Fortran77
117+ f77eNoTransform = makeParserFixed F77. programParser Fortran77Extended
118+ f77lNoTransform = makeParserFixed F77. programParser Fortran77Legacy
65119f90NoTransform = makeParserFree F90. programParser Fortran90
66120f95NoTransform = makeParserFree F95. programParser Fortran95
67121f2003NoTransform = makeParserFree F2003. programParser Fortran2003
@@ -70,9 +124,15 @@ f2003NoTransform = makeParserFree F2003.programParser Fortran2003
70124
71125transformAs
72126 :: Data a
73- => FortranVersion -> Parser (ProgramFile a ) -> Parser (ProgramFile a )
74- transformAs fv p fn bs =
75- runTransform mempty mempty (defaultTransformation fv) <$> p fn bs
127+ => FortranVersion -> Parser (ProgramFile a ) -> ModFiles
128+ -> Parser (ProgramFile a )
129+ transformAs fv p mods fn bs = do
130+ pf <- p fn bs
131+ let pf' = pfSetFilename fn pf
132+ return $ transform pf'
133+ where transform = runTransform (combinedTypeEnv mods)
134+ (combinedModuleMap mods)
135+ (defaultTransformation fv)
76136
77137-- | The default post-parse AST transformation for each Fortran version.
78138--
@@ -130,3 +190,60 @@ initParseStatePartial = ParseState
130190 , psFilename = undefined
131191 , psParanthesesCount = ParanthesesCount 0 False
132192 , psContext = [ ConStart ] }
193+
194+ --------------------------------------------------------------------------------
195+
196+ {- $f77includes
197+ The Fortran 77 parser can parse and inline includes at parse time. Parse errors
198+ are thrown as IO exceptions.
199+
200+ Can be cleaned up and generalized to use for other parsers.
201+ -}
202+
203+ f77lIncludes
204+ :: [FilePath ] -> ModFiles -> String -> B. ByteString
205+ -> IO (ProgramFile A0 )
206+ f77lIncludes incs mods fn bs = do
207+ case f77lNoTransform fn bs of
208+ Left e -> liftIO $ throwIO e
209+ Right pf -> do
210+ let pf' = pfSetFilename fn pf
211+ pf'' <- evalStateT (descendBiM (f77lIncludesInline incs [] ) pf') Map. empty
212+ let pf''' = runTransform (combinedTypeEnv mods)
213+ (combinedModuleMap mods)
214+ (defaultTransformation Fortran77Legacy )
215+ pf''
216+ return pf'''
217+
218+ f77lIncludesInner :: Parser [Block A0 ]
219+ f77lIncludesInner = makeParserFixed F77. includesParser Fortran77Legacy
220+
221+ f77lIncludesInline
222+ :: [FilePath ] -> [FilePath ] -> Statement A0
223+ -> StateT (Map String [Block A0 ]) IO (Statement A0 )
224+ f77lIncludesInline dirs seen st = case st of
225+ StInclude a s e@ (ExpValue _ _ (ValString path)) Nothing -> do
226+ if notElem path seen then do
227+ incMap <- get
228+ case Map. lookup path incMap of
229+ Just blocks' -> pure $ StInclude a s e (Just blocks')
230+ Nothing -> do
231+ (fullPath, inc) <- liftIO $ readInDirs dirs path
232+ case f77lIncludesInner fullPath inc of
233+ Right blocks -> do
234+ blocks' <- descendBiM (f77lIncludesInline dirs (path: seen)) blocks
235+ modify (Map. insert path blocks')
236+ return $ StInclude a s e (Just blocks')
237+ Left err -> liftIO $ throwIO err
238+ else return st
239+ _ -> return st
240+
241+ readInDirs :: [String ] -> String -> IO (String , B. ByteString )
242+ readInDirs [] f = fail $ " cannot find file: " ++ f
243+ readInDirs (d: ds) f = do
244+ let path = d</> f
245+ b <- doesFileExist path
246+ if b then
247+ (path,) <$> B. readFile path
248+ else
249+ readInDirs ds f
0 commit comments