re2 2.23.0 → 2.27.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.
- checksums.yaml +4 -4
- data/README.md +107 -4
- data/Rakefile +0 -4
- data/dependencies.yml +2 -2
- data/ext/re2/extconf.rb +4 -5
- data/ext/re2/re2.cc +962 -275
- data/lib/re2/string.rb +6 -6
- data/lib/re2/version.rb +1 -1
- data/ports/archives/20260107.1.tar.gz +0 -0
- data/spec/re2/match_data_spec.rb +495 -2
- data/spec/re2/regexp_spec.rb +324 -1
- data/spec/re2/scanner_spec.rb +134 -13
- data/spec/re2/set_spec.rb +75 -4
- data/spec/re2_spec.rb +217 -43
- metadata +3 -3
- data/ports/archives/20250814.1.tar.gz +0 -0
data/spec/re2/scanner_spec.rb
CHANGED
|
@@ -8,6 +8,56 @@ RSpec.describe RE2::Scanner do
|
|
|
8
8
|
|
|
9
9
|
expect(scanner.regexp).to equal(re)
|
|
10
10
|
end
|
|
11
|
+
|
|
12
|
+
it "raises an error when called on an uninitialized object" do
|
|
13
|
+
expect { described_class.allocate.regexp }.to raise_error(TypeError, /uninitialized RE2::Scanner/)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe "#dup" do
|
|
18
|
+
it "returns a usable copy of the scanner at the same position" do
|
|
19
|
+
scanner = RE2::Regexp.new('(\w+)').scan("foo bar baz")
|
|
20
|
+
scanner.scan
|
|
21
|
+
|
|
22
|
+
copy = scanner.dup
|
|
23
|
+
|
|
24
|
+
expect(copy.scan).to eq(["bar"])
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "creates an independent copy" do
|
|
28
|
+
scanner = RE2::Regexp.new('(\w+)').scan("foo bar baz")
|
|
29
|
+
scanner.scan
|
|
30
|
+
|
|
31
|
+
copy = scanner.dup
|
|
32
|
+
copy.scan
|
|
33
|
+
|
|
34
|
+
expect(scanner.scan).to eq(["bar"])
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "copies the EOF state of the scanner" do
|
|
38
|
+
scanner = RE2::Regexp.new('(\w+)').scan("foo")
|
|
39
|
+
scanner.scan
|
|
40
|
+
scanner.scan
|
|
41
|
+
|
|
42
|
+
copy = scanner.dup
|
|
43
|
+
|
|
44
|
+
expect(copy).to be_eof
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "raises an error when called on an uninitialized object" do
|
|
48
|
+
expect { described_class.allocate.dup }.to raise_error(TypeError, /uninitialized RE2::Scanner/)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe "#clone" do
|
|
53
|
+
it "returns a usable copy of the scanner at the same position" do
|
|
54
|
+
scanner = RE2::Regexp.new('(\w+)').scan("foo bar baz")
|
|
55
|
+
scanner.scan
|
|
56
|
+
|
|
57
|
+
copy = scanner.clone
|
|
58
|
+
|
|
59
|
+
expect(copy.scan).to eq(["bar"])
|
|
60
|
+
end
|
|
11
61
|
end
|
|
12
62
|
|
|
13
63
|
describe "#string" do
|
|
@@ -50,6 +100,10 @@ RSpec.describe RE2::Scanner do
|
|
|
50
100
|
|
|
51
101
|
expect(scanner.string).to equal(text)
|
|
52
102
|
end
|
|
103
|
+
|
|
104
|
+
it "raises an error when called on an uninitialized object" do
|
|
105
|
+
expect { described_class.allocate.string }.to raise_error(TypeError, /uninitialized RE2::Scanner/)
|
|
106
|
+
end
|
|
53
107
|
end
|
|
54
108
|
|
|
55
109
|
describe "#scan" do
|
|
@@ -131,11 +185,11 @@ RSpec.describe RE2::Scanner do
|
|
|
131
185
|
expect(scanner.scan).to be_nil
|
|
132
186
|
end
|
|
133
187
|
|
|
134
|
-
it "returns an array of
|
|
188
|
+
it "returns an array of empty strings with an empty input and capture", :aggregate_failures do
|
|
135
189
|
r = RE2::Regexp.new("()")
|
|
136
190
|
scanner = r.scan("")
|
|
137
191
|
|
|
138
|
-
expect(scanner.scan).to eq([
|
|
192
|
+
expect(scanner.scan).to eq([""])
|
|
139
193
|
expect(scanner.scan).to be_nil
|
|
140
194
|
end
|
|
141
195
|
|
|
@@ -150,25 +204,34 @@ RSpec.describe RE2::Scanner do
|
|
|
150
204
|
expect(scanner.scan).to be_nil
|
|
151
205
|
end
|
|
152
206
|
|
|
153
|
-
it "returns an array of
|
|
207
|
+
it "returns an array of empty strings if the pattern is an empty capturing group", :aggregate_failures do
|
|
154
208
|
r = RE2::Regexp.new("()")
|
|
155
209
|
scanner = r.scan("Foo")
|
|
156
210
|
|
|
157
|
-
expect(scanner.scan).to eq([
|
|
158
|
-
expect(scanner.scan).to eq([
|
|
159
|
-
expect(scanner.scan).to eq([
|
|
160
|
-
expect(scanner.scan).to eq([
|
|
211
|
+
expect(scanner.scan).to eq([""])
|
|
212
|
+
expect(scanner.scan).to eq([""])
|
|
213
|
+
expect(scanner.scan).to eq([""])
|
|
214
|
+
expect(scanner.scan).to eq([""])
|
|
161
215
|
expect(scanner.scan).to be_nil
|
|
162
216
|
end
|
|
163
217
|
|
|
164
|
-
it "returns array of
|
|
218
|
+
it "returns array of empty strings with multiple empty capturing groups", :aggregate_failures do
|
|
165
219
|
r = RE2::Regexp.new("()()()")
|
|
166
220
|
scanner = r.scan("Foo")
|
|
167
221
|
|
|
168
|
-
expect(scanner.scan).to eq([
|
|
169
|
-
expect(scanner.scan).to eq([
|
|
170
|
-
expect(scanner.scan).to eq([
|
|
171
|
-
expect(scanner.scan).to eq([
|
|
222
|
+
expect(scanner.scan).to eq(["", "", ""])
|
|
223
|
+
expect(scanner.scan).to eq(["", "", ""])
|
|
224
|
+
expect(scanner.scan).to eq(["", "", ""])
|
|
225
|
+
expect(scanner.scan).to eq(["", "", ""])
|
|
226
|
+
expect(scanner.scan).to be_nil
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
it "distinguishes zero-length matches from unmatched groups", :aggregate_failures do
|
|
230
|
+
r = RE2::Regexp.new("()(a)?")
|
|
231
|
+
scanner = r.scan("b")
|
|
232
|
+
|
|
233
|
+
expect(scanner.scan).to eq(["", nil])
|
|
234
|
+
expect(scanner.scan).to eq(["", nil])
|
|
172
235
|
expect(scanner.scan).to be_nil
|
|
173
236
|
end
|
|
174
237
|
|
|
@@ -176,7 +239,53 @@ RSpec.describe RE2::Scanner do
|
|
|
176
239
|
r = RE2::Regexp.new("()€")
|
|
177
240
|
scanner = r.scan("€")
|
|
178
241
|
|
|
179
|
-
expect(scanner.scan).to eq([
|
|
242
|
+
expect(scanner.scan).to eq([""])
|
|
243
|
+
expect(scanner.scan).to be_nil
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
it "advances by whole characters with zero-width matches on 2-byte UTF-8 input", :aggregate_failures do
|
|
247
|
+
r = RE2::Regexp.new("")
|
|
248
|
+
scanner = r.scan("à")
|
|
249
|
+
|
|
250
|
+
expect(scanner.scan).to eq([])
|
|
251
|
+
expect(scanner.scan).to eq([])
|
|
252
|
+
expect(scanner.scan).to be_nil
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
it "advances by whole characters with zero-width matches on 3-byte UTF-8 input", :aggregate_failures do
|
|
256
|
+
r = RE2::Regexp.new("")
|
|
257
|
+
scanner = r.scan("\u20AC")
|
|
258
|
+
|
|
259
|
+
expect(scanner.scan).to eq([])
|
|
260
|
+
expect(scanner.scan).to eq([])
|
|
261
|
+
expect(scanner.scan).to be_nil
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
it "advances by whole characters with zero-width matches on 4-byte UTF-8 input", :aggregate_failures do
|
|
265
|
+
r = RE2::Regexp.new("")
|
|
266
|
+
scanner = r.scan("\u{1F600}")
|
|
267
|
+
|
|
268
|
+
expect(scanner.scan).to eq([])
|
|
269
|
+
expect(scanner.scan).to eq([])
|
|
270
|
+
expect(scanner.scan).to be_nil
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
it "advances by single bytes with zero-width matches on Latin-1 input", :aggregate_failures do
|
|
274
|
+
r = RE2::Regexp.new("", utf8: false)
|
|
275
|
+
scanner = r.scan("\xC3\xA0")
|
|
276
|
+
|
|
277
|
+
expect(scanner.scan).to eq([])
|
|
278
|
+
expect(scanner.scan).to eq([])
|
|
279
|
+
expect(scanner.scan).to eq([])
|
|
280
|
+
expect(scanner.scan).to be_nil
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
it "handles truncated multi-byte sequences at the end of input", :aggregate_failures do
|
|
284
|
+
r = RE2::Regexp.new("")
|
|
285
|
+
scanner = r.scan("\xC3")
|
|
286
|
+
|
|
287
|
+
expect(scanner.scan).to eq([])
|
|
288
|
+
expect(scanner.scan).to eq([])
|
|
180
289
|
expect(scanner.scan).to be_nil
|
|
181
290
|
end
|
|
182
291
|
|
|
@@ -211,6 +320,10 @@ RSpec.describe RE2::Scanner do
|
|
|
211
320
|
|
|
212
321
|
expect(scanner.scan).to eq(["It"])
|
|
213
322
|
end
|
|
323
|
+
|
|
324
|
+
it "raises an error when called on an uninitialized object" do
|
|
325
|
+
expect { described_class.allocate.scan }.to raise_error(TypeError, /uninitialized RE2::Scanner/)
|
|
326
|
+
end
|
|
214
327
|
end
|
|
215
328
|
|
|
216
329
|
it "is enumerable" do
|
|
@@ -272,6 +385,10 @@ RSpec.describe RE2::Scanner do
|
|
|
272
385
|
|
|
273
386
|
expect(scanner).not_to be_eof
|
|
274
387
|
end
|
|
388
|
+
|
|
389
|
+
it "raises an error when called on an uninitialized object" do
|
|
390
|
+
expect { described_class.allocate.rewind }.to raise_error(TypeError, /uninitialized RE2::Scanner/)
|
|
391
|
+
end
|
|
275
392
|
end
|
|
276
393
|
|
|
277
394
|
describe "#eof?" do
|
|
@@ -320,5 +437,9 @@ RSpec.describe RE2::Scanner do
|
|
|
320
437
|
|
|
321
438
|
expect(scanner).to be_eof
|
|
322
439
|
end
|
|
440
|
+
|
|
441
|
+
it "raises an error when called on an uninitialized object" do
|
|
442
|
+
expect { described_class.allocate.eof? }.to raise_error(TypeError, /uninitialized RE2::Scanner/)
|
|
443
|
+
end
|
|
323
444
|
end
|
|
324
445
|
end
|
data/spec/re2/set_spec.rb
CHANGED
|
@@ -51,6 +51,18 @@ RSpec.describe RE2::Set do
|
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
describe "#dup" do
|
|
55
|
+
it "raises a TypeError" do
|
|
56
|
+
expect { described_class.new.dup }.to raise_error(TypeError, /cannot copy RE2::Set/)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
describe "#clone" do
|
|
61
|
+
it "raises a TypeError" do
|
|
62
|
+
expect { described_class.new.clone }.to raise_error(TypeError, /cannot copy RE2::Set/)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
54
66
|
describe "#add" do
|
|
55
67
|
it "allows multiple patterns to be added", :aggregate_failures do
|
|
56
68
|
set = RE2::Set.new
|
|
@@ -72,14 +84,12 @@ RSpec.describe RE2::Set do
|
|
|
72
84
|
expect { set.add("(?P<#{'o' * 200}") }.to raise_error(ArgumentError, "str rejected by RE2::Set->Add(): invalid named capture group: (?P<oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo")
|
|
73
85
|
end
|
|
74
86
|
|
|
75
|
-
it "raises
|
|
87
|
+
it "raises a FrozenError if called after #compile" do
|
|
76
88
|
set = RE2::Set.new(:unanchored, log_errors: false)
|
|
77
89
|
set.add("abc")
|
|
78
90
|
set.compile
|
|
79
91
|
|
|
80
|
-
|
|
81
|
-
expect { set.add("def") }.to raise_error(ArgumentError)
|
|
82
|
-
end
|
|
92
|
+
expect { set.add("def") }.to raise_error(FrozenError)
|
|
83
93
|
end
|
|
84
94
|
|
|
85
95
|
it "raises an error if given a pattern that can't be coerced to a String" do
|
|
@@ -93,6 +103,10 @@ RSpec.describe RE2::Set do
|
|
|
93
103
|
|
|
94
104
|
expect(set.add(StringLike.new("abc"))).to eq(0)
|
|
95
105
|
end
|
|
106
|
+
|
|
107
|
+
it "raises an error when called on an uninitialized object" do
|
|
108
|
+
expect { described_class.allocate.add("foo") }.to raise_error(TypeError, /uninitialized RE2::Set/)
|
|
109
|
+
end
|
|
96
110
|
end
|
|
97
111
|
|
|
98
112
|
describe "#compile" do
|
|
@@ -104,6 +118,33 @@ RSpec.describe RE2::Set do
|
|
|
104
118
|
|
|
105
119
|
expect(set.compile).to be_truthy
|
|
106
120
|
end
|
|
121
|
+
|
|
122
|
+
it "freezes the set on successful compilation" do
|
|
123
|
+
set = RE2::Set.new
|
|
124
|
+
set.add("abc")
|
|
125
|
+
set.compile
|
|
126
|
+
|
|
127
|
+
expect(set).to be_frozen
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it "is not frozen before compilation" do
|
|
131
|
+
set = RE2::Set.new
|
|
132
|
+
set.add("abc")
|
|
133
|
+
|
|
134
|
+
expect(set).to_not be_frozen
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it "cannot be re-initialized after compilation" do
|
|
138
|
+
set = RE2::Set.new
|
|
139
|
+
set.add("abc")
|
|
140
|
+
set.compile
|
|
141
|
+
|
|
142
|
+
expect { set.send(:initialize) }.to raise_error(FrozenError)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it "raises an error when called on an uninitialized object" do
|
|
146
|
+
expect { described_class.allocate.compile }.to raise_error(TypeError, /uninitialized RE2::Set/)
|
|
147
|
+
end
|
|
107
148
|
end
|
|
108
149
|
|
|
109
150
|
describe "#match" do
|
|
@@ -202,6 +243,24 @@ RSpec.describe RE2::Set do
|
|
|
202
243
|
|
|
203
244
|
expect(set.match(StringLike.new("abcdef"), exception: false)).to contain_exactly(0)
|
|
204
245
|
end
|
|
246
|
+
|
|
247
|
+
it "raises an error when called on an uninitialized object" do
|
|
248
|
+
expect { described_class.allocate.match("foo") }.to raise_error(TypeError, /uninitialized RE2::Set/)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
it "can be run concurrently" do
|
|
252
|
+
set = RE2::Set.new
|
|
253
|
+
set.add("abc")
|
|
254
|
+
set.add("def")
|
|
255
|
+
set.add("ghi")
|
|
256
|
+
set.compile
|
|
257
|
+
|
|
258
|
+
threads = 10.times.map do
|
|
259
|
+
Thread.new { set.match("abcdefghi", exception: false) }
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
expect(threads.map(&:value)).to all(eq([0, 1, 2]))
|
|
263
|
+
end
|
|
205
264
|
end
|
|
206
265
|
|
|
207
266
|
describe "#size" do
|
|
@@ -228,6 +287,12 @@ RSpec.describe RE2::Set do
|
|
|
228
287
|
|
|
229
288
|
expect { set.size }.to raise_error(RE2::Set::UnsupportedError)
|
|
230
289
|
end
|
|
290
|
+
|
|
291
|
+
it "raises an error when called on an uninitialized object" do
|
|
292
|
+
skip "Underlying RE2::Set has no Size method" unless RE2::Set.size?
|
|
293
|
+
|
|
294
|
+
expect { described_class.allocate.size }.to raise_error(TypeError, /uninitialized RE2::Set/)
|
|
295
|
+
end
|
|
231
296
|
end
|
|
232
297
|
|
|
233
298
|
describe "#length" do
|
|
@@ -246,6 +311,12 @@ RSpec.describe RE2::Set do
|
|
|
246
311
|
|
|
247
312
|
expect(set.length).to eq(2)
|
|
248
313
|
end
|
|
314
|
+
|
|
315
|
+
it "raises an error when called on an uninitialized object" do
|
|
316
|
+
skip "Underlying RE2::Set has no Size method" unless RE2::Set.size?
|
|
317
|
+
|
|
318
|
+
expect { described_class.allocate.length }.to raise_error(TypeError, /uninitialized RE2::Set/)
|
|
319
|
+
end
|
|
249
320
|
end
|
|
250
321
|
|
|
251
322
|
def silence_stderr
|