cursor 0.6 → 0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/cursor.rb CHANGED
@@ -1,385 +1,544 @@
1
1
  #!/bin/env ruby
2
2
  # = cursor.rb - external iterators with capabilities like a text editor cursor
3
- # $Id: cursor.rb,v 1.8 2005/05/25 16:12:22 eric_mahurin Exp $
3
+ # $Id: cursor.rb,v 1.53 2005/07/21 15:15:38 eric_mahurin Exp $
4
4
  # Author:: Eric Mahurin (Eric under Mahurin at yahoo dot com)
5
5
  # License:: Ruby license
6
6
  # Home:: http://rubyforge.org/projects/cursor
7
7
 
8
- # An object in this Cursor class can be best thought of as a cursor in a text
9
- # editor. Many of the same operations apply - insert, delete, replace, copy,
10
- # paste, move, goto begin/end, mark position, goto mark, etc. Unlike a
11
- # text editor, this class can operate on variety of data, not just characters
12
- # and strings. It is up to the derived classes to deal with what type of data
13
- # is stored (i.e. characters, arbitrary array objects) and how it is stored (in
14
- # an Array, String, IO, mapping to another Cursor, etc).
8
+ require 'weakrefset'
9
+
10
+ =begin rdoc
11
+ An object in this Cursor class can be best thought of as a cursor in a text
12
+ editor. Many of the same operations apply - insert, delete, replace, copy,
13
+ paste, move, goto begin/end, mark position, goto mark, etc. Unlike a
14
+ text editor, this class can operate on variety of data, not just characters
15
+ and strings. It is up to the derived classes to deal with what type of data
16
+ is stored (i.e. characters, arbitrary array objects) and how it is stored (in
17
+ an Array, String, IO, linked list, mapping to another Cursor, etc). The
18
+ minimal a derived class has to implement to be able to get all of the features
19
+ below is inserting and deleting an element (both directions). Various categories
20
+ of methods are described in the paragraphs below.
21
+
22
+ The single element methods are listed below. The suffix Next/Prev means operate
23
+ after/before the cursor and move the cursor in the same direction. The suffix After/Before
24
+ means operate after/before the cursor and leave the cursor in place. The ? suffix
25
+ looks what is being written over or deleted and returns it instead of +true+
26
+ (the reads are like ? versions of skips). The ! suffix forces the operation by
27
+ doing an insert instead of overwrite at the beginning/end. When any of these
28
+ operations fail (at beginning/end or a mismatch) +nil+ is returned.
29
+
30
+ #read1next, #read1prev, #read1after, #read1before,
31
+ #skip1next, #skip1prev, #skip1after, #skip1before,
32
+ #write1next, #write1prev, #write1after, #write1before,
33
+ #write1next!, #write1prev!, #write1after!, #write1before!,
34
+ #write1next?, #write1prev?, #write1after?, #write1before?,
35
+ #delete1after, #delete1before, #delete1after?, #delete1before?,
36
+ #insert1before, #insert1after,
37
+ #scan1next, #scan1prev,
38
+ #modify1next, #modify1prev,
39
+
40
+ Basic methods that operate on element sequences are listed below. These are
41
+ all bidirectional, can hold the cursor in place, and some may be able to
42
+ delete/insert. The ! suffix operates
43
+ until the beginning/end. The element sequences
44
+ are passed/returned in Array/String like things. The methods these sequences
45
+ need are #<< and/or #[].
46
+ #new_data controls the default sequence returned (but another object that
47
+ responds to #<< can be passed in instead). Sequences to be written/scanned
48
+ with need to respond to #[] (scanning requires this return something responding
49
+ to #==).
50
+
51
+ #read, #read!, #skip, #skip!,
52
+ #write, #write?,
53
+ #scan, #scan_until, #scan_partial,
54
+ #modify
55
+
56
+ The methods below deal with numeric positions (a pos) to represent the cursor location.
57
+ A non-negative number represents the number of elements from the beginning.
58
+ A negative number (including -0.0) represents the location relative to the
59
+ end. Also included in this list below is #prop for manipulating properities
60
+ for the current cursor location. One typical use is for tracking line and
61
+ column numbers.
62
+
63
+ #pos, #pos=, #to_i, #prop, #to_s,
64
+
65
+ The position methods below use a Cursor object (Cursor::Position in the base class) to
66
+ hold the position rather than simply a numeric position. These
67
+ position objects hold a #pos and whatever is
68
+ in #prop. Also, the #pos in these objects adjust based on
69
+ insertions and deletions. It is preferrable to use these position objects
70
+ over numeric positions because
71
+ a) derived classes may have a more efficient means of holding a position (i.e. linked list could hold a node reference),
72
+ b) they may trigger things like buffering in derived classes,
73
+ c) they hold what's in #prop,
74
+ d) they adjust with insert/delete,
75
+ e) when the numeric positions can't be implemented these methods still may, and
76
+ f) the covenience of saving and possibly restoring position
77
+ before/after executing a code block is available (should be implementable before
78
+ any of the others).
79
+
80
+ #position, #position=, #position?, #position!,
81
+ #close, #closed?
82
+
83
+ Queries about current position relative to another #position: #<=>, #-
84
+
85
+ Return a remote #position: #+, #succ, #pred, #begin, #end
86
+
87
+ Access the entire collection: #empty?, #length/#size, #data, #replace
88
+
89
+ Random access: #[]/#slice, #slice!, #[]=, #<<, #>>
90
+
91
+ Enumerable: #each, #collect!/#map!
92
+
93
+ The best way to get examples/demos of the methods and the classes is to use the
94
+ cursor/test_cursors.rb and cursor/test_circulars.rb scripts.
95
+
96
+ =end
15
97
  class Cursor
16
98
 
17
99
  include Comparable
18
100
  include Enumerable
19
101
 
20
- # Operate on data before the cursor rather than after
21
- Reverse = 1
22
- # Hold position - do the operation and come back
23
- Hold = 2
24
- # Just count the elements from get/delete instead of returning their value
25
- Ignore = 4
26
- # Read the data being replaced using put and return it (instead of the count)
27
- Read = 4
28
- # Delete instead of retrieve elements (used with get)
29
- Delete = 8
30
- # Insert instead of replace elements (used with put)
31
- Insert = 8
32
- # Put a single element instead of a sequence (needed when it looks like a sequence)
33
- Single = 16
34
- # Operate on what follows the cursor and then move foward
35
- Next = 0
36
- # Operate on what precedes the cursor and then move backward
37
- Prev = Reverse
38
- # Operate on what follows the cursor and hold position
39
- After = Hold
40
- # Operate on what precedes the cursor and hold position
41
- Before = Reverse|Hold
42
- # #pos for the beginning (positive zero)
43
- Begin = +0
44
- # #pos for the end (negative zero)
45
- End = -0.1
46
- # #get +len+ for getting the rest of the elements
47
- Rest = +1.0/0
48
-
102
+ # Create a new cursor from another cursor. Only single element methods and
103
+ # #new_data are
104
+ # delegated to the other cursor. Other methods are based on those single
105
+ # element methods.
106
+ def initialize(cursor)
107
+ @cursor = cursor
108
+ end
109
+
49
110
  protected
50
- # Delete one element at the cursor and return it. The observed bits in +flags+
51
- # are +Reverse+ (delete before the cursor instead of after) and +Ignore+
52
- # (return true instead of the deleted element). When the cursor is at the end (or beginning),
53
- # either nil or the code block result is returned (see #get).
54
- # <b>This method must be overriden
55
- # in a derived class</b>.
56
- def _delete1(flags,&more) # :yield: l=1
57
- raise(NotImplementedError)
58
- end
59
- # Insert one element at the cursor. Only the +Reverse+ bit
60
- # is observed in +flags+. nil should be returned. <b>This method must be overriden
61
- # in a derived class</b>.
62
- def _insert1(value,flags)
63
- raise(NotImplementedError)
64
- end
65
- # Get an element at the cursor. The observed bits in +flags+ are +Reverse+
66
- # (read before instead of after the cursor), +Hold+ (don't move the
67
- # cursor), and +Ignore+ (just return +true+ instead of the value). When the
68
- # cursor is at the end (or beginning), either nil or the code block result
69
- # is returned (see #get).
70
- def _get1(flags,&more) # :yield: l=1
71
- value = _delete1(flags&~Ignore) { |l| more&&more[l] || return }
72
- if (flags&Hold).nonzero?
73
- flags ^= Hold
74
- flags ^= Reverse
75
- end
76
- _insert1(value,flags)
77
- (flags&Ignore).nonzero? ? true : value
78
- end
79
- # Put an element at the cursor. The observed bits in +flags+ are +Reverse+
80
- # (put before instead of after the cursor), +Hold+ (don't move the
81
- # cursor), and +Read+ (read and return what is being replaced instead of
82
- # just returning +true+). When the cursor is at the end (or beginning),
83
- # either nil or the code block result is returned (see #get).
84
- # The value is inserted at that point when that happens.
85
- def _put1(value,flags,&more) # :yield: l=1
86
- value0 = _delete1(flags^Read,&more)
87
- if (flags&Hold).nonzero?
88
- flags ^= Hold
89
- flags ^= Reverse
90
- end
91
- _insert1(value,flags)
92
- value0
111
+ # adjust positions after a deletion
112
+ def _adjust_delete(len=1,reverse=false)
113
+ i = pos(false)
114
+ ret = nil
115
+ @positions.each { |p| ret = p.__send__(:_deletion,i,len,reverse,ret) }
116
+ end
117
+ # adjust positions before an insertion
118
+ def _adjust_insert(len=1)
119
+ i = pos(false)
120
+ ret = nil
121
+ @positions.each { |p| ret = p.__send__(:_insertion,i,len,ret) }
122
+ end
123
+ def _delete1after?
124
+ @cursor.delete1after?
125
+ end
126
+ def _delete1before?
127
+ @cursor.delete1before?
128
+ end
129
+ def _insert1before(v)
130
+ @cursor.insert1before(v)
131
+ end
132
+ def _insert1after(v)
133
+ @cursor.insert1after(v)
93
134
  end
