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.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -5
  3. data/LICENSE +1 -1
  4. data/Rakefile +5 -3
  5. data/lib/regexp_parser/error.rb +2 -0
  6. data/lib/regexp_parser/expression/base.rb +2 -7
  7. data/lib/regexp_parser/expression/classes/alternation.rb +3 -1
  8. data/lib/regexp_parser/expression/classes/anchor.rb +2 -0
  9. data/lib/regexp_parser/expression/classes/backreference.rb +7 -26
  10. data/lib/regexp_parser/expression/classes/character_set/intersection.rb +2 -0
  11. data/lib/regexp_parser/expression/classes/character_set/range.rb +4 -7
  12. data/lib/regexp_parser/expression/classes/character_set.rb +6 -8
  13. data/lib/regexp_parser/expression/classes/character_type.rb +2 -0
  14. data/lib/regexp_parser/expression/classes/conditional.rb +4 -28
  15. data/lib/regexp_parser/expression/classes/escape_sequence.rb +29 -96
  16. data/lib/regexp_parser/expression/classes/free_space.rb +5 -1
  17. data/lib/regexp_parser/expression/classes/group.rb +2 -22
  18. data/lib/regexp_parser/expression/classes/keep.rb +3 -1
  19. data/lib/regexp_parser/expression/classes/literal.rb +2 -0
  20. data/lib/regexp_parser/expression/classes/posix_class.rb +7 -5
  21. data/lib/regexp_parser/expression/classes/root.rb +2 -0
  22. data/lib/regexp_parser/expression/classes/unicode_property.rb +13 -11
  23. data/lib/regexp_parser/expression/methods/construct.rb +4 -4
  24. data/lib/regexp_parser/expression/methods/escape_sequence_char.rb +7 -0
  25. data/lib/regexp_parser/expression/methods/escape_sequence_codepoint.rb +76 -0
  26. data/lib/regexp_parser/expression/methods/human_name.rb +2 -0
  27. data/lib/regexp_parser/expression/methods/match.rb +2 -0
  28. data/lib/regexp_parser/expression/methods/match_length.rb +2 -0
  29. data/lib/regexp_parser/expression/methods/negative.rb +22 -0
  30. data/lib/regexp_parser/expression/methods/options.rb +2 -0
  31. data/lib/regexp_parser/expression/methods/parts.rb +25 -0
  32. data/lib/regexp_parser/expression/methods/printing.rb +28 -0
  33. data/lib/regexp_parser/expression/methods/referenced_expressions.rb +30 -0
  34. data/lib/regexp_parser/expression/methods/strfregexp.rb +2 -0
  35. data/lib/regexp_parser/expression/methods/tests.rb +42 -3
  36. data/lib/regexp_parser/expression/methods/traverse.rb +35 -20
  37. data/lib/regexp_parser/expression/quantifier.rb +33 -18
  38. data/lib/regexp_parser/expression/sequence.rb +7 -9
  39. data/lib/regexp_parser/expression/sequence_operation.rb +6 -9
  40. data/lib/regexp_parser/expression/shared.rb +40 -24
  41. data/lib/regexp_parser/expression/subexpression.rb +22 -18
  42. data/lib/regexp_parser/expression.rb +39 -31
  43. data/lib/regexp_parser/lexer.rb +17 -7
  44. data/lib/regexp_parser/parser.rb +101 -95
  45. data/lib/regexp_parser/scanner/errors/premature_end_error.rb +10 -0
  46. data/lib/regexp_parser/scanner/errors/scanner_error.rb +8 -0
  47. data/lib/regexp_parser/scanner/errors/validation_error.rb +65 -0
  48. data/lib/regexp_parser/scanner/properties/long.csv +61 -0
  49. data/lib/regexp_parser/scanner/properties/short.csv +15 -0
  50. data/lib/regexp_parser/scanner/property.rl +1 -1
  51. data/lib/regexp_parser/scanner/scanner.rl +90 -140
  52. data/lib/regexp_parser/scanner.rb +1336 -1450
  53. data/lib/regexp_parser/syntax/any.rb +2 -0
  54. data/lib/regexp_parser/syntax/base.rb +2 -0
  55. data/lib/regexp_parser/syntax/token/anchor.rb +5 -3
  56. data/lib/regexp_parser/syntax/token/assertion.rb +4 -2
  57. data/lib/regexp_parser/syntax/token/backreference.rb +11 -6
  58. data/lib/regexp_parser/syntax/token/character_set.rb +6 -1
  59. data/lib/regexp_parser/syntax/token/character_type.rb +6 -4
  60. data/lib/regexp_parser/syntax/token/conditional.rb +5 -3
  61. data/lib/regexp_parser/syntax/token/escape.rb +12 -8
  62. data/lib/regexp_parser/syntax/token/group.rb +8 -6
  63. data/lib/regexp_parser/syntax/token/keep.rb +3 -1
  64. data/lib/regexp_parser/syntax/token/meta.rb +11 -2
  65. data/lib/regexp_parser/syntax/token/posix_class.rb +4 -2
  66. data/lib/regexp_parser/syntax/token/quantifier.rb +8 -6
  67. data/lib/regexp_parser/syntax/token/unicode_property.rb +164 -70
  68. data/lib/regexp_parser/syntax/token/virtual.rb +13 -0
  69. data/lib/regexp_parser/syntax/token.rb +18 -16
  70. data/lib/regexp_parser/syntax/version_lookup.rb +4 -2
  71. data/lib/regexp_parser/syntax/versions/1.8.6.rb +2 -0
  72. data/lib/regexp_parser/syntax/versions/1.9.1.rb +2 -0
  73. data/lib/regexp_parser/syntax/versions/1.9.3.rb +2 -0
  74. data/lib/regexp_parser/syntax/versions/2.0.0.rb +2 -0
  75. data/lib/regexp_parser/syntax/versions/2.2.0.rb +2 -0
  76. data/lib/regexp_parser/syntax/versions/2.3.0.rb +2 -0
  77. data/lib/regexp_parser/syntax/versions/2.4.0.rb +2 -0
  78. data/lib/regexp_parser/syntax/versions/2.4.1.rb +2 -0
  79. data/lib/regexp_parser/syntax/versions/2.5.0.rb +2 -0
  80. data/lib/regexp_parser/syntax/versions/2.6.0.rb +2 -0
  81. data/lib/regexp_parser/syntax/versions/2.6.2.rb +2 -0
  82. data/lib/regexp_parser/syntax/versions/2.6.3.rb +2 -0
  83. data/lib/regexp_parser/syntax/versions/3.1.0.rb +2 -0
  84. data/lib/regexp_parser/syntax/versions/3.2.0.rb +2 -0
  85. data/lib/regexp_parser/syntax/versions/3.5.0.rb +4 -0
  86. data/lib/regexp_parser/syntax/versions/4.0.0.rb +4 -0
  87. data/lib/regexp_parser/syntax/versions.rb +3 -1
  88. data/lib/regexp_parser/syntax.rb +3 -1
  89. data/lib/regexp_parser/token.rb +2 -0
  90. data/lib/regexp_parser/version.rb +3 -1
  91. data/lib/regexp_parser.rb +8 -6
  92. data/regexp_parser.gemspec +7 -5
  93. metadata +19 -11
  94. data/CHANGELOG.md +0 -632
  95. 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-9a-fA-F{];
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
- group_ref = [gk];
87
- group_name_id_ab = ([^!0-9\->] | utf8_multibyte) . ([^>] | utf8_multibyte)*;
88
- group_name_id_sq = ([^0-9\-'] | utf8_multibyte) . ([^'] | utf8_multibyte)*;
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
- group_name_backref = 'k' . (('<' . group_name_id_ab? . group_level? '>') |
99
- ("'" . group_name_id_sq? . group_level? "'"));
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
- group_number_backref = 'k' . (('<' . group_number . group_level? '>') |
104
- ("'" . group_number . group_level? "'"));
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( text )
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
- validation_error(:sequence, 'sequence', text)
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 self.class.posix_classes.include?(class_name)
224
- validation_error(:posix_class, text)
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
- emit(:backref, :number, text)
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, action performed on all transitions to get the
461
- # correct closing count.
473
+ # special case for close to get the correct closing count.
462
474
  # ------------------------------------------------------------------------
