cursor 0.5 → 0.6

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