regexp_parser 2.0.3 → 2.1.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.
@@ -20,7 +20,7 @@
20
20
 
21
21
  group_open = '(';
22
22
  group_close = ')';
23
- parantheses = group_open | group_close;
23
+ parentheses = group_open | group_close;
24
24
 
25
25
  set_open = '[';
26
26
  set_close = ']';
@@ -58,6 +58,8 @@
58
58
 
59
59
  meta_sequence = 'M-' . (backslash . ('c' | 'C-'))? . backslash? . any;
60
60
 
61
+ sequence_char = [CMcux];
62
+
61
63
  zero_or_one = '?' | '??' | '?+';
62
64
  zero_or_more = '*' | '*?' | '*+';
63
65
  one_or_more = '+' | '+?' | '++';
@@ -106,11 +108,15 @@
106
108
 
107
109
  group_named = ('?' . group_name );
108
110
 
109
- group_name_ref = group_ref . (('<' . group_name_id_ab? . group_level? '>') |
110
- ("'" . group_name_id_sq? . group_level? "'"));
111
+ group_name_backref = 'k' . (('<' . group_name_id_ab? . group_level? '>') |
112
+ ("'" . group_name_id_sq? . group_level? "'"));
113
+ group_name_call = 'g' . (('<' . group_name_id_ab? . group_level? '>') |
114
+ ("'" . group_name_id_sq? . group_level? "'"));
111
115
 
112
- group_number_ref = group_ref . (('<' . group_number . group_level? '>') |
113
- ("'" . group_number . group_level? "'"));
116
+ group_number_backref = 'k' . (('<' . group_number . group_level? '>') |
117
+ ("'" . group_number . group_level? "'"));
118
+ group_number_call = 'g' . (('<' . ((group_number . group_level?) | '0') '>') |
119
+ ("'" . ((group_number . group_level?) | '0') "'"));
114
120
 
115
121
  group_type = group_atomic | group_passive | group_absence | group_named;
116
122
 
@@ -121,7 +127,7 @@
121
127
 
122
128
  # characters that 'break' a literal
123
129
  meta_char = dot | backslash | alternation |
124
- curlies | parantheses | brackets |
130
+ curlies | parentheses | brackets |
125
131
  line_anchor | quantifier_greedy;
126
132
 
127
133
  literal_delimiters = ']' | '}';
@@ -130,10 +136,12 @@
130
136
  ascii_nonprint = (0x01..0x1f | 0x7f);
131
137
 
132
138
  non_literal_escape = char_type_char | anchor_char | escaped_ascii |
133
- keep_mark | [xucCM];
139
+ keep_mark | sequence_char;
140
+
141
+ # escapes that also work within a character set
142
+ set_escape = backslash | brackets | escaped_ascii | property_char |
143
+ sequence_char | single_codepoint_char_type;
134
144
 
135
- non_set_escape = (anchor_char - 'b') | group_ref | keep_mark |
136
- multi_codepoint_char_type | [0-9cCM];
137
145
 
138
146
  # EOF error, used where it can be detected
