cursor 0.6 → 0.8

Sign up to get free protection for your applications and to get access to all the features.
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