11module FSharp.Compiler.Service.Tests.DepResolving
22
3+ open System.Collections .Generic
34open FSharp.Compiler .Service .Tests2 .SyntaxTreeTests .TypeTests
45open FSharp.Compiler .Syntax
56open NUnit.Framework
@@ -18,7 +19,7 @@ type Node =
1819 }
1920
2021/// Filenames with dependencies
21- type Graph = ( Node * List < Node> )[]
22+ type Graph = ( Node * Node[] )[]
2223
2324let extractModuleSegments ( stuff : Stuff ) : LongIdent [] =
2425 stuff
@@ -29,7 +30,7 @@ let extractModuleSegments (stuff : Stuff) : LongIdent[] =
2930 match x.Ident.Length with
3031 | 0
3132 | 1 -> None
32- | n -> x.Ident.GetSlice( Some 0 , n - 1 |> Some) |> Some
33+ | n -> x.Ident.GetSlice( Some 0 , n - 2 |> Some) |> Some
3334 )
3435 |> Seq.toArray
3536
@@ -41,6 +42,7 @@ type TrieNode =
4142 // TODO Use ValueTuples if not already
4243 Children : System .Collections .Generic .IDictionary < ModuleSegment , TrieNode >
4344 mutable Reachable : bool
45+ mutable Visited : bool
4446 /// Files/graph nodes represented by this TrieNode
4547 /// All files whose top-level module/namespace are same as this TrieNode's 'path'
4648 GraphNodes : System .Collections .Generic .List < Node >
@@ -49,13 +51,29 @@ type TrieNode =
4951let emptyList < 'a > () =
5052 System.Collections.Generic.List< 'a>()
5153
52- let cloneTrie ( trie : TrieNode ) : TrieNode =
53- failwith unsupported // TODO
54+ let rec cloneTrie ( trie : TrieNode ) : TrieNode =
55+ let children =
56+ // TODO Perf
57+ let children =
58+ trie.Children
59+ |> Seq.map ( fun ( KeyValue ( segment , child )) ->
60+ segment, cloneTrie child
61+ )
62+ |> dict
63+ // TODO Avoid tow dicts
64+ System.Collections.Generic.Dictionary<_,_>( children)
65+ {
66+ GraphNodes = List<_>( trie.GraphNodes)
67+ Children = children
68+ Reachable = trie.Reachable
69+ Visited = trie.Visited
70+ }
5471
5572let emptyTrie () : TrieNode =
5673 {
57- TrieNode.Children = dict ([])
74+ TrieNode.Children = Dictionary ([])
5875 Reachable = false
76+ Visited = false
5977 GraphNodes = emptyList()
6078 }
6179
@@ -91,8 +109,16 @@ let buildTrie (nodes : Node[]) : TrieNode =
91109
92110 root
93111
94- let search ( trie : TrieNode ) ( path : LongIdent ) =
95- trie
112+ let rec search ( trie : TrieNode ) ( path : LongIdent ) : TrieNode option =
113+ let mutable node = trie
114+ match path with
115+ | [] -> Some trie
116+ | segment :: rest ->
117+ match trie.Children.TryGetValue( segment.idText) with
118+ | true , child ->
119+ search child rest
120+ | false , _ ->
121+ None
96122
97123let algorithm ( nodes : FileAST list ) : Graph =
98124 // Create ASTs, extract module refs
@@ -118,7 +144,16 @@ let algorithm (nodes : FileAST list) : Graph =
118144 |> Array.map ( fun node ->
119145 let trie = cloneTrie trie
120146
121- // Keep a list of reachable nodes
147+ // Keep a list of visited nodes (ie. all reachable nodes and all their ancestors)
148+ let visited = emptyList< TrieNode>()
149+
150+ let markVisited ( node : TrieNode ) =
151+ if not node.Visited then
152+ printfn $" New node visited"
153+ node.Visited <- true
154+ visited.Add( node)
155+
156+ // Keep a list of reachable nodes (ie. ones that can be prefixes for later module/type references)
122157 let reachable = emptyList< TrieNode>()
123158
124159 let markReachable ( node : TrieNode ) =
@@ -127,24 +162,55 @@ let algorithm (nodes : FileAST list) : Graph =
127162 node.Reachable <- true
128163 reachable.Add( node)
129164
130- // Mark two nodes as reachable:
131- // - root (no prefix)
132- // - top-level module/namespace
165+ // Mark root (no prefix) as reachable and visited
133166 markReachable trie
134- let topNode = search trie node.Top
135- markReachable topNode
167+ markVisited trie
168+
169+ let rec extend ( id : LongIdent ) ( node : TrieNode ) =
170+ let rec extend ( node : TrieNode ) ( id : LongIdent ) =
171+ match id with
172+ // Reached end of the identifier - new reachable node
173+ | [] ->
174+ Some node
175+ // More segments exist
176+ | segment :: rest ->
177+ // Visit (not 'reach') the TrieNode
178+ markVisited node
179+ match node.Children.TryGetValue( segment.idText) with
180+ // A child for the segment exists - continue there
181+ | true , child ->
182+ extend child rest
183+ // A child for the segment doesn't exist - stop, since we don't care about the non-existent part of the Trie
184+ | false , _ ->
185+ None
186+ extend node id
136187
137188 // Process module refs in order, marking more and more TrieNodes as reachable
138189 let processRef ( id : LongIdent ) =
139- ()
140- node.ModuleRefs
190+ let newReachables =
191+ // Start at every reachable node,
192+ reachable
193+ // extend a reachable node by 'id', but without creating new nodes, mark all seen nodes as visited and the final one as reachable
194+ |> Seq.choose ( extend id)
195+ |> Seq.toArray
196+ reachable.AddRange( newReachables)
197+
198+ // Add top-level module/namespace as the first reference (possibly not necessary as maybe already in the list)
199+ let moduleRefs =
200+ Array.append [| node.Top|] node.ModuleRefs
201+
202+ // Process all refs
203+ moduleRefs
141204 |> Array.iter processRef
142205
143- // Collect files from all reachable TrieNodes
206+ // Collect files from all visited TrieNodes
144207 let reachableItems =
145- reachable
208+ visited
146209 |> Seq.collect ( fun node -> node.GraphNodes)
147- node, List< Node>( reachableItems)
210+ |> Seq.toArray
211+
212+ // Return the node and its dependencies
213+ node, reachableItems
148214 )
149215
150216[<Test>]
0 commit comments