@@ -1,297 +0,0 @@
1−{-# OPTIONS_GHC -Wall #-}
2−{-# LANGUAGE OverloadedStrings #-}
3−module File.Crawl
4− ( Result
5− , Graph(..)
6− , crawl
7− , crawlFromSource
8− )
9− where
10−
11−import Control.Concurrent.Chan (Chan, newChan, readChan)
12−import Control.Monad (foldM)
13−import Control.Monad.Except (liftIO)
14−import qualified Data.ByteString as BS
15−import qualified Data.Graph as Graph
16−import qualified Data.Map as Map
17−import qualified Data.Set as Set
18−
19−import qualified Elm.Compiler.Objects as Obj
20−import qualified Elm.Kernel as Kernel
21−import qualified Elm.ModuleName as ModuleName
22−import qualified Elm.Package as Pkg
23−
24−import qualified Elm.Project.Json as Project
25−import Elm.Project.Summary (Summary(..))
26−import qualified File.Args as Args
27−import qualified File.Find as Find
28−import qualified File.Header as Header
29−import qualified File.IO as IO
30−import qualified Reporting.Exit as Exit
31−import qualified Reporting.Exit.Crawl as E
32−import qualified Reporting.Task as Task
33−
34−
35−
36−-- GRAPH
37−
38−
39−data Graph kernel problems =
40− Graph
41− { _args :: Args.Args ModuleName.Raw
42− , _locals :: Map.Map ModuleName.Raw Header.Info
43− , _kernels :: Map.Map ModuleName.Raw kernel
44− , _foreigns :: Map.Map ModuleName.Raw Pkg.Canonical
45− , _problems :: problems
46− }
47−
48−
49−type Result =
50− Graph Obj.Kernel ()
51−
52−
53−type WorkGraph =
54− Graph (FilePath, Maybe FilePath) [E.Problem]
55−
56−
57−initWorkGraph :: Args.Args ModuleName.Raw -> Map.Map ModuleName.Raw Header.Info -> WorkGraph
58−initWorkGraph args locals =
59− Graph args locals Map.empty Map.empty []
60−
61−
62−
63−-- CRAWL
64−
65−
66−crawl :: Summary -> Args.Args FilePath -> Task.Task Result
67−crawl summary args =
68− case args of
69− Args.Pkg names ->
70− do let roots = map (Unvisited E.ElmJson) names
71− let graph = initWorkGraph (Args.Pkg names) Map.empty
72− depthFirstSearch summary roots graph
73−
74− Args.Roots path [] ->
75− crawlHelp summary =<< Header.readOneFile summary path
76−
77− Args.Roots p ps ->
78− do (h,hs) <- Header.readManyFiles summary p ps
79− let unvisited = concatMap toUnvisited (h:hs)
80− let graph = initWorkGraph (Args.Roots (fst h) (map fst hs)) (Map.fromList (h:hs))
81− depthFirstSearch summary unvisited graph
82− where
83− toUnvisited (name, Header.Info path _ _ deps) =
84− map (Unvisited (E.Module path name)) deps
85−
86−
87−crawlFromSource :: Summary -> BS.ByteString -> Task.Task Result
88−crawlFromSource summary@(Summary _ project _ _ _) source =
89− crawlHelp summary =<<
90− Header.readSource project source
91−
92−
93−crawlHelp :: Summary -> ( Maybe ModuleName.Raw, Header.Info ) -> Task.Task Result
94−crawlHelp summary ( maybeName, info@(Header.Info path _ _ deps) ) =
95− do let name = maybe "Main" id maybeName
96− let toUnvisited dep = Unvisited (maybe (E.File path) (E.Module path) maybeName) dep
97− let roots = map toUnvisited deps
98− let graph = initWorkGraph (Args.Roots name []) (Map.singleton name info)
99− depthFirstSearch summary roots graph
100−
101−
102−
103−-- DEPTH FIRST SEARCH
104−
105−
106−depthFirstSearch :: Summary -> [Unvisited] -> WorkGraph -> Task.Task Result
107−depthFirstSearch summary unvisited startGraph =
108− do chan <- liftIO newChan
109−
110− (Graph args locals kernelPaths foreigns problems) <-
111− dfs summary chan 0 Set.empty unvisited startGraph
112−
113− case problems of
114− [] ->
115− do checkForCycles locals
116− kernels <- visitKernels summary locals kernelPaths
117− return $ Graph args locals kernels foreigns ()
118−
119− problem : otherProblems ->
120− Task.throw (Exit.Crawl (E.DependencyProblems problem otherProblems))
121−
122−
123−
124−-- CHECK FOR CYCLES
125−
126−
127−checkForCycles :: Map.Map ModuleName.Raw Header.Info -> Task.Task ()
128−checkForCycles locals =
129− let
130− toNode (name, Header.Info _ _ _ imports) =
131− (name, name, imports)
132−
133− components =
134− Graph.stronglyConnComp (map toNode (Map.toList locals))
135− in
136− mapM_ checkComponent components
137−
138−
139−checkComponent :: Graph.SCC ModuleName.Raw -> Task.Task ()
140−checkComponent scc =
141− case scc of
142− Graph.AcyclicSCC _ ->
143− return ()
144−
145− Graph.CyclicSCC names ->
146− Task.throw (Exit.Cycle names)
147−
148−
149−
150−-- CONCURRENT DEPTH FIRST SEARCH
151−
152−
153−dfs
154− :: Summary
155− -> Chan (Either E.Problem Asset)
156− -> Int
157− -> Set.Set ModuleName.Raw
158− -> [Unvisited]
159− -> WorkGraph
160− -> Task.Task WorkGraph
161−dfs summary chan oldPending oldSeen unvisited graph =
162− do (seen, pending) <-
163− foldM (spawnVisitor summary chan) (oldSeen, oldPending) unvisited
164−
165− if pending == 0
166− then return graph
167− else
168− do asset <- liftIO $ readChan chan
169− case asset of
170− Right (Local name info@(Header.Info path _ _ imports)) ->
171− do let newGraph = graph { _locals = Map.insert name info (_locals graph) }
172− let deps = map (Unvisited (E.Module path name)) imports
173− dfs summary chan (pending - 1) seen deps newGraph
174−
175− Right (Kernel name info) ->
176− do let kernels = Map.insert name info (_kernels graph)
177− let newGraph = graph { _kernels = kernels }
178− dfs summary chan (pending - 1) seen [] newGraph
179−
180− Right (Foreign name pkg) ->
181− do let foreigns = Map.insert name pkg (_foreigns graph)
182− let newGraph = graph { _foreigns = foreigns }
183− dfs summary chan (pending - 1) seen [] newGraph
184−
185− Right ForeignKernel ->
186− dfs summary chan (pending - 1) seen [] graph
187−
188− Left problem ->
189− do let problems = problem : _problems graph
190− let newGraph = graph { _problems = problems }
191− dfs summary chan (pending - 1) seen [] newGraph
192−
193−
194−spawnVisitor
195− :: Summary
196− -> Chan (Either E.Problem Asset)
197− -> (Set.Set ModuleName.Raw, Int)
198− -> Unvisited
199− -> Task.Task (Set.Set ModuleName.Raw, Int)
200−spawnVisitor summary chan (seen, n) unvisited@(Unvisited _ name) =
201− if Set.member name seen then
202− return (seen, n)
203− else
204− do Task.workerChan chan (toVisitor summary unvisited)
205− return (Set.insert name seen, n + 1)
206−
207−
208−
209−-- VISITOR
210−
211−
212−data Unvisited =
213− Unvisited
214− { _origin :: E.Origin
215− , _name :: ModuleName.Raw
216− }
217−
218−
219−data Asset
220− = Local ModuleName.Raw Header.Info
221− | Kernel ModuleName.Raw (FilePath, Maybe FilePath)
222− | Foreign ModuleName.Raw Pkg.Canonical
223− | ForeignKernel
224−
225−
226−toVisitor :: Summary -> Unvisited -> Task.Task_ E.Problem Asset
227−toVisitor summary (Unvisited origin name) =
228− do asset <- Find.find summary origin name
229− case asset of
230− Find.Local path ->
231− uncurry Local <$> Header.readModule summary name path
232−
233− Find.Foreign pkg ->
234− return $ Foreign name pkg
235−
236− Find.Kernel clientPath maybeServerPath ->
237− return $ Kernel name (clientPath, maybeServerPath)
238−
239− Find.ForeignKernel ->
240− return ForeignKernel
241−
242−
243−
244−-- VISIT KERNELS
245−
246−
247−visitKernels
248− :: Summary
249− -> Map.Map ModuleName.Raw Header.Info
250− -> Map.Map ModuleName.Raw (FilePath, Maybe FilePath)
251− -> Task.Task (Map.Map ModuleName.Raw Obj.Kernel)
252−visitKernels summary locals kernelPaths =
253− if Map.null kernelPaths then
254− return Map.empty
255− else
256− -- TODO do this concurrently?
257− -- Need better concurrency primitives in Task
258− traverse (readKernel (toImportDict summary locals)) kernelPaths
259−
260−
261−readKernel :: ImportDict -> (FilePath, Maybe FilePath) -> Task.Task Obj.Kernel
262−readKernel importDict (clientPath, maybeServerPath) =
263− Obj.Kernel
264− <$> readKContent importDict clientPath
265− <*> traverse (readKContent importDict) maybeServerPath
266−
267−
268−readKContent :: ImportDict -> FilePath -> Task.Task Kernel.KContent
269−readKContent importDict path =
270− do source <- liftIO $ IO.readUtf8 path
271− case Kernel.parse importDict source of
272− Right content ->
273− return content
274−
275− Left _ ->
276− Task.throw (Exit.Crawl (E.BadKernelHeader path))
277−
278−
279−
280−-- IMPORT DICT
281−
282−
283−type ImportDict =
284− Map.Map ModuleName.Raw [Pkg.Name]
285−
286−
287−toImportDict :: Summary -> Map.Map ModuleName.Raw info -> ImportDict
288−toImportDict (Summary _ project exposed _ _) locals =
289− let
290− localPkg =
291− [Project.getName project]
292−
293− addLocal name _ dict =
294− Map.insertWith (++) name localPkg dict
295− in
296− Map.foldrWithKey addLocal (Map.map (map Pkg._name) exposed) locals
297−