94
135
 
95
136
  public
96
- # Create a cursor.
97
- def initialize
98
- @positions = []
99
- end
100
- # Return the class used for passing/returning a sequence of elements. This
101
- # class must behave as a String or an Array ([], []=, slice, slice!, length).
102
- # The default in the base class is Array.
103
- def data_class
104
- Array
137
+ # read next element
138
+ def read1next
139
+ @cursor.read1next
140
+ end
141
+ # read previous element
142
+ def read1prev
143
+ @cursor.read1prev
144
+ end
145
+ # read element after the cursor
146
+ def read1after
147
+ @cursor.read1after
148
+ end
149
+ # read element before the cursor
150
+ def read1before
151
+ @cursor.read1before
152
+ end
153
+ # skip next element and return true if succeeded
154
+ def skip1next
155
+ @cursor.skip1next
156
+ end
157
+ # skip previous element and return true if succeeded
158
+ def skip1prev
159
+ @cursor.skip1prev
160
+ end
161
+ # check if there is an element after the cursor
162
+ def skip1after
163
+ @cursor.skip1after
164
+ end
165
+ # check if there is an element before the cursor
166
+ def skip1before
167
+ @cursor.skip1before
168
+ end
169
+ # delete element after and return true if succeeded
170
+ def delete1after
171
+ v0 = @cursor.delete1after
172
+ v0 && @positions && _adjust_delete
173
+ v0
174
+ end
175
+ # delete element before cursor and return true if succeeded
176
+ def delete1before
177
+ v0 = @cursor.delete1before
178
+ v0 && @positions && _adjust_delete
179
+ v0
180
+ end
181
+ # delete element after cursor and return what is was
182
+ def delete1after?
183
+ v0 = @cursor.delete1after?
184
+ v0.nil? || @positions && _adjust_delete
185
+ v0
186
+ end
187
+ # delete element before cursor and return what is was
188
+ def delete1before?
189
+ v0 = @cursor.delete1before?
190
+ v0.nil? || @positions && _adjust_delete
191
+ v0
192
+ end
193
+ # write over next element and return true if successful
194
+ def write1next(v)
195
+ @cursor.write1next(v)
196
+ end
197
+ # write over previous element and return true if successful
198
+ def write1prev(v)
199
+ @cursor.write1prev(v)
200
+ end
201
+ # write over element after the cursor and return true if successful
202
+ def write1after(v)
203
+ @cursor.write1after(v)
204
+ end
205
+ # write over element before the cursor and return true if successful
206
+ def write1before(v)
207
+ @cursor.write1before(v)
208
+ end
209
+ # write next element or insert before if at end (returns true)
210
+ def write1next!(v)
211
+ write1next(v) || insert1before(v)
212
+ end
213
+ # write previous element or insert after if at beginning (returns true)
214
+ def write1prev!(v)
215
+ write1prev(v) || insert1after(v)
216
+ end
217
+ # write element after the cursor or insert after if at end (returns true)
218
+ def write1after!(v)
219
+ write1after(v) || insert1after(v)
220
+ end
221
+ # write element before the cursor or insert before if at beginning (returns true)
222
+ def write1before!(v)
223
+ write1before(v) || insert1before(v)
224
+ end
225
+ # write over next element and return what was overwritten
226
+ def write1next?(v)
227
+ @cursor.write1next?(v)
228
+ end
229
+ # write over previous element and return what was overwritten
230
+ def write1prev?(v)
231
+ @cursor.write1prev?(v)
232
+ end
233
+ # write over element after the cursor and return what was overwritten
234
+ def write1after?(v)
235
+ @cursor.write1after?(v)
236
+ end
237
+ # write over element before the cursor and return what was overwritten
238
+ def write1before?(v)
239
+ @cursor.write1before?(v)
240
+ end
241
+ # insert element before cursor (returns true)
242
+ def insert1before(v)
243
+ @positions && _adjust_insert
244
+ @cursor.insert1before(v)
245
+ end
246
+ # insert element after cursor (returns true)
247
+ def insert1after(v)
248
+ @positions && _adjust_insert
249
+ @cursor.insert1after(v)
250
+ end
251
+ # scan next element using == and return it when matched or nil (and go back) when mismatched
252
+ def scan1next(v)
253
+ @cursor.scan1next(v)
254
+ end
255
+ # scan previous element using == and return it when matched or nil (and go back) when mismatched
256
+ def scan1prev(v)
257
+ @cursor.scan1prev(v)
258
+ end
259
+ # modify next element using +lookup+.[] and return the original
260
+ def modify1next(lookup)
261
+ @cursor.modify1next(lookup)
262
+ end
263
+ # modify previous element using +lookup+.[] and return the original
264
+ def modify1prev(lookup)
265
+ @cursor.modify1prev(lookup)
105
266
  end
