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.
@@ -0,0 +1,102 @@
1
+ # Copyright (C) 2006 Caleb Clausen
2
+ # Distributed under the terms of Ruby's license.
3
+ require 'sequence/stringlike'
4
+ require 'fcntl'
5
+
6
+ class Sequence
7
+
8
+ #external iterator over data in an general IO object (a pipe, console, socket, serial port, or the like).
9
+ #For Files, please use Sequence::File instead, as it is more capable.
10
+ #This Sequence class can only go forward, and can only read, it cannot write to the data stream.
11
+ #Thus many of Sequence's usual methods will not work with this class.
12
+ #At the moment, this even includes #scan and friends, tho I will try to make those work somewhat.
13
+ #Also note that this is one of the few Sequence classes that might return less that the amount asked
14
+ #for in a read, even if not at the end of file.
15
+ #Due to use of nonblocking io, this might not work on windows.
16
+ #The value of #size in this sequence continually increases over its lifetime, and it isn't possible to
17
+ #know the final value beforehand. Likewise, #eof? may return false even tho it's destined to return
18
+ #true at the same position. This is because the 'other end' may not have closed the IO, even if there's
19
+ #no more data to send.
20
+ #
21
+ #if you need to be able to scan forward and back, consider wrapping the IO in a Buffered or Shifting
22
+ #Sequence.
23
+ class IO < Sequence
24
+ include StringLike
25
+ def initialize(io)
26
+ @io=io
27
+ @pos=0
28
+
29
+ @io.fcntl(::Fcntl::F_SETFL, ::Fcntl::O_NONBLOCK)
30
+ #I gather this won't work on windows....
31
+
32
+ @fragment=''
33
+ end
34
+
35
+ attr :pos
36
+
37
+ undef pos=, _pos=, position, position?, holding, holding?, holding!, readback, readback1
38
+ undef readahead, readbehind, readahead1, readbehind1, write#, write1
39
+ undef goto, move, move!, subseq, reversed, <=>, +, -, succ, pred, begin, end
40
+ undef begin!, end!, first, last, slice, [], modify, []=
41
+
42
+
43
+ def size
44
+ #refill fragment if needed
45
+ @fragment=@io.sysread(4096) if @fragment.empty?
46
+
47
+ return @pos+@fragment.size
48
+ end
49
+
50
+ def more_data?
51
+ #refill fragment if needed
52
+ @fragment=@io.sysread(4096) if @fragment.empty?
53
+
54
+ return !eof
55
+ end
56
+
57
+ def eof?;
58
+ @fragment.empty? and #need to be at buffer end
59
+ @io.eof?
60
+ end
61
+
62
+ def read len
63
+ if len<= @fragment.size
64
+ @pos+=len
65
+ @fragment.slice! 0,len
66
+ else
67
+ result=@fragment
68
+ len-=@fragment.size
69
+
70
+ readlen=len
71
+ rem=len%4096
72
+ rem.nonzero? and readlen+=4096-rem
73
+
74
+ @fragment=@io.sysread(readlen)
75
+ result+=@fragment.slice!(0,len)
76
+ @pos+=result.size
77
+ result
78
+ end
79
+ end
80
+
81
+ def match pat
82
+ @fragment.size>=scanbuflen or @fragment<<@io.sysread(4096)
83
+ result=@fragment.match(pat)
84
+ result if result.begin(0).zero?
85
+ end
86
+
87
+ def _pos=newpos
88
+ newpos<pos and raise ArgumentError
89
+ if newpos<=@pos+@fragment.size
90
+ len=newpos-@pos
91
+ @fragment.slice!(0,len)
92
+ @pos=newpos
93
+ else
94
+ len=newpos-(@pos+@fragment.size)
95
+ len > 10*4096 and raise ArgumentError
96
+ @fragment=''
97
+ tossit=@io.sysread(len)
98
+ @pos=newpos-(len-tossit.size)
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,292 @@
1
+ # Copyright (C) 2006 Caleb Clausen
2
+ # Distributed under the terms of Ruby's license.
3
+ class Sequence
4
+ class Circular < Sequence; end
5
+ class List < Sequence
6
+ def initialize(seqs)
7
+ seqs.empty? and raise ArgumentError
8
+ @list=seqs
9
+ @current_idx=0
10
+ @pos=0
11
+ @start_pos=[0]
12
+ _rebuild_idxs
13
+
14
+ @list=@list.inject([]){|li,seq|
15
+ Circular===seq and raise 'no circular seqs in lists'
16
+ Sequence===seq or raise ArgumentError
17
+ if List===seq then li+seq.list else li<<seq end
18
+ }
19
+ @list.each{|seq| seq.on_change_notify(self) }
20
+
21
+ extend seqs.first.like
22
+ end
23
+
24
+ def change_notification(cu,first,oldsize,newsize)
25
+ idx=@list.each_with_index{|item,i| cu.equal? item and break i}
26
+ diff=newsize-oldsize
27
+ (idx+1...@start_pos.size).each{|i|
28
+ @start_pos[i]+=diff
29
+ }
30
+ @pos=_adjust_pos_on_change(@pos, first,oldsize,newsize)
31
+ @current_idx=_lookup_idx @pos
32
+ notify_change(self,@start_pos[idx]+first,oldsize,newsize)
33
+ end
34
+
35
+ def _rebuild_idxs(start=1)
36
+ seed=@start_pos[0...start]
37
+ seed.empty? and seed=[0]
38
+ start==0 and start=1
39
+ @start_pos=(start..@list.size).inject(seed){|arr,i|
40
+ arr<<arr.last+@list[i-1].size
41
+ }
42
+ #@start_pos.pop
43
+
44
+ #maybe update @current_idx too?
45
+ end
46
+ =begin
47
+ def _lookup_idx(pos)
48
+ low=0;high=@start_pos.size-1
49
+ while(high>low+1)
50
+ mid=(low+high)/2
51
+ this_pos,next_pos=*@start_pos[mid,2]
52
+ if pos<this_pos
53
+ high=mid # - 1 ??
54
+ elsif pos<next_pos
55
+ return mid
56
+ elsif pos==next_pos
57
+ return mid+1
58
+ else
59
+ low=mid + 1
60
+ end
61
+ end
62
+ low
63
+ end
64
+ =end
65
+ def _lookup_idx(pos)
66
+ pos==size and return @list.size-1
67
+ assert((0...size)===pos)
68
+ assert @start_pos.size==@list.size+1
69
+ low=0;high=@start_pos.size-1
70
+ assert @start_pos[low]<=pos
71
+ assert @start_pos[high]>pos
72
+ while(high>low+1)
73
+ assert @start_pos[low]<=pos
74
+ assert @start_pos[high]>pos
75
+ mid=(low+high)/2
76
+ case pos<=>@start_pos[mid]
77
+ when -1: high=mid
78
+ when 0: break low=mid
79
+ when 1: low=mid
80
+ end
81
+ end
82
+ assert @start_pos[low]<=pos
83
+ assert @start_pos[low+1]>pos
84
+ low
85
+ end
86
+
87
+ def readahead(len)
88
+ idx=_lookup_idx(pos)
89
+ result=@list[idx][pos-@start_pos[idx],len] || new_data
90
+ len-=result.size
91
+ assert len>=0
92
+ i=nil
93
+ (idx+1).upto(@list.size-1){|i|
94
+ break(result+=@list[i][0,len]) if len<@list[i].size
95
+ result+=@list[i].all_data
96
+ len-=@list[i].size
97
+ }
98
+ result
99
+ end
100
+
101
+ def read(len)
102
+ result=readahead(len)
103
+ move result.size
104
+ result
105
+ end
106
+
107
+ def holding
108
+ oldpos,oldidx=@pos,@current_idx
109
+ begin
110
+ yield self
111
+ ensure
112
+ @pos,@current_idx=oldpos,oldidx
113
+ end
114
+ end
115
+
116
+ #like #holding, but position is reset only if block returns false or nil (or
117
+ #raises an exception).
118
+ def holding?
119
+ oldpos,oldidx=@pos,@current_idx
120
+ begin
121
+ result=yield self
122
+ ensure
123
+ (@pos,@current_idx=oldpos,oldidx) unless result
124
+ end
125
+ end
126
+
127
+ #like #holding, but block is instance_eval'd in the seq.
128
+ def holding! &block
129
+ oldpos,oldidx=@pos,@current_idx
130
+ begin
131
+ instance_eval self, &block
132
+ ensure
133
+ @pos,@current_idx=oldpos,oldidx
134
+ end
135
+ end
136
+
137
+ attr :pos
138
+
139
+ def _pos=pos
140
+ @pos=pos
141
+ assert((0..size)===pos)
142
+ @current_idx= _lookup_idx(pos)
143
+ end
144
+
145
+ def size
146
+ @start_pos.last
147
+ end
148
+
149
+ def eof?
150
+ @pos>=size
151
+ end
152
+
153
+ def + other
154
+ return super unless ::Sequence===other
155
+ return List[*@list+[other]]
156
+ end
157
+
158
+ def _fragment_discard_after(pos)
159
+ idx=_lookup_idx(pos)
160
+ pos-=@start_pos[idx]
161
+ @list[idx]=@list[idx].subseq(0...pos)
162
+ return idx
163
+ end
164
+
165
+ def _fragment_discard_before(pos)
166
+ idx=_lookup_idx(pos)
167
+ pos-=@start_pos[idx]
168
+ @list[idx]=@list[idx].subseq(pos..-1)
169
+ return idx
170
+ end
171
+
172
+
173
+ Overlaid =proc do
174
+ class<<self
175
+ def overlaid?; true end
176
+ def subseq(*)
177
+ result=super
178
+ result.instance_eval(& Overlaid)
179
+ end
180
+ end
181
+ end
182
+
183
+ def modify(*args)
184
+ result=repldata=args.pop
185
+
186
+ repllen=repldata.size
187
+ # unless repldata.empty?
188
+ repldata=repldata.dup.to_sequence
189
+
190
+
191
+ #mark replacement data as overlaid
192
+ repldata.instance_eval(&Overlaid)
193
+ # end
194
+ replseqs=[repldata]
195
+
196
+ first,len,only1=_parse_slice_args(*args)
197
+
198
+ f_idx=first.zero?? 0 : _lookup_idx(first-1)
199
+ l_idx=_lookup_idx(first+len)
200
+
201
+ assert f_idx <= l_idx
202
+
203
+ fragrest_f=first-@start_pos[i=_lookup_idx(first)]
204
+ fragrest_l=first+len-@start_pos[l_idx]
205
+
206
+ #@list[i] can be nil here... maybe because i==@list.size?
207
+ assert fragrest_f < @list[i].size
208
+ assert fragrest_l <= @list[l_idx].size unless l_idx == @list.size and fragrest_l.zero?
209
+
210
+ #merge replacement data with adjacent seq(s) if also overlaid
211
+ if fragrest_f.nonzero?
212
+ replseqs.unshift( item=@list[i].subseq(0...fragrest_f) )
213
+ if item.respond_to? :overlaid?
214
+ repldata.prepend item.all_data
215
+ replseqs.shift
216
+ end
217
+ end
218
+
219
+ if fragrest_l.nonzero?
220
+ replseqs.push( item=@list[l_idx].subseq(fragrest_l..-1) )
221
+ if item.respond_to? :overlaid?
222
+ repldata.append item.all_data
223
+ replseqs.pop
224
+ end
225
+ end
226
+
227
+
228
+ replseqs.delete_if{|cu| cu.empty? }
229
+
230
+ #now remove those elements in between and
231
+ #insert replacement data at the same point
232
+ assert f_idx >= 0
233
+ assert l_idx >= 0
234
+ assert f_idx < @list.size
235
+ assert l_idx <= @list.size
236
+ assert f_idx <= l_idx
237
+ @list[i...l_idx]=replseqs
238
+ #base=f_idx.zero?? 0 : @start_idx[f_idx-1]
239
+ #@start_idx[f_idx...l_idx]=[base+repldata.size]
240
+
241
+
242
+
243
+
244
+ #rebuild indeces after altered part
245
+ _rebuild_idxs(f_idx)
246
+ @pos=_adjust_pos_on_change(@pos, first,len,result.size)
247
+ @current_idx=_lookup_idx @pos
248
+
249
+ notify_change(self,first,len,repllen)
250
+ result
251
+ end
252
+
253
+
254
+ def append(data)
255
+ if @list.last.overlaid?
256
+ @list.last.append data
257
+ return self
258
+ end
259
+ data=data.dup.to_sequence
260
+ data.instance_eval(&Overlaid)
261
+ @list<<data
262
+ @start_pos<<@start_pos.last+data.size
263
+ notify_change(self,@start_pos[-2], 0, data.size)
264
+ self
265
+ end
266
+ def prepend(data)
267
+ if @list.first.overlaid?
268
+ @list.first.prepend data
269
+ return self
270
+ end
271
+ data=data.dup.to_sequence
272
+ data.instance_eval(&Overlaid)
273
+ @list[0,0]=data
274
+
275
+ #insert data.size into beginning of @start_pos
276
+ sz=data.size
277
+ @start_pos=@start_pos.map{|n| n+sz}
278
+ @start_pos[0,0]=0
279
+
280
+ notify_change(self,0, 0, data.size)
281
+ self
282
+ end
283
+ end
284
+
285
+ # class Indexed
286
+ # def subseq(*args)
287
+ # result=super
288
+ # result and respond_to? :overlaid? and def result.overlaid?; true end
289
+ # result
290
+ # end
291
+ # end
292
+ end
@@ -0,0 +1,38 @@
1
+ # Copyright (C) 2006 Caleb Clausen
2
+ # Distributed under the terms of Ruby's license.
3
+ require 'sequence'
4
+ require 'sequence/indexed'
5
+
6
+ class Sequence
7
+ class OfHash < OfArray
8
+ def initialize(hash,exceptions=[],include_default=false,modifiable=false)
9
+ @hash=hash
10
+ hash=hash.dup
11
+ exceptions.each{|exc| hash.delete exc }
12
+ @data=hash.inject([]){|l,pair| l+pair}
13
+ @data<<hash.default if include_default
14
+ @data.freeze unless modifiable
15
+ end
16
+
17
+ def modify(*args)
18
+ repldata=args.pop
19
+ start,len,only1=_parse_slice_args(*args)
20
+ len==1 or raise "scalar modifications to hashes only!"
21
+ if @data.size.%(2).nonzero? and @data.size.-(1)==start
22
+ @hash.default=repldata.first
23
+ elsif start.%(2).zero? #key
24
+ @hash[repldata.first]=@hash.delete @data[start]
25
+ else #value
26
+ @hash[@data[start-1]]=repldata.first
27
+ end
28
+ @data[first]=repldata.first
29
+ repldata
30
+ end
31
+ end
32
+ end
33
+
34
+ class Hash
35
+ def to_sequence(include_default=false)
36
+ Sequence::OfHash.new(self,include_default)
37
+ end
38
+ end
@@ -0,0 +1,29 @@
1
+ # Copyright (C) 2006 Caleb Clausen
2
+ # Distributed under the terms of Ruby's license.
3
+ require 'sequence'
4
+ require 'sequence/indexed'
5
+
6
+
7
+ class Sequence
8
+ class OfObjectIvars < OfArray
9
+ def initialize(obj,exceptions=[],modifiable=false)
10
+ @obj=obj
11
+ ivars=obj.instance_variables - exceptions
12
+ @data=ivars.inject([]){|l,name| l.push name,obj.instance_variable_get(name)}
13
+ @data.freeze unless modifiable
14
+ end
15
+
16
+ def modify(*args)
17
+ repldata=args.pop
18
+ start,len,only1=_parse_slice_args(*args)
19
+ len==1 or raise "scalar modifications to objects only!"
20
+ assert start.%(2).nonzero? #not a name
21
+
22
+ @obj.instance_variable_set(@data[start-1],repldata.first)
23
+ @data[start]=repldata.first
24
+ repldata
25
+
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,87 @@
1
+ # Copyright (C) 2006 Caleb Clausen
2
+ # Distributed under the terms of Ruby's license.
3
+ require 'sequence'
4
+ require 'sequence/indexed'
5
+ reuqire 'sequence/functional'
6
+ require 'set'
7
+
8
+
9
+ class Sequence
10
+ class OfObjectMethods < OfArray
11
+ def initialize obj, exceptions=[], extras=[] #,modifiable=false
12
+ @obj=obj
13
+ methods=obj.public_methods.-(Functional.nonfunctions_of(obj)).-(exceptions)
14
+ methods.reject!{|name| /[!=]$/==name }
15
+ @data.concat extras.map{|x| Symbol===x ? x.to_s : x }
16
+ @data=methods.inject([]){|l,name| l+[name,nil]}
17
+ #@data.freeze unless modifiable
18
+
19
+ @is_exception=0
20
+ @is_reified=0
21
+ end
22
+
23
+ def is_exception?(index) @is_exception&(1<<index/2) == 0 end
24
+ def set_exception!(index) @is_exception|= 1<<index/2 end
25
+
26
+ def is_reified?(index) @is_reified&(1<<index/2) == 0 end
27
+ def set_reified!(index) @is_reified|= 1<<index/2 end
28
+
29
+ def modify(*args)
30
+ repldata=args.pop
31
+ start,len,only1=_parse_slice_args(*args)
32
+ len==1 or raise ArgumentError,"scalar modifications to objects only!"
33
+ start.%(2).nonzero? or raise ArgumentError, "OfObjectMethods#modify will not change method names!"
34
+
35
+ if Array===@data[start-1]
36
+ if @data[start-1].first.to_s=='[]'
37
+ @obj.send(:[]=, @data[1..-1],repldata)
38
+ else raise ArgumentError, "trying to call settor with extra args"
39
+ end
40
+ else
41
+
42
+ @obj.send(@data[start-1]+"=",repldata.first)
43
+ @data[start]=repldata.first
44
+ end
45
+ repldata
46
+ end
47
+
48
+ def reify_from_index(i)
49
+ #if the call raises an exception, store the exception instead of the result
50
+ #and remember (in @is_exception) that this particular result is an exception
51
+ if is_reified? i
52
+ raise @data[i+1] if is_exception? i
53
+ return
54
+ end
55
+ set_reified! i
56
+ begin
57
+ @data[i+1]=
58
+ if Array===@data[i]
59
+ @obj.send(*@data[i])
60
+ else
61
+ @obj.send(@data[i])
62
+ end
63
+ rescue Exception=>exc
64
+ set_exception!(i)
65
+ @data[i+1]=exc
66
+ raise
67
+ end
68
+ end
69
+
70
+ def reify(itemref)
71
+
72
+ if itemref.is_a? String or itemref.is_a? Symbol
73
+ i=0
74
+ begin
75
+ i=@data.index(itemref.to_s,i)
76
+ i or @data.push itemref.to_s, nil
77
+ end while i and i%2!=0
78
+ reify_from_index(i)
79
+ elsif itemref.is_a? Integer and itemref>=0 and itemref%2==0
80
+ reify_from_index(itemref)
81
+ else
82
+ raise ArgumentError
83
+ end
84
+ return nil
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,100 @@
1
+ # $Id$
2
+ # Copyright (C) 2006 Caleb Clausen
3
+ # Distributed under the terms of Ruby's license.
4
+
5
+ require 'sequence'
6
+ require 'sequence/usedata'
7
+
8
+ class Sequence
9
+ # Objects in this class are mainly used to simply mark/remember the location
10
+ # of a parent sequence. But, this class also has the fully functionality of the
11
+ # parent. When this child wants to do an operation, it uses the parent to
12
+ # do it and returns the parent to where it was.
13
+ class Position < UseData # :nodoc:
14
+ def initialize(parent,pos=parent.pos)
15
+ Position===parent and raise ArgumentError
16
+ @data = parent
17
+ @pos = _normalize_pos pos
18
+ extend parent.like
19
+ prop(nil,parent.prop)
20
+ @data.on_change_notify self
21
+ end
22
+
23
+ def change_notification data,first,oldsize,newsize
24
+ assert @data==data
25
+ @pos=_adjust_pos_on_change @pos,first,oldsize,newsize
26
+
27
+ notify_change self,first,oldsize,newsize
28
+ end
29
+
30
+
31
+
32
+
33
+
34
+ def _pos=(p)
35
+ @pos = p
36
+ end
37
+ def position(pos=@pos)
38
+ @data.position(pos)
39
+ end
40
+
41
+ # undef_method(:_delete_position)
42
+
43
+ def_delegators :@data, :size, :data_class, :[], :[]=, :slice
44
+ def_delegators :@data, :new_data, :all_data, :pos?, :position?
45
+ def_delegators :@data, :begin, :end, :empty?, :index, :rindex, :slice!
46
+ def_delegators :@data, :modify, :append, :prepend, :overwrite
47
+ def_delegators :@data, :insert, :delete, :push, :pop, :shift, :unshift
48
+
49
+ alias dup position
50
+
51
+ def nearbegin(len,at=pos)
52
+ @data.nearbegin(len,at)
53
+ end
54
+
55
+ def nearend(len,at=pos)
56
+ @data.nearend(len,at)
57
+ end
58
+
59
+
60
+
61
+ def eof?; @pos>=size end
62
+
63
+ attr_reader :data,:pos
64
+
65
+
66
+ def closed?
67
+ super or @data.closed?
68
+ end
69
+
70
+ def close
71
+ @data._delete_position(self)
72
+ super
73
+ end
74
+ =begin ***
75
+ protected
76
+ def _deletion(pos,len=1,reverse=false,dummy=nil)
77
+ if @pos==pos
78
+ @anchor_after = false
79
+ elsif @pos>pos
80
+ @pos -= len
81
+ if @pos<pos
82
+ @pos = pos
83
+ @anchor_after = !reverse
84
+ elsif @pos==pos
85
+ @anchor_after = true
86
+ end
87
+ end
88
+ nil
89
+ end
90
+ def _insertion(pos,len=1,dummmy=nil)
91
+ if @pos>=pos+(@anchor_after ? 0 : 1)
92
+ @pos += len
93
+ end
94
+ nil
95
+ end
96
+ =end
97
+ end
98
+ end
99
+
100
+