forked from lua-stdlib/lua-stdlib
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtree.lua
More file actions
282 lines (251 loc) · 8.17 KB
/
tree.lua
File metadata and controls
282 lines (251 loc) · 8.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
--[[--
Tree container prototype.
Note that Functions listed below are only available from the Tree
prototype returned by requiring this module, because Container objects
cannot have object methods.
Prototype Chain
---------------
table
`-> Object
`-> Container
`-> Tree
@classmod std.tree
@see std.container
]]
local base = require "std.base"
local operator = require "std.operator"
local Container = require "std.container" {}
local ielems, ipairs, leaves, pairs, prototype =
base.ielems, base.ipairs, base.leaves, base.pairs, base.prototype
local last, len = base.last, base.len
local reduce = base.reduce
local Tree -- forward declaration
--- Tree iterator.
-- @tparam function it iterator function
-- @tparam tree|table tr tree or tree-like table
-- @treturn string type ("leaf", "branch" (pre-order) or "join" (post-order))
-- @treturn table path to node (`{i1, ...in}`)
-- @treturn node node
local function _nodes (it, tr)
local p = {}
local function visit (n)
if type (n) == "table" then
coroutine.yield ("branch", p, n)
for i, v in it (n) do
p[#p + 1] = i
visit (v)
table.remove (p)
end
coroutine.yield ("join", p, n)
else
coroutine.yield ("leaf", p, n)
end
end
return coroutine.wrap (visit), tr
end
local function clone (t, nometa)
local r = {}
if not nometa then
setmetatable (r, getmetatable (t))
end
local d = {[t] = r}
local function copy (o, x)
for i, v in pairs (x) do
if type (v) == "table" then
if not d[v] then
d[v] = {}
if not nometa then
setmetatable (d[v], getmetatable (v))
end
o[i] = copy (d[v], v)
else
o[i] = d[v]
end
else
o[i] = v
end
end
return o
end
return copy (r, t)
end
local function merge (t, u)
for ty, p, n in _nodes (pairs, u) do
if ty == "leaf" then
t[p] = n
end
end
return t
end
--[[ ============ ]]--
--[[ Tree Object. ]]--
--[[ ============ ]]--
local function X (decl, fn)
return require "std.debug".argscheck ("std.tree." .. decl, fn)
end
--- Tree prototype object.
-- @object Tree
-- @string[opt="Tree"] _type object name
-- @see std.container
-- @see std.object.__call
-- @usage
-- local std = require "std"
-- local Tree = std.tree {}
-- local tr = Tree {}
-- tr[{"branch1", 1}] = "leaf1"
-- tr[{"branch1", 2}] = "leaf2"
-- tr[{"branch2", 1}] = "leaf3"
-- print (tr[{"branch1"}]) --> Tree {leaf1, leaf2}
-- print (tr[{"branch1", 2}]) --> leaf2
-- print (tr[{"branch1", 3}]) --> nil
-- --> leaf1 leaf2 leaf3
-- for leaf in std.tree.leaves (tr) do
-- io.write (leaf .. "\t")
-- end
Tree = Container {
_type = "Tree",
--- Deep retrieval.
-- @static
-- @function __index
-- @tparam Tree tr a tree
-- @param i non-table, or list of keys `{i1, ...i_n}`
-- @return `tr[i1]...[i_n]` if *i* is a key list, `tr[i]` otherwise
-- @todo the following doesn't treat list keys correctly
-- e.g. tr[{{1, 2}, {3, 4}}], maybe flatten first?
-- @usage
-- del_other_window = keymap[{"C-x", "4", KEY_DELETE}]
__index = X ("__index (Tree, any)",
function (tr, i)
if prototype (i) == "table" then
return reduce (operator.deref, tr, ielems, i)
else
return rawget (tr, i)
end
end),
--- Deep insertion.
-- @static
-- @function __newindex
-- @tparam Tree tr a tree
-- @param i non-table, or list of keys `{i1, ...i_n}`
-- @param[opt] v value
-- @usage
-- function bindkey (keylist, fn) keymap[keylist] = fn end
__newindex = X ("__newindex (Tree, any, any?)",
function (tr, i, v)
if prototype (i) == "table" then
for n = 1, len (i) - 1 do
if prototype (tr[i[n]]) ~= "Tree" then
rawset (tr, i[n], Tree {})
end
tr = tr[i[n]]
end
rawset (tr, last (i), v)
else
rawset (tr, i, v)
end
end),
_functions = {
--- Make a deep copy of a tree, including any metatables.
-- @static
-- @function clone
-- @tparam table t tree or tree-like table
-- @tparam boolean nometa if non-`nil` don't copy metatables
-- @treturn Tree|table a deep copy of *tr*
-- @see std.table.clone
-- @usage
-- tr = {"one", {two=2}, {{"three"}, four=4}}
-- copy = clone (tr)
-- copy[2].two=5
-- assert (tr[2].two == 2)
clone = X ("clone (table, boolean|:nometa?)", clone),
--- Tree iterator which returns just numbered leaves, in order.
-- @static
-- @function ileaves
-- @tparam Tree|table tr tree or tree-like table
-- @treturn function iterator function
-- @treturn Tree|table the tree *tr*
-- @see inodes
-- @see leaves
-- @usage
-- --> t = {"one", "three", "five"}
-- for leaf in ileaves {"one", {two=2}, {{"three"}, four=4}}, foo="bar", "five"}
-- do
-- t[#t + 1] = leaf
-- end
ileaves = X ("ileaves (table)", function (t) return leaves (ipairs, t) end),
--- Tree iterator over numbered nodes, in order.
--
-- The iterator function behaves like @{nodes}, but only traverses the
-- array part of the nodes of *tr*, ignoring any others.
-- @static
-- @function inodes
-- @tparam Tree|table tr tree or tree-like table to iterate over
-- @treturn function iterator function
-- @treturn tree|table the tree, *tr*
-- @see nodes
inodes = X ("inodes (table)", function (t) return _nodes (ipairs, t) end),
--- Tree iterator which returns just leaves.
-- @static
-- @function leaves
-- @tparam table t tree or tree-like table
-- @treturn function iterator function
-- @treturn table *t*
-- @see ileaves
-- @see nodes
-- @usage
-- for leaf in leaves {"one", {two=2}, {{"three"}, four=4}}, foo="bar", "five"}
-- do
-- t[#t + 1] = leaf
-- end
-- --> t = {2, 4, "five", "foo", "one", "three"}
-- table.sort (t, lambda "=tostring(_1) < tostring(_2)")
leaves = X ("leaves (table)", function (t) return leaves (pairs, t) end),
--- Destructively deep-merge one tree into another.
-- @static
-- @function merge
-- @tparam table t destination tree
-- @tparam table u table with nodes to merge
-- @treturn table *t* with nodes from *u* merged in
-- @see std.table.merge
-- @usage
-- merge (dest, {{exists=1}, {{not = {present = { inside = "dest" }}}}})
merge = X ("merge (table, table)", merge),
--- Tree iterator over all nodes.
--
-- The returned iterator function performs a depth-first traversal of
-- `tr`, and at each node it returns `{node-type, tree-path, tree-node}`
-- where `node-type` is `branch`, `join` or `leaf`; `tree-path` is a
-- list of keys used to reach this node, and `tree-node` is the current
-- node.
--
-- Note that the `tree-path` reuses the same table on each iteration, so
-- you must `table.clone` a copy if you want to take a snap-shot of the
-- current state of the `tree-path` list before the next iteration
-- changes it.
-- @static
-- @function nodes
-- @tparam Tree|table tr tree or tree-like table to iterate over
-- @treturn function iterator function
-- @treturn Tree|table the tree, *tr*
-- @see inodes
-- @usage
-- -- tree = +-- node1
-- -- | +-- leaf1
-- -- | '-- leaf2
-- -- '-- leaf 3
-- tree = Tree { Tree { "leaf1", "leaf2"}, "leaf3" }
-- for node_type, path, node in nodes (tree) do
-- print (node_type, path, node)
-- end
-- --> "branch" {} {{"leaf1", "leaf2"}, "leaf3"}
-- --> "branch" {1} {"leaf1", "leaf"2")
-- --> "leaf" {1,1} "leaf1"
-- --> "leaf" {1,2} "leaf2"
-- --> "join" {1} {"leaf1", "leaf2"}
-- --> "leaf" {2} "leaf3"
-- --> "join" {} {{"leaf1", "leaf2"}, "leaf3"}
-- os.exit (0)
nodes = X ("nodes (table)", function (t) return _nodes (pairs, t) end),
},
}
return Tree