106
- # Get a single element or a sequence of them.
107
- #
108
- # +len+ can be +nil+ (get one element - like IO#getc), a non-negative number (get a
109
- # sequence of that many elements - like IO#read), a negative number (get that many in reverse),
110
- # or a termination sequence (get a sequence up until it finds a match to +len+ - like IO#gets).
111
- #
112
- # The observed bits in +flags+ are +Reverse+
113
- # (read before instead of after the cursor), +Hold+ (don't move the
114
- # cursor), +Ignore+ (just return +true+ or a count instead of the value),
115
- # and +Delete+ (delete the element or sequence while retrieving it).
116
- #
117
- # If there is anything left, either nil (no code block) or the result from code block is
118
- # returned. This code block could set some variable(s) to signify it
119
- # is done or return data from some other source to make it look as though
120
- # it is not done. When returning more data, it should return it in a
121
- # String or Array of up to +l+ elements.
122
- def get(len=nil,flags=Next,&more) # :yield: l
123
- g = (flags&Delete).nonzero? ? :_delete1 : :_get1
124
- return(__send__(g,flags,&more)) if not len
125
- if (flags&Hold).nonzero? and (flags&Delete).zero?
126
- flags ^= Hold
127
- position { get(len,flags,&more) }
128
- elsif len.respond_to?:zero?
129
- if len<0
130
- flags ^= Reverse
131
- len = -len
132
- end
133
- len -= 1
134
- if (flags&Ignore).nonzero?
135
- len1 = 0
136
- while len>=len1
137
- __send__(g,flags) { |l|
138
- more&&more[l] ||
139
- (return(len1.zero? ? nil : len1))
140
- }
141
- len1 += 1
142
- end
143
- len1
144
- else
145
- value = data_class.new
146
- while len>=value.size
147
- v = __send__(g,flags&~Ignore) { |l|
148
- more&&more[l] || (return(
149
- value.size.zero? ? nil :
150
- (flags&Reverse).nonzero? ? value.reverse : value
151
- ))
152
- }
153
- value << v
154
- end
155
- (flags&Reverse).nonzero? ? value.reverse : value
156
- end
157
- else
158
- if (flags&Ignore).nonzero?
159
- value = get(len,flags^Ignore,&more)
160
- return(value&&value.size)
161
- end
162
- value = data_class.new
163
- if (flags&Reverse).nonzero?
164
- step = -1
165
- start = len.size-1
166
- finish = -1
167
- else
168
- step = +1
169
- start = 0
170
- finish = len.size
171
- end
172
- i = start
173
- loop do
174
- v = __send__(g,flags) { |l|
175
- more&&more[l] || begin
176
- value = value.reverse if (flags&Reverse).nonzero?
177
- value = nil if value.size.zero?
178
- return
179
- end
180
- }
181
- value << v
182
- if v==len[i]
183
- i += step
184
- if i==finish
185
- value = value.reverse if (flags&Reverse).nonzero?
186
- return(value)
187
- end
188
- else
189
- i = start
190
- end
191
- end
267
+
268
+ # Return an empty object used for returning a sequence of elements.
269
+ # The only method required of this object is << (append to the sequence).
270
+ # Typically this object is determined by the #new_data of the cursor given
271
+ # to new or is the same class of the data (i.e. String/Array) given to new.
272
+ def new_data
273
+ @cursor.new_data
274
+ end
275
+ # read +len+ elements. A negative +len+ will go in reverse. +hold+ will
276
+ # hold the cursor in place. +hold+==+nil+ (not false) will make it delete.
277
+ def read(len,hold=false,buffer=new_data)
278
+ reverse = len<0
279
+ len = len.abs
280
+ meth = method(hold.nil? ?
281
+ (reverse ? :delete1before? : :delete1after?) :
282
+ (reverse ? :read1prev : :read1next) )
283
+ len0 = 0
284
+ while len>len0
285
+ (v0 = meth.call).nil? and return(
286
+ skip(reverse ? len0 : -len0) if hold
287
+ len0.nonzero?&&buffer
288
+ )
289
+ buffer << v0
290
+ len0 += 1
291
+ end
292
+ skip(reverse ? len0 : -len0) if hold
293
+ buffer
294
+ end
295
+ # read the remaining elements. +hold+ will
296
+ # hold the cursor in place. +hold+==+nil+ (not false) will make it delete.
297
+ def read!(reverse=false,hold=false,buffer=new_data)
298
+ meth = method(hold.nil? ?
299
+ (reverse ? :delete1before? : :delete1after?) :
300
+ (reverse ? :read1prev : :read1next) )
301
+ len0 = 0
302
+ loop do
303
+ (v0 = meth.call).nil? and return(
304
+ skip(reverse ? len0 : -len0) if hold
305
+ len0.nonzero?&&buffer
306
+ )
307
+ buffer << v0
308
+ len0 += 1
309
+ end
310
+ end
311
+ # skip +len+ elements and return the count. A negative +len+ will go in
312
+ # reverse. +hold+ will
313
+ # hold the cursor in place. +hold+==+nil+ (not false) will make it delete.
314
+ def skip(len,hold=false)
315
+ reverse = len<0
316
+ len = len.abs
317
+ meth = method(hold.nil? ?
318
+ (reverse ? :delete1before : :delete1after) :
319
+ (reverse ? :skip1prev : :skip1next) )
320
+ len0 = 0
321
+ while len>len0
322
+ meth.call or return(
323
+ skip(reverse ? len0 : -len0) if hold
324
+ len0.nonzero?
325
+ )
326
+ len0 += 1
327
+ end
328
+ skip(reverse ? len0 : -len0) if hold
329
+ len0
330
+ end
331
+ # skip the remaining elements and return the count. A negative +len+ will go in
332
+ # reverse. +hold+ will hold the cursor in place. +hold+==+nil+ (not false)
333
+ # will make it delete.
334
+ def skip!(reverse=false,hold=false)
335
+ meth = method(hold.nil? ?
336
+ (reverse ? :delete1before : :delete1after) :
337
+ (reverse ? :skip1prev : :skip1next) )
338
+ len0 = 0
339
+ loop do
340
+ meth.call or return(
341
+ skip(reverse ? len0 : -len0) if hold
342
+ len0.nonzero?
343
+ )
344
+ len0 += 1
345
+ end
346
+ end
347
+ # write a sequence of elements and return the overwrite count. +hold+ will
348
+ # hold the cursor in place. +hold+==+nil+ (not false) will make it insert.
349
+ # +overwrite_only+ will prevent insertion when the cursor is at the beginning/end.
350
+ def write(value,reverse=false,hold=false,overwrite_only=false)
351
+ meth = method(
352
+ hold.nil? ? (reverse ? :insert1after : :insert1before) :
353
+ overwrite_only ? (reverse ? :write1prev : :write1next) :
354
+ (reverse ? :write1prev! : :write1next!) )
355
+ i = 0
356
+ until (v = value[i]).nil?
357
+ meth.call(v) or return(
358
+ skip(reverse ? i : -i) if hold
359
+ i.nonzero?
360
+ )
361
+ i += 1
362
+ end
363
+ skip(reverse ? i : -i) if hold
364
+ i
365
+ end
366
+ # overwrite a sequence of elements and return the sequence overwritten.
367
+ # +hold+ will hold the cursor in place.
368
+ def write?(value,reverse=false,hold=false,buffer=value.class.new)
369
+ meth = method(reverse ? :write1prev? : :write1next?)
370
+ i = 0
371
+ until (v = value[i]).nil?
372
+ (v0 = meth.call(v)).nil? and return(
373
+ skip(reverse ? i : -i) if hold
374
+ i.nonzero? && buffer
375
+ )
376
+ buffer << v0
377
+ i += 1
378
+ end
379
+ skip(reverse ? i : -i) if hold
380
+ buffer
381
+ end
382
+ # scan for +value+ at the cursor. When there is a mismatch, +nil+ is returned
383
+ # and the cursor is move back to where it started (unless hold==+nil+:
384
+ # cursor position will indeterminate).
385
+ # +hold+ will hold the cursor in place even on a match.
386
+ def scan(value,reverse=false,hold=false,buffer=value.class.new)
387
+ meth = method(reverse ? :read1prev : :read1next)
388
+ i = 0
389
+ until (v = value[i]).nil?
390
+ v0 = meth.call
391
+ (!v0.nil? and i += 1 and v==v0) or return(
392
+ skip(reverse ? i : -i) if !hold.nil?
393
+ nil
394
+ )
395
+ buffer << v0
396
+ end
397
+ skip(reverse ? i : -i) if hold
398
+ buffer
399
+ end
400
+ # scan for +value+ until it is found (or the end is reached) and
401
+ # return that entire sequence. +hold+ will
402
+ # hold the cursor in place. +hold+==+nil+ (not false) will make it delete.
403
+ def scan_until(value,reverse=false,hold=false,buffer=value.class.new)
404
+ meth = method(hold.nil? ?
405
+ (reverse ? :delete1before? : :delete1after?) :
406
+ (reverse ? :read1prev : :read1next) )
407
+ buffer2 = []
408
+ len0 = 0
409
+ i = 0
410
+ until (v = value[i]).nil?
411
+ (v0 = meth.call).nil? and return(
412
+ skip(reverse ? len0 : -len0) if hold
413
+ len0.nonzero?&&buffer
414
+ )
415
+ buffer << v0
416
+ buffer2 << v0
417
+ len0 += 1
418
+ i += 1
419
+ if v!=v0
420
+ begin
421
+ buffer2.shift
422
+ i -= 1
423
+ end until i.times { |j| value[j]==buffer2[j] or (v=nil;break) }
424
+ v ||= value[i]
425
+ redo
426
+ end
427
+ end
428
+ skip(reverse ? len0 : -len0) if hold
429
+ buffer
430
+ end
431
+ # scan for +value+ and return as much of the sequence that matched
432
+ def scan_partial(value,reverse=false,hold=false,buffer=value.class.new)
433
+ meth = method(reverse ? :scan1prev : :scan1next)
434
+ i = 0
435
+ until (v = value[i]).nil?
436
+ v0 = meth.call(v)
437
+ !v0.nil? or return(
438
+ skip(reverse ? i : -i) if hold
439
+ i.nonzero?&&buffer
440
+ )
441
+ buffer << v0
442
+ i += 1
192
443
  end
444
+ skip(reverse ? i : -i) if hold
445
+ buffer
193
446
  end
