Skip to content

Commit e6ea7b7

Browse files
committed
graph: don't overcount dependents
Fixes #69 (nice)
1 parent 8542c66 commit e6ea7b7

File tree

2 files changed

+59
-21
lines changed

2 files changed

+59
-21
lines changed

build/cyan/graph.lua

Lines changed: 29 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cyan/graph.tl

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,26 +56,40 @@ local function mark_for_compile(n: Node)
5656
end
5757
end
5858

59+
local function collect_recursive_dependents(of: Node, set: {Node:boolean})
60+
if set[of] then return end
61+
set[of] = true
62+
for dependent in keys(of.dependents) do
63+
set[dependent] = true
64+
collect_recursive_dependents(dependent, set)
65+
end
66+
end
67+
5968
local function make_dependent_counter(): function(Node): integer
6069
local cache <const>: {Node:integer} = {}
70+
6171
local function count_dependents(n: Node): integer
6272
if cache[n] then return cache[n] end
63-
local deps = 0
64-
for v in keys(n.dependents) do
65-
deps = deps + count_dependents(v) + 1
66-
end
67-
cache[n] = deps
68-
return deps
73+
local set <const> = {}
74+
collect_recursive_dependents(n, set)
75+
set[n] = false
76+
local count = 0
77+
for _ in next, set do count = count + 1 end
78+
cache[n] = count
79+
return count
6980
end
7081
return count_dependents
7182
end
7283

7384
---@desc
74-
--- Iterate over nodes in order of dependents
85+
--- Collect the nodes of this graph into a mapping from the number of
86+
--- dependents to lists of nodes with that number of dependents
87+
---
88+
--- Returns that map of lists and the most dependents that any node had
7589
---
7690
--- If two nodes have the same number of dependent nodes, they are sorted by
7791
--- input filename lexicographically
78-
function Dag:nodes(): function(): Node
92+
function Dag:collect_by_dependent_count(): {integer:{Node}}, integer
7993
local count <const> = make_dependent_counter()
8094
local most_deps = 0
8195
local nodes_by_deps <const>: {integer:{Node}} = setmetatable({}, {
@@ -91,18 +105,23 @@ function Dag:nodes(): function(): Node
91105
for n in values(self._nodes_by_filename) do
92106
table.insert(nodes_by_deps[count(n)], n)
93107
end
94-
95108
setmetatable(nodes_by_deps, nil)
96-
97109
for i = 0, most_deps do
98110
if nodes_by_deps[i] then
99111
table.sort(nodes_by_deps[i], function(a: Node, b: Node): boolean
100112
return a.input:to_string() < b.input:to_string()
101113
end)
102114
end
103115
end
116+
return nodes_by_deps, most_deps
117+
end
104118

105-
local i = most_deps
119+
---@desc
120+
--- Iterate over nodes in order of dependents
121+
---
122+
--- This is a convenience wrapper over `collect_by_dependent_count`
123+
function Dag:nodes(): function(): Node
124+
local nodes_by_deps <const>, i = self:collect_by_dependent_count()
106125
if not nodes_by_deps[i] then
107126
return function(): nil end
108127
end

0 commit comments

Comments
 (0)