cursor 0.5 → 0.6

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.
Files changed (2) hide show
  1. data/cursor.rb +1257 -1138
  2. metadata +2 -2
data/cursor.rb CHANGED
@@ -1,1138 +1,1257 @@
1
- #!/bin/env ruby
2
- # cursor.rb
3
-
4
- # An object in this Cursor class can be best thought of as a cursor in a text
5
- # editor. Many of the same operations apply - insert, delete, replace, copy,
6
- # paste, move, goto begin/end, mark position, goto mark, etc. Unlike a
7
- # text editor, this class can operate on variety of data, not just characters
8
- # and strings. It is up to the derived classes to deal with what type of data
9
- # is stored (i.e. characters, arbitrary array objects) and how it is stored (in
10
- # an Array, String, IO, mapping to another Cursor, etc).
11
- class Cursor
12
-
13
- include Comparable
14
- include Enumerable
15
-
16
- # Operate on data before the cursor rather than after
17
- Reverse = 1
18
- # Hold position - do the operation and come back
19
- Hold = 2
20
- # Just count the elements from get/delete instead of returning their value
21
- Ignore = 4
22
- # Read the data being replaced using put and return it (instead of the count)
23
- Read = 4
24
- # Delete instead of retrieve elements (used with get)
25
- Delete = 8
26
- # Insert instead of replace elements (used with put)
27
- Insert = 8
28
- # Put a single element instead of a sequence (needed when it looks like a sequence)
29
- Single = 16
30
- # Operate on what follows the cursor and then move foward
31
- Next = 0
32
- # Operate on what precedes the cursor and then move backward
33
- Prev = Reverse
34
- # Operate on what follows the cursor and hold position
35
- After = Hold
36
- # Operate on what precedes the cursor and hold position
37
- Before = Reverse|Hold
38
-
39
- protected
40
-
41
- # Delete one element at the cursor and return it. The observed bits in +flags+
42
- # are +Reverse+ (delete before the cursor instead of after) and +Ignore+
43
- # (return true instead of the deleted element). When the cursor is at the end (or beginning),
44
- # either nil or the code block result is returned (see #get).
45
- # <b>This method must be overriden
46
- # in a derived class</b>.
47
- def _delete1(flags,&more) # :yield: len=1
48
- raise(NotImplementedError)
49
- end
50
- # Insert one element at the cursor. Only the +Reverse+ bit
51
- # is observed in +flags+. nil should be returned. <b>This method must be overriden
52
- # in a derived class</b>.
53
- def _insert1(value,flags)
54
- raise(NotImplementedError)
55
- end
56
- # Get an element at the cursor. The observed bits in +flags+ are +Reverse+
57
- # (read before instead of after the cursor), +Hold+ (don't move the
58
- # cursor), and +Ignore+ (just return +true+ instead of the value). When the
59
- # cursor is at the end (or beginning), either nil or the code block result
60
- # is returned (see #get).
61
- def _get1(flags,&more) # :yield: len=1
62
- value = _delete1(flags&~Ignore) { |len| more&&more[len] || return }
63
- if flags&Hold != 0
64
- flags ^= Hold
65
- flags ^= Reverse
66
- end
67
- _insert1(value,flags)
68
- (flags&Ignore != 0) ? true : value
69
- end
70
- # Put an element at the cursor. The observed bits in +flags+ are +Reverse+
71
- # (put before instead of after the cursor), +Hold+ (don't move the
72
- # cursor), and +Read+ (read and return what is being replaced instead of
73
- # just returning +true+). When the cursor is at the end (or beginning),
74
- # either nil or the code block result is returned (see #get).
75
- # The value is inserted at that point when that happens.
76
- def _put1(value,flags,&more) # :yield: len=1
77
- value0 = _delete1(flags^Read,&more)
78
- if flags&Hold != 0
79
- flags ^= Hold
80
- flags ^= Reverse
81
- end
82
- _insert1(value,flags)
83
- value0
84
- end
85
-
86
- public
87
-
88
- # Create a cursor.
89
- def initialize
90
- @positions = []
91
- end
92
- # Return the class used for passing/returning a sequence of elements. This
93
- # class must behave as a String or an Array ([], []=, slice, slice!, length).
94
- # The default in the base class is Array.
95
- def data_class
96
- Array
97
- end
98
- # Get a single element or a sequence of them.
99
- #
100
- # +len+ can be +nil+ (get one element), a positive number (get a
101
- # sequence of that many elements), 0 (get all remaining),
102
- # a negative number (get all but that many), or what +data_class+ says
103
- # (get a sequence up until it finds a match to +len+).
104
- #
105
- # The observed bits in +flags+ are +Reverse+
106
- # (read before instead of after the cursor), +Hold+ (don't move the
107
- # cursor), +Ignore+ (just return +true+ or a count instead of the value),
108
- # and +Delete+ (delete the element or sequence while retrieving it).
109
- #
110
- # If there is left, either nil (no code block) or the result from code block is
111
- # returned. This code block could set some variable(s) to say signify it
112
- # is done or return data from some other source to make it look as though
113
- # it is not done. When returning more data, it should return it in a
114
- # String or Array of up to +len+ elements.
115
- def get(len=nil,flags=Next,&more) # :yield: len
116
- g = (flags&Delete != 0) ? :_delete1 : :_get1
117
- return(__send__(g,flags,&more)) if not len
118
- if flags&Hold != 0 && flags&Delete == 0
119
- flags ^= Hold
120
- position { get(len,flags,&more) }
121
- elsif len.class==data_class
122
- if flags&Ignore != 0
123
- value = get(len,flags^Ignore,&more)
124
- return(value&&value.size)
125
- end
126
- value = data_class.new
127
- if flags&Reverse != 0
128
- i = len.size-1
129
- step = -1
130
- finish = -1
131
- else
132
- i = 0
133
- step = +1
134
- finish = len.size
135
- end
136
- loop do
137
- v = __send__(g,flags) { |len|
138
- more&&more[len] || begin
139
- value = value.reverse if flags&Reverse != 0
140
- value = nil if value.size==0
141
- return
142
- end
143
- }
144
- value << v
145
- if v==len[i]
146
- i += step
147
- if i==finish
148
- value = value.reverse if flags&Reverse != 0
149
- return(value)
150
- end
151
- else
152
- i = 0
153
- end
154
- end
155
- else
156
- if flags&Ignore != 0
157
- len1 = 0
158
- if len<=0
159
- if len<0 and flags&Delete != 0
160
- value = get(len,flags^Ignore,&more)
161
- return(value&&value.size)
162
- end
163
- continue = true
164
- loop do
165
- __send__(g,flags) { |len| continue = more&&more[len] }
166
- break unless continue
167
- len1 += 1
168
- end
169
- (len..-1).each do
170
- _get1(flags^Reverse) { |len|
171
- more&&more[len] || (return(len1))
172
- }
173
- len1 -= 1
174
- end
175
- else
176
- len -= 1
177
- while len1<=len
178
- __send__(g,flags) { |len|
179
- more&&more[len] ||
180
- (return(len1==0 ? nil : len1))
181
- }
182
- len1 += 1
183
- end
184
- end
185
- len1
186
- else
187
- value = data_class.new
188
- if len<=0
189
- continue = true
190
- loop do
191
- v = __send__(g,flags) { |len| continue = more&&more[len] }
192
- break unless continue
193
- value << v
194
- end
195
- (len..-1).each do
196
- return(nil) if value.size==0
197
- v = value.slice!(-1)
198
- if flags&Delete != 0
199
- _insert1(v,flags&~Read^Reverse)
200
- else
201
- _get1(flags^Reverse|Ignore) { raise(IndexError,"couldn't get back to where we were") }
202
- end
203
- end
204
- else
205
- len -= 1
206
- while value.size<=len
207
- v = __send__(g,flags&~Ignore) { |len|
208
- more&&more[len] || begin
209
- if value.size==0
210
- value = nil
211
- elsif flags&Reverse != 0
212
- value = value.reverse
213
- end
214
- return(value)
215
- end
216
- }
217
- value << v
218
- end
219
- end
220
- flags&Reverse != 0 ? value.reverse : value
221
- end
222
- end
223
- end
224
- # Put a single element or a sequence of them at the cursor.
225
- #
226
- # The observed bits in +flags+ are +Reverse+
227
- # (read before instead of after the cursor), +Hold+ (don't move the
228
- # cursor), +Read+ (return the what is overwritten instead of +true+ or a count),
229
- # +Insert+ (insert rather than replace +value+), and +Single+ (force +value+
230
- # to be treated as a single element rather than a sequence).
231
- #
232
- # When the cursor is at the end (or beginning),
233
- # either nil or the code block result is returned (see #get).
234
- # The value is inserted at that point when that happens.
235
- def put(value,flags=Next,&more) # :yield: len
236
- if flags&Single != 0 or value.class!=data_class
237
- if flags&Insert != 0
238
- if flags&Hold != 0
239
- flags ^= Hold
240
- flags ^= Reverse
241
- end
242
- return(_insert1(value,flags,&more))
243
- else
244
- return(_put1(value,flags,&more))
245
- end
246
- end
247
- if flags&Insert != 0
248
- flags |= Single
249
- if flags&Hold != 0
250
- flags ^= Hold
251
- flags ^= Reverse
252
- end
253
- if flags&Reverse != 0
254
- start = value.size-1
255
- finish = 0
256
- step = -1
257
- else
258
- start = 0
259
- finish = value.size-1
260
- step = +1
261
- end
262
- start.step(finish,step) do |i|
263
- _insert1(value[i],flags)
264
- end
265
- nil
266
- elsif flags&Hold != 0
267
- flags ^= Hold
268
- position { put(value,flags,&more) }
269
- else
270
- flags |= Single
271
- if flags&Reverse != 0
272
- start = value.size-1
273
- finish = 0
274
- step = -1
275
- else
276
- start = 0
277
- finish = value.size-1
278
- step = +1
279
- end
280
- if flags&Read != 0
281
- value0 = data_class.new
282
- replacing = true
283
- start.step(finish,step) do |i|
284
- v = _put1(value[i],flags) { |len|
285
- replacing = more&&more[len]
286
- }
287
- value0 << v if replacing
288
- end
289
- if value0.size==0
290
- nil
291
- elsif step<0
292
- value0.reverse
293
- else
294
- value0
295
- end
296
- else
297
- len0 = 0
298
- replacing = true
299
- start.step(finish,step) do |i|
300
- _put1(value[i],flags) { |len|
301
- replacing = more&&more[len]
302
- }
303
- len0 += 1 if replacing
304
- end
305
- if len0==0
306
- nil
307
- else
308
- len0
309
- end
310
- end
311
- end
312
- end
313
- # With no +p+ or code block, this will return an numeric position. This
314
- # base class uses 0 to mean the beginning and the total number of elements to
315
- # represent the end. Derived classes can shift these relative positions
316
- # if desired (i.e. Cursor::Reversed uses negative numbers).
317
- #
318
- # With a +p+ or code block specified, optionally set #pos= +p+,
319
- # execute the code block or find the #pos (without a block), and finally return to where it
320
- # started (using #pos/#pos=). The return value will be the
321
- # result of code block or the inner #pos.
322
- def pos(p=nil,&code) # :yield:
323
- if code or p
324
- start = pos
325
- self.pos = p if p
326
- ret = code ? code[] : pos
327
- self.pos = start
328
- ret
329
- else
330
- p = get(0,Prev|Ignore)
331
- return(0) if not p or p==0
332
- get(p,Next|Ignore)==p or raise(IndexError,"couldn't get back to where we were: #{p}")
333
- p
334
- end
335
- end
336
- # Returns the #pos
337
- def to_i
338
- pos
339
- end
340
- # Returns the string "pos=#{#pos}". Derived classes may override this
341
- # to provide more information (i.e. line and column).
342
- def to_s
343
- "pos=#{pos}"
344
- end
345
- # Set #pos to be +p+. This base class also allows +p+ to be negative
346
- # values measured from the end just like the indices for String/Array.
347
- def pos=(p)
348
- if p<0
349
- get(0,Next|Ignore)
350
- return(-0.1) if p>-1
351
- p = -(get(-p,Prev|Ignore)||0.1)
352
- else
353
- get(0,Prev|Ignore)
354
- return(0) if p<1
355
- get(p,Next|Ignore)||0
356
- end
357
- end
358
- def _finalizer(id) # :nodoc:
359
- i = @positions.index(id)
360
- @positions.slice!(i) if i
361
- end
362
- protected :_finalizer
363
- # Similar to #pos except Cursor::Position objects (which are Cursors) are
364
- # used to save and retrieve the position.
365
- def position(p=nil,&code) # :yield:
366
- if code or p
367
- start = position
368
- self.position = p if p
369
- ret = code ? code[] : position
370
- self.position = start
371
- start.close
372
- ret
373
- else
374
- p = Position.new(self,pos)
375
- @positions << (p.object_id >> 1)
376
- ObjectSpace.define_finalizer(p,method(:_finalizer))
377
- p
378
- end
379
- end
380
- # Set the position to +p+. +p+ can be numeric (from #pos) or from #position.
381
- def position=(p)
382
- if p.respond_to?(:pos)
383
- position?(p) or raise(TypeError,"invalid position #{p}")
384
- self.pos = p.pos
385
- else
386
- self.pos = p
387
- end
388
- end
389
- # Without a code block, this queries whether a particular #position +p+ is
390
- # valid (is a child) or determines if there is any outstanding #position
391
- # (when +p+=+nil+).
392
- #
393
- # With a code block, it sets optionally sets the #position= +p+ and
394
- # and executes the code. If the result of the code is false/nil, it will
395
- # return to the original #position (intially saved). Otherwise it will stay where the code
396
- # left it. The result of the code block is returned. This is useful when
397
- # you want the cursor to stay for a pass/match, and return to try something
398
- # else for a fail/mismatch.
399
- def position?(p=nil,&code) # :yield:
400
- if code
401
- start = position
402
- self.position = p if p
403
- ret = code[]
404
- self.position = start if not ret
405
- start.close
406
- ret
407
- elsif p
408
- i = @positions.rindex(p.object_id >> 1)
409
- if not i
410
- return(true) if p.object_id==self.object_id
411
- return(nil)
412
- end
413
- if p.closed?
414
- @positions.slice!(i)
415
- nil
416
- else
417
- true
418
- end
419
- else
420
- while @positions.size>0
421
- return(true) if not ObjectSpace._id2ref(@positions[0] << 1).closed?
422
- @positions.shift
423
- end
424
- end
425
- end
426
- # Either discard/close the given #position +p+ or discard/close every child #position (+p+=+nil+).
427
- def position!(p=nil)
428
- if p
429
- p.close
430
- position?(p) && raise(RuntimeError,"#{p} didn't seem to close")
431
- else
432
- @positions.each { |p| ObjectSpace._id2ref(p << 1).close }
433
- @positions = []
434
- end
435
- self
436
- end
437
- # Close the cursor. This will also close every child #position.
438
- def close
439
- position!
440
- # this should make just about any operation fail
441
- instance_variables.each { |v| remove_instance_variable(v) }
442
- nil
443
- end
444
- # Is the cursor closed?
445
- def closed?
446
- instance_variables.size==0
447
- end
448
- # Are we at the end? Or beginning if +reverse+.
449
- def eof?(reverse=false)
450
- flags = After|Ignore
451
- flags |= Reverse if reverse
452
- not get(nil,flags)
453
- end
454
- alias_method :eof, :eof?
455
- # Is +other+ (either #pos or #position) at the same location as the cursor?
456
- def ==(other)
457
- if other.respond_to?:pos
458
- position?(other) or return(false)
459
- other = other.pos
460
- else
461
- other = pos(other)
462
- end
463
- pos==other
464
- end
465
- alias_method :===, :==
466
- # Compare +other+ (from #pos or #position) to the current position. +1
467
- # for the self is after, -1 for self being before, and 0 for it being at
468
- # same location.
469
- def <=>(other)
470
- if other.respond_to?:pos
471
- position?(other) or raise(TypeError,"invalid position #{p}")
472
- other = other.pos
473
- else
474
- other = pos(other)
475
- end
476
- pos<=>other
477
-
478
- end
479
- # If +other+ is from #position, this will return the distance (number
480
- # or elements) from +other+ to +self+. This can be +, -, or 0.
481
- # If +other+ is numeric, this returns a new position that is decreased by
482
- # this amount (positive: before, negative: after). Otherwise, it is taken to
483
- # be a +len+ for #get (going backward).
484
- def -(other)
485
- if other.respond_to?:pos
486
- position?(other) or raise(TypeError,"invalid position #{p}")
487
- pos.to_i-other.pos.to_i
488
- elsif other.kind_of?Numeric
489
- if other>0
490
- position { get(other,Prev|Ignore);position }
491
- elsif other<0
492
- position { get(other,Next|Ignore);position }
493
- else
494
- position
495
- end
496
- else
497
- position { get(other,Prev|Ignore);position }
498
- end
499
- end
500
- # If +other+ is numeric, this returns a new position that is increased by
501
- # this amount (positive: before, negative: after). Otherwise, it is taken to
502
- # be a +len+ for #get (going forward).
503
- def +(other)
504
- if other.kind_of?Numeric
505
- if other>0
506
- position { get(other,Next|Ignore);position }
507
- elsif other<0
508
- position { get(other,Prev|Ignore);position }
509
- else
510
- position
511
- end
512
- else
513
- position { get(other,Next|Ignore);position }
514
- end
515
- end
516
- # Returns a Reversed cursor.
517
- def -@
518
- Cursor::Reversed.new(self)
519
- end
520
- # Increments the cursor by +len+ (same +len+ of #get - defaults to one element)
521
- # Returns +nil+ at the end and self otherwise.
522
- def succ!(len=nil); get(len,Next|Ignore) && self; end
523
- # Decrements the cursor by +len+ (same +len+ of #get - defaults to one element)
524
- # Returns +nil+ at the beginning and self otherwise.
525
- def pred!(len=nil); get(len,Prev|Ignore) && self; end
526
- # Similar to #succ! except a new #position is returned instead of
527
- # modifying the current.
528
- def succ(len=nil); position { get(len,Next|Ignore) && position }; end
529
- # Similar to #pred! except a new #position is returned instead of
530
- # modifying the current.
531
- def pred(len=nil); position { get(len,Prev|Ignore) && position }; end
532
- # Go to the beginning
533
- def first!; get(0,Prev|Ignore); self; end
534
- # Go to the end
535
- def last!; get(0,Next|Ignore); self; end
536
- # Similar to #first! except a new #position is returned instead of
537
- # modifying the current.
538
- def first; position { get(0,Prev|Ignore);position }; end
539
- # Similar to #last! except a new #position is returned instead of
540
- # modifying the current.
541
- def last; position { get(0,Next|Ignore);position }; end
542
- alias_method :begin, :first
543
- alias_method :begin!, :first!
544
- alias_method :end, :last
545
- alias_method :end!, :last!
546
- # Returns the number of elements.
547
- def size
548
- l1 = position { get(0,Next|Ignore) } || 0
549
- l0 = position { get(0,Prev|Ignore) } || 0
550
- l0+l1
551
- end
552
- alias_method :length, :size
553
- # Determines whether there is anything before or after the cursor.
554
- def empty?
555
- get(nil,After |Ignore)&&(return(false)) ||
556
- get(nil,Before|Ignore)&&(return(false)) ||
557
- true
558
- end
559
- # Removes all elements and returns the number removed
560
- def clear
561
- (get(0,Next|Delete|Ignore)||0)+(get(0,Prev|Delete|Ignore)||0)
562
- end
563
- # Appends a single element at the end
564
- def << (value)
565
- get(0,Next|Ignore)
566
- put(value,Next|Single)
567
- end
568
- # Prepends a single element at the beginning
569
- def >> (value)
570
- get(0,Prev|Ignore)
571
- put(value,Prev|Single)
572
- end
573
- protected
574
- def _index(index,len,flags) # :nodoc:
575
- if index.respond_to?:exclude_end?
576
- len = (index.last-index.first).to_i
577
- if index.exclude_end?
578
- len = len.ceil
579
- else
580
- len = len.to_i+1
581
- end
582
- index = index.first
583
- index += len if flags&Reverse != 0
584
- len = 0 if len<0
585
- elsif len.kind_of?Numeric
586
- if len<0
587
- flags ^= Reverse
588
- len = -len
589
- end
590
- len = len.to_i
591
- end
592
- self.position = index if index
593
- [len,flags]
594
- end
595
- public
596
- # Provides random access for the cursor like what is in Array/String.
597
- # +index+ can be +nil+ (start at the current location), a numeric (possibly range)
598
- # (just like Array/String), and even a range from start to end cursors.
599
- # +len+ can be positive/0 (just like in Array/String), negative (goes
600
- # in reverse), or even a +data_class+ (passed to #get).
601
- # +flags+ can take on the same things they can in get. A code block can
602
- # be given just like in #get.
603
- def [](index=nil,len=nil,flags=After,&more) # :yield: len
604
- len,flags = _index(index,len,flags)
605
- return(data_class.new) if len==0
606
- get(len,flags,&more)
607
- end
608
- # Random access store like in Array/String. Accepts the same enhancements
609
- # for index, len, and flags as #[] does.
610
- def []=(*args) # :args: (index=nil,len=nil,flags=After,value)
611
- value = args.slice!(-1)
612
- index,len,flags = *args
613
- flags ||= After
614
- len,flags = _index(index,len,flags)
615
- if not len
616
- ret = put(value,flags|Single)
617
- else
618
- flags &= ~Single
619
- if len==0
620
- flags &= ~Read
621
- ret = put(value,flags|Insert)
622
- elsif value.size==len
623
- ret = put(value,flags&~Insert)
624
- else
625
- ret = get(len,flags^Ignore|Delete)
626
- put(value,flags|Insert)
627
- end
628
- end
629
- if flags&Read != 0
630
- ret
631
- else
632
- value
633
- end
634
- end
635
- alias_method :slice, :[]
636
- # Random access slice! like in Array/String. Accepts the same enhancements
637
- # for index, len, flags, and code block as #[] does (same except +Delete+
638
- # bit is set).
639
- def slice!(index=nil,len=nil,flags=After,&more) # :yield: len
640
- len,flags = _index(index,len,flags)
641
- return(data_class.new) if len==0
642
- get(len,flags|Delete,&more)
643
- end
644
- # Performs each just to make this class Enumerable. Accepts the same enhancements
645
- # for index, len, and flags as #[] does. +index+ defaults to nil which
646
- # starts where the cursor (like IO) as opposed to the beginning (like Array).
647
- # The cursor will be left at the end (or beginning if the +Reverse+ flags bit is use).
648
- # nil is returned (or the break value if the code does a break).
649
- def each(index=nil,len=nil,flags=Next,&code) # :yield: value
650
- len,flags = _index(index,len,flags)
651
- raise if len==0 or flags&Hold != 0
652
- continue = true
653
- loop do
654
- value = get(len,flags) { continue = nil }
655
- if not continue
656
- code[value] if len and value
657
- break
658
- end
659
- code[value]
660
- end
661
- end
662
- # copies data from +index+ to the current location. Uses #[] and #[]=
663
- # to accomplish the copy so that all of those options are available.
664
- # +pflags are used for the put and +gflags+ for the get.
665
- def copy_from(index,len=nil,pflags=After,gflags=After)
666
- gflags &= ~Ignore
667
- if len
668
- pflags |= Single
669
- else
670
- pflags &= ~Single
671
- end
672
- self[nil,nil,pflags] = self[index,len,gflags]
673
- end
674
- # copies data from the current location to +index+. Uses #[] and #[]=
675
- # to accomplish the copy so that all of those options are available.
676
- # +pflags are used for the put and +gflags+ for the get.
677
- def copy_to(index,len=nil,pflags=After,gflags=After)
678
- gflags &= ~Ignore
679
- if len
680
- pflags |= Single
681
- else
682
- pflags &= ~Single
683
- end
684
- self[index,nil,pflags] = self[nil,len,gflags]
685
- end
686
-
687
- # Objects in this class are mainly used to simply mark/remember the location
688
- # of a parent cursor. But, this class also has the fully functionality of the
689
- # parent. When this child want to do an operation, it uses the parent to
690
- # do it and returns the parent to where it was. Derived classes where the
691
- # underlying data is random access may be able to implement this class to
692
- # directly access the data rather than go through the parent.
693
- class Position < Cursor
694
- def initialize(parent,p)
695
- @parent = parent
696
- @pos = p
697
- end
698
- def data_class
699
- @parent.data_class
700
- end
701
- def get(*args,&more) # :args: (len=nil, flags=Next) { |len| ... }
702
- @parent.pos(@pos) {
703
- ret = @parent.get(*args,&more)
704
- @pos = @parent.pos
705
- ret
706
- }
707
- end
708
- def put(*args,&more) # :args: (value, flags=Next) { |len| ... }
709
- @parent.pos(@pos) {
710
- ret = @parent.put(*args,&more)
711
- @pos = @parent.pos
712
- ret
713
- }
714
- end
715
- def position(p=nil,&code) # :yield:
716
- if p or code
717
- super(p,&code)
718
- else
719
- @parent.pos(@pos) { @parent.position }
720
- end
721
- end
722
- def position?(p=nil,&code) # :yield:
723
- if code
724
- super(p,&code)
725
- else
726
- @parent.position?(p)
727
- end
728
- end
729
- def position!(p=nil)
730
- if p
731
- @parent.position!(p)
732
- else
733
- nil
734
- end
735
- end
736
- def close
737
- parent = @parent
738
- super
739
- parent.position?(self)
740
- end
741
- end
742
-
743
- # This class can be used to reverse the direction of operations on a given
744
- # cursor. It operates on the given cursor directly moving it around.
745
- class Reversed < Cursor
746
- def initialize(cursor)
747
- super()
748
- @cursor = cursor
749
- end
750
- def data_class
751
- @cursor.data_class
752
- end
753
- def get(len=nil,flags=Next,&more) # :yield: len
754
- @cursor.get(len,flags^Reverse)
755
- end
756
- def put(value,flags=Next,&more) # :yield: len
757
- @cursor.put(value,flags^Reverse,&more)
758
- end
759
- # Every +pos+ returned here is negative (-0.1: end, -N: beginning)
760
- # unlike the base class.
761
- def pos(p=nil,&code) # :yield:
762
- if p or code
763
- super(*args,&code)
764
- else
765
- p = @cursor.pos
766
- p = -p
767
- p = -0.1 if p==0
768
- p
769
- end
770
- end
771
- def pos=(p)
772
- p = -p
773
- p = -0.1 if p==0
774
- p = (@cursor.pos = p)
775
- p = -p
776
- p = -0.1 if p==0
777
- p
778
- end
779
- end
780
-
781
- # This class is used to test the base class by overriding as few methods
782
- # as possible.
783
- class Test < Cursor
784
- def initialize(data=[],index=0)
785
- super()
786
- @data = data
787
- @pos = index
788
- @data_class = (@data.respond_to?:data_class) ?
789
- @data.data_class : @data.class
790
- end
791
- def data_class
792
- @data_class
793
- end
794
- def _delete1(flags,&more) # :yield: len=1
795
- if flags&Reverse != 0
796
- @pos>0 && (@pos -= 1)
797
- else
798
- @pos<@data.size
799
- end or return(
800
- if ret = more&&more[1]
801
- if flags&Ignore != 0
802
- ret = true
803
- else
804
- ret,=ret
805
- ret
806
- end
807
- end
808
- )
809
- if flags&Ignore != 0
810
- @data[@pos,1] = @data_class.new
811
- 1
812
- else
813
- @data.slice!(@pos)
814
- end
815
- end
816
- def _insert1(value,flags)
817
- @data[@pos,0] = (@data_class.new << value)
818
- @pos += 1 if flags&Reverse == 0
819
- end
820
- end
821
-
822
- # This class puts a cursor on an Array or String.
823
- class Indexed < Cursor
824
- def initialize(data=[],index=0)
825
- super()
826
- @data = data
827
- @pos = index
828
- @data_class = (@data.respond_to?:data_class) ?
829
- @data.data_class : @data.class
830
- end
831
- def data_class
832
- @data_class
833
- end
834
- def _delete1(flags,&more) # :yield: len=1
835
- if flags&Reverse != 0
836
- @pos>0 && @pos -= 1
837
- else
838
- @pos<@data.size
839
- end or return(
840
- if ret = more&&more[1]
841
- if flags&Ignore != 0
842
- ret = true
843
- else
844
- ret,=ret
845
- ret
846
- end
847
- end
848
- )
849
- if flags&Ignore != 0
850
- @data[@pos,1] = @data_class.new
851
- true
852
- else
853
- @data.slice!(@pos)
854
- end
855
- end
856
- def _insert1(value,flags)
857
- @data[@pos,0] = (@data_class.new << value)
858
- @pos += 1 if flags&Reverse == 0
859
- end
860
- end
861
-
862
- # This class treats an IO (or StringIO) as a Cursor. An IO is already
863
- # like a Cursor, but doesn't have as robust an interface. Deleting and
864
- # inserting is a slow/painful process.
865
- class IO < Cursor
866
- def initialize(io=StringIO.new)
867
- super()
868
- @io = io
869
- end
870
- def data_class
871
- String
872
- end
873
- def _delete1(flags,&more) # :yield: len=1
874
- if flags&Reverse != 0
875
- begin
876
- @io.seek(-1,::IO::SEEK_CUR)
877
- ret = @io.getc
878
- rescue
879
- ret = nil
880
- end
881
- else
882
- ret = @io.getc
883
- end or return(
884
- if ret = more&&more[1]
885
- if flags&Ignore != 0
886
- ret = true
887
- else
888
- ret,=ret
889
- ret
890
- end
891
- end
892
- )
893
- ret = true if flags&Ignore != 0
894
- after = 0
895
- while c = @io.getc
896
- after += 1
897
- @io.seek(-2,::IO::SEEK_CUR)
898
- @io.putc(c)
899
- @io.seek(+1,::IO::SEEK_CUR)
900
- end
901
- @io.seek(-1,::IO::SEEK_CUR)
902
- @io.truncate(@io.pos)
903
- @io.seek(-after,::IO::SEEK_CUR) if after>0
904
- ret
905
- end
906
- def _insert1(value,flags)
907
- after = 0
908
- while c = @io.getc
909
- after += 1
910
- @io.seek(-1,::IO::SEEK_CUR)
911
- @io.putc(value)
912
- value = c
913
- end
914
- @io.putc(value)
915
- after += 1 if flags&Reverse != 0
916
- @io.seek(-after,::IO::SEEK_CUR) if after>0
917
- nil
918
- end
919
- def _get1(flags,&more) # :yield: len=1
920
- if flags&Reverse != 0
921
- begin
922
- @io.seek(-1,::IO::SEEK_CUR)
923
- rescue
924
- if ret = more&&more[1]
925
- ret,=ret
926
- if flags&Hold != 0
927
- flags ^= Hold
928
- flags ^= Reverse
929
- end
930
- _insert(flags,ret)
931
- ret = true if flags&Ignore != 0
932
- end
933
- return(ret)
934
- end
935
- if flags&Ignore != 0
936
- @io.seek(+1,::IO::SEEK_CUR) if flags&Hold != 0
937
- ret = true
938
- else
939
- ret = @io.getc
940
- @io.seek(-1,::IO::SEEK_CUR) if flags&Hold == 0
941
- end
942
- else
943
- if not ret = @io.getc
944
- if ret = more&&more[1]
945
- ret,=ret
946
- @io.putc(ret)
947
- @io.seek(-1,::IO::SEEK_CUR) if flags&Hold != 0
948
- ret = true if flags&Ignore != 0
949
- end
950
- return(ret)
951
- end
952
- @io.ungetc(ret) if flags&Hold != 0
953
- ret = true if flags&Ignore != 0
954
- end
955
- ret
956
- end
957
- def _put1(value,flags,&more) # :yield: len=1
958
- if flags&Reverse != 0
959
- begin
960
- @io.seek(-1,::IO::SEEK_CUR)
961
- rescue
962
- if ret = more&&more[1]
963
- if flags&Read == 0
964
- ret = true
965
- else
966
- ret,=ret
967
- end
968
- end
969
- if flags&Hold != 0
970
- flags ^= Hold
971
- flags ^= Reverse
972
- end
973
- _insert1(value,flags)
974
- return(ret)
975
- end
976
- if flags&Read != 0
977
- ret = @io.getc
978
- @io.ungetc(ret)
979
- else
980
- ret = true
981
- end
982
- @io.putc(value)
983
- if flags&Hold == 0
984
- @io.seek(-1,::IO::SEEK_CUR)
985
- end
986
- else
987
- if flags&Read != 0
988
- if not ret = @io.getc
989
- if ret = more&&more[1]
990
- ret, = ret
991
- end
992
- else
993
- @io.ungetc(ret)
994
- end
995
- elsif @io.eof?
996
- ret = more&&more[1]&&true
997
- else
998
- ret = true
999
- end
1000
- @io.putc(value)
1001
- @io.seek(-1,::IO::SEEK_CUR) if flags&Hold != 0
1002
- end
1003
- ret
1004
- end
1005
- def close
1006
- @io.close
1007
- super
1008
- end
1009
- end
1010
-
1011
- # This Cursor class uses an Array/String for data before the cursor
1012
- # and another one for data after the cursor. The result of this is that
1013
- # data is only deleted/inserted at the end of these 2. Because of that
1014
- # most operations have similar expense and are linear with respect to the
1015
- # number of elements being moved, inserted, deleted, replaced, etc.
1016
- class Buffer < Cursor
1017
- def initialize(before=[],after=before.class.new)
1018
- super()
1019
- @before = before
1020
- @after = after
1021
- @data_class = (@before.respond_to?:data_class) ?
1022
- @before.data_class : @before.class
1023
- end
1024
- def data_class
1025
- @data_class
1026
- end
1027
- def _delete1(flags,&more) # :yield: len=1
1028
- data = (flags&Reverse != 0) ? @before : @after
1029
- data.size>0 or return(
1030
- if ret = more&&more[1]
1031
- if flags&Ignore != 0
1032
- ret = true
1033
- else
1034
- ret,=ret
1035
- ret
1036
- end
1037
- end
1038
- )
1039
- if flags&Ignore != 0
1040
- data[-1,1] = @data_class.new
1041
- true
1042
- else
1043
- data.slice!(-1)
1044
- end
1045
- end
1046
- def _insert1(value,flags)
1047
- data = (flags&Reverse != 0) ? @after : @before
1048
- data << value
1049
- end
1050
- end
1051
-
1052
- # This class gives unidirectional cursors (i.e. IO pipes) some
1053
- # bidirectional capabilities. An input cursor and/or an output cursor
1054
- # can be specified. The #position, #position?, and #position! methods are
1055
- # used to control buffering. Full cursor capability (limited by the buffer
1056
- # cursor) is accessible starting from the first #position. When the end of
1057
- # the buffer is reached more data is read from the input cursor (if not nil) using #get . When no
1058
- # #position is outstanding, everything before the buffer cursor is written
1059
- # to the output cursor (if not nil) using #put. If the cursor is attempted
1060
- # to be moved before the buffer, the output cursor is read in reverse.
1061
- class Buffered < Cursor
1062
- def initialize(input,output=nil,buffer=Buffer.new((input||output).data_class.new))
1063
- @input = input
1064
- @output = output
1065
- @buffer = buffer
1066
- @offset = 0
1067
- super()
1068
- end
1069
- def data_class
1070
- @buffer.data_class
1071
- end
1072
- def get(len=nil,flags=Next,&more) # :yield: len
1073
- value = @buffer.get(len,flags) { |len|
1074
- if flags&Reverse != 0
1075
- if @offset==0
1076
- more&&more[len]
1077
- else
1078
- v = @output.get(len,Prev,&more)
1079
- @offset -= v.size if v
1080
- v
1081
- end
1082
- elsif @input
1083
- @input.get(len,Next,&more)
1084
- else
1085
- more&&more[len]
1086
- end
1087
- }
1088
- if not position?
1089
- if output_value = @buffer.get(0,Prev|Delete)
1090
- @offset += output_value.size
1091
- @output.put(output_value,Next) if @output
1092
- end
1093
- end
1094
- value
1095
- end
1096
- def put(value,flags=Next,&more) # :yield: len
1097
- value0 = @buffer.put(value,flags) { |len|
1098
- if flags&Reverse != 0
1099
- if @offset==0
1100
- more&&more[len]
1101
- else
1102
- v = @output.get(len,Prev,&more)
1103
- @offset -= v.size if v
1104
- v
1105
- end
1106
- elsif @input
1107
- @input.get(len,Next,&more)
1108
- else
1109
- more&&more[len]
1110
- end
1111
- }
1112
- if not position?
1113
- if output_value = @buffer.get(0,Prev|Delete)
1114
- @offset += output_value.size
1115
- @output.put(output_value,Next) if @output
1116
- end
1117
- end
1118
- value0
1119
- end
1120
- def pos(p=nil,&code) # :yield:
1121
- if p or code
1122
- super(*args,&code)
1123
- else
1124
- @buffer.pos+@offset
1125
- end
1126
- end
1127
- def pos=(p)
1128
- if p<0
1129
- @buffer.pos = p
1130
- else
1131
- @buffer.pos = p-@offset
1132
- end
1133
- end
1134
- end
1135
-
1136
- end
1137
-
1138
-
1
+ #!/bin/env ruby
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 $
4
+ # Author:: Eric Mahurin (Eric under Mahurin at yahoo dot com)
5
+ # License:: Ruby license
6
+ # Home:: http://rubyforge.org/projects/cursor
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).
15
+ class Cursor
16
+
17
+ include Comparable
18
+ include Enumerable
19
+
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
+
49
+ 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
93
+ end
94
+
95
+ 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
105
+ 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
192
+ end
193
+ 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
281
+ end
282
+ end
283
+ # This will return a numeric position. When not +reverse+,
284
+ # 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
293
+ end
294
+ # Returns #pos.to_i
295
+ def to_i
296
+ pos.to_i
297
+ 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(" ")
309
+ else
310
+ s += p.to_s
311
+ end
312
+ s
313
+ 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
361
+ else
362
+ p = Position.new(self,pos,prop)
363
+ @positions << (p.object_id >> 1)
364
+ ObjectSpace.define_finalizer(p,method(:_finalizer))
365
+ p
366
+ end
367
+ end
368
+ # Set the position to +p+. +p+ can be numeric (from #pos) or from #position.
369
+ 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
377
+ end
378
+ # Without a code block, this queries whether a particular #position +p+ is
379
+ # valid (is a child) or determines if there is any outstanding #position
380
+ # (when +p+=+nil+).
381
+ #
382
+ # With a code block, it sets optionally sets the #position= +p+ and
383
+ # and executes the code. If the result of the code is false/nil, it will
384
+ # return to the original #position (intially saved). Otherwise it will stay where the code
385
+ # left it. The result of the code block is returned. This is useful when
386
+ # you want the cursor to stay for a pass/match, and return to try something
387
+ # else for a fail/mismatch.
388
+ def position?(p=nil,&code) # :yield:
389
+ 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
407
+ end
408
+ else
409
+ while @positions.size>0
410
+ return(true) if not ObjectSpace._id2ref(@positions[0] << 1).closed?
411
+ @positions.shift
412
+ end
413
+ end
414
+ end
415
+ # Either discard/close the given #position +p+ or discard/close every child #position (+p+=+nil+).
416
+ def position!(p=nil)
417
+ if p
418
+ p.close
419
+ else
420
+ @positions.clone.each { |p| ObjectSpace._id2ref(p << 1).close }
421
+ end
422
+ self
423
+ end
424
+ # Close the cursor. This will also close every child #position.
425
+ def close
426
+ position!
427
+ # this should make just about any operation fail
428
+ instance_variables.each { |v| remove_instance_variable(v) }
429
+ nil
430
+ end
431
+ # Is the cursor closed?
432
+ def closed?
433
+ instance_variables.size.zero?
434
+ 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
441
+ # for the self is after, -1 for self being before, and 0 for it being at
442
+ # same location.
443
+ 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).
456
+ 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
464
+ 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
+ # Returns the number of elements.
497
+ def size
498
+ l1 = position { get(Rest,Next|Ignore) } || 0
499
+ l0 = position { get(Rest,Prev|Ignore) } || 0
500
+ l0+l1
501
+ end
502
+ alias length size
503
+ # Determines whether there is anything before or after the cursor.
504
+ def empty?
505
+ get(nil,After |Ignore) and return(false)
506
+ get(nil,Before|Ignore) and return(false)
507
+ true
508
+ end
509
+ # Removes all elements and returns the number removed
510
+ def clear
511
+ (get(Rest,Next|Delete|Ignore)||0)+(get(Rest,Prev|Delete|Ignore)||0)
512
+ end
513
+ # If +obj+ is the #data_class, replace the all of the data with it. Otherwise
514
+ # use #position= +obj+. self is returned.
515
+ def replace(obj)
516
+ if data_class>=obj.class
517
+ clear
518
+ put(obj)
519
+ else
520
+ self.position = obj
521
+ end
522
+ self
523
+ end
524
+ # Get all of the data (beginning to the end)
525
+ def data
526
+ get(Rest,Prev|Ignore)
527
+ get(Rest,Next)
528
+ 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)
533
+ self
534
+ 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)
539
+ self
540
+ 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
571
+ # 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)
580
+ end
581
+ 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)
593
+ 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 }
620
+ else
621
+ self[to_index,len,to_flags] = self[from_index,len,from_flags]
622
+ end
623
+ 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).
668
+ # 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)
671
+ 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)
724
+ 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]
846
+ end
847
+ )
848
+ if (flags&Ignore).nonzero?
849
+ @data[@pos,1] = @data_class.new
850
+ true
851
+ 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
879
+ 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
+ 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)
1093
+ end
1094
+ self
1095
+ end
1096
+
1097
+ 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
1164
+ end
1165
+ end
1166
+ value0
1167
+ end
1168
+ def pos(reverse=false,&code) # :yield:
1169
+ if code or reverse
1170
+ super(reverse,&code)
1171
+ 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
1186
+ end
1187
+ 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
1250
+ end
1251
+ ret
1252
+ end
1253
+ end
1254
+
1255
+ end
1256
+
1257
+