194
- # Put a single element or a sequence of them at the cursor.
195
- #
196
- # The observed bits in +flags+ are +Reverse+
197
- # (read before instead of after the cursor), +Hold+ (don't move the
198
- # cursor), +Read+ (return the what is overwritten instead of +true+ or a count),
199
- # +Insert+ (insert rather than replace +value+), and +Single+ (force +value+
200
- # to be treated as a single element rather than a sequence).
201
- #
202
- # When the cursor is at the end (or beginning),
203
- # either nil or the code block result is returned (see #get).
204
- # The value is inserted at that point when that happens.
205
- def put(value,flags=Next,&more) # :yield: l
206
- if (flags&Single).nonzero? or not data_class>=value.class
207
- if (flags&Insert).nonzero?
208
- if (flags&Hold).nonzero?
209
- flags ^= Hold
210
- flags ^= Reverse
211
- end
212
- return(_insert1(value,flags,&more))
213
- else
214
- return(_put1(value,flags,&more))
215
- end
216
- end
217
- if (flags&Insert).nonzero?
218
- flags |= Single
219
- if (flags&Hold).nonzero?
220
- flags ^= Hold
221
- flags ^= Reverse
222
- end
223
- if (flags&Reverse).nonzero?
224
- start = value.size-1
225
- finish = 0
226
- step = -1
227
- else
228
- start = 0
229
- finish = value.size-1
230
- step = +1
231
- end
232
- start.step(finish,step) do |i|
233
- _insert1(value[i],flags)
234
- end
235
- nil
236
- elsif (flags&Hold).nonzero?
237
- flags ^= Hold
238
- position { put(value,flags,&more) }
239
- else
240
- flags |= Single
241
- if (flags&Reverse).nonzero?
242
- start = value.size-1
243
- finish = 0
244
- step = -1
245
- else
246
- start = 0
247
- finish = value.size-1
248
- step = +1
249
- end
250
- if (flags&Read).nonzero?
251
- value0 = data_class.new
252
- replacing = true
253
- start.step(finish,step) do |i|
254
- v = _put1(value[i],flags) { |l|
255
- replacing = more&&more[l]
256
- }
257
- value0 << v if replacing
258
- end
259
- if value0.size.zero?
260
- nil
261
- elsif step<0
262
- value0.reverse
263
- else
264
- value0
265
- end
266
- else
267
- len0 = 0
268
- replacing = true
269
- start.step(finish,step) do |i|
270
- _put1(value[i],flags) { |l|
271
- replacing = more&&more[l]
272
- }
273
- len0 += 1 if replacing
274
- end
275
- if len0.zero?
276
- nil
277
- else
278
- len0
279
- end
280
- end
447
+ # modify elements using +lookup+[orignalElement] until that returns nil
448
+ def modify(lookup,reverse=false,hold=false,buffer=new_data)
449
+ meth = method(reverse ? :modify1prev : :modify1next)
450
+ len0 = 0
451
+ until (v0 = meth.call(lookup)).nil?
452
+ buffer << v0
453
+ len0 += 1
281
454
  end
282
- end
455
+ skip(reverse ? len0 : -len0) if hold
456
+ buffer
457
+ end
458
+
283
459
  # This will return a numeric position. When not +reverse+,
284
460
  # this numeric position is the number of elements from the beginning (0 is at the beginning). With
285
- # +reverse+ it is negative and the number of elements from the end (-0.1 is at the end).
286
- def pos(reverse=false,&code) # :yield:
287
- flags = (reverse ? Prev : Next)|Ignore
288
- ref = reverse ? End : Begin
289
- p = get(Rest,flags^Reverse) or return(ref)
290
- get(p,flags)==p or raise(IndexError,"couldn't get back to where we were: #{p}")
291
- p = reverse ? -p : p
292
- p+ref.to_i
461
+ # +reverse+ it is negative and the number of elements from the end (-0.0 is at the end).
462
+ def pos(reverse=false)
463
+ reverse ? -(skip!(false,true)||0.0) : (skip!(true,true)||0)
293
464
  end
294
465
  # Returns #pos.to_i
295
466
  def to_i
296
467
  pos.to_i
297
468
  end
298
- # Returns "pos=#pos.to_s" followed by what's in prop in a reasonable format.
299
- def to_s
300
- s = "pos=#{pos.to_s}"
301
- p = prop
302
- if not p
303
- elsif p.respond_to?:members
304
- p.members.each { |m| s += " #{m}=#{p[m]}" }
305
- elsif p.respond_to?:keys
306
- p.keys.each { |k| s += " #{k}=#{p[k]}" }
307
- elsif p.respond_to?:to_a
308
- s += p.to_a.join(" ")
469
+ # Set #pos to be +p+. When +p+ is negative, it is set from the end.
470
+ def pos=(p)
471
+ #skip!((p.nonzero?||1.0/p)>=0)
472
+ #p.to_i.abs==skip(p) or raise(IndexError,"invalid pos=#{p}")
473
+ len = p-pos((p.nonzero?||1.0/p)<0)
474
+ len.to_i.abs==skip(len) or raise(IndexError,"invalid pos=#{p}")
475
+ p
476
+ end
477
+ # Get (no +value+) and set cursor properties. Normally, +name+
478
+ # should be a symbol. If +name+ is +nil+, it wil get/set using a hash
479
+ # representing all of the properties.
480
+ def prop(name=nil,*value) # :args: (name[,value])
481
+ if name.nil?
482
+ if value.size.zero?
483
+ @prop&&@prop.clone
484
+ else
485
+ if (value = value[0]).nil?
486
+ @prop&&remove_instance_variable(:@prop)
487
+ else
488
+ (@prop||={}).replace(value)
489
+ end
490
+ end
309
491
  else
310
- s += p.to_s
492
+ if value.size.zero?
493
+ @prop&&@prop[name]
494
+ else
495
+ (@prop||={})[name] = value[0]
496
+ end
311
497
  end
312
- s
313
498
  end
314
- # Set #pos to be +p+. When +p+ is negative, it is set from the end.
315
- def pos=(p)
316
- reverse = p<Begin
317
- flags = (reverse ? Prev : Next)|Ignore
318
- ref = reverse ? End : Begin
319
- p = (p-ref).round
320
- p = reverse ? -p : p
321
- get(Rest,flags^Reverse)
322
- p.nonzero? or return(ref)
323
- p = get(p,flags) or return(ref)
324
- p = reverse ? -p : p
325
- p+ref.to_i
326
- end
327
- # Get back what was stored by #prop=.
328
- def prop
329
- @prop
330
- end
331
- # Arbitrary data may be placed in here. It will be saved and restored by
332
- # #position and friends.
333
- def prop=(p)
334
- @prop = p.clone if p
335
- end
336
- def _finalizer(id) # :nodoc:
337
- i = @positions.index(id >> 1)
338
- @positions.slice!(i) if i
339
- end
340
- protected :_finalizer
341
- # These position* methods use a Cursor object (from Cursor::Position) to
342
- # hold the #position rather than simply a numeric position that #pos and
343
- # #pos= use. These Cursor::Position objects hold a #pos and whatever is
344
- # in #prop. For now, the #pos in these objects do not adjust based on
345
- # insertions and deletions, but that may change. So don't count
346
- # Without +p+ or a code block, #position returns one of these objects to
347
- # represent the current location of the cursor. With +p+ or a code block,
348
- # the current #position is saved, the #position is set to +p+ using
349
- # #position= (if +p+), the code block is executed (or the current #position
350
- # is found with no code block), and the #position is returned to where it
351
- # was saved (using #position=). The return value is the result of the code
352
- # block or the #position if that was executed instead.
353
- def position(p=nil,&code) # :yield:
354
- if code or p
355
- start = position
356
- self.position = p if p
357
- ret = code ? code[] : position
358
- self.position = start
359
- start.close
360
- ret
499
+ # Returns "pos=#pos.to_s" followed by what's in #prop in a reasonable format.
500
+ def to_s
501
+ "pos=#{pos.to_s}" +
502
+ (prop||{}).collect { |k,v| "#{k}=#{v}" }.join(" ")
503
+ end
504
+
505
+ # Without a code block, #position returns one of these objects to
506
+ # represent the current location of the cursor. When +reverse+, it will
507
+ # anchor the #position to the element after. Otherwise it will anchor it
508
+ # to the element before. This anchoring is used when an insertion occurs at
509
+ # that point affecting how this position is adjusted.
510
+ #
511
+ # With a code block, it saves the #position (also using +reverse+),
512
+ # executes the code block, and returns the #position to where it
513
+ # started. The return value is the result of the code block.
514
+ def position(reverse=false,&code) # :yield:
515
+ p = Position.new(self,reverse)
516
+ (@positions||=WeakRefSet.new) << p
517
+ if code
518
+ begin
519
+ code[]
520
+ ensure
521
+ begin
522
+ self.position = p
523
+ ensure
524
+ p.close
525
+ end
526
+ end
361
527
  else
362
- p = Position.new(self,pos,prop)
363
- @positions << (p.object_id >> 1)
364
- ObjectSpace.define_finalizer(p,method(:_finalizer))
365
528
  p
366
529
  end
367
530
  end
368
- # Set the position to +p+. +p+ can be numeric (from #pos) or from #position.
531
+ # Set the position to +p+ (from #position).
369
532
  def position=(p)
370
- if p.respond_to?(:pos)
371
- position?(p) or raise(TypeError,"invalid position #{p}")
372
- self.pos = p.pos
373
- self.prop = p.prop
374
- else
375
- self.pos = p
376
- end
533
+ self.pos = p.pos(nil)
534
+ self.prop(nil,p.prop)
535
+ nil
377
536
  end
378
537
  # Without a code block, this queries whether a particular #position +p+ is
379
538
  # valid (is a child) or determines if there is any outstanding #position
380
539
  # (when +p+=+nil+).
381
540
  #
