sequence 0.1.0

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