463
- group_open . group_comment $group_closed {
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
- validation_error(:group_option, $1 || "-#{$2}", text)
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
- validation_error(:group, 'named group', 'name is empty')
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
- else
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 . (group_name_backref | group_number_backref) > (backslashed, 4) {
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+\D$/
569
+ when /^\\k(.)0*[1-9]\d*['>]$/
557
570
  emit(:backref, $1 == '<' ? :number_ref_ab : :number_ref_sq, text)
558
- when /^\\k(.)-\d+\D$/
571
+ when /^\\k(.)-0*[1-9]\d*['>]$/
559
572
  emit(:backref, $1 == '<' ? :number_rel_ref_ab : :number_rel_ref_sq, text)
560
- when /^\\k(.)[^\p{digit}\-].*[+\-]\d+\D$/
573
+ when /^\\k(.)[^0-9\-].*[+\-]\d+['>]$/
561
574
  emit(:backref, $1 == '<' ? :name_recursion_ref_ab : :name_recursion_ref_sq, text)
562
- when /^\\k(.)-?\d+[+\-]\d+\D$/
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 . (group_name_call | group_number_call) > (backslashed, 4) {
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+\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
- # THIS IS A GENERATED FILE, DO NOT EDIT DIRECTLY
653
- # This file was generated from lib/regexp_parser/scanner/scanner.rl
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
- def self.posix_classes
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
- end
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
- validation_error(:sequence, 'escape', token.to_s)
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