regexp_parser 2.7.0 → 2.12.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/Gemfile +7 -5
- data/LICENSE +1 -1
- data/Rakefile +5 -3
- data/lib/regexp_parser/error.rb +2 -0
- data/lib/regexp_parser/expression/base.rb +2 -7
- data/lib/regexp_parser/expression/classes/alternation.rb +3 -1
- data/lib/regexp_parser/expression/classes/anchor.rb +2 -0
- data/lib/regexp_parser/expression/classes/backreference.rb +7 -26
- data/lib/regexp_parser/expression/classes/character_set/intersection.rb +2 -0
- data/lib/regexp_parser/expression/classes/character_set/range.rb +4 -7
- data/lib/regexp_parser/expression/classes/character_set.rb +6 -8
- data/lib/regexp_parser/expression/classes/character_type.rb +2 -0
- data/lib/regexp_parser/expression/classes/conditional.rb +4 -28
- data/lib/regexp_parser/expression/classes/escape_sequence.rb +29 -96
- data/lib/regexp_parser/expression/classes/free_space.rb +5 -1
- data/lib/regexp_parser/expression/classes/group.rb +2 -22
- data/lib/regexp_parser/expression/classes/keep.rb +3 -1
- data/lib/regexp_parser/expression/classes/literal.rb +2 -0
- data/lib/regexp_parser/expression/classes/posix_class.rb +7 -5
- data/lib/regexp_parser/expression/classes/root.rb +2 -0
- data/lib/regexp_parser/expression/classes/unicode_property.rb +13 -11
- data/lib/regexp_parser/expression/methods/construct.rb +4 -4
- data/lib/regexp_parser/expression/methods/escape_sequence_char.rb +7 -0
- data/lib/regexp_parser/expression/methods/escape_sequence_codepoint.rb +76 -0
- data/lib/regexp_parser/expression/methods/human_name.rb +2 -0
- data/lib/regexp_parser/expression/methods/match.rb +2 -0
- data/lib/regexp_parser/expression/methods/match_length.rb +2 -0
- data/lib/regexp_parser/expression/methods/negative.rb +22 -0
- data/lib/regexp_parser/expression/methods/options.rb +2 -0
- data/lib/regexp_parser/expression/methods/parts.rb +25 -0
- data/lib/regexp_parser/expression/methods/printing.rb +28 -0
- data/lib/regexp_parser/expression/methods/referenced_expressions.rb +30 -0
- data/lib/regexp_parser/expression/methods/strfregexp.rb +2 -0
- data/lib/regexp_parser/expression/methods/tests.rb +42 -3
- data/lib/regexp_parser/expression/methods/traverse.rb +35 -20
- data/lib/regexp_parser/expression/quantifier.rb +33 -18
- data/lib/regexp_parser/expression/sequence.rb +7 -9
- data/lib/regexp_parser/expression/sequence_operation.rb +6 -9
- data/lib/regexp_parser/expression/shared.rb +40 -24
- data/lib/regexp_parser/expression/subexpression.rb +22 -18
- data/lib/regexp_parser/expression.rb +39 -31
- data/lib/regexp_parser/lexer.rb +17 -7
- data/lib/regexp_parser/parser.rb +101 -95
- data/lib/regexp_parser/scanner/errors/premature_end_error.rb +10 -0
- data/lib/regexp_parser/scanner/errors/scanner_error.rb +8 -0
- data/lib/regexp_parser/scanner/errors/validation_error.rb +65 -0
- data/lib/regexp_parser/scanner/properties/long.csv +61 -0
- data/lib/regexp_parser/scanner/properties/short.csv +15 -0
- data/lib/regexp_parser/scanner/property.rl +1 -1
- data/lib/regexp_parser/scanner/scanner.rl +90 -140
- data/lib/regexp_parser/scanner.rb +1336 -1450
- data/lib/regexp_parser/syntax/any.rb +2 -0
- data/lib/regexp_parser/syntax/base.rb +2 -0
- data/lib/regexp_parser/syntax/token/anchor.rb +5 -3
- data/lib/regexp_parser/syntax/token/assertion.rb +4 -2
- data/lib/regexp_parser/syntax/token/backreference.rb +11 -6
- data/lib/regexp_parser/syntax/token/character_set.rb +6 -1
- data/lib/regexp_parser/syntax/token/character_type.rb +6 -4
- data/lib/regexp_parser/syntax/token/conditional.rb +5 -3
- data/lib/regexp_parser/syntax/token/escape.rb +12 -8
- data/lib/regexp_parser/syntax/token/group.rb +8 -6
- data/lib/regexp_parser/syntax/token/keep.rb +3 -1
- data/lib/regexp_parser/syntax/token/meta.rb +11 -2
- data/lib/regexp_parser/syntax/token/posix_class.rb +4 -2
- data/lib/regexp_parser/syntax/token/quantifier.rb +8 -6
- data/lib/regexp_parser/syntax/token/unicode_property.rb +164 -70
- data/lib/regexp_parser/syntax/token/virtual.rb +13 -0
- data/lib/regexp_parser/syntax/token.rb +18 -16
- data/lib/regexp_parser/syntax/version_lookup.rb +4 -2
- data/lib/regexp_parser/syntax/versions/1.8.6.rb +2 -0
- data/lib/regexp_parser/syntax/versions/1.9.1.rb +2 -0
- data/lib/regexp_parser/syntax/versions/1.9.3.rb +2 -0
- data/lib/regexp_parser/syntax/versions/2.0.0.rb +2 -0
- data/lib/regexp_parser/syntax/versions/2.2.0.rb +2 -0
- data/lib/regexp_parser/syntax/versions/2.3.0.rb +2 -0
- data/lib/regexp_parser/syntax/versions/2.4.0.rb +2 -0
- data/lib/regexp_parser/syntax/versions/2.4.1.rb +2 -0
- data/lib/regexp_parser/syntax/versions/2.5.0.rb +2 -0
- data/lib/regexp_parser/syntax/versions/2.6.0.rb +2 -0
- data/lib/regexp_parser/syntax/versions/2.6.2.rb +2 -0
- data/lib/regexp_parser/syntax/versions/2.6.3.rb +2 -0
- data/lib/regexp_parser/syntax/versions/3.1.0.rb +2 -0
- data/lib/regexp_parser/syntax/versions/3.2.0.rb +2 -0
- data/lib/regexp_parser/syntax/versions/3.5.0.rb +4 -0
- data/lib/regexp_parser/syntax/versions/4.0.0.rb +4 -0
- data/lib/regexp_parser/syntax/versions.rb +3 -1
- data/lib/regexp_parser/syntax.rb +3 -1
- data/lib/regexp_parser/token.rb +2 -0
- data/lib/regexp_parser/version.rb +3 -1
- data/lib/regexp_parser.rb +8 -6
- data/regexp_parser.gemspec +7 -5
- metadata +19 -11
- data/CHANGELOG.md +0 -632
- data/README.md +0 -503
|
@@ -30,11 +30,6 @@
|
|
|
30
30
|
|
|
31
31
|
class_posix = ('[:' . '^'? . [^\[\]]* . ':]');
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
# these are not supported in ruby at the moment
|
|
35
|
-
collating_sequence = '[.' . (alpha | [\-])+ . '.]';
|
|
36
|
-
character_equivalent = '[=' . alpha . '=]';
|
|
37
|
-
|
|
38
33
|
line_anchor = beginning_of_line | end_of_line;
|
|
39
34
|
anchor_char = [AbBzZG];
|
|
40
35
|
|
|
@@ -42,7 +37,8 @@
|
|
|
42
37
|
octal_sequence = [0-7]{1,3};
|
|
43
38
|
|
|
44
39
|
hex_sequence = 'x' . xdigit{1,2};
|
|
45
|
-
hex_sequence_err = 'x' . [^0-
|
|
40
|
+
hex_sequence_err = 'x' . [^0-9A-Fa-f];
|
|
41
|
+
high_hex_sequence = 'x' . [89A-Fa-f] . xdigit . ( '\\x' . [89A-Fa-f] . xdigit )*;
|
|
46
42
|
|
|
47
43
|
codepoint_single = 'u' . xdigit{4};
|
|
48
44
|
codepoint_list = 'u{' . xdigit{1,6} . (space . xdigit{1,6})* . '}';
|
|
@@ -83,10 +79,9 @@
|
|
|
83
79
|
# try to treat every other group head as options group, like Ruby
|
|
84
80
|
group_options = '?' . ( [^!#'():<=>~]+ . ':'? ) ?;
|
|
85
81
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
group_number = '-'? . [1-9] . [0-9]*;
|
|
82
|
+
group_name_id_ab = ([^!=0-9\->] | utf8_multibyte) . ([^>] | utf8_multibyte)*;
|
|
83
|
+
group_name_id_sq = ([^0-9\-'] | utf8_multibyte) . ([^'] | utf8_multibyte)*;
|
|
84
|
+
group_number = '-'? . [0-9]+;
|
|
90
85
|
group_level = [+\-] . [0-9]+;
|
|
91
86
|
|
|
92
87
|
group_name = ('<' . group_name_id_ab? . '>') |
|
|
@@ -95,15 +90,11 @@
|
|
|
95
90
|
|
|
96
91
|
group_named = ('?' . group_name );
|
|
97
92
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
group_name_call = 'g' . (('<' . group_name_id_ab? . group_level? '>') |
|
|
101
|
-
("'" . group_name_id_sq? . group_level? "'"));
|
|
93
|
+
group_ref_body = (('<' . (group_name_id_ab? | group_number) . group_level? '>') |
|
|
94
|
+
("'" . (group_name_id_sq? | group_number) . group_level? "'"));
|
|
102
95
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
group_number_call = 'g' . (('<' . ((group_number . group_level?) | '0') '>') |
|
|
106
|
-
("'" . ((group_number . group_level?) | '0') "'"));
|
|
96
|
+
group_ref = 'k' . group_ref_body;
|
|
97
|
+
group_call = 'g' . group_ref_body;
|
|
107
98
|
|
|
108
99
|
group_type = group_atomic | group_passive | group_absence | group_named;
|
|
109
100
|
|
|
@@ -134,13 +125,13 @@
|
|
|
134
125
|
# EOF error, used where it can be detected
|
|
135
126
|
action premature_end_error {
|
|
136
127
|
text = copy(data, ts ? ts-1 : 0, -1)
|
|
137
|
-
raise PrematureEndError.new(
|
|
128
|
+
raise PrematureEndError.new(text)
|
|
138
129
|
}
|
|
139
130
|
|
|
140
131
|
# Invalid sequence error, used from sequences, like escapes and sets
|
|
141
132
|
action invalid_sequence_error {
|
|
142
133
|
text = copy(data, ts ? ts-1 : 0, -1)
|
|
143
|
-
|
|
134
|
+
raise ValidationError.for(:sequence, 'sequence', text)
|
|
144
135
|
}
|
|
145
136
|
|
|
146
137
|
# group (nesting) and set open/close actions
|
|
@@ -220,21 +211,13 @@
|
|
|
220
211
|
type = :nonposixclass
|
|
221
212
|
end
|
|
222
213
|
|
|
223
|
-
unless
|
|
224
|
-
|
|
214
|
+
unless POSIX_CLASSES[class_name]
|
|
215
|
+
raise ValidationError.for(:posix_class, text)
|
|
225
216
|
end
|
|
226
217
|
|
|
227
218
|
emit(type, class_name.to_sym, text)
|
|
228
219
|
};
|
|
229
220
|
|
|
230
|
-
# These are not supported in ruby at the moment. Enable them if they are.
|
|
231
|
-
# collating_sequence >(open_bracket, 1) @set_closed @eof(premature_end_error) {
|
|
232
|
-
# emit(:set, :collation, copy(data, ts, te))
|
|
233
|
-
# };
|
|
234
|
-
# character_equivalent >(open_bracket, 1) @set_closed @eof(premature_end_error) {
|
|
235
|
-
# emit(:set, :equivalent, copy(data, ts, te))
|
|
236
|
-
# };
|
|
237
|
-
|
|
238
221
|
meta_char > (set_meta, 1) {
|
|
239
222
|
emit(:literal, :literal, copy(data, ts, te))
|
|
240
223
|
};
|
|
@@ -264,7 +247,7 @@
|
|
|
264
247
|
# Treat all remaining escapes - those not supported in sets - as literal.
|
|
265
248
|
# (This currently includes \^, \-, \&, \:, although these could potentially
|
|
266
249
|
# be meta chars when not escaped, depending on their position in the set.)
|
|
267
|
-
any > (escaped_set_alpha, 1) {
|
|
250
|
+
(any | utf8_multibyte) > (escaped_set_alpha, 1) {
|
|
268
251
|
emit(:escape, :literal, copy(data, ts-1, te))
|
|
269
252
|
fret;
|
|
270
253
|
};
|
|
@@ -274,9 +257,21 @@
|
|
|
274
257
|
# escape sequence scanner
|
|
275
258
|
# --------------------------------------------------------------------------
|
|
276
259
|
escape_sequence := |*
|
|
277
|
-
[1-9] {
|
|
260
|
+
[1-9] . [0-9]* {
|
|
278
261
|
text = copy(data, ts-1, te)
|
|
279
|
-
|
|
262
|
+
|
|
263
|
+
# If not enough groups have been opened, there is a fallback to either an
|
|
264
|
+
# octal or literal interpretation for 2+ digit numerical escapes.
|
|
265
|
+
digits = text[1..-1]
|
|
266
|
+
if digits.size == 1 || digits.to_i <= capturing_group_count
|
|
267
|
+
emit(:backref, :number, text)
|
|
268
|
+
elsif digits =~ /\A[0-7]{2,}\z/
|
|
269
|
+
emit(:escape, :octal, text)
|
|
270
|
+
else
|
|
271
|
+
emit(:escape, :literal, text[0..1])
|
|
272
|
+
emit(:literal, :literal, text[2..-1])
|
|
273
|
+
end
|
|
274
|
+
|
|
280
275
|
fret;
|
|
281
276
|
};
|
|
282
277
|
|
|
@@ -285,6 +280,13 @@
|
|
|
285
280
|
fret;
|
|
286
281
|
};
|
|
287
282
|
|
|
283
|
+
[8-9] . [0-9] { # special case, emits two tokens
|
|
284
|
+
text = copy(data, ts-1, te)
|
|
285
|
+
emit(:escape, :literal, text[0, 2])
|
|
286
|
+
emit(:literal, :literal, text[2])
|
|
287
|
+
fret;
|
|
288
|
+
};
|
|
289
|
+
|
|
288
290
|
meta_char {
|
|
289
291
|
case text = copy(data, ts-1, te)
|
|
290
292
|
when '\.'; emit(:escape, :dot, text)
|
|
@@ -332,6 +334,16 @@
|
|
|
332
334
|
fret;
|
|
333
335
|
};
|
|
334
336
|
|
|
337
|
+
high_hex_sequence > (escaped_alpha, 5) {
|
|
338
|
+
text = copy(data, ts-1, te)
|
|
339
|
+
if regexp_encoding == Encoding::BINARY
|
|
340
|
+
text.split(/(?=\\)/).each { |part| emit(:escape, :hex, part) }
|
|
341
|
+
else
|
|
342
|
+
emit(:escape, :utf8_hex, text)
|
|
343
|
+
end
|
|
344
|
+
fret;
|
|
345
|
+
};
|
|
346
|
+
|
|
335
347
|
hex_sequence > (escaped_alpha, 5) @eof(premature_end_error) {
|
|
336
348
|
emit(:escape, :hex, copy(data, ts-1, te))
|
|
337
349
|
fret;
|
|
@@ -375,6 +387,7 @@
|
|
|
375
387
|
conditional_expression := |*
|
|
376
388
|
group_lookup . ')' {
|
|
377
389
|
text = copy(data, ts, te-1)
|
|
390
|
+
text =~ /[^0]/ or raise ValidationError.for(:backref, 'condition', 'invalid ref ID')
|
|
378
391
|
emit(:conditional, :condition, text)
|
|
379
392
|
emit(:conditional, :condition_close, ')')
|
|
380
393
|
};
|
|
@@ -457,10 +470,9 @@
|
|
|
457
470
|
|
|
458
471
|
# (?#...) comments: parsed as a single expression, without introducing a
|
|
459
472
|
# new nesting level. Comments may not include parentheses, escaped or not.
|
|
460
|
-
# special case for close
|
|
461
|
-
# correct closing count.
|
|
473
|
+
# special case for close to get the correct closing count.
|
|
462
474
|
# ------------------------------------------------------------------------
|
|
463
|
-
group_open . group_comment
|
|
475
|
+
(group_open . group_comment) @group_closed {
|
|
464
476
|
emit(:group, :comment, copy(data, ts, te))
|
|
465
477
|
};
|
|
466
478
|
|
|
@@ -475,10 +487,10 @@
|
|
|
475
487
|
#
|
|
476
488
|
# (?imxdau-imx:subexp) option on/off for subexp
|
|
477
489
|
# ------------------------------------------------------------------------
|
|
478
|
-
group_open . group_options >group_opened {
|
|
490
|
+
(group_open . group_options) >group_opened {
|
|
479
491
|
text = copy(data, ts, te)
|
|
480
492
|
if text[2..-1] =~ /([^\-mixdau:]|^$)|-.*([dau])/
|
|
481
|
-
|
|
493
|
+
raise ValidationError.for(:group_option, $1 || "-#{$2}", text)
|
|
482
494
|
end
|
|
483
495
|
emit_options(text)
|
|
484
496
|
};
|
|
@@ -489,7 +501,7 @@
|
|
|
489
501
|
# (?<=subexp) look-behind
|
|
490
502
|
# (?<!subexp) negative look-behind
|
|
491
503
|
# ------------------------------------------------------------------------
|
|
492
|
-
group_open . assertion_type >group_opened {
|
|
504
|
+
(group_open . assertion_type) >group_opened {
|
|
493
505
|
case text = copy(data, ts, te)
|
|
494
506
|
when '(?='; emit(:assertion, :lookahead, text)
|
|
495
507
|
when '(?!'; emit(:assertion, :nlookahead, text)
|
|
@@ -506,14 +518,14 @@
|
|
|
506
518
|
# (?'name'subexp) named group (single quoted version)
|
|
507
519
|
# (subexp) captured group
|
|
508
520
|
# ------------------------------------------------------------------------
|
|
509
|
-
group_open . group_type >group_opened {
|
|
521
|
+
(group_open . group_type) >group_opened {
|
|
510
522
|
case text = copy(data, ts, te)
|
|
511
523
|
when '(?:'; emit(:group, :passive, text)
|
|
512
524
|
when '(?>'; emit(:group, :atomic, text)
|
|
513
525
|
when '(?~'; emit(:group, :absence, text)
|
|
514
526
|
|
|
515
527
|
when /^\(\?(?:<>|'')/
|
|
516
|
-
|
|
528
|
+
raise ValidationError.for(:group, 'named group', 'name is empty')
|
|
517
529
|
|
|
518
530
|
when /^\(\?<[^>]+>/
|
|
519
531
|
emit(:group, :named_ab, text)
|
|
@@ -525,6 +537,7 @@
|
|
|
525
537
|
};
|
|
526
538
|
|
|
527
539
|
group_open @group_opened {
|
|
540
|
+
self.capturing_group_count = capturing_group_count + 1
|
|
528
541
|
text = copy(data, ts, te)
|
|
529
542
|
emit(:group, :capture, text)
|
|
530
543
|
};
|
|
@@ -533,7 +546,7 @@
|
|
|
533
546
|
if conditional_stack.last == group_depth + 1
|
|
534
547
|
conditional_stack.pop
|
|
535
548
|
emit(:conditional, :close, ')')
|
|
536
|
-
|
|
549
|
+
elsif group_depth >= 0
|
|
537
550
|
if spacing_stack.length > 1 &&
|
|
538
551
|
spacing_stack.last[:depth] == group_depth + 1
|
|
539
552
|
spacing_stack.pop
|
|
@@ -541,41 +554,43 @@
|
|
|
541
554
|
end
|
|
542
555
|
|
|
543
556
|
emit(:group, :close, ')')
|
|
557
|
+
else
|
|
558
|
+
raise ValidationError.for(:group, 'group', 'unmatched close parenthesis')
|
|
544
559
|
end
|
|
545
560
|
};
|
|
546
561
|
|
|
547
562
|
|
|
548
563
|
# Group backreference, named and numbered
|
|
549
564
|
# ------------------------------------------------------------------------
|
|
550
|
-
backslash . (
|
|
565
|
+
backslash . (group_ref) > (backslashed, 4) {
|
|
551
566
|
case text = copy(data, ts, te)
|
|
552
|
-
when /^\\k(
|
|
553
|
-
validation_error(:backref, 'backreference', 'ref ID is empty')
|
|
554
|
-
when /^\\k(.)[^\p{digit}\-][^+\-]*\D$/
|
|
567
|
+
when /^\\k(.)[^0-9\-][^+\-]*['>]$/
|
|
555
568
|
emit(:backref, $1 == '<' ? :name_ref_ab : :name_ref_sq, text)
|
|
556
|
-
when /^\\k(.)\d
|
|
569
|
+
when /^\\k(.)0*[1-9]\d*['>]$/
|
|
557
570
|
emit(:backref, $1 == '<' ? :number_ref_ab : :number_ref_sq, text)
|
|
558
|
-
when /^\\k(.)
|
|
571
|
+
when /^\\k(.)-0*[1-9]\d*['>]$/
|
|
559
572
|
emit(:backref, $1 == '<' ? :number_rel_ref_ab : :number_rel_ref_sq, text)
|
|
560
|
-
when /^\\k(.)[
|
|
573
|
+
when /^\\k(.)[^0-9\-].*[+\-]\d+['>]$/
|
|
561
574
|
emit(:backref, $1 == '<' ? :name_recursion_ref_ab : :name_recursion_ref_sq, text)
|
|
562
|
-
when /^\\k(.)
|
|
575
|
+
when /^\\k(.)-?0*[1-9]\d*[+\-]\d+['>]$/
|
|
563
576
|
emit(:backref, $1 == '<' ? :number_recursion_ref_ab : :number_recursion_ref_sq, text)
|
|
577
|
+
else
|
|
578
|
+
raise ValidationError.for(:backref, 'backreference', 'invalid ref ID')
|
|
564
579
|
end
|
|
565
580
|
};
|
|
566
581
|
|
|
567
582
|
# Group call, named and numbered
|
|
568
583
|
# ------------------------------------------------------------------------
|
|
569
|
-
backslash . (
|
|
584
|
+
backslash . (group_call) > (backslashed, 4) {
|
|
570
585
|
case text = copy(data, ts, te)
|
|
571
|
-
when /^\\g(
|
|
572
|
-
validation_error(:backref, 'subexpression call', 'ref ID is empty')
|
|
573
|
-
when /^\\g(.)[^\p{digit}+\->][^+\-]*/
|
|
586
|
+
when /^\\g(.)[^0-9+\-].*['>]$/
|
|
574
587
|
emit(:backref, $1 == '<' ? :name_call_ab : :name_call_sq, text)
|
|
575
|
-
when /^\\g(.)\d
|
|
588
|
+
when /^\\g(.)(?:0|0*[1-9]\d*)['>]$/
|
|
576
589
|
emit(:backref, $1 == '<' ? :number_call_ab : :number_call_sq, text)
|
|
577
|
-
when /^\\g(.)[+-]\d
|
|
590
|
+
when /^\\g(.)[+-]0*[1-9]\d*/
|
|
578
591
|
emit(:backref, $1 == '<' ? :number_rel_call_ab : :number_rel_call_sq, text)
|
|
592
|
+
else
|
|
593
|
+
raise ValidationError.for(:backref, 'subexpression call', 'invalid ref ID')
|
|
579
594
|
end
|
|
580
595
|
};
|
|
581
596
|
|
|
@@ -649,72 +664,11 @@
|
|
|
649
664
|
*|;
|
|
650
665
|
}%%
|
|
651
666
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
require 'regexp_parser/error'
|
|
667
|
+
require_relative 'scanner/errors/scanner_error'
|
|
668
|
+
require_relative 'scanner/errors/premature_end_error'
|
|
669
|
+
require_relative 'scanner/errors/validation_error'
|
|
656
670
|
|
|
657
671
|
class Regexp::Scanner
|
|
658
|
-
# General scanner error (catch all)
|
|
659
|
-
class ScannerError < Regexp::Parser::Error; end
|
|
660
|
-
|
|
661
|
-
# Base for all scanner validation errors
|
|
662
|
-
class ValidationError < Regexp::Parser::Error
|
|
663
|
-
def initialize(reason)
|
|
664
|
-
super reason
|
|
665
|
-
end
|
|
666
|
-
end
|
|
667
|
-
|
|
668
|
-
# Unexpected end of pattern
|
|
669
|
-
class PrematureEndError < ScannerError
|
|
670
|
-
def initialize(where = '')
|
|
671
|
-
super "Premature end of pattern at #{where}"
|
|
672
|
-
end
|
|
673
|
-
end
|
|
674
|
-
|
|
675
|
-
# Invalid sequence format. Used for escape sequences, mainly.
|
|
676
|
-
class InvalidSequenceError < ValidationError
|
|
677
|
-
def initialize(what = 'sequence', where = '')
|
|
678
|
-
super "Invalid #{what} at #{where}"
|
|
679
|
-
end
|
|
680
|
-
end
|
|
681
|
-
|
|
682
|
-
# Invalid group. Used for named groups.
|
|
683
|
-
class InvalidGroupError < ValidationError
|
|
684
|
-
def initialize(what, reason)
|
|
685
|
-
super "Invalid #{what}, #{reason}."
|
|
686
|
-
end
|
|
687
|
-
end
|
|
688
|
-
|
|
689
|
-
# Invalid groupOption. Used for inline options.
|
|
690
|
-
# TODO: should become InvalidGroupOptionError in v3.0.0 for consistency
|
|
691
|
-
class InvalidGroupOption < ValidationError
|
|
692
|
-
def initialize(option, text)
|
|
693
|
-
super "Invalid group option #{option} in #{text}"
|
|
694
|
-
end
|
|
695
|
-
end
|
|
696
|
-
|
|
697
|
-
# Invalid back reference. Used for name a number refs/calls.
|
|
698
|
-
class InvalidBackrefError < ValidationError
|
|
699
|
-
def initialize(what, reason)
|
|
700
|
-
super "Invalid back reference #{what}, #{reason}"
|
|
701
|
-
end
|
|
702
|
-
end
|
|
703
|
-
|
|
704
|
-
# The property name was not recognized by the scanner.
|
|
705
|
-
class UnknownUnicodePropertyError < ValidationError
|
|
706
|
-
def initialize(name)
|
|
707
|
-
super "Unknown unicode character property name #{name}"
|
|
708
|
-
end
|
|
709
|
-
end
|
|
710
|
-
|
|
711
|
-
# The POSIX class name was not recognized by the scanner.
|
|
712
|
-
class UnknownPosixClassError < ValidationError
|
|
713
|
-
def initialize(text)
|
|
714
|
-
super "Unknown POSIX class #{text}"
|
|
715
|
-
end
|
|
716
|
-
end
|
|
717
|
-
|
|
718
672
|
# Scans the given regular expression text, or Regexp object and collects the
|
|
719
673
|
# emitted token into an array that gets returned at the end. If a block is
|
|
720
674
|
# given, it gets called for each emitted token.
|
|
@@ -732,6 +686,7 @@ class Regexp::Scanner
|
|
|
732
686
|
|
|
733
687
|
input = input_object.is_a?(Regexp) ? input_object.source : input_object
|
|
734
688
|
self.free_spacing = free_spacing?(input_object, options)
|
|
689
|
+
self.regexp_encoding = extract_encoding(input_object, options)
|
|
735
690
|
self.spacing_stack = [{:free_spacing => free_spacing, :depth => 0}]
|
|
736
691
|
|
|
737
692
|
data = input.unpack("c*")
|
|
@@ -742,6 +697,7 @@ class Regexp::Scanner
|
|
|
742
697
|
|
|
743
698
|
self.set_depth = 0
|
|
744
699
|
self.group_depth = 0
|
|
700
|
+
self.capturing_group_count = 0
|
|
745
701
|
self.conditional_stack = []
|
|
746
702
|
self.char_pos = 0
|
|
747
703
|
|
|
@@ -781,10 +737,11 @@ class Regexp::Scanner
|
|
|
781
737
|
File.read("#{__dir__}/scanner/properties/#{name}.csv").scan(/(.+),(.+)/).to_h
|
|
782
738
|
end
|
|
783
739
|
|
|
784
|
-
|
|
740
|
+
# Use each_with_object for required_ruby_version >= 2.2, or #to_h for >= 2.6
|
|
741
|
+
POSIX_CLASSES =
|
|
785
742
|
%w[alnum alpha ascii blank cntrl digit graph
|
|
786
743
|
lower print punct space upper word xdigit]
|
|
787
|
-
|
|
744
|
+
.inject({}) { |o, e| o.merge(e => true) }.freeze
|
|
788
745
|
|
|
789
746
|
# Emits an array with the details of the scanned pattern
|
|
790
747
|
def emit(type, token, text)
|
|
@@ -812,16 +769,25 @@ class Regexp::Scanner
|
|
|
812
769
|
end
|
|
813
770
|
end
|
|
814
771
|
|
|
815
|
-
attr_accessor :literal_run # only public for #||= to work on ruby <= 2.5
|
|
772
|
+
attr_accessor :capturing_group_count, :literal_run # only public for #||= to work on ruby <= 2.5
|
|
816
773
|
|
|
817
774
|
private
|
|
818
775
|
|
|
819
776
|
attr_accessor :block,
|
|
820
777
|
:collect_tokens, :tokens, :prev_token,
|
|
821
778
|
:free_spacing, :spacing_stack,
|
|
779
|
+
:regexp_encoding,
|
|
822
780
|
:group_depth, :set_depth, :conditional_stack,
|
|
823
781
|
:char_pos
|
|
824
782
|
|
|
783
|
+
def extract_encoding(input_object, options)
|
|
784
|
+
if input_object.is_a?(::Regexp)
|
|
785
|
+
input_object.encoding
|
|
786
|
+
elsif options && (options & Regexp::NOENCODING)
|
|
787
|
+
Encoding::BINARY
|
|
788
|
+
end
|
|
789
|
+
end
|
|
790
|
+
|
|
825
791
|
def free_spacing?(input_object, options)
|
|
826
792
|
if options && !input_object.is_a?(String)
|
|
827
793
|
raise ArgumentError, 'options cannot be supplied unless scanning a String'
|
|
@@ -891,24 +857,8 @@ class Regexp::Scanner
|
|
|
891
857
|
|
|
892
858
|
def emit_meta_control_sequence(data, ts, te, token)
|
|
893
859
|
if data.last < 0x00 || data.last > 0x7F
|
|
894
|
-
|
|
860
|
+
raise ValidationError.for(:sequence, 'escape', token.to_s)
|
|
895
861
|
end
|
|
896
862
|
emit(:escape, token, copy(data, ts-1, te))
|
|
897
863
|
end
|
|
898
|
-
|
|
899
|
-
# Centralizes and unifies the handling of validation related
|
|
900
|
-
# errors.
|
|
901
|
-
def validation_error(type, what, reason = nil)
|
|
902
|
-
error =
|
|
903
|
-
case type
|
|
904
|
-
when :backref then InvalidBackrefError.new(what, reason)
|
|
905
|
-
when :group then InvalidGroupError.new(what, reason)
|
|
906
|
-
when :group_option then InvalidGroupOption.new(what, reason)
|
|
907
|
-
when :posix_class then UnknownPosixClassError.new(what)
|
|
908
|
-
when :property then UnknownUnicodePropertyError.new(what)
|
|
909
|
-
when :sequence then InvalidSequenceError.new(what, reason)
|
|
910
|
-
end
|
|
911
|
-
|
|
912
|
-
raise error # unless @@config.validation_ignore
|
|
913
|
-
end
|
|
914
864
|
end # module Regexp::Scanner
|