-----------------------------------------------------------------------------
-- |
-- Module : Imports
-- Copyright : Malcolm Wallace
--
-- Maintainer : Malcolm Wallace <[email protected]>
-- Stability : Stable
-- Portability : All
--
-- Get the imports for a single Haskell module after performing a cpp.
-----------------------------------------------------------------------------
module Imports
( getImports
) where
import Char
import ListUtil (takeUntil)
import Language.Preprocessor.Cpphs.CppIfdef (cppIfdef)
import Language.Preprocessor.Cpphs.Options (BoolOptions(..)
,defaultBoolOptions)
#if !defined(__HASKELL98__)
#define isAlphaNum isAlphanum
#endif
-- | Get the imports for this Haskell module.
getImports :: FilePath -- ^ The path to the module
-> [String] -- ^ Definitions to build a symbol table (for cpp)
-> [String] -- ^ Search-path for #include'd files
-> String -- ^ The input file to be parsed for imports
-> [String] -- ^ A list of imported modules
getImports fp defines includes = leximports fp
. unlines . map snd
. cppIfdef fp defs includes
defaultBoolOptions { macros=False
, locations=False
, warnings=False }
where
defs = map (\defn-> let (s,d) = break (=='=') defn in
(s, if null d then "1" else tail d)) defines
-- | /leximports/ takes a cpp-ed input and returns the list of imports
leximports :: FilePath -> String -> [String]
leximports fp =
let
nestcomment n ('{':'-':cs) | n>=0 = nestcomment (n+1) cs
nestcomment n ('-':'}':cs) | n>0 = nestcomment (n-1) cs
nestcomment n (c:cs) | n>0 = nestcomment n cs
nestcomment 0 ('-':'}':cs) =
error ("In file "++fp++"\n"
++" found close comment -} but no matching open {-")
nestcomment 0 ('-':'-':cs) =
if null munch
|| isSpace nextchr
|| nextchr `elem` ",()[]{};\"'`"
|| isAlphaNum nextchr
then nestcomment 0 (dropWhile (/='\n') munch)
else '-':'-': nestcomment 0 cs
where munch = dropWhile (=='-') cs
nextchr = head munch
nestcomment 0 ('\'':'"':'\'':cs) = '\'':'"':'\'': nestcomment 0 cs
nestcomment 0 ('\\':'"':cs) = '\\':'"': nestcomment 0 cs
nestcomment 0 ('"':cs) = '"': endstring cs
nestcomment 0 ('{':cs) = '{':'\n': nestcomment 0 cs -- HACK
nestcomment 0 (';':cs) = '\n': nestcomment 0 cs -- HACK
nestcomment 0 (c:cs) = c: nestcomment 0 cs
nestcomment 0 [] = []
nestcomment n [] =
error ("In file "++fp++"\n found "++show n
++" open comments {- but no matching close -}")
endstring ('\\':'\\':cs) = '\\':'\\': endstring cs
endstring ('\\':'"':cs) = '\\':'"': endstring cs
endstring ('\\':w:cs) | isSpace w = stringgap cs
endstring ('"':cs) = '"': nestcomment 0 cs
endstring (c:cs) = c : endstring cs
endstring [] = []
stringgap ('\\':cs) = endstring cs
stringgap (c:cs) = stringgap cs
stringgap [] = [] -- source file is corrupt
getmodnames (x:xs)
| null x || all isSpace x = getmodnames xs
| otherwise =
case concatMap words (x:xs) of
("import":ws) -> modname ws: getmodnames xs
-- allow for import spanning several lines.
("data":ws) -> [] -- truncate search at first non-import keyword
("type":ws) -> []
("class":ws) -> []
("infix":ws) -> []
("infixl":ws) -> []
("infixr":ws) -> []
("newtype":ws) -> []
("default":ws) -> []
("instance":ws)-> []
_ -> getmodnames xs -- non-keyword: continue search
getmodnames [] = []
modname ws =
let one = head ws
two = head (tail ws)
in
if one == "qualified" then
takeUntil "(-{;" two
else takeUntil "(-{;" one
in (getmodnames . lines . nestcomment 0)
----
|