v2 / vlib / v / depgraph / depgraph.v
239 lines · 218 sloc · 5.07 KB · e2e5cf8db56f3562c7baa735061690be936bdf3e
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4// Directed acyclic graph
5// this implementation is specifically suited to ordering dependencies
6module depgraph
7
8import v.dotgraph
9
10pub struct DepGraphNode {
11pub mut:
12 name string
13 value i64
14 deps []string
15}
16
17pub struct DepGraph {
18pub mut:
19 acyclic bool
20 nodes []DepGraphNode
21 values map[string]i64
22}
23
24struct OrderedDepMap {
25mut:
26 keys []string
27 data map[string][]string
28}
29
30pub fn new_ordered_dependency_map() OrderedDepMap {
31 mut res := OrderedDepMap{}
32 unsafe { res.keys.flags.set(.noslices) }
33 return res
34}
35
36pub fn (mut o OrderedDepMap) set(name string, deps []string) {
37 if name !in o.data {
38 o.keys << name
39 }
40 o.data[name] = deps
41}
42
43pub fn (mut o OrderedDepMap) add(name string, deps []string) {
44 mut d := o.get(name)
45 for dep in deps {
46 if dep !in d {
47 d << dep
48 }
49 }
50 o.set(name, d)
51}
52
53pub fn (o &OrderedDepMap) get(name string) []string {
54 res := o.data[name] or { []string{} }
55 return res
56}
57
58@[direct_array_access]
59pub fn (mut o OrderedDepMap) delete(name string) {
60 if name !in o.data {
61 panic('delete: no such key: ${name}')
62 }
63 for i, _ in o.keys {
64 item := o.keys[i]
65 if item.len == name.len && item == name {
66 o.keys.delete(i)
67 break
68 }
69 }
70 o.data.delete(name)
71}
72
73pub fn (mut o OrderedDepMap) apply_diff(name string, deps []string) {
74 mut diff := []string{}
75 deps_of_name := o.get(name)
76 for dep in deps_of_name {
77 if dep !in deps {
78 diff << dep
79 }
80 }
81 o.set(name, diff)
82}
83
84pub fn (o &OrderedDepMap) size() int {
85 return o.data.len
86}
87
88pub fn new_dep_graph() &DepGraph {
89 return &DepGraph{
90 acyclic: true
91 nodes: []DepGraphNode{cap: 1024}
92 }
93}
94
95pub fn (mut graph DepGraph) add(mod string, deps []string) {
96 new_node := DepGraphNode{
97 name: mod
98 deps: deps.clone()
99 }
100 graph.nodes << new_node
101 graph.values[mod] = 0
102}
103
104pub fn (mut graph DepGraph) add_with_value(mod string, deps []string, value i64) {
105 new_node := DepGraphNode{
106 name: mod
107 value: value
108 deps: deps.clone()
109 }
110 graph.nodes << new_node
111 graph.values[mod] = value
112}
113
114pub fn (graph &DepGraph) resolve() &DepGraph {
115 mut node_names := new_ordered_dependency_map()
116 mut node_deps := new_ordered_dependency_map()
117 for node in graph.nodes {
118 node_names.add(node.name, node.deps)
119 node_deps.add(node.name, node.deps)
120 }
121 mut resolved := new_dep_graph()
122 for node_deps.size() != 0 {
123 mut ready_set := []string{}
124 for name in node_deps.keys {
125 deps := node_deps.get(name)
126 if deps.len == 0 {
127 ready_set << name
128 }
129 }
130 if ready_set.len == 0 {
131 resolved.acyclic = false
132 for name in node_deps.keys {
133 resolved.add_with_value(name, node_names.get(name), graph.values[name])
134 }
135 return resolved
136 }
137 for name in ready_set {
138 node_deps.delete(name)
139 resolved_deps := node_names.get(name)
140 resolved.add_with_value(name, resolved_deps, graph.values[name])
141 }
142 for name in node_deps.keys {
143 node_deps.apply_diff(name, ready_set)
144 }
145 }
146 return resolved
147}
148
149pub fn (graph &DepGraph) last_node() DepGraphNode {
150 return graph.nodes.last()
151}
152
153pub fn (graph &DepGraph) display() string {
154 mut out := []string{}
155 for node in graph.nodes {
156 if node.deps.len == 0 {
157 out << ' * ${node.name}'
158 } else {
159 for dep in node.deps {
160 out << ' * ${node.name} -> ${dep}'
161 }
162 }
163 }
164 return out.join('\n')
165}
166
167struct NodeNames {
168mut:
169 is_cycle map[string]bool
170 names map[string][]string
171}
172
173pub fn (graph &DepGraph) display_cycles() string {
174 mut seen := false
175 mut out := []string{}
176 mut nn := NodeNames{}
177 for node in graph.nodes {
178 nn.names[node.name] = node.deps
179 }
180 for k, _ in nn.names {
181 mut cycle_names := []string{}
182 if k in nn.is_cycle {
183 continue
184 }
185 seen, cycle_names = nn.is_part_of_cycle(k, cycle_names)
186 if seen {
187 out << ' * ' + cycle_names.join(' -> ')
188 nn.is_cycle = map[string]bool{}
189 }
190 }
191 return out.join('\n')
192}
193
194fn (mut nn NodeNames) is_part_of_cycle(name string, already_seen []string) (bool, []string) {
195 mut seen := false
196 mut new_already_seen := already_seen.clone()
197 if name in nn.is_cycle {
198 return nn.is_cycle[name], new_already_seen
199 }
200 if name in already_seen {
201 new_already_seen << name
202 nn.is_cycle[name] = true
203 return true, new_already_seen
204 }
205 new_already_seen << name
206 deps := nn.names[name]
207 if deps.len == 0 {
208 nn.is_cycle[name] = false
209 return false, new_already_seen
210 }
211 for d in deps {
212 mut d_already_seen := new_already_seen.clone()
213 seen, d_already_seen = nn.is_part_of_cycle(d, d_already_seen)
214 if seen {
215 new_already_seen = d_already_seen.clone()
216 nn.is_cycle[name] = true
217 return true, new_already_seen
218 }
219 }
220 nn.is_cycle[name] = false
221 return false, new_already_seen
222}
223
224pub fn show(graph &DepGraph, path string) {
225 mut dg := dotgraph.new('ModGraph', 'ModGraph for ${path}', 'blue')
226 mbuiltin := 'builtin'
227 for node in graph.nodes {
228 is_main := node.name == 'main'
229 dg.new_node(node.name, should_highlight: is_main)
230 mut deps := node.deps.clone()
231 if node.name != mbuiltin && mbuiltin !in deps {
232 deps << mbuiltin
233 }
234 for dep in deps {
235 dg.new_edge(node.name, dep, should_highlight: is_main)
236 }
237 }
238 dg.finish()
239}
240