Skip to content

Commit 3e101cc

Browse files
authored
Merge pull request JuliaCollections#83 from JuliaCollections/kf/refactor
WIP/RFC: Major refactor
2 parents 437aa8b + 318dea1 commit 3e101cc

File tree

11 files changed

+443
-271
lines changed

11 files changed

+443
-271
lines changed

examples/binarytree_infer.jl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,9 @@ AbstractTrees.parentlinks(::Type{BinaryNode{T}}) where T = AbstractTrees.StoredP
3232
AbstractTrees.siblinglinks(::Type{BinaryNode{T}}) where T = AbstractTrees.StoredSiblings()
3333
# Use the native iteration for the children
3434
AbstractTrees.children(node::BinaryNode) = node
35+
AbstractTrees.parent(node::BinaryNode) = isdefined(node, :parent) ? node.parent : nothing
3536

36-
Base.parent(root::BinaryNode, node::BinaryNode) = isdefined(node, :parent) ? node.parent : nothing
37-
38-
function AbstractTrees.nextsibling(tree::BinaryNode, child::BinaryNode)
37+
function AbstractTrees.nextsibling(child::BinaryNode)
3938
isdefined(child, :parent) || return nothing
4039
p = child.parent
4140
if isdefined(p, :right)

src/AbstractTrees.jl

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,63 @@ module AbstractTrees
99
export children, ischild, intree, isdescendant, treesize, treebreadth, treeheight
1010
export print_tree, TreeCharSet
1111
export TreeIterator, Leaves, PostOrderDFS, PreOrderDFS, StatelessBFS
12-
export Tree, ShadowTree, AnnotationNode, treemap, treemap!
12+
export Tree, ShadowTree, AnnotationNode, treemap, treemap!, Indexed
1313

1414
import Base: getindex, setindex!, iterate, nextind, print, show,
1515
eltype, IteratorSize, IteratorEltype, length, push!, pop!
1616
using Base: SizeUnknown, EltypeUnknown
1717

1818

1919
abstract type AbstractShadowTree end
20+
struct ImplicitRootIndex; end
21+
22+
struct Indexed{T}; tree::T; end
23+
Base.getindex(tree::Indexed, ind) = tree.tree[ind]
24+
Base.getindex(tree::Indexed, ::ImplicitRootIndex) = tree
25+
rootindex(tree) = ImplicitRootIndex()
26+
27+
"""
28+
children([tree,] x)
29+
30+
Get the immediate children of node `x` (optionally in the context of tree `tree`).
31+
32+
This is the primary function that needs to be implemented for custom tree types. It should return an
33+
iterable object for which an appropriate implementation of `Base.pairs` is available.
34+
35+
The default behavior is to assume that if an object is iterable, iterating over
36+
it gives its children. Non-iterable types are treated as leaf nodes.
37+
"""
38+
children(x) = Base.isiterable(typeof(x)) ? x : ()
39+
children(tree, node) = children(node)
40+
41+
function children(i::Indexed, ind)
42+
Base.depwarn("No children overload for tree declared as indexed. Overloading childindices(...) is deprecated. Use children(::Indexed{MyTree}, index).", :childindices)
43+
return childindices(i.tree, ind)
44+
end
45+
46+
"""
47+
parent([tree,] x)
48+
49+
Get the immediate parent of a node `x`
50+
51+
This function should be implemented for trees that have stored parents.
52+
"""
53+
parent(tree, x) = parent(x)
54+
55+
isroot(tree, x) = parent(tree, x) === nothing
56+
isroot(x) = parent(x) === nothing
57+
58+
has_children(x) = children(x) !== ()
2059

2160

2261
include("traits.jl")
2362
include("base.jl")
24-
include("implicitstacks.jl")
63+
include("cursors.jl")
2564
include("printing.jl")
2665
include("indexing.jl")
2766
include("iteration.jl")
2867
include("builtins.jl")
68+
include("wrappers.jl")
2969