139
147
  action premature_end_error {
@@ -249,16 +257,16 @@
249
257
  # set escapes scanner
250
258
  # --------------------------------------------------------------------------
251
259
  set_escape_sequence := |*
252
- non_set_escape > (escaped_set_alpha, 2) {
253
- emit(:escape, :literal, copy(data, ts-1, te))
254
- fret;
255
- };
256
-
257
- any > (escaped_set_alpha, 1) {
260
+ set_escape > (escaped_set_alpha, 2) {
258
261
  fhold;
259
262
  fnext character_set;
260
263
  fcall escape_sequence;
261
264
  };
265
+
266
+ any > (escaped_set_alpha, 1) {
267
+ emit(:escape, :literal, copy(data, ts-1, te))
268
+ fret;
269
+ };
262
270
  *|;
263
271
 
264
272
 
@@ -538,67 +546,35 @@
538
546
 
539
547
  # Group backreference, named and numbered
540
548
  # ------------------------------------------------------------------------
541
- backslash . (group_name_ref | group_number_ref) > (backslashed, 4) {
549
+ backslash . (group_name_backref | group_number_backref) > (backslashed, 4) {
542
550
  case text = copy(data, ts, te)
543
- when /^\\([gk])(<>|'')/ # angle brackets
544
- validation_error(:backref, 'ref/call', 'ref ID is empty')
545
-
546
- # TODO: finer quirks of choosing recursive or non-recursive refs/calls.
547
- # e.g.: `a-1` is a valid group id: 'aa'[/(?<a-1>a)\g<a-1>/] # => 'aa'
548
- when /^\\([gk])<[^\p{digit}+\->][^>+\-]*>/ # angle-brackets
549
- if $1 == 'k'
550
- emit(:backref, :name_ref_ab, text)
551
- else
552
- emit(:backref, :name_call_ab, text)
553
- end
554
-
555
- when /^\\([gk])'[^\p{digit}+\-'][^'+\-]*'/ # single quotes
556
- if $1 == 'k'
557
- emit(:backref, :name_ref_sq, text)
558
- else
559
- emit(:backref, :name_call_sq, text)
560
- end
561
-
562
- when /^\\([gk])<\d+>/ # angle-brackets
563
- if $1 == 'k'
564
- emit(:backref, :number_ref_ab, text)
565
- else
566
- emit(:backref, :number_call_ab, text)
567
- end
568
-
569
- when /^\\([gk])'\d+'/ # single quotes
570
- if $1 == 'k'
571
- emit(:backref, :number_ref_sq, text)
572
- else
573
- emit(:backref, :number_call_sq, text)
574
- end
575
-
576
- when /^\\(?:g<\+|g<-|(k)<-)\d+>/ # angle-brackets
577
- if $1 == 'k'
578
- emit(:backref, :number_rel_ref_ab, text)
579
- else
580
- emit(:backref, :number_rel_call_ab, text)
581
- end
582
-
583
- when /^\\(?:g'\+|g'-|(k)'-)\d+'/ # single quotes
584
- if $1 == 'k'
585
- emit(:backref, :number_rel_ref_sq, text)
586
- else
587
- emit(:backref, :number_rel_call_sq, text)
588
- end
589
-
590
- when /^\\k<[^\p{digit}+\->][^>]*[+\-]\d+>/ # angle-brackets
591
- emit(:backref, :name_recursion_ref_ab, text)
592
-
593
- when /^\\k'[^\p{digit}+\-'][^']*[+\-]\d+'/ # single-quotes
594
- emit(:backref, :name_recursion_ref_sq, text)
595
-
596
- when /^\\([gk])<[+\-]?\d+[+\-]\d+>/ # angle-brackets
597
- emit(:backref, :number_recursion_ref_ab, text)
598
-
599
- when /^\\([gk])'[+\-]?\d+[+\-]\d+'/ # single-quotes
600
- emit(:backref, :number_recursion_ref_sq, text)
551
+ when /^\\k(<>|'')/
552
+ validation_error(:backref, 'backreference', 'ref ID is empty')
553
+ when /^\\k(.)[^\p{digit}\-][^+\-]*\D$/
554
+ emit(:backref, $1 == '<' ? :name_ref_ab : :name_ref_sq, text)
555
+ when /^\\k(.)\d+\D$/
556
+ emit(:backref, $1 == '<' ? :number_ref_ab : :number_ref_sq, text)
557
+ when /^\\k(.)-\d+\D$/
558
+ emit(:backref, $1 == '<' ? :number_rel_ref_ab : :number_rel_ref_sq, text)
559
+ when /^\\k(.)[^\p{digit}\-].*[+\-]\d+\D$/
560
+ emit(:backref, $1 == '<' ? :name_recursion_ref_ab : :name_recursion_ref_sq, text)
561
+ when /^\\k(.)-?\d+[+\-]\d+\D$/
562
+ emit(:backref, $1 == '<' ? :number_recursion_ref_ab : :number_recursion_ref_sq, text)
563
+ end
564
+ };
601
565
 
566
+ # Group call, named and numbered
567
+ # ------------------------------------------------------------------------
568
+ backslash . (group_name_call | group_number_call) > (backslashed, 4) {
569
+ case text = copy(data, ts, te)
570
+ when /^\\g(<>|'')/
571
+ validation_error(:backref, 'subexpression call', 'ref ID is empty')
572
+ when /^\\g(.)[^\p{digit}+\->][^+\-]*/
573
+ emit(:backref, $1 == '<' ? :name_call_ab : :name_call_sq, text)
574
+ when /^\\g(.)\d+\D$/
575
+ emit(:backref, $1 == '<' ? :number_call_ab : :number_call_sq, text)
576
+ when /^\\g(.)[+-]\d+/
577
+ emit(:backref, $1 == '<' ? :number_rel_call_ab : :number_rel_call_sq, text)
602
578
  end
603
579
  };
604
580
 
@@ -677,10 +653,10 @@
677
653
 
678
654
  class Regexp::Scanner
679
655
  # General scanner error (catch all)
680
- class ScannerError < StandardError; end
656
+ class ScannerError < Regexp::Parser::Error; end
681
657
 
682
658
  # Base for all scanner validation errors
683
- class ValidationError < StandardError
659
+ class ValidationError < Regexp::Parser::Error
684
660
  def initialize(reason)
685
661
  super reason
686
662
  end
@@ -782,7 +758,7 @@ class Regexp::Scanner
782
758
 
783
759
  # lazy-load property maps when first needed
784
760
  require 'yaml'
785
- PROP_MAPS_DIR = File.expand_path('../scanner/properties', __FILE__)
761
+ PROP_MAPS_DIR = File.join(__dir__, 'scanner', 'properties')
786
762
 
787
763
  def self.short_prop_map
788
764
  @short_prop_map ||= YAML.load_file("#{PROP_MAPS_DIR}/short.yml")
@@ -1,9 +1,9 @@
1
1
  module Regexp::Syntax
2
- class SyntaxError < StandardError; end
2
+ class SyntaxError < Regexp::Parser::Error; end
3
3
  end
4
4
 
5
- require File.expand_path('../syntax/tokens', __FILE__)
6
- require File.expand_path('../syntax/base', __FILE__)
7
- require File.expand_path('../syntax/any', __FILE__)
8
- require File.expand_path('../syntax/version_lookup', __FILE__)
9
- require File.expand_path('../syntax/versions', __FILE__)
5
+ require_relative 'syntax/tokens'
6
+ require_relative 'syntax/base'
7
+ require_relative 'syntax/any'
8
+ require_relative 'syntax/version_lookup'
9
+ require_relative 'syntax/versions'
@@ -4,7 +4,7 @@ module Regexp::Syntax
4
4
  # is useful during development, testing, and should be useful for some types
5
5
  # of transformations as well.
6
6
  class Any < Base
7
- def initialize
7
+ def initialize # rubocop:disable Lint/MissingSuper
8
8
  @implements = { :* => [:*] }
9
9
  end
10
10
 
@@ -3,4 +3,4 @@
3
3
  #
4
4
  # Aliases for the latest patch version are provided as 'ruby/n.n',
5
5
  # e.g. 'ruby/1.9' refers to Ruby v1.9.3.
6
- Dir[File.expand_path('../versions/*.rb', __FILE__)].sort.each { |f| require f }
6
+ Dir[File.join(__dir__, 'versions', '*.rb')].sort.each { |f| require f }
@@ -1,5 +1,5 @@
1
1
  class Regexp
2
2
  class Parser
3
- VERSION = '2.0.3'
3
+ VERSION = '2.1.0'
4
4
  end
5
5
  end
@@ -27,8 +27,8 @@ RSpec.describe('Expression#clone') do
27
27
  expect(root_2.quantifier.object_id).not_to eq copy_2.quantifier.object_id
28
28
 
29
29
  # regression test
30
- expect { root_2.clone }.not_to change { root_2.quantifier.object_id }
31
- expect { root_2.clone }.not_to change { root_2.quantifier.text.object_id }
30
+ expect { root_2.clone }.not_to(change { root_2.quantifier.object_id })
31
+ expect { root_2.clone }.not_to(change { root_2.quantifier.text.object_id })
32
32
  end
33
33
 
34
34
  specify('Subexpression#clone') do
@@ -48,7 +48,7 @@ RSpec.describe('Expression#clone') do
48
48
  end
49
49
 
50
50
  # regression test
51
- expect { root.clone }.not_to change { root.expressions.object_id }
51
+ expect { root.clone }.not_to(change { root.expressions.object_id })
52
52
  end
53
53
 
54
54
  specify('Group::Named#clone') do
@@ -69,7 +69,39 @@ RSpec.describe('Expression#clone') do
69
69
  end
70
70
 
71
71
  # regression test
72
- expect { root_1.clone }.not_to change { root_1.name.object_id }
72
+ expect { root_1.clone }.not_to(change { root_1.name.object_id })
73
+ end
74
+
75
+ specify('Group::Options#clone') do
76
+ root = RP.parse('foo(?i)bar')
77
+ copy = root.clone
78
+
79
+ expect(copy.to_s).to eq root.to_s
80
+
81
+ root_1 = root[1]
82
+ copy_1 = copy[1]
83
+
84
+ expect(root_1.option_changes).to eq copy_1.option_changes
85
+ expect(root_1.option_changes.object_id).not_to eq copy_1.option_changes.object_id
86
+
87
+ # regression test
88
+ expect { root_1.clone }.not_to(change { root_1.option_changes.object_id })
89
+ end
90
+
91
+ specify('Backreference::Base#clone') do
92
+ root = RP.parse('(foo)\1')
93
+ copy = root.clone
94
+
95
+ expect(copy.to_s).to eq root.to_s
96
+
97
+ root_1 = root[1]
98
+ copy_1 = copy[1]
99
+
100
+ expect(root_1.referenced_expression.to_s).to eq copy_1.referenced_expression.to_s
101
+ expect(root_1.referenced_expression.object_id).not_to eq copy_1.referenced_expression.object_id
102
+
103
+ # regression test
104
+ expect { root_1.clone }.not_to(change { root_1.referenced_expression.object_id })
73
105
  end
74
106
 
75
107
  specify('Sequence#clone') do
@@ -10,7 +10,7 @@ RSpec.describe(Regexp::Expression::FreeSpace) do
10
10
  space = root[0]
11
11
 
12
12
  expect(space).to be_instance_of(FreeSpace::WhiteSpace)
13
- expect { space.quantify(:dummy, '#') }.to raise_error(RuntimeError)
13
+ expect { space.quantify(:dummy, '#') }.to raise_error(Regexp::Parser::Error)
14
14
  end
15
15
 
16
16
  specify('comment quantify raises error') do
@@ -22,6 +22,6 @@ RSpec.describe(Regexp::Expression::FreeSpace) do
22
22
  comment = root[3]
23
23
 
24
24
  expect(comment).to be_instance_of(FreeSpace::Comment)
25
- expect { comment.quantify(:dummy, '#') }.to raise_error(RuntimeError)
25
+ expect { comment.quantify(:dummy, '#') }.to raise_error(Regexp::Parser::Error)
26
26
  end
27
27
  end
@@ -1,8 +1,8 @@
1
1
  require 'spec_helper'
2
2
 
3
- RSpec.describe(Regexp::MatchLength) do
4
- ML = described_class
3
+ ML = Regexp::MatchLength
5
4
 
5
+ RSpec.describe(Regexp::MatchLength) do
6
6
  specify('literal') { expect(ML.of(/a/).minmax).to eq [1, 1] }
7
7
  specify('literal sequence') { expect(ML.of(/abc/).minmax).to eq [3, 3] }
8
8
  specify('dot') { expect(ML.of(/./).minmax).to eq [1, 1] }
@@ -32,6 +32,11 @@ RSpec.describe('RefCall lexing') do
32
32
  include_examples 'lex', "(abc)\\g'1'",
33
33
  3 => [:backref, :number_call, "\\g'1'", 5, 10, 0, 0, 0]
34
34
 
35
+ include_examples 'lex', '\g<0>',
36
+ 0 => [:backref, :number_call, '\g<0>', 0, 5, 0, 0, 0]
37
+ include_examples 'lex', "\\g'0'",
38
+ 0 => [:backref, :number_call, "\\g'0'", 0, 5, 0, 0, 0]
39
+
35
40
  include_examples 'lex', '(abc)\g<-1>',
36
41
  3 => [:backref, :number_rel_call, '\g<-1>', 5, 11, 0, 0, 0]
37
42
  include_examples 'lex', "(abc)\\g'-1'",
@@ -34,10 +34,10 @@ RSpec.describe(Regexp::Parser) do
34
34
  end
35
35
 
36
36
  specify('parse no quantifier target raises error') do
37
- expect { RP.parse('?abc') }.to raise_error(ArgumentError)
37
+ expect { RP.parse('?abc') }.to raise_error(Regexp::Parser::Error)
38
38
  end
39
39
 
40
40
  specify('parse sequence no quantifier target raises error') do
41
- expect { RP.parse('abc|?def') }.to raise_error(ArgumentError)
41
+ expect { RP.parse('abc|?def') }.to raise_error(Regexp::Parser::Error)
42
42
  end
43
43
  end
@@ -29,6 +29,11 @@ RSpec.describe('Refcall parsing') do
29
29
  include_examples 'parse', /(abc)\g'1'/,
30
30
  1 => [:backref, :number_call, Backreference::NumberCall, number: 1]
31
31
 
32
+ include_examples 'parse', '\g<0>',
33
+ 0 => [:backref, :number_call, Backreference::NumberCall, number: 0]
34
+ include_examples 'parse', "\\g'0'",
35
+ 0 => [:backref, :number_call, Backreference::NumberCall, number: 0]
36
+
32
37
  include_examples 'parse', /(abc)\g<-1>/,
33
38
  1 => [:backref, :number_rel_call, Backreference::NumberCallRelative, number: -1]
34
39
  include_examples 'parse', /(abc)\g'-1'/,
@@ -4,7 +4,7 @@ RSpec.describe('Escape scanning') do
4
4
  include_examples 'scan', /c\at/, 1 => [:escape, :bell, '\a', 1, 3]
5
5
 
6
6
  # not an escape outside a character set
7
- include_examples 'scan', /c\bt/, 1 => [:anchor, :word_boundary, '\b', 1, 3]
7
+ include_examples 'scan', /c\bt/, 1 => [:anchor, :word_boundary, '\b', 1, 3]
8
8
 
9
9
  include_examples 'scan', /c\ft/, 1 => [:escape, :form_feed, '\f', 1, 3]
10
10
  include_examples 'scan', /c\nt/, 1 => [:escape, :newline, '\n', 1, 3]
@@ -5,9 +5,19 @@ RSpec.describe('RefCall scanning') do
5
5
  include_examples 'scan', '(abc)\1' , 3 => [:backref, :number, '\1', 5, 7]
6
6
 
7
7
  # Group back-references, named, numbered, and relative
8
+ #
9
+ # NOTE: only \g supports forward-looking references using '+', e.g. \g<+1>
10
+ # refers to the next group, but \k<+1> refers to a group named '+1'.
11
+ # Inversely, only \k supports addition or substraction of a recursion level.
12
+ # E.g. \k<x+0> refers to a group named 'x' at the current recursion level,
13
+ # but \g<x+0> refers to a a group named 'x+0'.
14
+ #
8
15
  include_examples 'scan', '(?<X>abc)\k<X>', 3 => [:backref, :name_ref_ab, '\k<X>', 9, 14]
9
16
  include_examples 'scan', "(?<X>abc)\\k'X'", 3 => [:backref, :name_ref_sq, "\\k'X'", 9, 14]
10
17
 
18
+ include_examples 'scan', '(?<+1>abc)\k<+1>', 3 => [:backref, :name_ref_ab, '\k<+1>', 10, 16]
19
+ include_examples 'scan', "(?<+1>abc)\\k'+1'", 3 => [:backref, :name_ref_sq, "\\k'+1'", 10, 16]
20
+
11
21
  include_examples 'scan', '(abc)\k<1>', 3 => [:backref, :number_ref_ab, '\k<1>', 5, 10]
12
22
  include_examples 'scan', "(abc)\\k'1'", 3 => [:backref, :number_ref_sq, "\\k'1'", 5, 10]
13
23
 
@@ -18,9 +28,15 @@ RSpec.describe('RefCall scanning') do
18
28
  include_examples 'scan', '(?<X>abc)\g<X>', 3 => [:backref, :name_call_ab, '\g<X>', 9, 14]
19
29
  include_examples 'scan', "(?<X>abc)\\g'X'", 3 => [:backref, :name_call_sq, "\\g'X'", 9, 14]
20
30
 
31
+ include_examples 'scan', '(?<X>abc)\g<X-1>', 3 => [:backref, :name_call_ab, '\g<X-1>', 9, 16]
32
+ include_examples 'scan', "(?<X>abc)\\g'X-1'", 3 => [:backref, :name_call_sq, "\\g'X-1'", 9, 16]
33
+
21
34
  include_examples 'scan', '(abc)\g<1>', 3 => [:backref, :number_call_ab, '\g<1>', 5, 10]
22
35
  include_examples 'scan', "(abc)\\g'1'", 3 => [:backref, :number_call_sq, "\\g'1'", 5, 10]
23
36
 
37
+ include_examples 'scan', 'a(b|\g<0>)', 4 => [:backref, :number_call_ab, '\g<0>', 4, 9]
38
+ include_examples 'scan', "a(b|\\g'0')", 4 => [:backref, :number_call_sq, "\\g'0'", 4, 9]
39
+
24
40
  include_examples 'scan', '(abc)\g<-1>', 3 => [:backref, :number_rel_call_ab, '\g<-1>', 5, 11]
25
41
  include_examples 'scan', "(abc)\\g'-1'", 3 => [:backref, :number_rel_call_sq, "\\g'-1'", 5, 11]
26
42
 
@@ -33,4 +49,7 @@ RSpec.describe('RefCall scanning') do
33
49
 
34
50
  include_examples 'scan', '(abc)\k<1-0>', 3 => [:backref, :number_recursion_ref_ab, '\k<1-0>', 5, 12]
35
51
  include_examples 'scan', "(abc)\\k'1-0'", 3 => [:backref, :number_recursion_ref_sq, "\\k'1-0'", 5, 12]
52
+
53
+ include_examples 'scan', '(abc)\k<+1-0>', 3 => [:backref, :name_recursion_ref_ab, '\k<+1-0>', 5, 13]
54
+ include_examples 'scan', "(abc)\\k'+1-0'", 3 => [:backref, :name_recursion_ref_sq, "\\k'+1-0'", 5, 13]
36
55
  end
@@ -6,8 +6,18 @@ RSpec.describe('Set scanning') do
6
6
  include_examples 'scan', /[^n]/, 1 => [:set, :negate, '^', 1, 2]
7
7
 
8
8
  include_examples 'scan', /[c]/, 1 => [:literal, :literal, 'c', 1, 2]
9
- include_examples 'scan', /[\b]/, 1 => [:escape, :backspace, '\b', 1, 3]
10
- include_examples 'scan', /[A\bX]/, 2 => [:escape, :backspace, '\b', 2, 4]
9
+ include_examples 'scan', /[^d]/, 2 => [:literal, :literal, 'd', 2, 3]
10
+
11
+ include_examples 'scan', /[\b]/, 1 => [:escape, :backspace, '\b', 1, 3]
12
+ include_examples 'scan', /[A\bX]/, 2 => [:escape, :backspace, '\b', 2, 4]
13
+
14
+ include_examples 'scan', /[\a]/, 1 => [:escape, :bell, '\a', 1, 3]
15
+ include_examples 'scan', /[\e]/, 1 => [:escape, :escape, '\e', 1, 3]
16
+ include_examples 'scan', /[\f]/, 1 => [:escape, :form_feed, '\f', 1, 3]
17
+ include_examples 'scan', /[\n]/, 1 => [:escape, :newline, '\n', 1, 3]
18
+ include_examples 'scan', /[\r]/, 1 => [:escape, :carriage, '\r', 1, 3]
19
+ include_examples 'scan', /[\t]/, 1 => [:escape, :tab, '\t', 1, 3]
20
+ include_examples 'scan', /[\v]/, 1 => [:escape, :vertical_tab, '\v', 1, 3]
11
21
 
12
22
  include_examples 'scan', /[.]/, 1 => [:literal, :literal, '.', 1, 2]
13
23
  include_examples 'scan', /[?]/, 1 => [:literal, :literal, '?', 1, 2]
@@ -18,22 +28,36 @@ RSpec.describe('Set scanning') do
18
28
  include_examples 'scan', /[<]/, 1 => [:literal, :literal, '<', 1, 2]
19
29
  include_examples 'scan', /[>]/, 1 => [:literal, :literal, '>', 1, 2]
20
30
 
21
- include_examples 'scan', /[\x20]/, 1 => [:escape, :hex, '\x20', 1, 5]
22
-
23
- include_examples 'scan', '[\.]', 1 => [:escape, :dot, '\.', 1, 3]
31
+ include_examples 'scan', '[\\\\]', 1 => [:escape, :backslash, '\\\\', 1, 3]
32
+ include_examples 'scan', '[\u0040]', 1 => [:escape, :codepoint, '\u0040', 1, 7]
33
+ include_examples 'scan', '[\u{40}]', 1 => [:escape, :codepoint_list, '\u{40}', 1, 7]
34
+ include_examples 'scan', '[\c2]', 1 => [:escape, :control, '\c2', 1, 4]
35
+ include_examples 'scan', '[\C-C]', 1 => [:escape, :control, '\C-C', 1, 5]
36
+ include_examples 'scan', '[\x20]', 1 => [:escape, :hex, '\x20', 1, 5]
37
+ include_examples 'scan', '[\M-Z]', 1 => [:escape, :meta_sequence, '\M-Z', 1, 5]
38
+ include_examples 'scan', '[\M-\C-X]', 1 => [:escape, :meta_sequence, '\M-\C-X', 1, 8]
39
+ include_examples 'scan', '[\\[]', 1 => [:escape, :set_open, '\[', 1, 3]
40
+ include_examples 'scan', '[\\]]', 1 => [:escape, :set_close, '\]', 1, 3]
41
+ include_examples 'scan', '[a\-]', 2 => [:escape, :literal, '\-', 2, 4]
42
+ include_examples 'scan', '[\-c]', 1 => [:escape, :literal, '\-', 1, 3]
43
+ include_examples 'scan', '[\.]', 1 => [:escape, :literal, '\.', 1, 3]
44
+ include_examples 'scan', '[\?]', 1 => [:escape, :literal, '\?', 1, 3]
45
+ include_examples 'scan', '[\*]', 1 => [:escape, :literal, '\*', 1, 3]
46
+ include_examples 'scan', '[\+]', 1 => [:escape, :literal, '\+', 1, 3]
47
+ include_examples 'scan', '[\|]', 1 => [:escape, :literal, '\|', 1, 3]
48
+ include_examples 'scan', '[\{]', 1 => [:escape, :literal, '\{', 1, 3]
49
+ include_examples 'scan', '[\}]', 1 => [:escape, :literal, '\}', 1, 3]
50
+ include_examples 'scan', '[\(]', 1 => [:escape, :literal, '\(', 1, 3]
51
+ include_examples 'scan', '[\)]', 1 => [:escape, :literal, '\)', 1, 3]
24
52
  include_examples 'scan', '[\!]', 1 => [:escape, :literal, '\!', 1, 3]
25
53
  include_examples 'scan', '[\#]', 1 => [:escape, :literal, '\#', 1, 3]
26
- include_examples 'scan', '[\\]]', 1 => [:escape, :set_close, '\]', 1, 3]
27
- include_examples 'scan', '[\\\\]', 1 => [:escape, :backslash, '\\\\', 1, 3]
28
54
  include_examples 'scan', '[\A]', 1 => [:escape, :literal, '\A', 1, 3]
29
55
  include_examples 'scan', '[\z]', 1 => [:escape, :literal, '\z', 1, 3]
30
56
  include_examples 'scan', '[\g]', 1 => [:escape, :literal, '\g', 1, 3]
31
57
  include_examples 'scan', '[\K]', 1 => [:escape, :literal, '\K', 1, 3]
32
58
  include_examples 'scan', '[\R]', 1 => [:escape, :literal, '\R', 1, 3]
33
59
  include_examples 'scan', '[\X]', 1 => [:escape, :literal, '\X', 1, 3]
34
- include_examples 'scan', '[\c2]', 1 => [:escape, :literal, '\c', 1, 3]
35
60
  include_examples 'scan', '[\B]', 1 => [:escape, :literal, '\B', 1, 3]
36
- include_examples 'scan', '[a\-c]', 2 => [:escape, :literal, '\-', 2, 4]
37
61
 
38
62
  include_examples 'scan', /[\d]/, 1 => [:type, :digit, '\d', 1, 3]
39
63
  include_examples 'scan', /[\da-z]/, 1 => [:type, :digit, '\d', 1, 3]
@@ -54,8 +78,14 @@ RSpec.describe('Set scanning') do
54
78
  include_examples 'scan', /[a-b-]/, 4 => [:literal, :literal, '-', 4, 5]
55
79
  include_examples 'scan', /[-a]/, 1 => [:literal, :literal, '-', 1, 2]
56
80
  include_examples 'scan', /[a-c^]/, 4 => [:literal, :literal, '^', 4, 5]
57
- include_examples 'scan', /[a-bd-f]/, 2 => [:set, :range, '-', 2, 3]
58
- include_examples 'scan', /[a-cd-f]/, 5 => [:set, :range, '-', 5, 6]
81
+ include_examples 'scan', /[a-bd-f]/, 2 => [:set, :range, '-', 2, 3]
82
+ include_examples 'scan', /[a-cd-f]/, 5 => [:set, :range, '-', 5, 6]
83
+ # this is a buggy range, it matches only `c`, but not `a`, `b` or `-`
84
+ include_examples 'scan', /[a-[c]]/, 2 => [:set, :range, '-', 2, 3]
85
+ # these are not ranges, they match `a`, `c` and `-` (or non-`-` if negated)
86
+ include_examples 'scan', /[[a]-[c]]/, 4 => [:literal, :literal, '-', 4, 5]
87
+ include_examples 'scan', /[[a]-c]/, 4 => [:literal, :literal, '-', 4, 5]
88
+ include_examples 'scan', /[^-c]/, 2 => [:literal, :literal, '-', 2, 3]
59
89
 
60
90
  include_examples 'scan', /[a[:digit:]c]/, 2 => [:posixclass, :digit, '[:digit:]', 2, 11]
61
91
  include_examples 'scan', /[[:digit:][:space:]]/, 2 => [:posixclass, :space, '[:space:]', 10, 19]
@@ -64,6 +94,7 @@ RSpec.describe('Set scanning') do
64
94
  include_examples 'scan', /[a-d&&g-h]/, 4 => [:set, :intersection, '&&', 4, 6]
65
95
  include_examples 'scan', /[a&&]/, 2 => [:set, :intersection, '&&', 2, 4]
66
96
  include_examples 'scan', /[&&z]/, 1 => [:set, :intersection, '&&', 1, 3]
97
+ include_examples 'scan', /[&&]/, 1 => [:set, :intersection, '&&', 1, 3]
67
98
 
68
99
  include_examples 'scan', /[a\p{digit}c]/, 2 => [:property, :digit, '\p{digit}', 2, 11]
69
100
  include_examples 'scan', /[a\P{digit}c]/, 2 => [:nonproperty, :digit, '\P{digit}', 2, 11]