382
- # With a code block, it sets optionally sets the #position= +p+ and
541
+ # With a code block, it saves the #position (passing +p+ as the anchoring flag)
383
542
  # and executes the code. If the result of the code is false/nil, it will
384
543
  # return to the original #position (intially saved). Otherwise it will stay where the code
385
544
  # left it. The result of the code block is returned. This is useful when
@@ -387,37 +546,30 @@ class Cursor
387
546
  # else for a fail/mismatch.
388
547
  def position?(p=nil,&code) # :yield:
389
548
  if code
390
- start = position
391
- self.position = p if p
392
- ret = code[]
393
- self.position = start if not ret
394
- start.close
395
- ret
396
- elsif p
397
- i = @positions.rindex(p.object_id >> 1)
398
- if not i
399
- return(true) if p.object_id==self.object_id
400
- return(nil)
401
- end
402
- if p.closed?
403
- @positions.slice!(i)
404
- nil
405
- else
406
- true
549
+ start = position(p)
550
+ begin
551
+ ret = code[]
552
+ ensure
553
+ begin
554
+ self.position = start if not ret
555
+ ensure
556
+ start.close
557
+ end
407
558
  end
559
+ elsif p
560
+ @positions && @positions.include?(p) || equal?(p) || nil
408
561
  else
409
- while @positions.size>0
410
- return(true) if not ObjectSpace._id2ref(@positions[0] << 1).closed?
411
- @positions.shift
412
- end
562
+ @positions && (!@positions.empty? || nil)
413
563
  end
414
564
  end
415
- # Either discard/close the given #position +p+ or discard/close every child #position (+p+=+nil+).
565
+ # Discard/close every child #position (+p+=+nil+) or discard (not close)
566
+ # the give +p+ (you probably want +p+.close instead).
416
567
  def position!(p=nil)
417
568
  if p
418
- p.close
419
- else
420
- @positions.clone.each { |p| ObjectSpace._id2ref(p << 1).close }
569
+ @positions.delete(p)
570
+ elsif @positions
571
+ @positions.each { |p| p.close }
572
+ remove_instance_variable(:@positions)
421
573
  end
422
574
  self
423
575
  end
@@ -425,833 +577,164 @@ class Cursor
425
577
  def close
426
578
  position!
427
579
  # this should make just about any operation fail
428
- instance_variables.each { |v| remove_instance_variable(v) }
580
+ instance_variables.each { |v| instance_variable_set(v,nil) }
429
581
  nil
430
582
  end
431
583
  # Is the cursor closed?
432
584
  def closed?
433
- instance_variables.size.zero?
585
+ not instance_variables.find { |v| !instance_variable_get(v).nil? }
434
586
  end
435
- # Are we at the end? Or beginning if +reverse+.
436
- def eof?(reverse=false)
437
- not get(nil,(reverse ? Before : After)|Ignore)
438
- end
439
- alias eof eof?
440
- # Compare +other+ (from #pos or #position) to the current position. +1
587
+
588
+ # Compare +other+ (a #position) to the current position. +1
441
589
  # for the self is after, -1 for self being before, and 0 for it being at
442
590
  # same location.
443
591
  def <=>(other)
444
- if other.respond_to?:pos
445
- position?(other) or return(nil)
446
- other = other.pos
447
- end
448
- other.respond_to?:zero? or return(nil)
449
- pos(other<0).to_i<=>other.to_i
450
- end
451
- alias === ==
452
- # If +other+ is from #position, this will return the distance (number
453
- # or elements) from +other+ to +self+. This can be +, -, or 0.
454
- # Otherwise, a new #position is returned that is decreased by this amount
455
- # (using #get).
592
+ position?(other) and pos<=>other.pos
593
+ end
594
+ # Return the distance (number
595
+ # or elements) from +other+ (a #position) to +self+. This can be +, -, or 0.
456
596
  def -(other)
457
- if other.respond_to?:pos
458
- position?(other) or return(nil)
459
- other = other.pos
460
- pos(other<0).to_i-other.to_i
461
- elsif other.respond_to?:zero?
462
- position { get(other,Prev|Ignore);position }
463
- end
597
+ pos-other.pos
598
+ end
599
+ # Returns a new #position increased by +len+ (positive or negative).
600
+ def +(len)
601
+ position { skip(len);position((len.nonzero?||1.0/len)<0) }
602
+ end
603
+
604
+ # Return a new #position for next cursor location or +nil+ if we are at the end.
605
+ def succ
606
+ position { skip1next && position(false) }
607
+ end
608
+ # Return a new #position for previous cursor location or +nil+ if we are at the beginning.
609
+ def pred
610
+ position { skip1prev && position(true) }
611
+ end
612
+ # Return a new #position for the beginning.
613
+ def begin
614
+ position { skip!(true);position(false) }
615
+ end
616
+ # Return a new #position for the end.
617
+ def end
618
+ position { skip!(false);position(true) }
464
619
  end
465
- # Returns a new #position increased by +other+ (using #get).
466
- def +(other)
467
- position { get(other,Next|Ignore);position }
468
- end
469
- # Returns a Reversed cursor.
470
- def reverse
471
- Reversed.new(self)
472
- end
473
- alias -@ reverse
474
- # Increments the cursor by +len+ (same +len+ of #get)
475
- # Returns +nil+ at the end and self otherwise.
476
- def succ!(len=nil); get(len,Next|Ignore) && self; end
477
- # Decrements the cursor by +len+ (same +len+ of #get)
478
- # Returns +nil+ at the beginning and self otherwise.
479
- def pred!(len=nil); get(len,Prev|Ignore) && self; end
480
- # Similar to #succ! except a new #position is returned instead of
481
- # modifying the current.
482
- def succ(len=nil); position { get(len,Next|Ignore) && position }; end
483
- # Similar to #pred! except a new #position is returned instead of
484
- # modifying the current.
485
- def pred(len=nil); position { get(len,Prev|Ignore) && position }; end
486
- # Go to the beginning
487
- def begin!; get(Rest,Prev|Ignore); self; end
488
- # Go to the end
489
- def end!; get(Rest,Next|Ignore); self; end
490
- # Similar to #first! except a new #position is returned instead of
491
- # modifying the current.
492
- def begin; position { get(Rest,Prev|Ignore);position }; end
493
- # Similar to #last! except a new #position is returned instead of
494
- # modifying the current.
495
- def end; position { get(Rest,Next|Ignore);position }; end
496
620
  # Returns the number of elements.
497
621
  def size
498
- l1 = position { get(Rest,Next|Ignore) } || 0
499
- l0 = position { get(Rest,Prev|Ignore) } || 0
500
- l0+l1
622
+ (skip!(true,true)||0)+(skip!(false,true)||0)
501
623
  end
502
624
  alias length size
503
625
  # Determines whether there is anything before or after the cursor.
504
626
  def empty?
505
- get(nil,After |Ignore) and return(false)
506
- get(nil,Before|Ignore) and return(false)
507
- true
627
+ skip1after.nil? && skip1before.nil?
508
628
  end
509
- # Removes all elements and returns the number removed
629
+ # Removes all elements and returns the number removed.
510
630
  def clear
511
- (get(Rest,Next|Delete|Ignore)||0)+(get(Rest,Prev|Delete|Ignore)||0)
631
+ (skip!(false,nil)||0)+(skip!(true,nil)||0)
512
632
  end
513
- # If +obj+ is the #data_class, replace the all of the data with it. Otherwise
514
- # use #position= +obj+. self is returned.
633
+ # Replace the all of the data and return self.
515
634
  def replace(obj)
516
- if data_class>=obj.class
517
- clear
518
- put(obj)
519
- else
520
- self.position = obj
521
- end
635
+ clear
636
+ write(obj,false,nil)
522
637
  self
523
638
  end
524
- # Get all of the data (beginning to the end)
639
+ # Get all of the data and leave the cursor at the end.
525
640
  def data
526
- get(Rest,Prev|Ignore)
527
- get(Rest,Next)
641
+ skip!(true)
642
+ read!(false)
528
643
  end
529
- # Appends a single element at the end and returns self
530
- def << (value)
531
- get(Rest,Next|Ignore)
532
- put(value,Next|Single)
644
+ # Appends a single element at the end and returns self.
645
+ def << (v)
646
+ skip!(false)
647
+ insert1before(v)
533
648
  self
534
649
  end
535
- # Prepends a single element at the beginning and returns self
536
- def >> (value)
537
- get(Rest,Prev|Ignore)
538
- put(value,Prev|Single)
650
+ # Prepends a single element at the beginning and returns self.
651
+ def >> (v)
652
+ skip!(true)
653
+ insert1after(v)
539
654
  self
540
655
  end
