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.
- checksums.yaml +4 -4
- data/Gemfile +6 -4
- data/LICENSE +1 -1
- data/Rakefile +5 -3
- data/lib/regexp_parser/error.rb +2 -0
- data/lib/regexp_parser/expression/base.rb +2 -0
- data/lib/regexp_parser/expression/classes/alternation.rb +2 -0
- data/lib/regexp_parser/expression/classes/anchor.rb +2 -0
- data/lib/regexp_parser/expression/classes/backreference.rb +3 -20
- data/lib/regexp_parser/expression/classes/character_set/intersection.rb +2 -0
- data/lib/regexp_parser/expression/classes/character_set/range.rb +2 -0
- data/lib/regexp_parser/expression/classes/character_set.rb +3 -4
- data/lib/regexp_parser/expression/classes/character_type.rb +2 -0
- data/lib/regexp_parser/expression/classes/conditional.rb +2 -14
- data/lib/regexp_parser/expression/classes/escape_sequence.rb +26 -95
- data/lib/regexp_parser/expression/classes/free_space.rb +2 -0
- data/lib/regexp_parser/expression/classes/group.rb +2 -0
- 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 +2 -4
- data/lib/regexp_parser/expression/classes/root.rb +2 -0
- data/lib/regexp_parser/expression/classes/unicode_property.rb +8 -9
- data/lib/regexp_parser/expression/methods/construct.rb +2 -0
- 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 +2 -0
- data/lib/regexp_parser/expression/methods/printing.rb +2 -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 +2 -0
- data/lib/regexp_parser/expression/methods/traverse.rb +2 -0
- data/lib/regexp_parser/expression/quantifier.rb +3 -1
- data/lib/regexp_parser/expression/sequence.rb +2 -0
- data/lib/regexp_parser/expression/sequence_operation.rb +2 -0
- data/lib/regexp_parser/expression/shared.rb +6 -3
- data/lib/regexp_parser/expression/subexpression.rb +2 -0
- data/lib/regexp_parser/expression.rb +39 -33
- data/lib/regexp_parser/lexer.rb +2 -0
- data/lib/regexp_parser/parser.rb +16 -9
- data/lib/regexp_parser/scanner/errors/premature_end_error.rb +2 -0
- data/lib/regexp_parser/scanner/errors/scanner_error.rb +3 -1
- data/lib/regexp_parser/scanner/errors/validation_error.rb +2 -0
- data/lib/regexp_parser/scanner/properties/long.csv +50 -0
- data/lib/regexp_parser/scanner/properties/short.csv +13 -0
- data/lib/regexp_parser/scanner/scanner.rl +62 -18
- data/lib/regexp_parser/scanner.rb +1041 -936
- 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 +8 -6
- data/lib/regexp_parser/syntax/token/character_set.rb +3 -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 +9 -7
- 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 +4 -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 +149 -71
- data/lib/regexp_parser/syntax/token/virtual.rb +5 -3
- 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 +13 -11
- data/CHANGELOG.md +0 -691
- 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-
|
|
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 = ([
|
|
82
|
-
group_name_id_sq = ([^0-9\-']
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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'
|