sequence 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ ---
2
+ SHA512:
3
+ metadata.gz: ed3c3847f992a80b9633c47fb04f1c51bf5f99b72b67a9341e2835f31fabb4a7b233011d3fc0c9061e4d70d436d3dadca44ce5c1e5356fabb90815209aad6871
4
+ data.tar.gz: 8cfa5d3d16cf63995b39747be1a6d0228d3d5bc25a2747cf71edbdc8cfa456c60a334f714e5c70bd0ac2813347b4761b749fe13045adb5305d558d52829ece90
@@ -1,3 +1,13 @@
1
+ === 0.2.4 / 16sep2011
2
+ * 1 Major Bugfix:
3
+ * hopefully should not break StringIO anymore
4
+ * 3 Minor Bugfixes:
5
+ * increased 1.9 compatibility
6
+ * force rewritten regexps created by sequence to be binary encoded (hacky)
7
+ * in gemspec, seek for files relative to __FILE__
8
+ * 1 Minor Enhancement:
9
+ * in Sequence::List.new, coerce subseqs to Sequences if needed
10
+
1
11
  === 0.2.3 / 21dec2009
2
12
  * 3 Minor Bugfixes:
3
13
  * use Array#join instead of Array#to_s for 1.9 compatibility
data/Makefile CHANGED
@@ -1,3 +1,4 @@
1
+ #Copyright (C) 2011 Caleb Clausen
1
2
  name=Sequence
2
3
  lname=sequence
3
4
  gemname=sequence
data/README.txt CHANGED
@@ -1,19 +1,23 @@
1
1
  = sequence
2
2
 
3
- * http://sequence.rubyforge.org/
4
3
  * http://github.com/coatl/sequence
4
+ * http://sequence.rubyforge.org/
5
5
 
6
6
  == DESCRIPTION:
7
7
  Sequence provides a unified api for access to sequential data types, like
