sequence 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +58 -0
- data/GPL +340 -0
- data/Manifest.txt +36 -0
- data/README.txt +320 -0
- data/Rakefile +32 -0
- data/lib/assert.rb +16 -0
- data/lib/sequence/arraylike.rb +57 -0
- data/lib/sequence/buffered.rb +188 -0
- data/lib/sequence/circular.rb +272 -0
- data/lib/sequence/enum.rb +260 -0
- data/lib/sequence/file.rb +172 -0
- data/lib/sequence/functional.rb +152 -0
- data/lib/sequence/generator.rb +290 -0
- data/lib/sequence/indexed.rb +234 -0
- data/lib/sequence/io.rb +102 -0
- data/lib/sequence/list.rb +292 -0
- data/lib/sequence/ofhash.rb +38 -0
- data/lib/sequence/ofobjectivars.rb +29 -0
- data/lib/sequence/ofobjectmethods.rb +87 -0
- data/lib/sequence/position.rb +100 -0
- data/lib/sequence/reversed.rb +180 -0
- data/lib/sequence/shifting.rb +190 -0
- data/lib/sequence/singleitem.rb +50 -0
- data/lib/sequence/stringlike.rb +482 -0
- data/lib/sequence/subseq.rb +90 -0
- data/lib/sequence/usedata.rb +35 -0
- data/lib/sequence/version.rb +5 -0
- data/lib/sequence.rb +721 -0
- data/lib/weakrefset.rb +254 -0
- data/test/test.rb +609 -0
- data/test/test_all.rb +6 -0
- data/test/test_changes.rb +44 -0
- data/test/test_circulars.rb +89 -0
- data/test/test_rexscan.rb +899 -0
- data/test/test_seqrex.rb +204 -0
- data/test/test_sequences.rb +106 -0
- metadata +90 -0
data/lib/sequence.rb
ADDED
@@ -0,0 +1,721 @@
|
|
1
|
+
# Copyright (C) 2006 Caleb Clausen
|
2
|
+
# Distributed under the terms of Ruby's license.
|
3
|
+
# = sequence.rb - external iterators with capabilities like a text editor cursor
|
4
|
+
# $Id$
|
5
|
+
#
|
6
|
+
# Author: Caleb Clausen (sequence-owner @at@ inforadical .dot. net)
|
7
|
+
# Original Author: Eric Mahurin
|
8
|
+
# License: Ruby license
|
9
|
+
# Home: http://rubyforge.org/projects/sequence
|
10
|
+
|
11
|
+
require "forwardable"
|
12
|
+
require 'assert'
|
13
|
+
require 'weakrefset'
|
14
|
+
require 'sequence/version'
|
15
|
+
|
16
|
+
=begin todo
|
17
|
+
|
18
|
+
|
19
|
+
(.... need to steal more features from Array, String, File, StringScanner, Enumerable?, like:)
|
20
|
+
|
21
|
+
|
22
|
+
pack/unpack
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
match/matchback
|
27
|
+
|
28
|
+
|
29
|
+
=end
|
30
|
+
|
31
|
+
class Sequence
|
32
|
+
|
33
|
+
|
34
|
+
include Comparable
|
35
|
+
include Enumerable
|
36
|
+
extend Forwardable
|
37
|
+
|
38
|
+
def initialize; abstract end
|
39
|
+
def to_sequence; self end
|
40
|
+
|
41
|
+
class<<self
|
42
|
+
if nil #borken
|
43
|
+
alias original__new new
|
44
|
+
undef new #bye-bye
|
45
|
+
def new(seq)
|
46
|
+
case seq
|
47
|
+
when File,IO,Array,String,Enumerable:
|
48
|
+
seq.to_sequence
|
49
|
+
else
|
50
|
+
if seq.respond_to? :to_str
|
51
|
+
seq.to_str.to_sequence
|
52
|
+
else
|
53
|
+
raise ArgumentError
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
def [](*x) new(*x) end
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
public
|
63
|
+
# read next element or nil if eof and advance position
|
64
|
+
def read1
|
65
|
+
(read 1)[0]
|
66
|
+
end
|
67
|
+
|
68
|
+
# read previous element or nil if start of input and move position back
|
69
|
+
def readback1
|
70
|
+
(readback 1)[0]
|
71
|
+
end
|
72
|
+
|
73
|
+
# read element after the pos or nil if eof, leaving position alone
|
74
|
+
def readahead1
|
75
|
+
slice pos
|
76
|
+
end
|
77
|
+
|
78
|
+
# read element before the pos or nil if start of input, leaving position alone
|
79
|
+
def readbehind1
|
80
|
+
slice pos-1 unless pos.zero?
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
# Return an empty object used for returning a sequence of elements.
|
85
|
+
# The only method required of this object is << (append to the sequence).
|
86
|
+
#usually [] or ""
|
87
|
+
def new_data
|
88
|
+
data_class.new
|
89
|
+
end
|
90
|
+
|
91
|
+
# attempt to read up to +len+ elements. the position is left just after the data read.
|
92
|
+
# #read may return less than the whole requested amount if less data than requested in
|
93
|
+
#+len+ is available. This can happen at end of file or if more data is simply unavailable
|
94
|
+
#currently (ie with a Sequence::IO). Don't assume that getting less than you requested
|
95
|
+
#means you're at end of file; use #eof? to test for that instead.
|
96
|
+
def read(len)
|
97
|
+
abstract
|
98
|
+
end
|
99
|
+
|
100
|
+
#like read, but position is left alone.
|
101
|
+
def readahead(len)
|
102
|
+
holding{read(len)}
|
103
|
+
end
|
104
|
+
|
105
|
+
#read data behind the current position, leaving position unchanged
|
106
|
+
def readbehind(len)
|
107
|
+
read move( -len)
|
108
|
+
end
|
109
|
+
|
110
|
+
#read data behind the current position, leaving position just before the data read.
|
111
|
+
def readback(len)
|
112
|
+
readahead move( -len )
|
113
|
+
end
|
114
|
+
|
115
|
+
# read the remaining elements.
|
116
|
+
# if reverse, read everything behind position
|
117
|
+
def read!(reverse=false)
|
118
|
+
if reverse
|
119
|
+
readback pos
|
120
|
+
else
|
121
|
+
read rest_size
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def all_data
|
126
|
+
holding_position{|posi|
|
127
|
+
posi.begin!
|
128
|
+
posi.read!
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
|
134
|
+
|
135
|
+
#a StringScanner-like interface for pattern scanning within sequences.
|
136
|
+
#See StringScanner for /(scan|skip|check)(_until)?|match\?|exist\?/.
|
137
|
+
#Some notes on the implementation: scanning is all done at the current
|
138
|
+
#position. Unlike StringScanner, which only scans for Regexp, this
|
139
|
+
#version of #scan, etc allow more pattern types. The pattern type and how
|
140
|
+
#it is treated are determined by whether the underlying data is String-
|
141
|
+
#like (contains only characters/bytes), or Array-like (contains any
|
142
|
+
#Object).
|
143
|
+
|
144
|
+
#If String-like: scan and friends can take a Regexp, String, or
|
145
|
+
#Integer (for a single char) parameter.
|
146
|
+
#If Array-like: scan and friends can take a scalar matcher (something
|
147
|
+
#that responds to ===). Eventually, vector matchers (Reg::Multiple)
|
148
|
+
#will be supported as well here. Literal arrays, as patterns to scan for,
|
149
|
+
#are not supported: too hard, and there's the quoting problem.
|
150
|
+
#if you actually want to scan for items that are equal to a particular
|
151
|
+
#Regexp (for example), instead of items that match it, use duck.rb
|
152
|
+
#to reassign === as ==. Or, if using Reg, create a Reg::Literal by
|
153
|
+
#calling #lit on the pattern (which has the same effect).
|
154
|
+
|
155
|
+
#when scanning string-like sequences, anchors in Regexps are treated somewhat
|
156
|
+
#specially:
|
157
|
+
#when scanning forward, ^ and \A match at the current position, and
|
158
|
+
#$ and \Z match at the end of the sequence.
|
159
|
+
#when scanning backward, ^ and \A match at the beginning of sequence and $ and
|
160
|
+
#\Z match at the current position.
|
161
|
+
#^ and $ still match at beginning and end of lines, as usual.
|
162
|
+
|
163
|
+
#when scanning or matching backwards, ^ has some special problems:
|
164
|
+
# these won't work....
|
165
|
+
#an anchor that might or might not match will break the current implementation in some cases...
|
166
|
+
#regexes that should match overall even if the anchor in them doesn't...
|
167
|
+
# /(^|)/
|
168
|
+
|
169
|
+
#... in strings represents more data which is before the current position
|
170
|
+
# position in string is represented by |
|
171
|
+
# /(^foo)?bar/==="...xxxfoobar|"
|
172
|
+
# /(^[ab]+|b+)/==="...xxxaaabbbbbb|"
|
173
|
+
# /(b+|^[ab]+)/==="...xxxaaabbbbbb|"
|
174
|
+
|
175
|
+
#scan buffers and maxmatchlen (and ?maxuntillen)
|
176
|
+
|
177
|
+
|
178
|
+
#Regexps (or Reg::Multiples in Array-like sequences) are implicitly anchored
|
179
|
+
#to the current position unless one of the _until forms (or exist?) is used.
|
180
|
+
|
181
|
+
|
182
|
+
|
183
|
+
=begin can't be abstract... defined in modules
|
184
|
+
def scan(pat)
|
185
|
+
abstract
|
186
|
+
end
|
187
|
+
|
188
|
+
def scan_until(pat)
|
189
|
+
abstract
|
190
|
+
end
|
191
|
+
def scanback(pat)
|
192
|
+
abstract
|
193
|
+
end
|
194
|
+
|
195
|
+
def scanback_until(pat)
|
196
|
+
abstract
|
197
|
+
end
|
198
|
+
|
199
|
+
def match(pat)
|
200
|
+
abstract
|
201
|
+
end
|
202
|
+
|
203
|
+
def matchback(pat)
|
204
|
+
abstract
|
205
|
+
end
|
206
|
+
=end
|
207
|
+
|
208
|
+
def skip(pat) match= scan(pat) and match.length end
|
209
|
+
def check(pat) holding{scan(pat)} end
|
210
|
+
def match?(pat) holding{skip(pat)} end
|
211
|
+
|
212
|
+
def skip_until(pat) match= scan_until(pat) and match.length end
|
213
|
+
def check_until(pat) holding{scan_until(pat)} end
|
214
|
+
def exist?(pat) holding{skip_until(pat)} end
|
215
|
+
|
216
|
+
def skipback(pat) match= scanback(pat) and match.length end
|
217
|
+
def checkback(pat) holding{scanback(pat)} end
|
218
|
+
def matchback?(pat) holding{skipback(pat)} end
|
219
|
+
|
220
|
+
def skipback_until(pat) match= scanback_until(pat) and match.length end
|
221
|
+
def checkback_until(pat) holding{scanback_until(pat)} end
|
222
|
+
def existback?(pat) holding{skipback_until(pat) }end
|
223
|
+
|
224
|
+
def skip_literal(lits)
|
225
|
+
sz=lits.size
|
226
|
+
lits==readahead(sz) and move sz
|
227
|
+
end
|
228
|
+
alias skip_literals skip_literal
|
229
|
+
|
230
|
+
def skip_until_literal(lits)
|
231
|
+
sz=lits.size
|
232
|
+
first=lits[0]
|
233
|
+
holding?{
|
234
|
+
until eof?
|
235
|
+
skip_until(first)
|
236
|
+
lits==readahead(sz) and break pos
|
237
|
+
end
|
238
|
+
}
|
239
|
+
end
|
240
|
+
alias skip_until_literals skip_until_literal
|
241
|
+
|
242
|
+
attr_accessor :last_match
|
243
|
+
attr_writer :maxmatchlen
|
244
|
+
|
245
|
+
def _default_maxmatchlen; 1024 end
|
246
|
+
|
247
|
+
def maxmatchlen(backwards)
|
248
|
+
size=self.size
|
249
|
+
|
250
|
+
list=[ _default_maxmatchlen,
|
251
|
+
backwards ? pos : size-pos%size
|
252
|
+
]
|
253
|
+
list.push @maxmatchlen if defined? @maxmatchlen
|
254
|
+
list.min
|
255
|
+
end
|
256
|
+
|
257
|
+
#hold current position while executing a block. The block is passed the current
|
258
|
+
#sequence as its parameter. you can move the position around or call methods
|
259
|
+
#like read that do it, but after the block returns, the position is reset to the
|
260
|
+
#original location. The return value is the result of the block.
|
261
|
+
def holding
|
262
|
+
oldpos=pos
|
263
|
+
begin
|
264
|
+
yield self
|
265
|
+
ensure
|
266
|
+
self.pos=oldpos
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
#like #holding, but position is reset only if block returns false or nil (or
|
271
|
+
#raises an exception).
|
272
|
+
def holding?
|
273
|
+
oldpos=pos
|
274
|
+
begin
|
275
|
+
result=yield self
|
276
|
+
ensure
|
277
|
+
(self.pos=oldpos) unless result
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
#like #holding, but block is instance_eval'd in the sequence.
|
282
|
+
def holding! &block
|
283
|
+
oldpos=pos
|
284
|
+
begin
|
285
|
+
instance_eval self, &block
|
286
|
+
ensure
|
287
|
+
self.pos=oldpos
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
|
292
|
+
def holding_position
|
293
|
+
pos=position
|
294
|
+
begin
|
295
|
+
result=yield self
|
296
|
+
ensure
|
297
|
+
self.position=pos
|
298
|
+
pos.close
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
def holding_position?
|
303
|
+
pos=position
|
304
|
+
begin
|
305
|
+
result=yield self
|
306
|
+
ensure
|
307
|
+
self.position=pos unless result
|
308
|
+
pos.close
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
def holding_position! &block
|
313
|
+
pos=position
|
314
|
+
begin
|
315
|
+
result=instance_eval self,&block
|
316
|
+
ensure
|
317
|
+
self.position=pos
|
318
|
+
pos.close
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
|
323
|
+
# number of elements from the beginning (0 is at the beginning).
|
324
|
+
def pos()
|
325
|
+
abstract
|
326
|
+
end
|
327
|
+
def rest_size; size - pos end
|
328
|
+
|
329
|
+
# this checks to see if p is a valid numeric position.
|
330
|
+
def pos?(p)
|
331
|
+
sz=size
|
332
|
+
(-sz..sz)===p
|
333
|
+
end
|
334
|
+
|
335
|
+
# Set #pos to be +p+. When +p+ is negative, it is set from the end.
|
336
|
+
def pos=(p)
|
337
|
+
position?(p) and p=p.pos unless Integer===p
|
338
|
+
self._pos=_normalize_pos p
|
339
|
+
end
|
340
|
+
|
341
|
+
#go to an absolute position; identical to #pos=
|
342
|
+
def goto p
|
343
|
+
self.pos= p
|
344
|
+
end
|
345
|
+
|
346
|
+
def _pos=(p)
|
347
|
+
abstract
|
348
|
+
end
|
349
|
+
# move position +len+ elements, relative to the current position.
|
350
|
+
# A negative +len+ will go in reverse.
|
351
|
+
# The (positive) amount actually moved is returned (<+len+ if reached beginning/end).
|
352
|
+
def move(len)
|
353
|
+
oldpos=pos
|
354
|
+
goto oldpos+len
|
355
|
+
return (pos-oldpos).abs
|
356
|
+
end
|
357
|
+
# move to end of the remaining elements.
|
358
|
+
# reverse=true to move to beginning instead of end
|
359
|
+
# The amount moved is returned.
|
360
|
+
def move!(reverse=false)
|
361
|
+
reverse ? begin! : end!
|
362
|
+
end
|
363
|
+
|
364
|
+
# Get (if no +value+) and set properties. Normally, +name+
|
365
|
+
# should be a symbol. If +name+ is +nil+, it wil get/set using a hash
|
366
|
+
# representing all of the properties.
|
367
|
+
def prop(name=nil,*value) # :args: (name[,value])
|
368
|
+
if name.nil?
|
369
|
+
if value.size.zero?
|
370
|
+
defined?(@prop) &&@prop&&@prop.clone
|
371
|
+
else
|
372
|
+
if (value = value[0]).nil?
|
373
|
+
defined?(@prop) &&@prop&&remove_instance_variable(:@prop)
|
374
|
+
else
|
375
|
+
(@prop||={}).replace(value)
|
376
|
+
end
|
377
|
+
end
|
378
|
+
else
|
379
|
+
if value.size.zero?
|
380
|
+
defined?(@prop) &&@prop&&@prop[name]
|
381
|
+
else
|
382
|
+
(@prop||={})[name] = value[0]
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
# #position returns a Sequence::Position to represent a location within this sequence.
|
388
|
+
# The argument allows you to specify a numeric location for the position; default is
|
389
|
+
# currrent position. If the element that a
|
390
|
+
# Position is anchored to is deleted, that Position may become invalid
|
391
|
+
# or have an unknown behavior.
|
392
|
+
def position(_pos=pos)
|
393
|
+
Position.new(self,_pos)
|
394
|
+
end
|
395
|
+
# Set the position to a Position +p+ (from #position).
|
396
|
+
def position=(p)
|
397
|
+
self.pos = p.pos
|
398
|
+
self.prop(nil,p.prop)
|
399
|
+
p
|
400
|
+
end
|
401
|
+
|
402
|
+
# this queries whether a particular #position +p+ is valid (is a child or self).
|
403
|
+
# numeric positions and also be tested
|
404
|
+
def position?(p)
|
405
|
+
case p
|
406
|
+
when Integer: (-size..size)===p
|
407
|
+
when Position: equal? p.data
|
408
|
+
else equal? p
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
#make a new sequence out of a subrange of current sequence data.
|
413
|
+
#the subseq and parent seq share data, so changes in one
|
414
|
+
#will be reflected in the other.
|
415
|
+
def subseq(*args)
|
416
|
+
assert !closed?
|
417
|
+
first,len,only1=_parse_slice_args(*args)
|
418
|
+
SubSeq.new(self,first,len)
|
419
|
+
end
|
420
|
+
|
421
|
+
#make a new sequence that reverses the order of data.
|
422
|
+
#reversed and parent sequence share data.
|
423
|
+
def reversed
|
424
|
+
Reversed.new self
|
425
|
+
end
|
426
|
+
|
427
|
+
# Close the sequence. This will also close/invalidate every child
|
428
|
+
# position or derived sequence.
|
429
|
+
def close
|
430
|
+
defined? @change_listeners and @change_listeners.each { |p|
|
431
|
+
Sequence===p and p.close
|
432
|
+
}
|
433
|
+
# this should make just about any operation fail
|
434
|
+
instance_variables.each { |v| remove_instance_variable(v) }
|
435
|
+
nil
|
436
|
+
end
|
437
|
+
# Is this sequence closed?
|
438
|
+
def closed?
|
439
|
+
instance_variables.empty?
|
440
|
+
end
|
441
|
+
|
442
|
+
# Compare +other+ (a Position or Integer) to the current position. return +1
|
443
|
+
# for the self is after, -1 for self being before, and 0 for it being at
|
444
|
+
# same location, nil (or false) if other is not a position of self.
|
445
|
+
def <=>(other)
|
446
|
+
if other.respond_to? :to_i then pos<=>other
|
447
|
+
elsif position?(other) then pos<=>other.pos
|
448
|
+
end
|
449
|
+
end
|
450
|
+
#if passed an integer arg, return a new position decreased by len. if passed
|
451
|
+
# a position, return the distance (number
|
452
|
+
# or elements) from +other+ (a #position) to +self+. This can be +, -, or 0.
|
453
|
+
def -(other)
|
454
|
+
if position?(other)
|
455
|
+
pos-other.pos
|
456
|
+
else
|
457
|
+
position(pos-other)
|
458
|
+
end
|
459
|
+
end
|
460
|
+
# Returns a new #position increased by +len+ (positive or negative).
|
461
|
+
def +(other)
|
462
|
+
if ::Sequence===other
|
463
|
+
List[self, other]
|
464
|
+
else
|
465
|
+
position(pos+other)
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
# Return a new #position for next location or +nil+ if we are at the end.
|
470
|
+
def succ
|
471
|
+
self+1 unless eof?
|
472
|
+
end
|
473
|
+
# Return a new #position for previous location or +nil+ if we are at the beginning.
|
474
|
+
def pred
|
475
|
+
self-1 unless pos.zero?
|
476
|
+
end
|
477
|
+
# Return a new #position for the beginning.
|
478
|
+
def begin
|
479
|
+
position(0)
|
480
|
+
end
|
481
|
+
# Return a new #position for the end.
|
482
|
+
def end
|
483
|
+
position(size)
|
484
|
+
end
|
485
|
+
|
486
|
+
#go to beginning
|
487
|
+
def begin!
|
488
|
+
self._pos=0
|
489
|
+
end
|
490
|
+
#go to end
|
491
|
+
def end!
|
492
|
+
self._pos=size
|
493
|
+
end
|
494
|
+
|
495
|
+
#-------------------------------------
|
496
|
+
#is position within len elements of the beginning?
|
497
|
+
def nearbegin(len,at=pos)
|
498
|
+
at<=len
|
499
|
+
end
|
500
|
+
|
501
|
+
#-------------------------------------
|
502
|
+
#is position within len elements of the end?
|
503
|
+
def nearend(len,at=pos)
|
504
|
+
at+len>=size
|
505
|
+
end
|
506
|
+
|
507
|
+
#is there any more data after the position?
|
508
|
+
def more_data?
|
509
|
+
#!eof?
|
510
|
+
(size-pos).nonzero?
|
511
|
+
end
|
512
|
+
|
513
|
+
#has any data been seen so far, or are we still at the beginning?
|
514
|
+
def was_data?
|
515
|
+
pos.nonzero?
|
516
|
+
end
|
517
|
+
|
518
|
+
|
519
|
+
# Returns the number of elements.
|
520
|
+
def size
|
521
|
+
abstract
|
522
|
+
end
|
523
|
+
def length; size end
|
524
|
+
|
525
|
+
#are we at past the end of the sequence data, with no more data ever to arrive?
|
526
|
+
def eof?
|
527
|
+
abstract
|
528
|
+
end
|
529
|
+
|
530
|
+
# is there any data in the sequence?
|
531
|
+
def empty?
|
532
|
+
size==0
|
533
|
+
end
|
534
|
+
|
535
|
+
#return first element of data
|
536
|
+
def first
|
537
|
+
slice 0
|
538
|
+
end
|
539
|
+
|
540
|
+
#return last element of data
|
541
|
+
def last
|
542
|
+
slice( -1)
|
543
|
+
end
|
544
|
+
|
545
|
+
def _normalize_pos(pos,size=self.size)
|
546
|
+
if pos<0
|
547
|
+
pos+=size
|
548
|
+
pos<0 and pos=0
|
549
|
+
elsif pos>size
|
550
|
+
pos=size
|
551
|
+
end
|
552
|
+
|
553
|
+
assert((0..size)===pos)
|
554
|
+
pos
|
555
|
+
end
|
556
|
+
|
557
|
+
def _parse_slice_args(*args)
|
558
|
+
asize=args.size
|
559
|
+
assert !closed?
|
560
|
+
size=self.size
|
561
|
+
case r=args.first
|
562
|
+
when Range:
|
563
|
+
asize==1 or raise ArgumentError
|
564
|
+
first,last=r.first,r.last
|
565
|
+
first=_normalize_pos(first,size)
|
566
|
+
last=_normalize_pos(last,size)
|
567
|
+
len=last-first
|
568
|
+
r.exclude_end? or len+=1
|
569
|
+
when Integer:
|
570
|
+
asize<=2 or raise ArgumentError
|
571
|
+
first=_normalize_pos(r,size)
|
572
|
+
len=args[1] || (only1=1)
|
573
|
+
when nil:
|
574
|
+
asize==0 or raise ArgumentError
|
575
|
+
first=nil
|
576
|
+
len=only1=1
|
577
|
+
else raise ArgumentError
|
578
|
+
end
|
579
|
+
return first,len,only1
|
580
|
+
end
|
581
|
+
|
582
|
+
# Provides random access for the sequence like what is in Array/String.
|
583
|
+
# +index+ can be +nil+ (start at the current location) or a numeric
|
584
|
+
# (for #pos=) or a range.
|
585
|
+
# +len+ can be +nil+ (get a single element) or the number of elements to
|
586
|
+
# #read (positive or negative). The sequence's position is left alone.
|
587
|
+
def slice(*args) #index|range=nil,len=nil
|
588
|
+
first,len,only1=_parse_slice_args( *args)
|
589
|
+
pos==first and first=nil
|
590
|
+
holding {
|
591
|
+
self.pos = first if first
|
592
|
+
only1 ? read1 : read(len)
|
593
|
+
}
|
594
|
+
end
|
595
|
+
def [](*a) slice(*a) end
|
596
|
+
def slice1(idx) slice(idx) end
|
597
|
+
|
598
|
+
# Like #slice except the element(s) are deleted.
|
599
|
+
def slice!(*args) #index|range, len
|
600
|
+
first,len,only1=_parse_slice_args( *args)
|
601
|
+
result=slice(first,len)
|
602
|
+
delete(first,len)
|
603
|
+
only1 ? result.first : result
|
604
|
+
end
|
605
|
+
def slice1!(idx) slice!(idx) end
|
606
|
+
|
607
|
+
# Similar to #slice except data is written. +index+ and +len+ have the
|
608
|
+
# same meaning as they do in #slice. +len+ elements are deleted and +replacedata+
|
609
|
+
# is inserted. +replacedata+ is a single item if len is ommitted and 1st param is Fixnum
|
610
|
+
def modify(*args) #index|range, len, replacedata
|
611
|
+
abstract
|
612
|
+
end
|
613
|
+
def []=(*a) modify(*a) end
|
614
|
+
|
615
|
+
def delete(*args) #index|range, len
|
616
|
+
modify( *args<<new_data)
|
617
|
+
nil
|
618
|
+
end
|
619
|
+
|
620
|
+
def insert index, replacedata
|
621
|
+
modify index,0, replacedata
|
622
|
+
end
|
623
|
+
|
624
|
+
def overwrite index, replacedata
|
625
|
+
modify index,replacedata.size, replacedata
|
626
|
+
end
|
627
|
+
|
628
|
+
def pop count=nil
|
629
|
+
slice!(count ? -count...size : -1)
|
630
|
+
end
|
631
|
+
|
632
|
+
def shift count=nil
|
633
|
+
slice!(count ? 0...count : 0 )
|
634
|
+
end
|
635
|
+
|
636
|
+
def <<(x) push x; return self end
|
637
|
+
|
638
|
+
#push/unshift in stringlike/arraylike
|
639
|
+
|
640
|
+
def append stuff
|
641
|
+
insert(size, stuff)
|
642
|
+
self
|
643
|
+
end
|
644
|
+
|
645
|
+
def prepend stuff
|
646
|
+
insert(0, stuff)
|
647
|
+
self
|
648
|
+
end
|
649
|
+
|
650
|
+
def write(data)
|
651
|
+
assert oldpos=pos
|
652
|
+
writeahead(data)
|
653
|
+
assert oldpos==pos
|
654
|
+
move data.size
|
655
|
+
end
|
656
|
+
|
657
|
+
def writeback(data)
|
658
|
+
assert oldpos=pos
|
659
|
+
writebehind(data)
|
660
|
+
assert oldpos==pos
|
661
|
+
move( -data.size)
|
662
|
+
end
|
663
|
+
|
664
|
+
def writeahead(data)
|
665
|
+
raise ArgumentError, "attempted overwrite at end of #{self}" if data.size>rest_size
|
666
|
+
overwrite(pos,data)
|
667
|
+
data.size
|
668
|
+
end
|
669
|
+
|
670
|
+
def writebehind(data)
|
671
|
+
raise ArgumentError, "attempted overwrite at begin of #{self}" if data.size>pos
|
672
|
+
overwrite(pos-data.size,data)
|
673
|
+
data.size
|
674
|
+
end
|
675
|
+
|
676
|
+
def _adjust_pos_on_change pos,first,oldsize,newsize
|
677
|
+
# assert newsize != oldsize
|
678
|
+
if pos>=first+oldsize
|
679
|
+
oldsize.zero? and pos==first and return pos
|
680
|
+
pos+newsize-oldsize
|
681
|
+
elsif pos>first
|
682
|
+
first
|
683
|
+
else pos
|
684
|
+
end
|
685
|
+
end
|
686
|
+
|
687
|
+
def on_change_notify obj
|
688
|
+
Symbol===obj and raise ArgumentError
|
689
|
+
obj.respond_to? :change_notification or raise ArgumentError
|
690
|
+
@change_listeners||=WeakRefSet[]
|
691
|
+
@change_listeners<<obj
|
692
|
+
end
|
693
|
+
|
694
|
+
def notify_change *args #seq, first, oldsize, newsize
|
695
|
+
args[0]=self
|
696
|
+
defined? @change_listeners and @change_listeners.each{|obj|
|
697
|
+
obj.change_notification(*args)
|
698
|
+
}
|
699
|
+
end
|
700
|
+
|
701
|
+
# Delete +p+ from the list of children (from #position).
|
702
|
+
# Should only be used by child Position.
|
703
|
+
def _delete_position(p) # :nodoc:
|
704
|
+
@change_listeners.delete(p)
|
705
|
+
end
|
706
|
+
|
707
|
+
# Performs each just to make this class Enumerable.
|
708
|
+
# self is returned (or the break value if the code does a break).
|
709
|
+
def each # :yield: value
|
710
|
+
holding {
|
711
|
+
begin!
|
712
|
+
until eof?
|
713
|
+
yield read1
|
714
|
+
end or self
|
715
|
+
}
|
716
|
+
end
|
717
|
+
|
718
|
+
|
719
|
+
end
|
720
|
+
|
721
|
+
require 'sequence/position'
|