command-set 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,300 @@
1
+ module Command
2
+ module Results
3
+ #The root class of the List class family. A compositable tree, iterated by ListIterator.
4
+ class ListItem
5
+ class Exception < ::Exception; end
6
+ class NoMatch < Exception; end
7
+
8
+ @@next_seq = 0
9
+
10
+ def initialize(value)
11
+ @sequence = (@@next_seq +=1)
12
+ @value = value
13
+ @order = nil
14
+ @parent = nil
15
+ @options = {}
16
+ @depth = nil
17
+ end
18
+
19
+ attr_reader :value, :sequence
20
+ attr_accessor :parent, :order, :options, :depth
21
+
22
+ def ==(other)
23
+ return (ListItem === other &&
24
+ value == other.value)
25
+ end
26
+
27
+ def tree_order_next
28
+ right = self.next_sibling
29
+ if right.nil?
30
+ if self.parent.nil?
31
+ return nil
32
+ else
33
+ return self.parent.list_end
34
+ end
35
+ else
36
+ return right
37
+ end
38
+ end
39
+
40
+ def to_s
41
+ return @value.to_s
42
+ end
43
+
44
+ def eql?(other)
45
+ return self == other
46
+ end
47
+
48
+ def next_sibling
49
+ return nil if self.parent.nil?
50
+ return self.parent.after(self)
51
+ end
52
+
53
+ def match_on(value)
54
+ return value.to_s == self.to_s
55
+ end
56
+
57
+ def match(key)
58
+ if key == :*
59
+ return true
60
+ elsif key == :**
61
+ return true
62
+ else
63
+ match_on(key)
64
+ end
65
+ end
66
+
67
+ def filter(path)
68
+ if path.empty? || path == [:**]
69
+ return self
70
+ else
71
+ raise NoMatch
72
+ end
73
+ end
74
+
75
+ def inspect
76
+ "<i(#{@order}) #{value.inspect}>"
77
+ end
78
+ end
79
+
80
+ #A sort of virtual list element, used to denote the end of a list in iteration.
81
+ class ListEnd < ListItem
82
+ def initialize(end_of)
83
+ @end_of = end_of
84
+ end
85
+
86
+ attr_reader :end_of
87
+
88
+ def parent
89
+ @end_of.parent
90
+ end
91
+
92
+ def order
93
+ @end_of.order
94
+ end
95
+
96
+ def next_sibling
97
+ @end_of.next_sibling
98
+ end
99
+
100
+ def to_s
101
+ @end_of.to_s
102
+ end
103
+
104
+ def inspect
105
+ "<e(#{@end_of.order}):#{@end_of.name}>"
106
+ end
107
+ end
108
+
109
+ #A List contains ListItems - this class is sorely under-documented.
110
+ #Honestly, much of it's functionality is either speculative (and it's quite
111
+ #possible that you ain't gonna need it) or else required by internal
112
+ #code and probably not needed by client code.
113
+ #
114
+ #What's most important to understand is that Lists can nest both
115
+ #ListItems and other Lists, which makes them a bit like a tree, and a
116
+ #bit like a list.
117
+ class List < ListItem
118
+ def initialize(name, values=[])
119
+ @name = name
120
+ create_order = 0
121
+ values = values.map do |item|
122
+ if ListItem === item
123
+ if item.order.nil?
124
+ item.order = (create_order += 1)
125
+ else
126
+ create_order = item.order
127
+ end
128
+ else
129
+ item = ListItem.new(item)
130
+ item.order = (create_order += 1)
131
+ end
132
+
133
+ if item.parent.nil?
134
+ item.parent = self
135
+ end
136
+
137
+ item
138
+ end
139
+ super(values)
140
+ @open = true
141
+ end
142
+
143
+ attr_reader :name
144
+ alias values value
145
+
146
+ def to_s
147
+ @name.to_s
148
+ end
149
+
150
+ def list_end
151
+ return ListEnd.new(self)
152
+ end
153
+
154
+ def tree_order_next
155
+ deeper = self.first_child
156
+ if deeper.nil?
157
+ return self.list_end
158
+ else
159
+ return deeper
160
+ end
161
+ end
162
+
163
+ #Helper for #filter
164
+ def filter_into_array(path)
165
+ next_path = path.first == :** ? path : path[1..-1]
166
+ return values.find_all do |value|
167
+ value.match(path[0])
168
+ end.map do |item|
169
+ begin
170
+ item.filter(next_path)
171
+ rescue NoMatch => nm
172
+ nm
173
+ end
174
+ end.reject do |value|
175
+ NoMatch === value
176
+ end
177
+ end
178
+
179
+ #+path+ should be an array of arguments to match (against list names
180
+ #or item values). The special path element +:**+ can be used to keep
181
+ #opening lists to find whatever. The result will be a List that
182
+ #contains only matching elements. +:*+ will match any single item at
183
+ #that level.
184
+ def filter(path)
185
+ if path == [:**]
186
+ list = List.new(@name, @value)
187
+ list.order = @order
188
+ return list
189
+ end
190
+
191
+ if path.first == :**
192
+ double_stars = filter_into_array(path)
193
+ path = path[1..-1]
194
+ else
195
+ double_stars = nil
196
+ end
197
+
198
+ list = filter_into_array(path)
199
+
200
+ if (double_stars.nil? || double_stars.empty?) && path.length > 0 && list.empty?
201
+ raise NoMatch
202
+ end
203
+
204
+ unless double_stars.nil?
205
+ list.each do |item|
206
+ unless double_stars.find {|starred| starred.order == item.order }
207
+ double_stars << item
208
+ end
209
+ end
210
+
211
+ double_stars.sort! {|left, right| left.order <=> right.order}
212
+
213
+ list = double_stars
214
+ end
215
+
216
+ list = List.new(@name, list)
217
+ list.order = @order
218
+ return list
219
+ end
220
+
221
+ def open?
222
+ @open
223
+ end
224
+
225
+ def first_child
226
+ if values.empty?
227
+ return nil
228
+ else
229
+ return values[0]
230
+ end
231
+ end
232
+
233
+ def after(item)
234
+ index = nil
235
+ values.each_with_index do |value, idx|
236
+ if value.equal?(item)
237
+ index = idx
238
+ break
239
+ end
240
+ end
241
+ if index.nil? or index >= values.length
242
+ return nil
243
+ else
244
+ return values[index + 1]
245
+ end
246
+ end
247
+
248
+ def close
249
+ return self unless @open
250
+ @open = false
251
+ values.each do |item|
252
+ next unless List === item
253
+ item.close
254
+ end
255
+ return self
256
+ end
257
+
258
+ def add(item)
259
+ unless ListItem === item
260
+ item = ListItem.new(item)
261
+ end
262
+ item.parent = self
263
+ item.order = values.length
264
+ values.push(item)
265
+ return item
266
+ end
267
+
268
+ def ==(other)
269
+ return (List === other &&
270
+ name == other.name &&
271
+ values == other.values)
272
+ end
273
+
274
+ def eql?(other)
275
+ return self == other
276
+ end
277
+
278
+ def inspect
279
+ "<#{@open ? "L":"l"}(#@order):#{name.to_s} #{values.inspect} #{name.to_s}>"
280
+ end
281
+ end
282
+
283
+ #Basically an Enumerator over a List. Give it any list element, and
284
+ ##each will take you to the end of the list.
285
+ class ListIterator
286
+ include Enumerable
287
+ def initialize(list)
288
+ @list = list
289
+ end
290
+
291
+ def each
292
+ thumb = @list
293
+ until thumb.nil?
294
+ yield thumb
295
+ thumb = thumb.tree_order_next
296
+ end
297
+ end
298
+ end
299
+ end
300
+ end