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,152 @@
1
+ # Copyright (C) 2006 Caleb Clausen
2
+ # Distributed under the terms of Ruby's license.
3
+
4
+ class Sequence
5
+ #i thought ruby had this already, but i can't find it...
6
+ class WeakHash
7
+ #dunno if this is thread-safe
8
+ def initialize hash={},default=nil,&block
9
+ @hash=block ? Hash.new(&block) : Hash.new(default)
10
+ hash.each{|(k,v)|
11
+ self[k]=v
12
+ }
13
+ end
14
+
15
+ def delete_when_dies key
16
+ ObjectSpace.define_finalizer(key){|id| @hash.include? id and @hash.delete id}
17
+ return key
18
+ end
19
+
20
+ def [] key
21
+ @hash[key.__id__]
22
+ end
23
+
24
+ def []= key, val
25
+ delete_when_dies key
26
+ @hash[key.__id__]=val
27
+ end
28
+
29
+ def delete key
30
+ @hash.delete key.__id__
31
+ end
32
+
33
+ def values
34
+ @hash.values
35
+ end
36
+
37
+ def keys
38
+ @hash.keys.map!{|id| ObjectSpace._id2ref(id)}
39
+ end
40
+ end
41
+
42
+ #methods (and constants) related to functional programming
43
+ module Functional; end
44
+ class<<Functional
45
+ HAS_SIDE_EFFECT=WeakHash.new
46
+ NO_SIDE_EFFECT=WeakHash.new
47
+ #hashes of Module (or Class, including meta-Class) to a list of method names
48
+ #which do or don't have side effects
49
+ def functions_of(obj)
50
+ published=Set[public_methods_of(obj).delete_if{|name| /[!=]$/===name}]
51
+ result=[]
52
+ list=class<<obj; ancestors.unshift self end
53
+ list.each{|mod|
54
+ result.push( *published&NO_SIDE_EFFECT[mod] )
55
+ published&=Set[*NO_SIDE_EFFECT[mod]+HAS_SIDE_EFFECT[mod]]
56
+ }
57
+ return result
58
+ end
59
+
60
+ def maybe_functions_of(obj)
61
+ published=public_methods_of(obj).delete_if{|name| /[!=]$/===name}
62
+ result=[]
63
+ list=class<<obj; ancestors.unshift self end
64
+ list.each{|mod|
65
+ published.delete_if{|name|
66
+ NO_SIDE_EFFECT[mod].include? name ||
67
+ HAS_SIDE_EFFECT[mod].include? name
68
+ }
69
+ }
70
+ return result
71
+ end
72
+
73
+ def nonfunctions_of(obj)
74
+ public_methods_of(obj)-functions_of(obj)-maybe_functions_of(obj)
75
+ end
76
+
77
+ def is_function?(obj,name)
78
+ list=class<<obj; ancestors.unshift self end
79
+ list.each{|mod|
80
+ return true if NO_SIDE_EFFECT[mod].include? name
81
+ return false if HAS_SIDE_EFFECT[mod].include? name
82
+ }
83
+ return false
84
+ end
85
+
86
+ def is_maybe_not_function?(obj,name)
87
+ !is_function(obj,name)
88
+ end
89
+
90
+ def is_maybe_function?(obj,name)
91
+ list=class<<obj; ancestors.unshift self end
92
+ list.each{|mod|
93
+ return true if NO_SIDE_EFFECT[mod].include? name
94
+ return false if HAS_SIDE_EFFECT[mod].include? name
95
+ }
96
+ return true
97
+ end
98
+
99
+ def is_not_function?(obj,name)
100
+ !is_maybe_function(obj,name)
101
+ end
102
+
103
+ PMETHS_REF= ::Object.instance_method("public_methods")
104
+ def public_methods_of(obj)
105
+ PMETHS_REF.bind(obj).call
106
+ end
107
+ end
108
+
109
+ end
110
+
111
+ class Module
112
+ def has_side_effect(*names)
113
+ names.map!{|name| name.to_s}
114
+ Functional::HAS_SIDE_EFFECT[self]|= names
115
+ Functional::NO_SIDE_EFFECT[self] -= names
116
+ end
117
+
118
+ def no_side_effect(*names)
119
+ names.map!{|name| name.to_s}
120
+ Functional::NO_SIDE_EFFECT[self] |= names
121
+ Functional::HAS_SIDE_EFFECT[self]-=names
122
+ end
123
+ end
124
+
125
+ class Object
126
+ has_side_effect *l=%w(__send__ display extend freeze instance_eval instance_exec instance_variable_set method_missing send taint untaint)
127
+ no_side_effect *instance_methods(false)-l
128
+ end
129
+ class String
130
+ has_side_effect *l=%w(<< []= capitalize! chomp! chop! concat delete! downcase! gsub! lstrip! next! replace reverse! rstrip! slice! squeeze! strip! sub! succ! swapcase! tr! tr_s! upcase!)
131
+ no_side_effect *instance_methods(false)-l
132
+ end
133
+ class Array
134
+ has_side_effect *l=%w(<< []= clear collect! compact! concat delete delete_at delete_if fill flatten! insert map! pop push reject! replace reverse! shift slice! sort! uniq! unshift)
135
+ no_side_effect *instance_methods(false)-l
136
+ end
137
+ class Hash
138
+ has_side_effect *l=%w([]= clear default= delete delete_if merge! rehash reject! replace shift store update)
139
+ no_side_effect *instance_methods(false)-l
140
+ end
141
+ class Class
142
+ has_side_effect *%w(inheirited new)
143
+ no_side_effect *%w(allocate superclass)
144
+ end
145
+ class Module
146
+ has_side_effect *l=%w(class_eval const_set module_eval private_class_method public_class_method)+private_instance_methods(false)-["class_variable_get"]
147
+ no_side_effect *["class_variable_get"]+instance_methods(false)-l
148
+ end
149
+ class Proc
150
+ has_side_effect *%w[[] call]
151
+ no_side_effect "arity"
152
+ end
@@ -0,0 +1,290 @@
1
+ #!/usr/bin/env ruby
2
+ #--
3
+ # $Idaemons: /home/cvs/rb/generator.rb,v 1.8 2001/10/03 08:54:32 knu Exp $
4
+ # $RoughId: generator.rb,v 1.10 2003/10/14 19:36:58 knu Exp $
5
+ # $Id: generator.rb,v 1.12 2005/12/31 02:56:46 ocean Exp $
6
+ #++
7
+
8
+ #this is a copy of (the newest, fastest) generator.rb from the standard lib,
9
+ #with SyncEnumerator removed. -cc
10
+
11
+ #
12
+ # = generator.rb: convert an internal iterator to an external one
13
+ #
14
+ # Copyright (c) 2001,2003 Akinori MUSHA <knu@iDaemons.org>
15
+ #
16
+ # All rights reserved. You can redistribute and/or modify it under
17
+ # the same terms as Ruby.
18
+ #
19
+ # == Overview
20
+ #
21
+ # This library provides the Generator class, which converts an
22
+ # internal iterator (i.e. an Enumerable object) to an external
23
+ # iterator. In that form, you can roll many iterators independently.
24
+ #
25
+ # The SyncEnumerator class, which is implemented using Generator,
26
+ # makes it easy to roll many Enumerable objects synchronously.
27
+ #
28
+ # See the respective classes for examples of usage.
29
+
30
+
31
+ #
32
+ # Generator converts an internal iterator (i.e. an Enumerable object)
33
+ # to an external iterator.
34
+ #
35
+ # == Example
36
+ #
37
+ # require 'generator'
38
+ #
39
+ # # Generator from an Enumerable object
40
+ # g = Generator.new(['A', 'B', 'C', 'Z'])
41
+ #
42
+ # while g.next?
43
+ # puts g.next
44
+ # end
45
+ #
46
+ # # Generator from a block
47
+ # g = Generator.new { |g|
48
+ # for i in 'A'..'C'
49
+ # g.yield i
50
+ # end
51
+ #
52
+ # g.yield 'Z'
53
+ # }
54
+ #
55
+ # # The same result as above
56
+ # while g.next?
57
+ # puts g.next
58
+ # end
59
+ #
60
+
61
+ #if existing Generator library is the slow one, replace with the faster version (above)
62
+ require 'generator.rb'
63
+ unless Generator.new.instance_variables.include("@loop_thread")
64
+ remove_const :Generator
65
+ class Generator
66
+ include Enumerable
67
+
68
+ # Creates a new generator either from an Enumerable object or from a
69
+ # block.
70
+ #
71
+ # In the former, block is ignored even if given.
72
+ #
73
+ # In the latter, the given block is called with the generator
74
+ # itself, and expected to call the +yield+ method for each element.
75
+ def initialize(enum = nil, &block)
76
+ if enum
77
+ @block = proc{|g| enum.each{|value| g.yield value}}
78
+ else
79
+ @block = block
80
+ end
81
+ @index = 0
82
+ @queue = []
83
+ @main_thread = nil
84
+ @loop_thread.kill if defined?(@loop_thread)
85
+ @loop_thread = Thread.new do
86
+ Thread.stop
87
+ begin
88
+ @block.call(self)
89
+ rescue
90
+ @main_thread.raise $!
91
+ ensure
92
+ @main_thread.wakeup
93
+ end
94
+ end
95
+ Thread.pass until @loop_thread.stop?
96
+ self
97
+ end
98
+
99
+ # Yields an element to the generator.
100
+ def yield(value)
101
+ if Thread.current != @loop_thread
102
+ raise "should be called in Generator.new{|g| ... }"
103
+ end
104
+ Thread.critical = true
105
+ begin
106
+ @queue << value
107
+ @main_thread.wakeup
108
+ Thread.stop
109
+ ensure
110
+ Thread.critical = false
111
+ end
112
+ self
113
+ end
114
+
115
+ # Returns true if the generator has reached the end.
116
+ def end?
117
+ if @queue.empty?
118
+ if @main_thread
119
+ raise "should not be called in Generator.new{|g| ... }"
120
+ end
121
+ Thread.critical = true
122
+ begin
123
+ @main_thread = Thread.current
124
+ @loop_thread.wakeup
125
+ Thread.stop
126
+ rescue ThreadError
127
+ # ignore
128
+ ensure
129
+ @main_thread = nil
130
+ Thread.critical = false
131
+ end
132
+ end
133
+ @queue.empty?
134
+ end
135
+
136
+ # Returns true if the generator has not reached the end yet.
137
+ def next?
138
+ !end?
139
+ end
140
+
141
+ # Returns the current index (position) counting from zero.
142
+ def index
143
+ @index
144
+ end
145
+
146
+ # Returns the current index (position) counting from zero.
147
+ def pos
148
+ @index
149
+ end
150
+
151
+ # Returns the element at the current position and moves forward.
152
+ def next
153
+ raise EOFError.new("no more elements available") if end?
154
+ @index += 1
155
+ @queue.shift
156
+ end
157
+
158
+ # Returns the element at the current position.
159
+ def current
160
+ raise EOFError.new("no more elements available") if end?
161
+ @queue.first
162
+ end
163
+
164
+ # Rewinds the generator.
165
+ def rewind
166
+ initialize(nil, &@block) if @index.nonzero?
167
+ self
168
+ end
169
+
170
+ # Rewinds the generator and enumerates the elements.
171
+ def each
172
+ rewind
173
+ until end?
174
+ yield self.next
175
+ end
176
+ self
177
+ end
178
+ end
179
+ end
180
+
181
+
182
+
183
+ if $0 == __FILE__
184
+ eval DATA.read, nil, $0, __LINE__+4
185
+ end
186
+
187
+ __END__
188
+
189
+ require 'test/unit'
190
+
191
+ class TC_Generator < Test::Unit::TestCase
192
+ def test_block1
193
+ g = Generator.new { |g|
194
+ # no yield's
195
+ }
196
+
197
+ assert_equal(0, g.pos)
198
+ assert_raises(EOFError) { g.current }
199
+ end
200
+
201
+ def test_block2
202
+ g = Generator.new { |g|
203
+ for i in 'A'..'C'
204
+ g.yield i
205
+ end
206
+
207
+ g.yield 'Z'
208
+ }
209
+
210
+ assert_equal(0, g.pos)
211
+ assert_equal('A', g.current)
212
+
213
+ assert_equal(true, g.next?)
214
+ assert_equal(0, g.pos)
215
+ assert_equal('A', g.current)
216
+ assert_equal(0, g.pos)
217
+ assert_equal('A', g.next)
218
+
219
+ assert_equal(1, g.pos)
220
+ assert_equal(true, g.next?)
221
+ assert_equal(1, g.pos)
222
+ assert_equal('B', g.current)
223
+ assert_equal(1, g.pos)
224
+ assert_equal('B', g.next)
225
+
226
+ assert_equal(g, g.rewind)
227
+
228
+ assert_equal(0, g.pos)
229
+ assert_equal('A', g.current)
230
+
231
+ assert_equal(true, g.next?)
232
+ assert_equal(0, g.pos)
233
+ assert_equal('A', g.current)
234
+ assert_equal(0, g.pos)
235
+ assert_equal('A', g.next)
236
+
237
+ assert_equal(1, g.pos)
238
+ assert_equal(true, g.next?)
239
+ assert_equal(1, g.pos)
240
+ assert_equal('B', g.current)
241
+ assert_equal(1, g.pos)
242
+ assert_equal('B', g.next)
243
+
244
+ assert_equal(2, g.pos)
245
+ assert_equal(true, g.next?)
246
+ assert_equal(2, g.pos)
247
+ assert_equal('C', g.current)
248
+ assert_equal(2, g.pos)
249
+ assert_equal('C', g.next)
250
+
251
+ assert_equal(3, g.pos)
252
+ assert_equal(true, g.next?)
253
+ assert_equal(3, g.pos)
254
+ assert_equal('Z', g.current)
255
+ assert_equal(3, g.pos)
256
+ assert_equal('Z', g.next)
257
+
258
+ assert_equal(4, g.pos)
259
+ assert_equal(false, g.next?)
260
+ assert_raises(EOFError) { g.next }
261
+ end
262
+
263
+ def test_each
264
+ a = [5, 6, 7, 8, 9]
265
+
266
+ g = Generator.new(a)
267
+
268
+ i = 0
269
+
270
+ g.each { |x|
271
+ assert_equal(a[i], x)
272
+
273
+ i += 1
274
+
275
+ break if i == 3
276
+ }
277
+
278
+ assert_equal(3, i)
279
+
280
+ i = 0
281
+
282
+ g.each { |x|
283
+ assert_equal(a[i], x)
284
+
285
+ i += 1
286
+ }
287
+
288
+ assert_equal(5, i)
289
+ end
290
+ end
@@ -0,0 +1,234 @@
1
+ # $Id$
2
+ # Copyright (C) 2006 Caleb Clausen
3
+ # Distributed under the terms of Ruby's license.
4
+
5
+ require 'sequence'
6
+ require 'sequence/arraylike'
7
+ require 'sequence/stringlike'
8
+ require 'sequence/usedata'
9
+ require 'sequence/subseq'
10
+
11
+ class Sequence
12
+ # This class makes a seq over an Array or String.
13
+ class Indexed < UseData
14
+
15
+ class <<self
16
+ alias _orig_new new
17
+ def new(data,p=0)
18
+ if data.respond_to? :to_sequence
19
+ data.to_sequence
20
+ else _orig_new(data,p)
21
+ end
22
+ end
23
+ end
24
+
25
+ def initialize(data,pos=0)
26
+ @data = data
27
+ @pos = pos
28
+ end
29
+ # :stopdoc:
30
+ def new_data
31
+ @data.class.new
32
+ end
33
+
34
+ =begin
35
+ protected
36
+ def _delete1after?
37
+ @data.slice!(@pos)
38
+ end
39
+ def _delete1before?
40
+ @pos==0 ? nil : @data.slice!(@pos-=1)
41
+ end
42
+ def _insert1before(v)
43
+ @data[@pos,0] = (new_data << v)
44
+ @pos += 1
45
+ true
46
+ end
47
+ def _insert1after(v)
48
+ @data[@pos,0] = (new_data << v)
49
+ true
50
+ end
51
+ public
52
+ =end
53
+
54
+ attr_reader :pos,:data
55
+ def _pos= x; @pos=x end
56
+
57
+ def eof?; @pos >= size end
58
+
59
+ def_delegators :@data, :size, :slice, :[]
60
+
61
+ def maxmatchlen(backwards) size end
62
+
63
+ #can't change maxmatchlen (maybe someday?)
64
+ def maxmatchlen= x; end
65
+
66
+ def modify(*args)
67
+ newsize=args.last.size
68
+ first,len,only1=_parse_slice_args( *args[0...-1])
69
+ @data.[]=(*args)
70
+ @pos=_adjust_pos_on_change(@pos, first,len,newsize)
71
+ notify_change(self,first,len,newsize)
72
+ return args.last
73
+ end
74
+ alias []= modify
75
+
76
+ end
77
+
78
+ class OfArray < Indexed
79
+ class<<self
80
+ alias new _orig_new
81
+ end
82
+ include ArrayLike
83
+ #scan,scan_until provided by ArrayLike
84
+
85
+ #a better, string-like #index... with an offset parameter
86
+ #scalar matchers only
87
+ def index(pat,offset=0)
88
+ pat=pat.dup
89
+ class<<pat; alias == ===; end
90
+ offset.zero? and return( @data.index pat)
91
+ @data[offset..-1].index(pat)+offset
92
+ end
93
+
94
+ #a better, string-like #rindex... with an offset parameter
95
+ #scalar matchers only
96
+ def rindex(pat,offset=size)
97
+ pat=pat.dup
98
+ class<<pat; alias == ===; end
99
+ (offset==size ? @data : @data[0...offset]).rindex pat
100
+ end
101
+
102
+ def append(arr)
103
+ sz=size
104
+ @data.push(*arr)
105
+ notify_change(self,sz,0,arr.size)
106
+ self
107
+ end
108
+
109
+ def new_data
110
+ []
111
+ end
112
+
113
+ end
114
+
115
+ class OfString < Indexed
116
+ class<<self
117
+ alias new _orig_new
118
+ end
119
+
120
+ include StringLike
121
+ def scan(pat)
122
+ holding?{case pat
123
+ when Regexp:
124
+ if (m=match pat,true)
125
+ @pos= m.end(0)
126
+ m.to_s
127
+ end
128
+ when Integer: (res=@data[@pos])==pat and @pos+=1 and res
129
+ when String: @data[@pos...@pos+=pat.size]==pat and pat
130
+ end}
131
+ end
132
+
133
+ def scanback(pat)
134
+ holding?{case pat
135
+ when Regexp:
136
+ if m=matchback(pat,true)
137
+ @pos= m.begin(0)
138
+ m.to_s
139
+ end
140
+ when Integer: @data[@pos-=1]==pat and pat.chr
141
+ when String: @data[@pos-=pat.size,pat.size]==pat and pat
142
+ end}
143
+ end
144
+
145
+ def scan_until(pat)
146
+ if Regexp===pat
147
+ return(if (m=match pat,false)
148
+ @pos= m.end(0)
149
+ m.pre_match+m.to_s
150
+ end)
151
+ end
152
+ i=@data.index(pat,pos) or return
153
+ @data[@pos...@pos=i]
154
+ end
155
+
156
+ def scanback_until(pat)
157
+ if Regexp===pat
158
+ return(if (m=matchback pat,true)
159
+ @pos= m.begin(0)
160
+ m.to_s+m.post_match
161
+ end)
162
+ end
163
+ i=@data.rindex(pat,pos) or return
164
+ oldpos=@pos
165
+ @data[@pos=i...oldpos]
166
+ end
167
+
168
+ def match pat,anchored=true,len=size
169
+ len=size
170
+ anchored and pat=_anchor(pat)
171
+ #pat.last_match=
172
+ self.last_match=Thread.current[:last_match]=
173
+ #can't use String#index here... doesn't do anchors right
174
+ if pat.match @data[pos..-1]
175
+ newpos=@pos+$~.end(0)
176
+ fixup_match_result( $~,[],@pos,:post){
177
+ SubSeq.new(self,newpos,size-newpos)
178
+ }
179
+ end
180
+ end
181
+
182
+ def matchback pat,anchored=true
183
+ anchored and pat=_anchor(pat,:back)
184
+ #pat.last_match=
185
+ self.last_match=Thread.current[:last_match]=
186
+ if pat.match @data[0...pos]
187
+ fixup_match_result($~,[],0,:pre){
188
+ cu=SubSeq.new(self,0,pos=$~.pre_match.size)
189
+ cu.pos=pos
190
+ cu
191
+ }
192
+ end
193
+ end
194
+
195
+ def index(pat,offset=0)
196
+ @data.index(pat,offset)
197
+ end
198
+
199
+ def rindex(pat,offset=0)
200
+ @data.rindex(pat,offset)
201
+ end
202
+
203
+
204
+ def append(str)
205
+ sz=size
206
+ @data << str
207
+ notify_change(self,sz,0,str.size)
208
+ self
209
+ end
210
+
211
+ def new_data
212
+ ""
213
+ end
214
+
215
+
216
+ end
217
+
218
+ end
219
+
220
+ class Array
221
+ # convert an array to a seq starting at +pos+
222
+ def to_sequence(pos=0)
223
+ Sequence::OfArray.new(self,pos)
224
+ end
225
+ end
226
+
227
+ class String
228
+ # convert a string to a seq starting at +pos+
229
+ def to_sequence (pos=0)
230
+ Sequence::OfString.new(self,pos)
231
+ end
232
+ end
233
+
234
+