regexp_parser 2.8.1 → 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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +6 -4
  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 -0
  7. data/lib/regexp_parser/expression/classes/alternation.rb +2 -0
  8. data/lib/regexp_parser/expression/classes/anchor.rb +2 -0
  9. data/lib/regexp_parser/expression/classes/backreference.rb +3 -20
  10. data/lib/regexp_parser/expression/classes/character_set/intersection.rb +2 -0
  11. data/lib/regexp_parser/expression/classes/character_set/range.rb +2 -0
  12. data/lib/regexp_parser/expression/classes/character_set.rb +3 -4
  13. data/lib/regexp_parser/expression/classes/character_type.rb +2 -0
  14. data/lib/regexp_parser/expression/classes/conditional.rb +2 -14
  15. data/lib/regexp_parser/expression/classes/escape_sequence.rb +26 -95
  16. data/lib/regexp_parser/expression/classes/free_space.rb +2 -0
  17. data/lib/regexp_parser/expression/classes/group.rb +2 -0
  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 +2 -4
  21. data/lib/regexp_parser/expression/classes/root.rb +2 -0
  22. data/lib/regexp_parser/expression/classes/unicode_property.rb +8 -9
  23. data/lib/regexp_parser/expression/methods/construct.rb +2 -0
  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 +2 -0
  32. data/lib/regexp_parser/expression/methods/printing.rb +2 -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 +2 -0
  36. data/lib/regexp_parser/expression/methods/traverse.rb +2 -0
  37. data/lib/regexp_parser/expression/quantifier.rb +3 -1
  38. data/lib/regexp_parser/expression/sequence.rb +2 -0
  39. data/lib/regexp_parser/expression/sequence_operation.rb +2 -0
  40. data/lib/regexp_parser/expression/shared.rb +6 -3
  41. data/lib/regexp_parser/expression/subexpression.rb +2 -0
  42. data/lib/regexp_parser/expression.rb +39 -33
  43. data/lib/regexp_parser/lexer.rb +2 -0
  44. data/lib/regexp_parser/parser.rb +16 -9
  45. data/lib/regexp_parser/scanner/errors/premature_end_error.rb +2 -0
  46. data/lib/regexp_parser/scanner/errors/scanner_error.rb +3 -1
  47. data/lib/regexp_parser/scanner/errors/validation_error.rb +2 -0
  48. data/lib/regexp_parser/scanner/properties/long.csv +50 -0
  49. data/lib/regexp_parser/scanner/properties/short.csv +13 -0
  50. data/lib/regexp_parser/scanner/scanner.rl +62 -18
  51. data/lib/regexp_parser/scanner.rb +1041 -936
  52. data/lib/regexp_parser/syntax/any.rb +2 -0
  53. data/lib/regexp_parser/syntax/base.rb +2 -0
  54. data/lib/regexp_parser/syntax/token/anchor.rb +5 -3
  55. data/lib/regexp_parser/syntax/token/assertion.rb +4 -2
  56. data/lib/regexp_parser/syntax/token/backreference.rb +8 -6
  57. data/lib/regexp_parser/syntax/token/character_set.rb +3 -1
  58. data/lib/regexp_parser/syntax/token/character_type.rb +6 -4
  59. data/lib/regexp_parser/syntax/token/conditional.rb +5 -3
  60. data/lib/regexp_parser/syntax/token/escape.rb +9 -7
  61. data/lib/regexp_parser/syntax/token/group.rb +8 -6
  62. data/lib/regexp_parser/syntax/token/keep.rb +3 -1
  63. data/lib/regexp_parser/syntax/token/meta.rb +4 -2
  64. data/lib/regexp_parser/syntax/token/posix_class.rb +4 -2
  65. data/lib/regexp_parser/syntax/token/quantifier.rb +8 -6
  66. data/lib/regexp_parser/syntax/token/unicode_property.rb +149 -71
  67. data/lib/regexp_parser/syntax/token/virtual.rb +5 -3
  68. data/lib/regexp_parser/syntax/token.rb +18 -16
  69. data/lib/regexp_parser/syntax/version_lookup.rb +4 -2
  70. data/lib/regexp_parser/syntax/versions/1.8.6.rb +2 -0
  71. data/lib/regexp_parser/syntax/versions/1.9.1.rb +2 -0
  72. data/lib/regexp_parser/syntax/versions/1.9.3.rb +2 -0
  73. data/lib/regexp_parser/syntax/versions/2.0.0.rb +2 -0
  74. data/lib/regexp_parser/syntax/versions/2.2.0.rb +2 -0
  75. data/lib/regexp_parser/syntax/versions/2.3.0.rb +2 -0
  76. data/lib/regexp_parser/syntax/versions/2.4.0.rb +2 -0
  77. data/lib/regexp_parser/syntax/versions/2.4.1.rb +2 -0
  78. data/lib/regexp_parser/syntax/versions/2.5.0.rb +2 -0
  79. data/lib/regexp_parser/syntax/versions/2.6.0.rb +2 -0
  80. data/lib/regexp_parser/syntax/versions/2.6.2.rb +2 -0
  81. data/lib/regexp_parser/syntax/versions/2.6.3.rb +2 -0
  82. data/lib/regexp_parser/syntax/versions/3.1.0.rb +2 -0
  83. data/lib/regexp_parser/syntax/versions/3.2.0.rb +2 -0
  84. data/lib/regexp_parser/syntax/versions/3.5.0.rb +4 -0
  85. data/lib/regexp_parser/syntax/versions/4.0.0.rb +4 -0
  86. data/lib/regexp_parser/syntax/versions.rb +3 -1
  87. data/lib/regexp_parser/syntax.rb +3 -1
  88. data/lib/regexp_parser/token.rb +2 -0
  89. data/lib/regexp_parser/version.rb +3 -1
  90. data/lib/regexp_parser.rb +8 -6
  91. data/regexp_parser.gemspec +7 -5
  92. metadata +13 -11
  93. data/CHANGELOG.md +0 -691
  94. data/README.md +0 -506