8
- Strings, Arrays, Files, IOs, and Enumerations. Each sequence encapsulates
9
- some data and a current position within it. Some operations apply to data
10
- at (or relative to) the position, others are independant of position. The
11
- api contains operations for moving the position, reading and writing data
12
- (with or without moving the position) forward or backward from the current
13
- position or anywhere, scanning for patterns (like StringScanner, but it
14
- works in Files too, among others), and saving a position that will remain
15
- valid even after data is deleted or inserted elsewhere within the
16
- sequence.
8
+ Strings, Arrays, Files, IOs, and Enumerations. This is the external
9
+ iterator pattern (ruby's usual iterators are internal). Each sequence
10
+ encapsulates some data and a current position within it. Some operations
11
+ apply to data at (or relative to) the position, others are independant
12
+ of position. The api contains operations for moving the position, and
13
+ reading and writing data (with or without moving the position) forward
14
+ or backward from the current position or anywhere.
15
+
16
+ Its perhaps most unusual feature is the ability to scan for Regexps in
17
+ not just Strings, but Files and any other type of sequence.
18
+
19
+ It can also save a position that will remain valid even after data is
20
+ deleted or inserted elsewhere within the sequence.
17
21
 
18
22
  There are also some utility classes for making sequences reversed or
19
23
  circular, turning one-way sequences into two-way, buffering, and making
@@ -25,8 +29,8 @@ very much a derivative of his.
25
29
 
26
30
 
27
31
  Sequences always fall into one of two broad categories: string-like and
28
- array- like. String-like cursors contain only character data, whereas
29
- Array-like cursors contain objects.
32
+ array- like. String-like sequences contain only character data, whereas
33
+ Array-like sequences contain objects.
30
34
 
31
35
 
32
36
 
@@ -54,7 +58,7 @@ No unit tests at all for array-like sequences
54
58
 
55
59
  == LICENSE:
56
60
 
57
- Copyright (C) 2006,2008 Caleb Clausen
61
+ Copyright (C) 2006,2008, 2011 Caleb Clausen
58
62
  Distributed under the terms of Ruby's license. (See the file COPYING.)
59
63
 
60
64
  == USING:
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2006 Caleb Clausen
1
+ # Copyright (C) 2006, 2011 Caleb Clausen
2
2
  # Distributed under the terms of Ruby's license.
3
3
 
4
4
  module Kernel
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2006,2008 Caleb Clausen
1
+ # Copyright (C) 2006,2008, 2011 Caleb Clausen
2
2
  # Distributed under the terms of Ruby's license.
3
3
  # = sequence.rb - external iterators with capabilities like a text editor cursor
4
4
  # $Id$
@@ -34,131 +34,131 @@ class Sequence
34
34
  # WeakRefSet=::Set
35
35
  # warn "warning: sequence uses Set instead of WeakRefSet; memory leak results"
36
36
 
37
- include Comparable
38
- include Enumerable
39
- extend Forwardable
40
-
41
- def initialize; abstract end
42
- def to_sequence; self end
43
-
44
- class<<self
45
- if nil #borken
46
- alias original__new new
47
- undef new #bye-bye
48
- def new(seq)
49
- case seq
50
- when File,IO,Array,String,Enumerable
51
- seq.to_sequence
37
+ #include Comparable #bah, endless trouble, no use
38
+ include Enumerable
39
+ extend Forwardable
40
+
41
+ def initialize; abstract end
42
+ def to_sequence; self end
43
+
44
+ class<<self
45
+ if nil #borken
46
+ alias original__new new
47
+ undef new #bye-bye
48
+ def new(seq)
49
+ case seq
50
+ when File,IO,Array,String,Enumerable
51
+ seq.to_sequence
52
+ else
53
+ if seq.respond_to? :to_str
54
+ seq.to_str.to_sequence
52
55
  else
53
- if seq.respond_to? :to_str
54
- seq.to_str.to_sequence
55
- else
56
- raise ArgumentError
57
- end
58
- end
59
- end
56
+ raise ArgumentError
57
+ end
60
58
  end
61
- def [](*x) new(*x) end
62
59
  end
60
+ end
61
+ def [](*x) new(*x) end
62
+ end
63
63
 
64
64
 
65
- public
66
- # read next element or nil if eof and advance position
67
- def read1
68
- (read 1)[0]
69
- end
70
-
71
- # read previous element or nil if start of input and move position back
72
- def readback1
73
- (readback 1)[0]
74
- end
75
-
76
- # read element after the pos or nil if eof, leaving position alone
77
- def readahead1
78
- slice pos
79
- end
80
-
81
- # read element before the pos or nil if start of input, leaving position alone
82
- def readbehind1
83
- slice pos-1 unless pos.zero?
84
- end
85
-
65
+ public
66
+ # read next element or nil if eof and advance position
67
+ def read1
68
+ (read 1)[0]
69
+ end
86
70
 
87
- # attempt to read up to +len+ elements. the position is left just after the data read.
88
- # #read may return less than the whole requested amount if less data than requested in
89
- #+len+ is available. This can happen at end of file or if more data is simply unavailable
90
- #currently (ie with a Sequence::IO). Don't assume that getting less than you requested
91
- #means you're at end of file; use #eof? to test for that instead.
92
- def read(len)
93
- abstract
94
- end
95
-
96
- #like read, but position is left alone.
97
- def readahead(len)
98
- holding{read(len)}
99
- end
100
-
101
- #read data behind the current position, leaving position unchanged
102
- def readbehind(len)
103
- len>pos and len=pos
104
- read move( -len)
105
- end
106
-
107
- #read data behind the current position, leaving position just before the data read.
108
- def readback(len)
109
- len>pos and len=pos
110
- readahead move( -len )
111
- end
71
+ # read previous element or nil if start of input and move position back
72
+ def readback1
73
+ (readback 1)[0]
74
+ end
112
75
 
113
- # read the remaining elements.
114
- # if reverse, read everything behind position
115
- def read!(reverse=false)
116
- if reverse
117
- readback pos
118
- else
119
- read rest_size
120
- end
121
- end
122
-
123
- def all_data
124
- holding_position{|posi|
125
- posi.begin!
126
- posi.read!
127
- }
76
+ # read element after the pos or nil if eof, leaving position alone
77
+ def readahead1
78
+ slice pos
79
+ end
80
+
81
+ # read element before the pos or nil if start of input, leaving position alone
82
+ def readbehind1
83
+ slice pos-1 unless pos.zero?
84
+ end
85
+
86
+
87
+ # attempt to read up to +len+ elements. the position is left just after the data read.
88
+ # #read may return less than the whole requested amount if less data than requested in
89
+ #+len+ is available. This can happen at end of file or if more data is simply unavailable
90
+ #currently (ie with a Sequence::IO). Don't assume that getting less than you requested
91
+ #means you're at end of file; use #eof? to test for that instead.
92
+ def read(len)
93
+ abstract
94
+ end
95
+
96
+ #like read, but position is left alone.
97
+ def readahead(len)
98
+ holding{read(len)}
99
+ end
100
+
101
+ #read data behind the current position, leaving position unchanged
102
+ def readbehind(len)
103
+ len>pos and len=pos
104
+ read move( -len)
105
+ end
106
+
107
+ #read data behind the current position, leaving position just before the data read.
108
+ def readback(len)
109
+ len>pos and len=pos
110
+ readahead move( -len )
111
+ end
112
+
113
+ # read the remaining elements.
114
+ # if reverse, read everything behind position
115
+ def read!(reverse=false)
116
+ if reverse
117
+ readback pos
118
+ else
119
+ read rest_size
128
120
  end
121
+ end
129
122
 
123
+ def all_data
124
+ holding_position{|posi|
125
+ posi.begin!
126
+ posi.read!
127
+ }
128
+ end
130
129
 
131
130
 
132
131
 
133
- #a StringScanner-like interface for pattern scanning within sequences.
134
- #See StringScanner for /(scan|skip|check)(_until)?|match\?|exist\?/.
135
- #Some notes on the implementation: scanning is all done at the current
136
- #position. Unlike StringScanner, which only scans for Regexp, this
137
- #version of #scan, etc allow more pattern types. The pattern type and how
138
- #it is treated are determined by whether the underlying data is String-
139
- #like (contains only characters/bytes), or Array-like (contains any
140
- #Object).
141
-
142
- #If String-like: scan and friends can take a Regexp, String, or
143
- #Integer (for a single char) parameter.
144
- #If Array-like: scan and friends can take a scalar matcher (something
145
- #that responds to ===). Eventually, vector matchers (Reg::Multiple)
146
- #will be supported as well here. Literal arrays, as patterns to scan for,
147
- #are not supported: too hard, and there's the quoting problem.
148
- #if you actually want to scan for items that are equal to a particular
149
- #Regexp (for example), instead of items that match it, use duck.rb
150
- #to reassign === as ==. Or, if using Reg, create a Reg::Literal by
151
- #calling #lit on the pattern (which has the same effect).
152
-
153
- #when scanning string-like sequences, anchors in Regexps are treated somewhat
154
- #specially:
155
- #when scanning forward, ^ and \A match at the current position, and
156
- #$ and \Z match at the end of the sequence.
157
- #when scanning backward, ^ and \A match at the beginning of sequence and $ and
158
- #\Z match at the current position.
159
- #^ and $ still match at begining and end of lines, as usual.
160
-
161
- #when scanning or matching backwards, ^ has some special problems:
132
+
133
+ #a StringScanner-like interface for pattern scanning within sequences.
134
+ #See StringScanner for /(scan|skip|check)(_until)?|match\?|exist\?/.
135
+ #Some notes on the implementation: scanning is all done at the current
136
+ #position. Unlike StringScanner, which only scans for Regexp, this
137
+ #version of #scan, etc allow more pattern types. The pattern type and how
138
+ #it is treated are determined by whether the underlying data is String-
139
+ #like (contains only characters/bytes), or Array-like (contains any
140
+ #Object).
141
+
142
+ #If String-like: scan and friends can take a Regexp, String, or
143
+ #Integer (for a single char) parameter.
144
+ #If Array-like: scan and friends can take a scalar matcher (something
145
+ #that responds to ===). Eventually, vector matchers (Reg::Multiple)
146
+ #will be supported as well here. Literal arrays, as patterns to scan for,
147
+ #are not supported: too hard, and there's the quoting problem.
148
+ #if you actually want to scan for items that are equal to a particular
149
+ #Regexp (for example), instead of items that match it, use duck.rb
150
+ #to reassign === as ==. Or, if using Reg, create a Reg::Literal by
151
+ #calling #lit on the pattern (which has the same effect).
152
+
153
+ #when scanning string-like sequences, anchors in Regexps are treated somewhat
154
+ #specially:
155
+ #when scanning forward, ^ and \A match at the current position, and
156
+ #$ and \Z match at the end of the sequence.
157
+ #when scanning backward, ^ and \A match at the beginning of sequence and $ and
158
+ #\Z match at the current position.
159
+ #^ and $ still match at begining and end of lines, as usual.
160
+
161
+ #when scanning or matching backwards, ^ has some special problems:
162
162
  # these won't work....
163
163
  #an anchor that might or might not match will break the current implementation in some cases...
164
164
  #regexes that should match overall even if the anchor in them doesn't...
@@ -179,321 +179,321 @@ class Sequence
179
179
 
180
180
 
181
181
  =begin can't be abstract... defined in modules
182
- def scan(pat)
183
- abstract
184
- end
182
+ def scan(pat)
183
+ abstract
184
+ end
185
185
 
186
- def scan_until(pat)
187
- abstract
188
- end
189
- def scanback(pat)
190
- abstract
191
- end
186
+ def scan_until(pat)
187
+ abstract
188
+ end
189
+ def scanback(pat)
190
+ abstract
191
+ end
192
192
 
193
- def scanback_until(pat)
194
- abstract
195
- end
196
-
197
- def match(pat)
198
- abstract
199
- end
200
-
201
- def matchback(pat)
202
- abstract
203
- end
193
+ def scanback_until(pat)
194
+ abstract
195
+ end
196
+
197
+ def match(pat)
198
+ abstract
199
+ end
200
+
201
+ def matchback(pat)
202
+ abstract
203
+ end
204
204
  =end
205
205
 
206
- def skip(pat) match= scan(pat) and match.length end
207
- def check(pat) holding{scan(pat)} end
208
- def match?(pat) holding{skip(pat)} end
209
-
210
- def skip_until(pat) match= scan_until(pat) and match.length end
211
- def check_until(pat) holding{scan_until(pat)} end
212
- def exist?(pat) holding{skip_until(pat)} end
206
+ def skip(pat) match= scan(pat) and match.length end
207
+ def check(pat) holding{scan(pat)} end
208
+ def match?(pat) holding{skip(pat)} end
213
209
 
214
- def skipback(pat) match= scanback(pat) and match.length end
215
- def checkback(pat) holding{scanback(pat)} end
216
- def matchback?(pat) holding{skipback(pat)} end
217
-
218
- def skipback_until(pat) match= scanback_until(pat) and match.length end
219
- def checkback_until(pat) holding{scanback_until(pat)} end
220
- def existback?(pat) holding{skipback_until(pat) }end
221
-
222
- def skip_literal(lits)
223
- sz=lits.size
224
- lits==readahead(sz) and move sz
225
- end
226
- alias skip_literals skip_literal
227
-
228
- def skip_until_literal(lits)
229
- sz=lits.size
230
- first=lits[0]
231
- holding?{
232
- until eof?
233
- skip_until(first)
234
- lits==readahead(sz) and break pos
235
- end
236
- }
237
- end
238
- alias skip_until_literals skip_until_literal
239
-
240
- attr_accessor :last_match
241
- attr_writer :maxmatchlen
242
-
243
- def _default_maxmatchlen; 1024 end
244
-
245
- def maxmatchlen(backwards)
246
- size=self.size
247
-
248
- list=[ _default_maxmatchlen,
249
- backwards ? pos : size-pos%size
250
- ]
251
- list.push @maxmatchlen if defined? @maxmatchlen
252
- list.min
253
- end
210
+ def skip_until(pat) match= scan_until(pat) and match.length end
211
+ def check_until(pat) holding{scan_until(pat)} end
212
+ def exist?(pat) holding{skip_until(pat)} end
254
213
 
255
- #hold current position while executing a block. The block is passed the current
256
- #sequence as its parameter. you can move the position around or call methods
257
- #like read that do it, but after the block returns, the position is reset to the
258
- #original location. The return value is the result of the block.
259
- def holding
260
- oldpos=pos
261
- begin
262
- yield self
263
- ensure
264
- self.pos=oldpos
265
- end
266
- end
214
+ def skipback(pat) match= scanback(pat) and match.length end
215
+ def checkback(pat) holding{scanback(pat)} end
216
+ def matchback?(pat) holding{skipback(pat)} end
267
217
 
268
- #like #holding, but position is reset only if block returns false or nil (or
269
- #raises an exception).
270
- def holding?
271
- oldpos=pos
272
- begin
273
- result=yield self
274
- ensure
275
- (self.pos=oldpos) unless result
276
- end
277
- end
218
+ def skipback_until(pat) match= scanback_until(pat) and match.length end
219
+ def checkback_until(pat) holding{scanback_until(pat)} end
220
+ def existback?(pat) holding{skipback_until(pat) }end
278
221
 
279
- #like #holding, but block is instance_eval'd in the sequence.
280
- def holding! &block
281
- oldpos=pos
282
- begin
283
- instance_eval self, &block
284
- ensure
285
- self.pos=oldpos
286
- end
222
+ def skip_literal(lits)
223
+ sz=lits.size
224
+ lits==readahead(sz) and move sz
225
+ end
226
+ alias skip_literals skip_literal
227
+
228
+ def skip_until_literal(lits)
229
+ sz=lits.size
230
+ first=lits[0]
231
+ holding?{
232
+ until eof?
233
+ skip_until(first)
234
+ lits==readahead(sz) and break pos
287
235
  end
236
+ }
237
+ end
238
+ alias skip_until_literals skip_until_literal
288
239
 
240
+ attr_accessor :last_match
241
+ attr_writer :maxmatchlen
289
242
 
290
- def holding_position
291
- pos=position
292
- begin
293
- result=yield self
294
- ensure
295
- self.position=pos
296
- pos.close
297
- end
298
- end
299
-
300
- def holding_position?
301
- pos=position
302
- begin
303
- result=yield self
304
- ensure
305
- self.position=pos unless result
306
- pos.close
307
- end
243
+ def _default_maxmatchlen; 1024 end
244
+
245
+ def maxmatchlen(backwards)
246
+ size=self.size
247
+
248
+ list=[ _default_maxmatchlen,
249
+ backwards ? pos : size-pos%size
250
+ ]
251
+ list.push @maxmatchlen if defined? @maxmatchlen
252
+ list.min
253
+ end
254
+
255
+ #hold current position while executing a block. The block is passed the current
256
+ #sequence as its parameter. you can move the position around or call methods
257
+ #like read that do it, but after the block returns, the position is reset to the
258
+ #original location. The return value is the result of the block.
259
+ def holding
260
+ oldpos=pos
261
+ begin
262
+ yield self
263
+ ensure
264
+ self.pos=oldpos
308
265
  end
309
-
310
- def holding_position! &block
311
- pos=position
312
- begin
313
- result=instance_eval self,&block
314
- ensure
315
- self.position=pos
316
- pos.close
317
- end
266
+ end
267
+
268
+ #like #holding, but position is reset only if block returns false or nil (or
269
+ #raises an exception).
270
+ def holding?
271
+ oldpos=pos
272
+ begin
273
+ result=yield self
274
+ ensure
275
+ (self.pos=oldpos) unless result
318
276
  end
319
-
277
+ end
320
278
 
321
- # number of elements from the beginning (0 is at the beginning).
322
- def pos()
323
- abstract
279
+ #like #holding, but block is instance_eval'd in the sequence.
280
+ def holding! &block
281
+ oldpos=pos
282
+ begin
283
+ instance_eval self, &block
284
+ ensure
285
+ self.pos=oldpos
324
286
  end
325
- def rest_size; size - pos end
287
+ end
288
+
326
289
 
327
- # this checks to see if p is a valid numeric position.
328
- def pos?(p)
329
- sz=size
330
- (-sz..sz)===p
290
+ def holding_position
291
+ pos=position
292
+ begin
293
+ result=yield self
294
+ ensure
295
+ self.position=pos
296
+ pos.close
331
297
  end
298
+ end
332
299
 
333
- # Set #pos to be +p+. When +p+ is negative, it is set from the end.
334
- def pos=(p)
335
- position?(p) and p=p.pos unless Integer===p
336
- self._pos=_normalize_pos p
300
+ def holding_position?
301
+ pos=position
302
+ begin
303
+ result=yield self
304
+ ensure
305
+ self.position=pos unless result
306
+ pos.close
337
307
  end
338
-
339
- #go to an absolute position; identical to #pos=
340
- def goto p
341
- self.pos= p
308
+ end
309
+
310
+ def holding_position! &block
311
+ pos=position
312
+ begin
313
+ result=instance_eval self,&block
314
+ ensure
315
+ self.position=pos
316
+ pos.close
342
317
  end
343
-
344
- def _pos=(p)
318
+ end
319
+
320
+
321
+ # number of elements from the beginning (0 is at the beginning).
322
+ def pos()
345
323
  abstract
346
- end
347
- # move position +len+ elements, relative to the current position.
348
- # A negative +len+ will go in reverse.
349
- # The (positive) amount actually moved is returned (<+len+ if reached beginning/end).
350
- def move(len)
351
- oldpos=pos
352
- newpos=oldpos+len
353
- newpos<0 and newpos=0
354
- goto newpos
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
324
+ end
325
+ def rest_size; size - pos end
363
326
 
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
327
+ # this checks to see if p is a valid numeric position.
328
+ def pos?(p)
329
+ sz=size
330
+ (-sz..sz)===p
331
+ end
386
332
 
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
333
+ # Set #pos to be +p+. When +p+ is negative, it is set from the end.
334
+ def pos=(p)
335
+ position?(p) and p=p.pos unless Integer===p
336
+ self._pos=_normalize_pos p
337
+ end
401
338
 
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
339
+ #go to an absolute position; identical to #pos=
340
+ def goto p
341
+ self.pos= p
342
+ end
441
343
 
442
- =begin who needs it?
443
- # Compare +other+ (a Position or Integer) to the current position. return +1
444
- # for the self is after, -1 for self being before, and 0 for it being at
445
- # same location, nil (or false) if other is not a position of self.
446
- def <=>(other)
447
- if other.respond_to? :to_i then pos<=>other
448
- elsif position?(other) then pos<=>other.pos
449
- end
450
- end
451
- =end
344
+ def _pos=(p)
345
+ abstract
346
+ end
347
+ # move position +len+ elements, relative to the current position.
348
+ # A negative +len+ will go in reverse.
349
+ # The (positive) amount actually moved is returned (<+len+ if reached beginning/end).
350
+ def move(len)
351
+ oldpos=pos
352
+ newpos=oldpos+len
353
+ newpos<0 and newpos=0
354
+ goto newpos
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
452
363
 
453
- #if passed an integer arg, return a new position decreased by len. if passed
454
- # a position, return the distance (number
455
- # or elements) from +other+ (a #position) to +self+. This can be +, -, or 0.
456
- def -(other)
457
- if position?(other)
458
- pos-other.pos
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
459
371
  else
460
- position(pos-other)
372
+ if (value = value[0]).nil?
373
+ defined?(@prop) &&@prop&&remove_instance_variable(:@prop)
374
+ else
375
+ (@prop||={}).replace(value)
376
+ end
461
377
  end
462
- end
463
- # Returns a new #position increased by +len+ (positive or negative).
464
- def +(other)
465
- if ::Sequence===other
466
- List[self, other]
378
+ else
379
+ if value.size.zero?
380
+ defined?(@prop) &&@prop&&@prop[name]
467
381
  else
468
- position(pos+other)
382
+ (@prop||={})[name] = value[0]
469
383
  end
470
384
  end
385
+ end
471
386
 
472
- # Return a new #position for next location or +nil+ if we are at the end.
473
- def succ
474
- self+1 unless eof?
475
- end
476
- # Return a new #position for previous location or +nil+ if we are at the beginning.
477
- def pred
478
- self-1 unless pos.zero?
479
- end
480
- # Return a new #position for the beginning.
481
- def begin
482
- position(0)
483
- end
484
- # Return a new #position for the end.
485
- def end
486
- position(size)
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
487
409
  end
488
-
489
- #go to beginning
490
- def begin!
491
- self._pos=0
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
+ =begin who needs it?
443
+ # Compare +other+ (a Position or Integer) to the current position. return +1
444
+ # for the self is after, -1 for self being before, and 0 for it being at
445
+ # same location, nil (or false) if other is not a position of self.
446
+ def <=>(other)
447
+ if other.respond_to? :to_i then pos<=>other
448
+ elsif position?(other) then pos<=>other.pos
449
+ end
450
+ end
451
+ =end
452
+
453
+ #if passed an integer arg, return a new position decreased by len. if passed
454
+ # a position, return the distance (number
455
+ # or elements) from +other+ (a #position) to +self+. This can be +, -, or 0.
456
+ def -(other)
457
+ if position?(other)
458
+ pos-other.pos
459
+ else
460
+ position(pos-other)
492
461
  end
493
- #go to end
494
- def end!
495
- self._pos=size
462
+ end
463
+ # Returns a new #position increased by +len+ (positive or negative).
464
+ def +(other)
465
+ if ::Sequence===other
466
+ List[self, other]
467
+ else
468
+ position(pos+other)
496
469
  end
470
+ end
471
+
472
+ # Return a new #position for next location or +nil+ if we are at the end.
473
+ def succ
474
+ self+1 unless eof?
475
+ end
476
+ # Return a new #position for previous location or +nil+ if we are at the beginning.
477
+ def pred
478
+ self-1 unless pos.zero?
479
+ end
480
+ # Return a new #position for the beginning.
481
+ def begin
482
+ position(0)
483
+ end
484
+ # Return a new #position for the end.
485
+ def end
486
+ position(size)
487
+ end
488
+
489
+ #go to beginning
490
+ def begin!
491
+ self._pos=0
492
+ end
493
+ #go to end
494
+ def end!
495
+ self._pos=size
496
+ end
497
497
 
498
498
  #-------------------------------------
499
499
  #is position within len elements of the beginning?
@@ -507,216 +507,216 @@ class Sequence
507
507
  at+len>=size
508
508
  end
509
509
 
510
- #is there any more data after the position?
511
- def more_data?
512
- #!eof?
513
- (size-pos).nonzero?
514
- end
515
-
516
- #has any data been seen so far, or are we still at the beginning?
517
- def was_data?
518
- pos.nonzero?
519
- end
520
-
510
+ #is there any more data after the position?
511
+ def more_data?
512
+ #!eof?
513
+ (size-pos).nonzero?
514
+ end
521
515
 
522
- # Returns the number of elements.
523
- def size
524
- abstract
525
- end
526
- def length; size end
516
+ #has any data been seen so far, or are we still at the beginning?
517
+ def was_data?
518
+ pos.nonzero?
519
+ end
527
520
 
528
- #are we at past the end of the sequence data, with no more data ever to arrive?
529
- def eof?
530
- abstract
531
- end
532
521
 
533
- # is there any data in the sequence?
534
- def empty?
535
- size==0
536
- end
522
+ # Returns the number of elements.
523
+ def size
524
+ abstract
525
+ end
526
+ def length; size end
537
527
 
538
- #return first element of data
539
- def first
540
- slice 0
541
- end
542
-
543
- #return last element of data
544
- def last
545
- slice( -1)
546
- end
528
+ #are we at past the end of the sequence data, with no more data ever to arrive?
529
+ def eof?
530
+ abstract
531
+ end
547
532
 
548
- def _normalize_pos(pos,size=self.size)
549
- if pos<0
550
- pos+=size
551
- pos<0 and pos=0
552
- elsif pos>size
553
- pos=size
554
- end
555
-
556
- assert((0..size)===pos)
557
- pos
558
- end
533
+ # is there any data in the sequence?
534
+ def empty?
535
+ size==0
536
+ end
559
537
 
560
- def _parse_slice_args(*args)
561
- asize=args.size
562
- assert !closed?
563
- size=self.size
564
- case r=args.first
565
- when Range
566
- asize==1 or raise ArgumentError
567
- first,last=r.first,r.last
568
- first=_normalize_pos(first,size)
569
- last=_normalize_pos(last,size)
570
- len=last-first
571
- r.exclude_end? or len+=1
572
- when Integer
573
- asize<=2 or raise ArgumentError
574
- first=_normalize_pos(r,size)
575
- len=args[1] || (only1=1)
576
- when nil
577
- asize==0 or raise ArgumentError
578
- first=nil
579
- len=only1=1
580
- else raise ArgumentError
581
- end
582
- return first,len,only1
583
- end
538
+ #return first element of data
539
+ def first
540
+ slice 0
541
+ end
584
542
 
585
- # Provides random access for the sequence like what is in Array/String.
586
- # +index+ can be +nil+ (start at the current location) or a numeric
587
- # (for #pos=) or a range.
588
- # +len+ can be +nil+ (get a single element) or the number of elements to
589
- # #read (positive or negative). The sequence's position is left alone.
590
- def slice(*args) #index|range=nil,len=nil
591
- first,len,only1=_parse_slice_args( *args)
592
- pos==first and first=nil
593
- holding {
594
- self.pos = first if first
595
- only1 ? read1 : read(len)
596
- }
597
- end
598
- def [](*a) slice(*a) end
599
- def slice1(idx) slice(idx) end
600
-
601
- # Like #slice except the element(s) are deleted.
602
- def slice!(*args) #index|range, len
603
- first,len,only1=_parse_slice_args( *args)
604
- result=slice(first,len)
605
- delete(first,len)
606
- only1 ? result.first : result
607
- end
608
- def slice1!(idx) slice!(idx) end
543
+ #return last element of data
544
+ def last
545
+ slice( -1)
546
+ end
609
547
 
610
- # Similar to #slice except data is written. +index+ and +len+ have the
611
- # same meaning as they do in #slice. +len+ elements are deleted and +replacedata+
612
- # is inserted. +replacedata+ is a single item if len is ommitted and 1st param is Fixnum
613
- def modify(*args) #index|range, len, replacedata
614
- abstract
615
- end
616
- def []=(*a) modify(*a) end
617
-
618
- def delete(*args) #index|range, len
619
- modify( *args<<new_data)
620
- nil
621
- end
622
-
623
- def insert index, replacedata
624
- modify index,0, replacedata
625
- end
626
-
627
- def overwrite index, replacedata
628
- modify index,replacedata.size, replacedata
629
- end
630
-
631
- def pop count=nil
632
- slice!(count ? -count...size : -1)
633
- end
634
-
635
- def shift count=nil
636
- slice!(count ? 0...count : 0 )
637
- end
638
-
639
- def <<(x) push x; return self end
640
-
641
- #push/unshift in stringlike/arraylike
642
-
643
- def append stuff
644
- insert(size, stuff)
645
- self
548
+ def _normalize_pos(pos,size=self.size)
549
+ if pos<0
550
+ pos+=size
551
+ pos<0 and pos=0
552
+ elsif pos>size
553
+ pos=size
646
554
  end
647
-
648
- def prepend stuff
649
- insert(0, stuff)
650
- self
651
- end
652
-
653
- def write(data)
654
- assert oldpos=pos
655
- writeahead(data)
656
- assert oldpos==pos
657
- move data.size
658
- end
659
-
660
- def writeback(data)
661
- assert oldpos=pos
662
- writebehind(data)
663
- assert oldpos==pos
664
- move( -data.size)
665
- end
666
-
667
- def writeahead(data)
668
- raise ArgumentError, "attempted overwrite at end of #{self}" if data.size>rest_size
669
- overwrite(pos,data)
670
- data.size
671
- end
672
-
673
- def writebehind(data)
674
- raise ArgumentError, "attempted overwrite at begin of #{self}" if data.size>pos
675
- overwrite(pos-data.size,data)
676
- data.size
677
- end
678
-
679
- def _adjust_pos_on_change pos,first,oldsize,newsize
555
+
556
+ assert((0..size)===pos)
557
+ pos
558
+ end
559
+
560
+ def _parse_slice_args(*args)
561
+ asize=args.size
562
+ assert !closed?
563
+ size=self.size
564
+ case r=args.first
565
+ when Range
566
+ asize==1 or raise ArgumentError
567
+ first,last=r.first,r.last
568
+ first=_normalize_pos(first,size)
569
+ last=_normalize_pos(last,size)
570
+ len=last-first
571
+ r.exclude_end? or len+=1
572
+ when Integer
573
+ asize<=2 or raise ArgumentError
574
+ first=_normalize_pos(r,size)
575
+ len=args[1] || (only1=1)
576
+ when nil
577
+ asize==0 or raise ArgumentError
578
+ first=nil
579
+ len=only1=1
580
+ else raise ArgumentError
581
+ end
582
+ return first,len,only1
583
+ end
584
+
585
+ # Provides random access for the sequence like what is in Array/String.
586
+ # +index+ can be +nil+ (start at the current location) or a numeric
587
+ # (for #pos=) or a range.
588
+ # +len+ can be +nil+ (get a single element) or the number of elements to
589
+ # #read (positive or negative). The sequence's position is left alone.
590
+ def slice(*args) #index|range=nil,len=nil
591
+ first,len,only1=_parse_slice_args( *args)
592
+ pos==first and first=nil
593
+ holding {
594
+ self.pos = first if first
595
+ only1 ? read1 : read(len)
596
+ }
597
+ end
598
+ def [](*a) slice(*a) end
599
+ def slice1(idx) slice(idx) end
600
+
601
+ # Like #slice except the element(s) are deleted.
602
+ def slice!(*args) #index|range, len
603
+ first,len,only1=_parse_slice_args( *args)
604
+ result=slice(first,len)
605
+ delete(first,len)
606
+ only1 ? result.first : result
607
+ end
608
+ def slice1!(idx) slice!(idx) end
609
+
610
+ # Similar to #slice except data is written. +index+ and +len+ have the
611
+ # same meaning as they do in #slice. +len+ elements are deleted and +replacedata+
612
+ # is inserted. +replacedata+ is a single item if len is ommitted and 1st param is Fixnum
613
+ def modify(*args) #index|range, len, replacedata
614
+ abstract
615
+ end
616
+ def []=(*a) modify(*a) end
617
+
618
+ def delete(*args) #index|range, len
619
+ modify( *args<<new_data)
620
+ nil
621
+ end
622
+
623
+ def insert index, replacedata
624
+ modify index,0, replacedata
625
+ end
626
+
627
+ def overwrite index, replacedata
628
+ modify index,replacedata.size, replacedata
629
+ end
630
+
631
+ def pop count=nil
632
+ slice!(count ? -count...size : -1)
633
+ end
634
+
635
+ def shift count=nil
636
+ slice!(count ? 0...count : 0 )
637
+ end
638
+
639
+ def <<(x) push x; return self end
640
+
641
+ #push/unshift in stringlike/arraylike
642
+
643
+ def append stuff
644
+ insert(size, stuff)
645
+ self
646
+ end
647
+
648
+ def prepend stuff
649
+ insert(0, stuff)
650
+ self
651
+ end
652
+
653
+ def write(data)
654
+ assert oldpos=pos
655
+ writeahead(data)
656
+ assert oldpos==pos
657
+ move data.size
658
+ end
659
+
660
+ def writeback(data)
661
+ assert oldpos=pos
662
+ writebehind(data)
663
+ assert oldpos==pos
664
+ move( -data.size)
665
+ end
666
+
667
+ def writeahead(data)
668
+ raise ArgumentError, "attempted overwrite at end of #{self}" if data.size>rest_size
669
+ overwrite(pos,data)
670
+ data.size
671
+ end
672
+
673
+ def writebehind(data)
674
+ raise ArgumentError, "attempted overwrite at begin of #{self}" if data.size>pos
675
+ overwrite(pos-data.size,data)
676
+ data.size
677
+ end
678
+
679
+ def _adjust_pos_on_change pos,first,oldsize,newsize
680
680
  # assert newsize != oldsize
681
- if pos>=first+oldsize
682
- oldsize.zero? and pos==first and return pos
683
- pos+newsize-oldsize
684
- elsif pos>first
685
- first
686
- else pos
687
- end
681
+ if pos>=first+oldsize
682
+ oldsize.zero? and pos==first and return pos
683
+ pos+newsize-oldsize
684
+ elsif pos>first
685
+ first
686
+ else pos
688
687
  end
688
+ end
689
689
 
690
- def on_change_notify obj
691
- Symbol===obj and raise ArgumentError
692
- obj.respond_to? :change_notification or raise ArgumentError
693
- @change_listeners||=WeakRefSet[]
694
- @change_listeners<<obj
695
- end
696
-
697
- def notify_change *args #seq, first, oldsize, newsize
698
- args[0]=self
699
- defined? @change_listeners and @change_listeners.each{|obj|
700
- obj.change_notification(*args)
701
- }
702
- end
703
-
704
- # Delete +p+ from the list of children (from #position).
705
- # Should only be used by child Position.
706
- def _delete_position(p) # :nodoc:
707
- @change_listeners.delete(p)
708
- end
690
+ def on_change_notify obj
691
+ Symbol===obj and raise ArgumentError
692
+ obj.respond_to? :change_notification or raise ArgumentError
693
+ @change_listeners||=WeakRefSet[]
694
+ @change_listeners<<obj
695
+ end
709
696
 
710
- # Performs each just to make this class Enumerable.
711
- # self is returned (or the break value if the code does a break).
712
- def each # :yield: value
713
- holding {
714
- begin!
715
- until eof?
716
- yield read1
717
- end or self
718
- }
719
- end
697
+ def notify_change *args #seq, first, oldsize, newsize
698
+ args[0]=self
699
+ defined? @change_listeners and @change_listeners.each{|obj|
700
+ obj.change_notification(*args)
701
+ }
702
+ end
703
+
704
+ # Delete +p+ from the list of children (from #position).
705
+ # Should only be used by child Position.
706
+ def _delete_position(p) # :nodoc:
707
+ @change_listeners.delete(p)
708
+ end
709
+
710
+ # Performs each just to make this class Enumerable.
711
+ # self is returned (or the break value if the code does a break).
712
+ def each # :yield: value
713
+ holding {
714
+ begin!
715
+ until eof?
716
+ yield read1
717
+ end or self
718
+ }
719
+ end
720
720
 
721
721
 
722
722
  end