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.
- 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
|
+
|