541
- # like Array#first, except it takes a +len+
542
- def first(len=nil)
543
- get(Rest,Prev|Ignore)
544
- get(len,Next)
545
- end
546
- # like Array#last, except it takes a +len+
547
- def last(len=nil)
548
- get(Rest,Next|Ignore)
549
- get(len,Prev)
550
- end
551
- protected
552
- def _index(index,len,flags) # :nodoc:
553
- if index.respond_to?:exclude_end?
554
- if index.first.respond_to?:zero? and (index.first<0)!=(index.last<0)
555
- len = (size-index.last)-abs(index.first)
556
- else
557
- len = index.last-index.first
558
- end
559
- if index.exclude_end?
560
- len = (len<0) ? len.floor : len.ceil
561
- else
562
- len = (len<0) ? len.to_i-1 : len.to_i+1
563
- end
564
- index = index.first
565
- index += len if (flags&Reverse).nonzero?
566
- end
567
- self.position = index if index
568
- [len,flags]
569
- end
570
- public
656
+
571
657
  # Provides random access for the cursor like what is in Array/String.
572
- # +index+ can be +nil+ (start at the current location), a numeric (possibly range)
573
- # (just like Array/String), and even a range from start to end cursors.
574
- # +len+ can be positive/0 (just like in Array/String), negative (goes
575
- # in reverse), or even a matching sequence (passed to #get).
576
- # +flags+ can take on the same things they can in get.
577
- def slice(index=nil,len=nil,flags=After)
578
- len,flags = _index(index,len,flags)
579
- get(len,flags)
658
+ # +index+ can be +nil+ (start at the current location) or a numeric (for #pos=).
659
+ # +len+ can be +nil+ (get a single element) or the number of elements to
660
+ # #read (positive or negative). The cursor is left at +index+ (or not
661
+ # moved if +nil+).
662
+ def slice(index=nil,len=nil)
663
+ self.pos = index if index
664
+ len.nil? ? read1after : read(len,true)
580
665
  end
581
666
  alias [] slice
582
- alias at slice
583
- # Random access slice! like in Array/String. Accepts the same enhancements
584
- # for index, len, flags, and code block as #slice does (same except +Delete+
585
- # bit is set).
586
- def slice!(index=nil,len=nil,flags=After)
587
- len,flags = _index(index,len,flags)
588
- get(len,flags|Delete)
589
- end
590
- # Random access store like in Array/String. Accepts the same enhancements
591
- # for index, len, and flags as #slice does.
592
- def []=(*args) # :args: (index=nil,len=nil,flags=After,value)
667
+ # Like #slice except the element(s) are deleted.
668
+ def slice!(index=nil,len=nil)
669
+ self.pos = index if index
670
+ len.nil? ? delete1after? : read(len,nil)
671
+ end
672
+ # Similar to #slice except data is written. +index+ and +len+ have the
673
+ # same meaning as they do in #slice. +value+ is written using #write.
674
+ def []=(*args) # :args: (index=nil,len=nil,value)
593
675
  value = args.slice!(-1)
594
- index,len,flags = *args
595
- flags ||= After
596
- len,flags = _index(index,len,flags)
597
- if not len
598
- ret = put(value,flags|Single)
599
- else
600
- flags &= ~Single
601
- ret = get(len,flags^Ignore|Delete)
602
- put(value,flags|Insert)
603
- end
604
- if (flags&Read).nonzero?
605
- ret
606
- else
607
- value
608
- end
609
- end
610
- # copies data from one place to another. The default from and to indices are
611
- # nil which is the current cursor location. All of the options of #slice
612
- # are available for the indices, +len+, and flags.
613
- def copy(from_index=nil,to_index=nil,len=nil,from_flags=After,to_flags=After)
614
- if not to_index
615
- value = position { self[from_index,len,from_flags] }
616
- self[to_index,len,to_flags] = value
617
- elsif not from_index
618
- value = self[from_index,len,from_flags]
619
- position { self[to_index,len,to_flags] = value }
676
+ index,len = *args
677
+ self.pos = index if index
678
+ if len.nil?
679
+ write1after!(value)
620
680
  else
621
- self[to_index,len,to_flags] = self[from_index,len,from_flags]
681
+ skip(len,nil)
682
+ write(value,(len.nonzero?||1.0/len)>=0,nil)
622
683
  end
684
+ value
623
685
  end
624
- # Performs each just to make this class Enumerable. Accepts the same enhancements
625
- # for index, len, and flags as #slice does.
626
- # The cursor will be left at the end (or beginning if the +Reverse+ flags bit is use).
627
- # nil is returned (or the break value if the code does a break).
628
- def each(index=Begin,len=nil,flags=Next,&code) # :yield: value
629
- len,flags = _index(index,len,flags)
630
- continue = true
631
- loop do
632
- value = get(len,flags) { continue = nil }
633
- if not continue
634
- code[value] if len and value
635
- break
636
- end
637
- code[value]
638
- end
639
- end
640
- # Same as #each except go in Reverse.
641
- def reverse_each(index=End,len=nil,flags=Prev,&code) # :yield: value
642
- each(index,len,flags,&code)
643
- end
644
- # Performs an in-place collect! (like Array#collect!). Accepts the same enhancements
645
- # for index, len, and flags as #slice does.
646
- # The cursor will be left at the end (or beginning if the +Reverse+ flags bit is use).
647
- # nil is returned (or the break value if the code does a break).
648
- def collect!(index=Begin,len=nil,flags=Next,&code) # :yield: value
649
- len,flags = _index(index,len,flags)
650
- continue = true
651
- loop do
652
- value = get(len,flags|Delete) { continue = nil }
653
- if not continue
654
- if len and value
655
- value = code[value]
656
- put(value,flags|Insert) if value
657
- end
658
- break
659
- end
660
- value = code[value]
661
- put(value,flags|Insert|(len ? 0 : Single)) if value || !len
662
- end
663
- end
664
- alias map! collect!
665
- # Performs an in-place reject! (like Array#reject!). Accepts the same enhancements
666
- # for index, len, and flags as #slice does.
667
- # The cursor will be left at the end (or beginning if the +Reverse+ flags bit is use).
686
+
687
+ # Performs each just to make this class Enumerable. +index+ can be +nil+ or
688
+ # a #pos like in #slice. If +reverse+ each will move in reverse. If a
689
+ # +terminator+ is specified, each iteration will get a sequence using
690
+ # #scan_until(+terminator+) instead of using a single element.
691
+ # The cursor will be left at the end (or beginning if +reverse+).
668
692
  # nil is returned (or the break value if the code does a break).
669
- def reject!(index=Begin,len=nil,flags=Next,&code) # :yield: value
670
- len,flags = _index(index,len,flags)
693
+ def each(index=+0.0,reverse=false,terminator=nil,&code) # :yield: value
694
+ self.pos = index if index
671
695
  continue = true
672
- ret = nil
673
- loop do
674
- value = get(len,flags) { continue = nil }
675
- if not continue
676
- if len and value
677
- if code[value]
678
- get(len,flags^Reverse|Delete|Ignore)
679
- ret = self
680
- end
681
- end
682
- break
683
- end
684
- if code[value]
685
- get(len,flags^Reverse|Delete|Ignore)
686
- ret = self
687
- end
688
- end
689
- ret
690
- end
691
-
692
-
693
- # Objects in this class are mainly used to simply mark/remember the location
694
- # of a parent cursor. But, this class also has the fully functionality of the
695
- # parent. When this child want to do an operation, it uses the parent to
696
- # do it and returns the parent to where it was. Derived classes where the
697
- # underlying data is random access may be able to implement this class to
698
- # directly access the data rather than go through the parent.
699
- class Position < Cursor
700
- def initialize(parent,pos,prop)
701
- @parent = parent
702
- @pos,self.prop = pos,prop
703
- end
704
- def data_class
705
- @parent.data_class
706
- end
707
- protected
708
- def _pos(&code) # :nodoc:
709
- start = @parent.pos,@parent.prop
710
- @parent.pos,@parent.prop = @pos,self.prop
711
- ret = code[]
712
- @pos,self.prop = @parent.pos,@parent.prop
713
- @parent.pos,@parent.prop = start
714
- ret
715
- end
716
- public
717
- def get(len=nil,flags=Next,&more) # :yield: l
718
- _pos { @parent.get(len,flags,&more) }
719
- end
720
- def put(value,flags=Next,&more) # :yield: l
721
- _pos { @parent.put(value,flags,&more) }
722
- end
723
- def pos(reverse=false)
696
+ if terminator.nil?
724
697
  if reverse