3070

3171
end # module

src/cursors.jl

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
"""
2+
NodeCompletion
3+
4+
A NodeCompletion is an adaptor for a node of a tree where the nodes are not explicitally part of a tree
5+
This allows `children` to work without explicitly calling `children(tree, node)`
6+
"""
7+
struct NodeCompletion{R, T}
8+
tree::R
9+
node::T
10+
end
11+
12+
getnode(nc::NodeCompletion) = nc.node
13+
children(nc::NodeCompletion) = nc
14+
function Base.iterate(nc::NodeCompletion)
15+
cs = children(nc.tree, nc.node)
16+
r = iterate(cs)
17+
r === nothing && return nothing
18+
(node, state) = r
19+
NodeCompletion(nc.tree, node), (cs, state)
20+
end
21+
function Base.iterate(nc::NodeCompletion, (cs, state))
22+
r = iterate(cs, state)
23+
r === nothing && return nothing
24+
(node, state) = r
25+
NodeCompletion(nc.tree, node), (cs, state)
26+
end
27+
28+
29+
"""
30+
TreeCursor
31+
32+
A TreeCursor is an adaptor that allows parent/sibling navigation over a tree that
33+
does not otherwise explicitly store these relations.
34+
"""
35+
abstract type TreeCursor end
36+
parentlinks(::Type{<:TreeCursor}) = StoredParents()
37+
siblinglinks(::Type{<:TreeCursor}) = StoredSiblings()
38+
39+
struct UnIndex{T} <: TreeCursor
40+
tc::T
41+
end
42+
function nextsibling(ui::UnIndex)
43+
ns = nextsibling(ui.tc)
44+
ns === nothing && return nothing
45+
UnIndex(ns)
46+
end
47+
function prevsibling(ui::UnIndex)
48+
ps = prevsibling(ui.tc)
49+
ps === nothing && return nothing
50+
UnIndex(ps)
51+
end
52+
function getnode(ui::UnIndex)
53+
nc = getnode(ui.tc)::NodeCompletion
54+
nc.tree[nc.node]
55+
end
56+
function parent(ui::UnIndex)
57+
return UnIndex(parent(ui.tc))
58+
end
59+
children(ui::UnIndex) = ui
60+
function iterate(ui::UnIndex, state...)
61+
r = iterate(ui.tc, state...)
62+
r === nothing && return nothing
63+
(node, state) = r
64+
UnIndex(node), state
65+
end
66+
isroot(ui::UnIndex) = isroot(ui.tc)
67+
68+
struct SiblingState{ST, SIT}
69+
siblings::ST
70+
index::SIT
71+
end
72+
73+
# Not actually executed. Used for inference
74+
function mk_sib_state(tree)
75+
cs = children(tree)
76+
(_, state) = iterate(cs)
77+
SiblingState(cs, state)
78+
end
79+
80+
struct LinkedTreeCursor{SiblingLinks, PT, T, SI <: SiblingState} <: TreeCursor
81+
parent::Union{Nothing, PT, LinkedTreeCursor{SiblingLinks, PT, T, SI}}
82+
node::T
83+
nodepos::Union{SI, Nothing}
84+
85+
function LinkedTreeCursor(parent::LinkedTreeCursor{SB, PT, S, SI1}, node::T, nodepos) where {SB, PT, T, S, SI1}
86+
TN = typejoin(T, S)
87+
SN = typejoin(SI1, typeof(nodepos))
88+
SN <: SiblingState || (SN = SiblingState)
89+
if typeof(parent) <: LinkedTreeCursor{SB, PT, TN, SN}
90+
PTN = PT
91+
else
92+
PTN = typejoin(PT, typeof(parent))
93+
PTN <: LinkedTreeCursor || (PTN = LinkedTreeCursor)
94+
end
95+
new{SB, PTN, TN, SN}(parent, node, nodepos)
96+
end
97+
98+
function LinkedTreeCursor(parent::Nothing, node::T, nodepos::Union{SiblingState, Nothing}) where {T}
99+
SN = nodepos === nothing ? Base._return_type(mk_sib_state, Tuple{T}) : typeof(nodepos)
100+
SN <: SiblingState || (SN = SiblingState)
101+
new{siblinglinks(node), Union{}, T, SN}(parent, node, nodepos)
102+
end
103+
end
104+
105+
function LinkedTreeCursor(tree)
106+
LinkedTreeCursor(nothing, tree, nothing)
107+
end
108+
109+
isroot(cursor::LinkedTreeCursor) = cursor.parent === nothing
110+
parent(cursor::LinkedTreeCursor) = cursor.parent::LinkedTreeCursor
111+
112+
function nextsibling(cursor::LinkedTreeCursor{StoredSiblings()})
113+
ns = nextsibling(cursor.node)
114+
ns === nothing && return ns
115+
typeof(cursor)(cursor.parent, ns, nothing)
116+
end
117+
118+
function prevsibling(cursor::LinkedTreeCursor{StoredSiblings()})
119+
ps = prevsibling(cursor.node)
120+
ps === nothing && return ns
121+
typeof(cursor)(cursor.parent, ps, nothing)
122+
end
123+
124+
function nextsibling(cursor::LinkedTreeCursor{ImplicitSiblings()})
125+
siblings = cursor.nodepos.siblings
126+
pos = cursor.nodepos.index
127+
r = iterate(siblings, pos)
128+
r === nothing && return nothing
129+
ns, nextpos = r
130+
LinkedTreeCursor(cursor.parent, ns, SiblingState(siblings, nextpos))
131+
end
132+
133+
function prevsibling(cursor::LinkedTreeCursor{ImplicitSiblings()})
134+
siblings = cursor.nodepos.siblings
135+
pos = cursor.nodepos.index
136+
r = iterate(Reverse(siblings), pos)
137+
r === nothing && return nothing
138+
ns, prevpos = r
139+
typeof(cursor)(cursor.parent, ns, SiblingIndex(siblings, prevpos))
140+
end
141+
142+
function Base.iterate(ltc::LinkedTreeCursor)
143+
cs = children(ltc.node)
144+
r = iterate(cs)
145+
r === nothing && return nothing
146+
ns, state = r
147+
LinkedTreeCursor(ltc, ns, SiblingState(cs, state)), (cs, state)
148+
end
149+
150+
function Base.iterate(ltc::LinkedTreeCursor, (cs, state))
151+
r = iterate(cs, state)
152+
r === nothing && return nothing
153+
node, state = r
154+
LinkedTreeCursor(ltc, node, SiblingState(cs, state)), (cs, state)
155+
end
156+
157+
function Base.getindex(ltc::LinkedTreeCursor, idx)
158+
cs = children(ltc.node)
159+
LinkedTreeCursor(ltc, cs[idx], SiblingState(cs, idx))
160+
end
161+
Base.lastindex(ltc) = lastindex(ltc.node)
162+
163+
abstract type TreeIterator{T} end
164+
IteratorEltype(::Type{<:TreeIterator}) = EltypeUnknown()
165+
166+
struct InplaceStackedTreeCursor{T} <: TreeCursor
167+
stack::Vector{T}
168+
end
169+
InplaceStackedTreeCursor(tree) = InplaceStackedTreeCursor([tree])
170+
getnode(istc::InplaceStackedTreeCursor) = istc.stack[end]
171+
isroot(istc::InplaceStackedTreeCursor) = length(istc.stack) == 1
172+
children(istc::InplaceStackedTreeCursor) = istc
173+
174+
Base.isempty(istc::InplaceStackedTreeCursor) = isempty(children(istc.stack[end]))
175+
function Base.iterate(istc::InplaceStackedTreeCursor)
176+
cs = children(istc.stack[end])
177+
r = iterate(cs)
178+
r === nothing && return nothing
179+
ns, state = r
180+
if typeof(ns) <: eltype(istc.stack)
181+
# DANGER: This is the inplace version. Hopefully in the future we have
182+
# fast immutable arrays that will obviate this.
183+
push!(istc.stack, ns)
184+
stack = istc.stack
185+
else
186+
JT = typejoin(eltype(istc.stack), typeof(ns))
187+
stack = convert(Vector{JT}, istc.stack)
188+
push!(stack, ns)
189+
end
190+
InplaceStackedTreeCursor(stack), (cs, state)
191+
end
192+
193+
function update_stack!(stack, ns)
194+
if typeof(ns) <: eltype(stack)
195+
# DANGER: See above
196+
else
197+
JT = typejoin(eltype(stack), typeof(ns))
198+
stack = convert(Vector{JT}, stack)
199+
end
200+
stack[end] = ns
201+
stack
202+
end
203+
204+
function Base.iterate(istc::InplaceStackedTreeCursor, (cs, state))
205+
r = iterate(cs, state)
206+
if r === nothing
207+
pop!(istc.stack)
208+
return nothing
209+
end
210+
ns, state = r
211+
InplaceStackedTreeCursor(update_stack!(istc.stack, ns)), (cs, state)
212+
end
213+
214+
function nextsibling(istc::InplaceStackedTreeCursor)
215+
ns = nextsibling(istc.stack[end])
216+
ns === nothing && return nothing
217+
InplaceStackedTreeCursor(update_stack!(istc.stack, ns))
218+
end
219+
220+
function prevsibling(cursor::LinkedTreeCursor{ImplicitSiblings()})
221+
ps = prevsibling(istc.stack[end])
222+
ps === nothing && return nothing
223+
InplaceStackedTreeCursor(update_stack!(istc.stack, ps))
224+
end
225+
226+
function Base.getindex(istc::InplaceStackedTreeCursor, idx)
227+
cs = children(istc.stack[end])
228+
# DANGER: This is the inplace version. Hopefully in the future we have
229+
# fast immutable arrays that will obviate this
230+
ns = cs[idx]
231+
if typeof(ns) <: eltype(istc.stack)
232+
# DANGER: This is the inplace version. Hopefully in the future we have
233+
# fast immutable arrays that will obviate this.
234+
push!(istc.stack, ns)
235+
stack = istc.stack
236+
else
237+
JT = typejoin(eltype(istc.stack), typeof(ns))
238+
stack = convert(Vector{JT}, istc.stack)
239+
push!(stack, ns)
240+
end
241+
InplaceStackedTreeCursor(stack)
242+
end
243+
244+
function parent(istc::InplaceStackedTreeCursor)
245+
pop!(istc.stack)
246+
InplaceStackedTreeCursor(istc.stack)
247+
end
248+
249+
# Decide what kind of cursor to use for this tree. Trees may override this
250+
# function to return a different cursor type.
251+
function TreeCursor(tree)
252+
if treekind(tree) === IndexedTree()
253+
return UnIndex(TreeCursor(NodeCompletion(Indexed(tree), rootindex(tree))))
254+
end
255+
pl = parentlinks(tree)
256+
sl = siblinglinks(tree)
257+
if pl === StoredParents() && sl === StoredSiblings()
258+
# If both parents and siblings are stored, there is no need for a cursor,
259+
# the tree itself supports everything we need.
260+
return tree
261+
end
262+
# Ok, we need some kind of cursor. If the siblings are stored, we will
263+
# consider using a stack based cursor, otherwise we will fall back to the
264+
# pointer tree one.
265+
if sl === StoredSiblings()
266+
# TODO: Some kind of consideration of mutability/non-mutability here?
267+
return InplaceStackedTreeCursor(tree)
268+
end
269+
return LinkedTreeCursor(tree)
270+
end

0 commit comments

Comments
 (0)