sequence 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +58 -0
- data/GPL +340 -0
- data/Manifest.txt +36 -0
- data/README.txt +320 -0
- data/Rakefile +32 -0
- data/lib/assert.rb +16 -0
- data/lib/sequence/arraylike.rb +57 -0
- data/lib/sequence/buffered.rb +188 -0
- data/lib/sequence/circular.rb +272 -0
- data/lib/sequence/enum.rb +260 -0
- data/lib/sequence/file.rb +172 -0
- data/lib/sequence/functional.rb +152 -0
- data/lib/sequence/generator.rb +290 -0
- data/lib/sequence/indexed.rb +234 -0
- data/lib/sequence/io.rb +102 -0
- data/lib/sequence/list.rb +292 -0
- data/lib/sequence/ofhash.rb +38 -0
- data/lib/sequence/ofobjectivars.rb +29 -0
- data/lib/sequence/ofobjectmethods.rb +87 -0
- data/lib/sequence/position.rb +100 -0
- data/lib/sequence/reversed.rb +180 -0
- data/lib/sequence/shifting.rb +190 -0
- data/lib/sequence/singleitem.rb +50 -0
- data/lib/sequence/stringlike.rb +482 -0
- data/lib/sequence/subseq.rb +90 -0
- data/lib/sequence/usedata.rb +35 -0
- data/lib/sequence/version.rb +5 -0
- data/lib/sequence.rb +721 -0
- data/lib/weakrefset.rb +254 -0
- data/test/test.rb +609 -0
- data/test/test_all.rb +6 -0
- data/test/test_changes.rb +44 -0
- data/test/test_circulars.rb +89 -0
- data/test/test_rexscan.rb +899 -0
- data/test/test_seqrex.rb +204 -0
- data/test/test_sequences.rb +106 -0
- metadata +90 -0
@@ -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
|
+
|