725
- super(reverse)
726
- else
727
- @pos
728
- end
729
- end
730
- def position(p=nil,&code) # :yield:
731
- if p or code
732
- super(p,&code)
733
- else
734
- _pos { @parent.position }
735
- end
736
- end
737
- def position?(p=nil,&code) # :yield:
738
- if code
739
- super(p,&code)
740
- else
741
- @parent.position?(p)
742
- end
743
- end
744
- def position!(p=nil)
745
- if p
746
- @parent.position!(p)
747
- else
748
- nil
749
- end
750
- end
751
- def close
752
- parent = @parent
753
- super()
754
- parent.position?(self)
755
- end
756
- end
757
-
758
- # This class can be used to reverse the direction of operations on a given
759
- # cursor. It operates on the given cursor directly moving it around.
760
- class Reversed < Cursor
761
- def initialize(cursor)
762
- super()
763
- @cursor = cursor
764
- end
765
- def data_class
766
- @cursor.data_class
767
- end
768
- def get(len=nil,flags=Next,&more) # :yield: l
769
- @cursor.get(len,flags^Reverse)
770
- end
771
- def put(value,flags=Next,&more) # :yield: l
772
- @cursor.put(value,flags^Reverse,&more)
773
- end
774
- def pos(reverse=false,&code) # :yield:
775
- if code
776
- super(!reverse)
777
- else
778
- p = @cursor.pos(!reverse)
779
- p = -p+Begin+End
780
- end
781
- end
782
- def pos=(p)
783
- reverse = p<Begin
784
- p = -p+Begin+End
785
- p = (@cursor.pos = p)
786
- p = -p+Begin+End
787
- end
788
- end
789
-
790
- # This class is used to test the base class by overriding as few methods
791
- # as possible.
792
- class Test < Cursor
793
- def initialize(data=[],index=0)
794
- super()
795
- @data = data
796
- @pos = index
797
- @data_class = (@data.respond_to?:data_class) ?
798
- @data.data_class : @data.class
799
- end
800
- def data_class
801
- @data_class
802
- end
803
- def _delete1(flags,&more) # :yield: l=1
804
- if (flags&Reverse).nonzero?
805
- @pos>0 && (@pos -= 1)
806
- else
807
- @pos<@data.size
808
- end or return(
809
- if ret = more&&more[1]
810
- (flags&Ignore).nonzero? ? true : ret[0]
811
- end
812
- )
813
- if (flags&Ignore).nonzero?
814
- @data[@pos,1] = @data_class.new
815
- 1
816
- else
817
- @data.slice!(@pos)
818
- end
819
- end
820
- def _insert1(value,flags)
821
- @data[@pos,0] = (@data_class.new << value)
822
- @pos += 1 if (flags&Reverse).zero?
823
- end
824
- end
825
-
826
- # This class puts a cursor on an Array or String.
827
- class Indexed < Cursor
828
- def initialize(data=[],index=0)
829
- super()
830
- @data = data
831
- @pos = index
832
- @data_class = (@data.respond_to?:data_class) ?
833
- @data.data_class : @data.class
834
- end
835
- def data_class
836
- @data_class
837
- end
838
- def _delete1(flags,&more) # :yield: l=1
839
- if (flags&Reverse).nonzero?
840
- @pos>0 && @pos -= 1
841
- else
842
- @pos<@data.size
843
- end or return(
844
- if ret = more&&more[1]
845
- (flags&Ignore).nonzero? ? true : ret[0]
698
+ while v0 = read1prev
699
+ code[v0]
846
700
  end
847
- )
848
- if (flags&Ignore).nonzero?
849
- @data[@pos,1] = @data_class.new
850
- true
851
701
  else
852
- @data.slice!(@pos)
853
- end
854
- end
855
- def _insert1(value,flags)
856
- @data[@pos,0] = (@data_class.new << value)
857
- @pos += 1 if (flags&Reverse).zero?
858
- end
859
- end
860
-
861
- # This class treats an IO (or StringIO) as a Cursor. An IO is already
862
- # like a Cursor, but doesn't have as robust an interface. Deleting and
863
- # inserting is a slow/painful process.
864
- class IO < Cursor
865
- def initialize(io=StringIO.new)
866
- super()
867
- @io = io
868
- end
869
- def data_class
870
- String
871
- end
872
- def _delete1(flags,&more) # :yield: l=1
873
- if (flags&Reverse).nonzero?
874
- begin
875
- @io.seek(-1,::IO::SEEK_CUR)
876
- ret = @io.getc
877
- rescue
878
- ret = nil
702
+ while v0 = read1next
703
+ code[v0]
879
704
  end
880
- else
881
- ret = @io.getc
882
- end or return(
883
- if ret = more&&more[1]
884
- (flags&Ignore).nonzero? ? true : ret[0]
885
- end
886
- )
887
- ret = true if (flags&Ignore).nonzero?
888
- after = 0
889
- while c = @io.getc
890
- after += 1
891
- @io.seek(-2,::IO::SEEK_CUR)
892
- @io.putc(c)
893
- @io.seek(+1,::IO::SEEK_CUR)
894
705
  end
895
- @io.seek(-1,::IO::SEEK_CUR)
896
- @io.truncate(@io.pos)
897
- @io.seek(-after,::IO::SEEK_CUR) if after>0
898
- ret
899
- end
900
- def _insert1(value,flags)
901
- after = 0
902
- while c = @io.getc
903
- after += 1
904
- @io.seek(-1,::IO::SEEK_CUR)
905
- @io.putc(value)
906
- value = c
907
- end
908
- @io.putc(value)
909
- after += 1 if (flags&Reverse).nonzero?
910
- @io.seek(-after,::IO::SEEK_CUR) if after>0
911
- nil
912
- end
913
- def _get1(flags,&more) # :yield: l=1
914
- if (flags&Reverse).nonzero?
915
- begin
916
- @io.seek(-1,::IO::SEEK_CUR)
917
- rescue
918
- return(
919
- if ret = more&&more[1]
920
- if (flags&Hold).nonzero?
921
- flags ^= Hold
922
- flags ^= Reverse
923
- end
924
- _insert(flags,ret=ret[0])
925
- (flags&Ignore).nonzero? ? true : ret
926
- end
927
- )
928
- end
929
- if (flags&Ignore).nonzero?
930
- @io.seek(+1,::IO::SEEK_CUR) if (flags&Hold).nonzero?
931
- ret = true
932
- else
933
- ret = @io.getc
934
- @io.seek(-1,::IO::SEEK_CUR) if (flags&Hold).zero?
935
- end
936
- else
937
- if not ret = @io.getc
938
- return(
939
- if ret = more&&more[1]
940
- @io.putc(ret=ret[0])
941
- @io.seek(-1,::IO::SEEK_CUR) if (flags&Hold).nonzero?
942
- (flags&Ignore).nonzero? ? true : ret
943
- end
944
- )
945
- end
946
- @io.ungetc(ret) if (flags&Hold).nonzero?
947
- ret = true if (flags&Ignore).nonzero?
948
- end
949
- ret
950
- end
951
- def _put1(value,flags,&more) # :yield: l=1
952
- if (flags&Reverse).nonzero?
953
- begin
954
- @io.seek(-1,::IO::SEEK_CUR)
955
- rescue
956
- if ret = more&&more[1]
957
- ret = (flags&Read).zero? ? true : ret[0]
958
- end
959
- if (flags&Hold).nonzero?
960
- flags ^= Hold
961
- flags ^= Reverse
962
- end
963
- _insert1(value,flags)
964
- return(ret)
965
- end
966
- if (flags&Read).nonzero?
967
- ret = @io.getc
968
- @io.ungetc(ret)
969
- else
970
- ret = true
971
- end
972
- @io.putc(value)
973
- if (flags&Hold).zero?
974
- @io.seek(-1,::IO::SEEK_CUR)
975
- end
976
- else
977
- if (flags&Read).nonzero?
978
- if not ret = @io.getc
979
- if ret = more&&more[1]
980
- ret = ret[0]
981
- end
982
- else
983
- @io.ungetc(ret)
984
- end
985
- elsif @io.eof?
986
- ret = more&&more[1]&&true
987
- else
988
- ret = true
989
- end
990
- @io.putc(value)
991
- @io.seek(-1,::IO::SEEK_CUR) if (flags&Hold).nonzero?
992
- end
993
- ret
994
- end
995
- def close
996
- @io.close
997
- super()
998
- end
999
- end
1000
-
1001
- # This Cursor class uses an Array/String for data before the cursor
1002
- # and another one for data after the cursor. The result of this is that
1003
- # data is only deleted/inserted at the end of these 2. Because of that
1004
- # most operations have similar expense and are linear with respect to the
1005
- # number of elements being moved, inserted, deleted, replaced, etc.
1006
- class Buffer < Cursor
1007
- def initialize(before=[],after=before.class.new)
1008
- super()
1009
- @before = before
1010
- @after = after
1011
- @data_class = (@before.respond_to?:data_class) ?
1012
- @before.data_class : @before.class
1013
- end
1014
- def data_class
1015
- @data_class
1016
- end
1017
- def _delete1(flags,&more) # :yield: l=1
1018
- data = (flags&Reverse).nonzero? ? @before : @after
1019
- data.size>0 or return(
1020
- if ret = more&&more[1]
1021
- (flags&Ignore).nonzero? ? true : ret[0]
1022
- end
1023
- )
1024
- if (flags&Ignore).nonzero?
1025
- data[-1,1] = @data_class.new
1026
- true
1027
- else
1028
- data.slice!(-1)
1029
- end
1030
- end
1031
- def _insert1(value,flags)
1032
- data = (flags&Reverse).nonzero? ? @after : @before
1033
- data << value
1034
- end
1035
- end
1036
-
1037
- # This Cursor class implements a circular buffer (not very efficient now). The beginning and end
1038
- # are treated to be the current position. #pos/#position and friends really
1039
- # don't make sense here (at least right now).
1040
- class Circular < Cursor
1041
- def initialize(data=[])
1042
- super()
1043
- @data = data
1044
- @data_class = (@data.respond_to?:data_class) ?
1045
- @data.data_class : @data.class
1046
- end
1047
- def data_class
1048
- @data_class
1049
- end
1050
- def _delete1(flags,&more) # :yield: l=1
1051
- @data.size>0 or return(
1052
- if ret = more&&more[1]
1053
- (flags&Ignore).nonzero? ? true : ret[0]
1054
- end
1055
- )
1056
- index = (flags&Reverse).nonzero? ? -1 : 0
1057
- if (flags&Ignore).nonzero?
1058
- @data[index,1] = @data_class.new
1059
- true
1060
- else
1061
- @data.slice!(index)
1062
- end
1063
- end
1064
- def _insert1(value,flags)
1065
- if (flags&Reverse).nonzero?
1066
- @data[0,0] = (@data_class.new << value)
1067
- else
1068
- @data << value
1069
- end
1070
- end
1071
- def get(len=nil,flags=Next,&more) # :yield: l
1072
- if len==Rest
1073
- nil
1074
- else
1075
- super(len,flags,&more)
1076
- end
1077
- end
1078
- def size
1079
- @data.size
1080
- end
1081
- alias length size
1082
- def clear
1083
- @data.replace(@data_class.new)
1084
- end
1085
- def data
1086
- @data.clone()
1087
- end
1088
- def replace(obj)
1089
- if @data_class>=obj.class
1090
- @data.replace(obj)
1091
- else
1092
- super(obj)
706
+ else
707
+ while v0 = scan_until(terminator,reverse)
708
+ code[v0]
1093
709
  end
