2 % (c) The University of Glasgow, 2000
4 \section[CmSummarise]{Module summariser for GHCI}
7 module CmSummarise ( ModSummary(..), summarise, name_of_summary,
8 getImports {-, source_has_changed-} )
11 #include "HsVersions.h"
14 import Char ( isAlphaNum )
15 --import Time ( ClockTime )
16 --import Directory ( getModificationTime )
18 import Util ( unJust )
19 import HscTypes ( ModuleLocation(..) )
27 -- The ModuleLocation contains both the original source filename and the
28 -- filename of the cleaned-up source file after all preprocessing has been
29 -- done. The point is that the summariser will have to cpp/unlit/whatever
30 -- all files anyway, and there's no point in doing this twice -- just
31 -- park the result in a temp file, put the name of it in the location,
32 -- and let @compile@ read from that file on the way back up.
35 ms_mod :: Module, -- name, package
36 ms_location :: ModuleLocation, -- location
37 ms_srcimps :: [ModuleName], -- source imports
38 ms_imps :: [ModuleName] -- non-source imports
39 --ms_date :: Maybe ClockTime -- timestamp of summarised
40 -- file, if home && source
43 instance Outputable ModSummary where
45 = sep [--text "ModSummary { ms_date = " <> text (show ms_date),
47 nest 3 (sep [text "ms_mod =" <+> ppr (ms_mod ms) <> comma,
48 text "ms_imps =" <+> ppr (ms_imps ms),
49 text "ms_srcimps =" <+> ppr (ms_srcimps ms)]),
53 name_of_summary :: ModSummary -> ModuleName
54 name_of_summary = moduleName . ms_mod
57 -- The first arg is supposed to be DriverPipeline.preprocess.
58 -- Passed in here to avoid a hard-to-avoid circular dependency
59 -- between CmSummarise and DriverPipeline. Same deal as with
61 summarise :: (FilePath -> IO FilePath)
62 -> Module -> ModuleLocation -> IO ModSummary
63 summarise preprocess mod location
64 | isModuleInThisPackage mod
65 = do let hs_fn = unJust (ml_hs_file location) "summarise"
66 hspp_fn <- preprocess hs_fn
67 modsrc <- readFile hspp_fn
68 let (srcimps,imps) = getImports modsrc
71 -- <- case ml_hs_file location of
72 -- Nothing -> return Nothing
73 -- Just src_fn -> getModificationTime src_fn >>= Just
75 return (ModSummary mod location{ml_hspp_file=Just hspp_fn}
79 = return (ModSummary mod location [] [])
81 -- Compare the timestamp on the source file with that already
82 -- in the summary, and see if the source file is younger. If
83 -- in any doubt, return True (because False could cause compilation
86 source_has_changed :: ModSummary -> IO Bool
87 source_has_changed summary
88 = case ms_date summary of {
89 Nothing -> True; -- don't appear to have a previous timestamp
91 case ml_hs_file (ms_loc summary) of {
92 Nothing -> True; -- don't appear to have a source file (?!?!)
93 Just src_fn -> do now_date <- getModificationTime src_fn
94 return (now_date > summ_date)
99 Collect up the imports from a Haskell source module. This is
100 approximate: we don't parse the module, but we do eliminate comments
101 and strings. Doesn't currently know how to unlit or cppify the module
105 getImports :: String -> ([ModuleName], [ModuleName])
107 = let all_imps = (nub . gmiBase . clean) str
108 srcs = concatMap (either unit nil) all_imps
109 normals = concatMap (either nil unit) all_imps
114 -- really get the imports from a de-litted, cpp'd, de-literal'd string
115 -- Lefts are source imports. Rights are normal ones.
116 gmiBase :: String -> [Either ModuleName ModuleName]
120 f ("foreign" : "import" : ws) = f ws
121 f ("import" : "{-#" : "SOURCE" : "#-}" : "qualified" : m : ws)
122 = Left (mkMN m) : f ws
123 f ("import" : "{-#" : "SOURCE" : "#-}" : m : ws)
124 = Left (mkMN m) : f ws
125 f ("import" : "qualified" : m : ws)
126 = Right (mkMN m) : f ws
127 f ("import" : m : ws)
128 = Right (mkMN m) : f ws
132 mkMN str = mkModuleName (takeWhile isModId str)
133 isModId c = isAlphaNum c || c `elem` "'_"
135 -- remove literals and comments from a string
136 clean :: String -> String
140 -- running through text we want to keep
142 keep ('"':cs) = dquote cs -- "
143 -- try to eliminate single quotes when they're part of
145 keep (c:'\'':cs) | isAlphaNum c || c == '_' = keep (dropWhile (=='\'') cs)
146 keep ('\'':cs) = squote cs
147 keep ('-':'-':cs) = linecomment cs
148 keep ('{':'-':'#':' ':cs) = "{-# " ++ keep cs
149 keep ('{':'-':cs) = runcomment cs -- -}
150 keep (c:cs) = c : keep cs
152 -- in a double-quoted string
154 dquote ('\\':'\"':cs) = dquote cs -- "
155 dquote ('\\':'\\':cs) = dquote cs
156 dquote ('\"':cs) = keep cs -- "
157 dquote (c:cs) = dquote cs
159 -- in a single-quoted string
161 squote ('\\':'\'':cs) = squote cs
162 squote ('\\':'\\':cs) = squote cs
163 squote ('\'':cs) = keep cs
164 squote (c:cs) = squote cs
168 linecomment ('\n':cs) = '\n':keep cs
169 linecomment (c:cs) = linecomment cs
171 -- in a running comment
173 runcomment ('-':'}':cs) = keep cs
174 runcomment (c:cs) = runcomment cs