@@ -37,7 +37,8 @@
37
37
  octal_sequence = [0-7]{1,3};
38
38
 
39
39
  hex_sequence = 'x' . xdigit{1,2};
40
- 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 )*;
41
42
 
42
43
  codepoint_single = 'u' . xdigit{4};
43
44
  codepoint_list = 'u{' . xdigit{1,6} . (space . xdigit{1,6})* . '}';
@@ -78,8 +79,8 @@
78
79
  # try to treat every other group head as options group, like Ruby
79
80
  group_options = '?' . ( [^!#'():<=>~]+ . ':'? ) ?;
80
81
 
81
- group_name_id_ab = ([^!0-9\->] | utf8_multibyte) . ([^>] | utf8_multibyte)*;
82
- group_name_id_sq = ([^0-9\-'] | utf8_multibyte) . ([^'] | utf8_multibyte)*;
82
+ group_name_id_ab = ([^!=0-9\->] | utf8_multibyte) . ([^>] | utf8_multibyte)*;
83
+ group_name_id_sq = ([^0-9\-'] | utf8_multibyte) . ([^'] | utf8_multibyte)*;
83
84
  group_number = '-'? . [0-9]+;
84
85
  group_level = [+\-] . [0-9]+;
85
86
 
@@ -210,7 +211,7 @@
210
211
  type = :nonposixclass
211
212
  end
212
213
 
213
- unless self.class.posix_classes.include?(class_name)
214
+ unless POSIX_CLASSES[class_name]
214
215
  raise ValidationError.for(:posix_class, text)
215
216
  end
216
217
 
@@ -246,7 +247,7 @@
246
247
  # Treat all remaining escapes - those not supported in sets - as literal.
247
248
  # (This currently includes \^, \-, \&, \:, although these could potentially
248
249
  # be meta chars when not escaped, depending on their position in the set.)
249
- any > (escaped_set_alpha, 1) {
250
+ (any | utf8_multibyte) > (escaped_set_alpha, 1) {
250
251
  emit(:escape, :literal, copy(data, ts-1, te))
251
252
  fret;
252
253
  };
@@ -256,9 +257,21 @@
256
257
  # escape sequence scanner
257
258
  # --------------------------------------------------------------------------
258
259
  escape_sequence := |*
259
- [1-9] {
260
+ [1-9] . [0-9]* {
260
261
  text = copy(data, ts-1, te)
261
- 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
+
262
275
  fret;
263
276
  };
264
277
 
@@ -267,6 +280,13 @@
267
280
  fret;
268
281
  };
269
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
+
270
290
  meta_char {
271
291
  case text = copy(data, ts-1, te)
272
292
  when '\.'; emit(:escape, :dot, text)
@@ -314,6 +334,16 @@
314
334
  fret;
315
335
  };
316
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
+
317
347
  hex_sequence > (escaped_alpha, 5) @eof(premature_end_error) {
318
348
  emit(:escape, :hex, copy(data, ts-1, te))
319
349
  fret;
@@ -357,6 +387,7 @@
357
387
  conditional_expression := |*
358
388
  group_lookup . ')' {
359
389
  text = copy(data, ts, te-1)
390
+ text =~ /[^0]/ or raise ValidationError.for(:backref, 'condition', 'invalid ref ID')
360
391
  emit(:conditional, :condition, text)
361
392
  emit(:conditional, :condition_close, ')')
362
393
  };
@@ -506,6 +537,7 @@
506
537
  };
507
538
 
508
539
  group_open @group_opened {
540
+ self.capturing_group_count = capturing_group_count + 1
509
541
  text = copy(data, ts, te)
510
542
  emit(:group, :capture, text)
511
543
  };
@@ -534,13 +566,13 @@
534
566
  case text = copy(data, ts, te)
535
567
  when /^\\k(.)[^0-9\-][^+\-]*['>]$/
536
568
  emit(:backref, $1 == '<' ? :name_ref_ab : :name_ref_sq, text)
537
- when /^\\k(.)[1-9]\d*['>]$/
569
+ when /^\\k(.)0*[1-9]\d*['>]$/
538
570
  emit(:backref, $1 == '<' ? :number_ref_ab : :number_ref_sq, text)
539
- when /^\\k(.)-[1-9]\d*['>]$/
571
+ when /^\\k(.)-0*[1-9]\d*['>]$/
540
572
  emit(:backref, $1 == '<' ? :number_rel_ref_ab : :number_rel_ref_sq, text)
541
573
  when /^\\k(.)[^0-9\-].*[+\-]\d+['>]$/
542
574
  emit(:backref, $1 == '<' ? :name_recursion_ref_ab : :name_recursion_ref_sq, text)
543
- when /^\\k(.)-?[1-9]\d*[+\-]\d+['>]$/
575
+ when /^\\k(.)-?0*[1-9]\d*[+\-]\d+['>]$/
544
576
  emit(:backref, $1 == '<' ? :number_recursion_ref_ab : :number_recursion_ref_sq, text)
545
577
  else
546
578
  raise ValidationError.for(:backref, 'backreference', 'invalid ref ID')
@@ -553,9 +585,9 @@
553
585
  case text = copy(data, ts, te)
554
586
  when /^\\g(.)[^0-9+\-].*['>]$/
555
587
  emit(:backref, $1 == '<' ? :name_call_ab : :name_call_sq, text)
556
- when /^\\g(.)\d+['>]$/
588
+ when /^\\g(.)(?:0|0*[1-9]\d*)['>]$/
557
589
  emit(:backref, $1 == '<' ? :number_call_ab : :number_call_sq, text)
558
- when /^\\g(.)[+-]\d+/
590
+ when /^\\g(.)[+-]0*[1-9]\d*/
559
591
  emit(:backref, $1 == '<' ? :number_rel_call_ab : :number_rel_call_sq, text)
560
592
  else
561
593
  raise ValidationError.for(:backref, 'subexpression call', 'invalid ref ID')
@@ -632,9 +664,9 @@
632
664
  *|;
633
665
  }%%
634
666
 
635
- require 'regexp_parser/scanner/errors/scanner_error'
636
- require 'regexp_parser/scanner/errors/premature_end_error'
637
- require 'regexp_parser/scanner/errors/validation_error'
667
+ require_relative 'scanner/errors/scanner_error'
668
+ require_relative 'scanner/errors/premature_end_error'
669
+ require_relative 'scanner/errors/validation_error'
638
670
 
639
671
  class Regexp::Scanner
640
672
  # Scans the given regular expression text, or Regexp object and collects the
@@ -654,6 +686,7 @@ class Regexp::Scanner
654
686
 
655
687
  input = input_object.is_a?(Regexp) ? input_object.source : input_object
656
688
  self.free_spacing = free_spacing?(input_object, options)
689
+ self.regexp_encoding = extract_encoding(input_object, options)
657
690
  self.spacing_stack = [{:free_spacing => free_spacing, :depth => 0}]
658
691
 
659
692
  data = input.unpack("c*")
@@ -664,6 +697,7 @@ class Regexp::Scanner
664
697
 
665
698
  self.set_depth = 0
666
699
  self.group_depth = 0
700
+ self.capturing_group_count = 0
667
701
  self.conditional_stack = []
668
702
  self.char_pos = 0
669
703
 
@@ -703,10 +737,11 @@ class Regexp::Scanner
703
737
  File.read("#{__dir__}/scanner/properties/#{name}.csv").scan(/(.+),(.+)/).to_h
704
738
  end
705
739
 
706
- def self.posix_classes
740
+ # Use each_with_object for required_ruby_version >= 2.2, or #to_h for >= 2.6
741
+ POSIX_CLASSES =
707
742
  %w[alnum alpha ascii blank cntrl digit graph
708
743
  lower print punct space upper word xdigit]
709
- end
744
+ .inject({}) { |o, e| o.merge(e => true) }.freeze
710
745
 
711
746
  # Emits an array with the details of the scanned pattern
712
747
  def emit(type, token, text)
@@ -734,16 +769,25 @@ class Regexp::Scanner
734
769
  end
735
770
  end
736
771
 
737
- 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
738
773
 
739
774
  private
740
775
 
741
776
  attr_accessor :block,
742
777
  :collect_tokens, :tokens, :prev_token,
743
778
  :free_spacing, :spacing_stack,
779
+ :regexp_encoding,
744
780
  :group_depth, :set_depth, :conditional_stack,
745
781
  :char_pos
746
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
+
747
791
  def free_spacing?(input_object, options)
748
792
  if options && !input_object.is_a?(String)
749
793
  raise ArgumentError, 'options cannot be supplied unless scanning a String'