1094
- self
1095
710
  end
1096
-
1097
711
  end
1098
-
1099
- # This class gives unidirectional cursors (i.e. IO pipes) some
1100
- # bidirectional capabilities. An input cursor and/or an output cursor
1101
- # can be specified. The #position, #position?, and #position! methods are
1102
- # used to control buffering. Full cursor capability (limited by the buffer
1103
- # cursor) is accessible starting from the first #position. When the end of
1104
- # the buffer is reached more data is read from the input cursor (if not nil) using #get . When no
1105
- # #position is outstanding, everything before the buffer cursor is written
1106
- # to the output cursor (if not nil) using #put. If the cursor is attempted
1107
- # to be moved before the buffer, the output cursor is read in reverse (which
1108
- # the output cursor may not like).
1109
- class Buffered < Cursor
1110
- def initialize(input,output=nil,buffer=Buffer.new((input||output).data_class.new))
1111
- @input = input
1112
- @output = output
1113
- @buffer = buffer
1114
- @offset = 0
1115
- super()
1116
- end
1117
- def data_class
1118
- @buffer.data_class
1119
- end
1120
- def get(len=nil,flags=Next,&more) # :yield: l
1121
- value = @buffer.get(len,flags) { |l|
1122
- if (flags&Reverse).nonzero?
1123
- if @offset.zero?
1124
- more&&more[l]
1125
- else
1126
- v = @output.get(l,Prev,&more)
1127
- @offset -= v.size if v
1128
- v
1129
- end
1130
- elsif @input
1131
- @input.get(l,Next,&more)
1132
- else
1133
- more&&more[l]
1134
- end
1135
- }
1136
- if not position?
1137
- @offset = @buffer.pos+@offset
1138
- if output_value = @buffer.get(Rest,Prev|Delete)
1139
- @output.put(output_value,Next) if @output
1140
- end
1141
- end
1142
- value
1143
- end
1144
- def put(value,flags=Next,&more) # :yield: l
1145
- value0 = @buffer.put(value,flags) { |l|
1146
- if (flags&Reverse).nonzero?
1147
- if @offset.zero?
1148
- more&&more[l]
1149
- else
1150
- v = @output.get(l,Prev,&more)
1151
- @offset -= v.size if v
1152
- v
1153
- end
1154
- elsif @input
1155
- @input.get(l,Next,&more)
1156
- else
1157
- more&&more[l]
1158
- end
1159
- }
1160
- if not position?
1161
- @offset = @buffer.pos+@offset
1162
- if output_value = @buffer.get(Rest,Prev|Delete)
1163
- @output.put(output_value,Next) if @output
712
+ # Performs an in-place collect! (like Array#collect!).
713
+ # When the code returns +nil+ the element/sequence will be deleted.
714
+ # Accepts the same enhancements as #each does.
715
+ # nil is returned (or the break value if the code does a break).
716
+ def collect!(index=+0.0,reverse=false,terminator=nil,&code) # :yield: value
717
+ self.pos = index if index
718
+ continue = true
719
+ if terminator.nil?
720
+ if reverse
721
+ while v0 = delete1before
722
+ (v = code[v0]).nil? or insert1after(v)
1164
723
  end
1165
- end
1166
- value0
1167
- end
1168
- def pos(reverse=false,&code) # :yield:
1169
- if code or reverse
1170
- super(reverse,&code)
1171
724
  else
1172
- @buffer.pos+@offset
1173
- end
1174
- end
1175
- def pos=(p)
1176
- if p<Begin
1177
- super(p)
1178
- else
1179
- p = p-@offset
1180
- if p<Begin
1181
- @buffer.pos = Begin
1182
- get(-p,Prev|Ignore)
1183
- @offset
1184
- else
1185
- (@buffer.pos = p)+@offset
725
+ while v0 = delete1after
726
+ (v = code[v0]).nil? or insert1before(v)
1186
727
  end
1187
728
  end
1188
- end
1189
- def close
1190
- value = @buffer.get(Rest,Prev|Delete) and @output and
1191
- @output.put(value,Next)
1192
- value = @buffer.get(Rest,Next|Delete) and @output and
1193
- @output.put(value,Next)
1194
- super()
1195
- end
1196
- end
1197
-
1198
- # This class tracks the current line and column. Both line and column
1199
- # start at 0. When a newline is found, the line is incremented and the
1200
- # column is reset. With other characters, the column is incremented.
1201
- # The line and column numbers are only tracked when reading forward, but
1202
- # using #position (and friends) will hold the line and column.
1203
- class LineColumnNumbered < Cursor
1204
- # Structure stored in the #prop attribute. line and column numbers
1205
- # are accessed through #prop.line and #prop.column.
1206
- LineColumn = Struct.new("LineColumn",:line,:column)
1207
- # Create a new Cursor from another that tracks line and column.
1208
- # +newline+ can be any valid argument for String#index (or the
1209
- # index method for whatever the underlying #data_class is). For example,
1210
- # +newline+=+/\n|\r(?!\n)/+ could be used for unix, mac, dos/cpm style
1211
- # newlines.
1212
- def initialize(cursor,newline="\n"[0])
1213
- @cursor = cursor
1214
- @newline = newline
1215
- self.prop = LineColumn.new(0,0)
1216
- super()
1217
- end
1218
- def get(len=nil,flags=Next,&more) # :yield: l
1219
- ret = @cursor.get(len,flags,&more)
1220
- if (flags&Ignore).zero? and (flags&Reverse).zero? and ret
1221
- if not len
1222
- if ret==@newline
1223
- prop.line += 1
1224
- prop.column = 0
1225
- else
1226
- prop.column += 1
1227
- end
1228
- else
1229
- i0 = 0
1230
- begin
1231
- while i = index(@newline,i0)
1232
- prop.line += 1
1233
- prop.column = 0
1234
- i0 = i+1
1235
- end
1236
- rescue
1237
- #index with 2 args not supported
1238
- i = i0
1239
- while i<ret.size
1240
- if @newline==ret[i]
1241
- prop.line += 1
1242
- prop.column = 0
1243
- i0 = i+1
1244
- end
1245
- i += 1
1246
- end
1247
- end
1248
- prop.column += ret.size-i0
1249
- end
729
+ else
730
+ while v0 = scan_until(terminator,reverse,nil)
731
+ (v = code[v0]).nil? or write(v,reverse,nil)
1250
732
  end
1251
- ret
1252
733
  end
1253
734
  end
1254
-
735
+ alias map! collect!
1255
736
  end
1256
737
 
738
+ require 'cursor/position'
739
+
1257
740