regexp-examples 1.1.3 → 1.1.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: aa40a179d06f78f6bb791ebda870b8f82b027d4e
4
- data.tar.gz: 2abbd95ec396bb61b69d10035e9fd61e0d851fff
3
+ metadata.gz: 43bddf0ebdc30ee2ae0e7ed35722165921d25c93
4
+ data.tar.gz: 3e52ef064a6dd7c484f06814c15135997f7fb19d
5
5
  SHA512:
6
- metadata.gz: 368a793ed4f68e60449525e4d1ce539da4513f83e4a8b301dfcf909643b9b6ca865752962d2f8d344485820ca2621a7ea2e30ee6cde7baf4930d220dcf4d4b87
7
- data.tar.gz: 79475ed89f948dec9731457af73fec7fa6b5f87f50f5c75f3fd9ba7f2c5508a7876d4778804f0c4b64a68c6d5bfda2a75036bda73cf394656304cbddc48d041b
6
+ metadata.gz: 9e53baeb48bce1a0e5d00e610b0ea933bc5a014d1fe0ea5b9678875c63b5507aaa32baab9f339e60610ce1b9c97e4173729a89340b8b75530912a6560352ca19
7
+ data.tar.gz: e63cf93bfb1ec76e4aa710bb15da91bd61e27f259c30d6011e0805c64f3b219420b20cbe2343cf6736ba2e7e40c7cc413b6b8a04779eb4cf7438cad3e4aa04cf
data/.rubocop.yml CHANGED
@@ -1,2 +1,5 @@
1
1
  Metrics/LineLength:
2
2
  Max: 90
3
+
4
+ Style/FileName:
5
+ Enabled: false
data/.travis.yml CHANGED
@@ -5,9 +5,3 @@ rvm:
5
5
  - 2.2.0
6
6
  - 2.2.2
7
7
  - ruby-head
8
- matrix:
9
- allow_failures:
10
- # One (ruby 2.3-dev) test fails, due to a change of behaviour in Array#delete_if,
11
- # but I don't know if this is intentional. I'll fix it once the behaviour change is documented.
12
- # For now, I don't really care if 2.3-dev tests all pass.
13
- - rvm: ruby-head
@@ -0,0 +1 @@
1
+ unicode_ranges_2.0.pstore
Binary file
Binary file
@@ -1,5 +1,8 @@
1
1
  module CoreExtensions
2
2
  module Regexp
3
+ # A wrapper module to namespace/isolate the Regexp#examples and Regexp#random_exanple
4
+ # monkey patches.
5
+ # No core classes are extended in any way, other than the above two methods.
3
6
  module Examples
4
7
  def examples(**config_options)
5
8
  RegexpExamples::ResultCountLimiters.configure!(
@@ -1,24 +1,40 @@
1
1
  module RegexpExamples
2
+ # A helper class to fill-in backrefences AFTER the example(s) have been generated.
3
+ # In a nutshell, this works by doing the following:
4
+ # * Given a regex that contains a capute group and backreference, e.g. `/(a|b) \1/`
5
+ # * After generating examples, the backreference is tored as a placeholder:
6
+ # `["a __1__", "b __1__"]`
7
+ # * This class is used to fill in each placeholder accordingly:
8
+ # `["a a", "b b"]`
9
+ # * Also, beware of octal groups and cases where the backref invalidates the example!!
2
10
  class BackReferenceReplacer
3
- BackrefNotFound = Class.new(StandardError)
11
+ # Named capture groups can only contain alphanumeric chars, and hyphens
12
+ PLACEHOLDER_REGEX = Regexp.new(
13
+ RegexpExamples::BackReferenceGroup::PLACEHOLDER_FORMAT % '([a-zA-Z0-9-]+)'
14
+ )
4
15
 
5
16
  def substitute_backreferences(full_examples)
6
17
  full_examples.map do |full_example|
7
- begin
8
- while full_example.match(/__(\w+?)__/)
9
- full_example.sub!(/__(\w+?)__/, find_backref_for(full_example, Regexp.last_match(1)))
10
- end
11
- full_example
12
- rescue BackrefNotFound
13
- # For instance, one "full example" from /(a|(b)) \2/: "a __2__"
14
- # should be rejected because the backref (\2) does not exist
15
- nil
18
+ # For instance, one "full example" from /(a|(b)) \2/: "a __2__"
19
+ # should be rejected because the backref (\2) does not exist
20
+ catch(:backref_not_found) do
21
+ substitute_backrefs_one_at_a_time(full_example)
16
22
  end
17
23
  end.compact
18
24
  end
19
25
 
20
26
  private
21
27
 
28
+ def substitute_backrefs_one_at_a_time(full_example)
29
+ while full_example.match(PLACEHOLDER_REGEX)
30
+ full_example.sub!(
31
+ PLACEHOLDER_REGEX,
32
+ find_backref_for(full_example, Regexp.last_match(1))
33
+ )
34
+ end
35
+ full_example
36
+ end
37
+
22
38
  def find_backref_for(full_example, group_id)
23
39
  full_example.all_subgroups.detect do |subgroup|
24
40
  subgroup.group_id == group_id
@@ -26,11 +42,11 @@ module RegexpExamples
26
42
  end
27
43
 
28
44
  def octal_char_for(octal_chars)
29
- # For octal characters in the range \10 - \177
30
- if octal_chars =~ /\A[01]?[0-7]{1,2}\z/ && octal_chars.to_i >= 10
45
+ # For octal characters in the range \00 - \177
46
+ if octal_chars =~ /\A[01]?[0-7]{1,2}\z/ && octal_chars.length > 1
31
47
  Integer(octal_chars, 8).chr
32
48
  else
33
- fail(BackrefNotFound)
49
+ throw :backref_not_found
34
50
  end
35
51
  end
36
52
  end
@@ -1,3 +1,5 @@
1
+ require_relative 'parser_helpers/charset_negation_helper'
2
+
1
3
  module RegexpExamples
2
4
  # A "sub-parser", for char groups in a regular expression
3
5
  # Some examples of what this class needs to parse:
@@ -7,20 +9,23 @@ module RegexpExamples
7
9
  # [^abc] - negated group
8
10
  # [[a][bc]] - sub-groups (should match "a", "b" or "c")
9
11
  # [[:lower:]] - POSIX group
10
- # [[a-f]&&[d-z]] - set intersection (should match "d", "f" or "f")
12
+ # [[a-f]&&[d-z]] - set intersection (should match "d", "e" or "f")
11
13
  # [[^:alpha:]&&[\n]a-c] - all of the above!!!! (should match "\n")
12
14
  class ChargroupParser
13
- attr_reader :regexp_string
15
+ include CharsetNegationHelper
16
+
17
+ attr_reader :regexp_string, :current_position
18
+ alias_method :length, :current_position
19
+
14
20
  def initialize(regexp_string, is_sub_group: false)
15
21
  @regexp_string = regexp_string
16
22
  @is_sub_group = is_sub_group
17
23
  @current_position = 0
18
- parse
24
+ @charset = []
25
+ @negative = false
19
26
  end
20
27
 
21
28
  def parse
22
- @charset = []
23
- @negative = false
24
29
  parse_first_chars
25
30
  until next_char == ']'
26
31
  case next_char
@@ -40,12 +45,8 @@ module RegexpExamples
40
45
  @current_position += 1 # To account for final "]"
41
46
  end
42
47
 
43
- def length
44
- @current_position
45
- end
46
-
47
48
  def result
48
- @negative ? (CharSets::Any - @charset) : @charset
49
+ negate_if(@charset, @negative)
49
50
  end
50
51
 
51
52
  private
@@ -66,12 +67,7 @@ module RegexpExamples
66
67
  end
67
68
 
68
69
  def parse_posix_group(negation_flag, name)
69
- chars = if negation_flag.empty?
70
- POSIXCharMap[name]
71
- else
72
- CharSets::Any - POSIXCharMap[name]
73
- end
74
- @charset.concat chars
70
+ @charset.concat negate_if(POSIXCharMap[name], !negation_flag.empty?)
75
71
  @current_position += (negation_flag.length + # 0 or 1, if '^' is present
76
72
  name.length +
77
73
  2) # Length of opening and closing colons (always 2)
@@ -101,6 +97,7 @@ module RegexpExamples
101
97
  def parse_sub_group_concat
102
98
  @current_position += 1
103
99
  sub_group_parser = self.class.new(rest_of_string, is_sub_group: true)
100
+ sub_group_parser.parse
104
101
  @charset.concat sub_group_parser.result
105
102
  @current_position += sub_group_parser.length
106
103
  end
@@ -117,6 +114,7 @@ module RegexpExamples
117
114
  def parse_sub_group_intersect
118
115
  @current_position += 2
119
116
  sub_group_parser = self.class.new(rest_of_string, is_sub_group: true)
117
+ sub_group_parser.parse
120
118
  @charset &= sub_group_parser.result
121
119
  @current_position += (sub_group_parser.length - 1)
122
120
  end
@@ -127,7 +125,7 @@ module RegexpExamples
127
125
  @current_position += 1
128
126
  else
129
127
  @current_position += 1
130
- @charset.concat (@charset.last..parse_checking_backlash.first).to_a
128
+ @charset.concat((@charset.last..parse_checking_backlash.first).to_a)
131
129
  @current_position += 1
132
130
  end
133
131
  end
@@ -1,4 +1,6 @@
1
+ # :nodoc:
1
2
  module RegexpExamples
3
+ # Configuration settings to limit the number/length of Regexp examples generated
2
4
  class ResultCountLimiters
3
5
  # The maximum variance for any given repeater, to prevent a huge/infinite number of
4
6
  # examples from being listed. For example, if @@max_repeater_variance = 2 then:
@@ -7,30 +9,32 @@ module RegexpExamples
7
9
  # .{2,} is equivalent to .{2,4}
8
10
  # .{,3} is equivalent to .{0,2}
9
11
  # .{3,8} is equivalent to .{3,5}
10
- MaxRepeaterVarianceDefault = 2
12
+ MAX_REPEATER_VARIANCE_DEFAULT = 2
11
13
 
12
14
  # Maximum number of characters returned from a char set, to reduce output spam
13
15
  # For example, if @@max_group_results = 5 then:
14
16
  # \d is equivalent to [01234]
15
17
  # \w is equivalent to [abcde]
16
- MaxGroupResultsDefault = 5
18
+ MAX_GROUP_RESULTS_DEFAULT = 5
17
19
 
18
20
  class << self
19
21
  attr_reader :max_repeater_variance, :max_group_results
20
22
  def configure!(max_repeater_variance, max_group_results = nil)
21
- @max_repeater_variance = (max_repeater_variance || MaxRepeaterVarianceDefault)
22
- @max_group_results = (max_group_results || MaxGroupResultsDefault)
23
+ @max_repeater_variance = (max_repeater_variance || MAX_REPEATER_VARIANCE_DEFAULT)
24
+ @max_group_results = (max_group_results || MAX_GROUP_RESULTS_DEFAULT)
23
25
  end
24
26
  end
25
27
  end
26
28
 
27
- def self.MaxRepeaterVariance
29
+ def self.max_repeater_variance
28
30
  ResultCountLimiters.max_repeater_variance
29
31
  end
30
- def self.MaxGroupResults
32
+ def self.max_group_results
31
33
  ResultCountLimiters.max_group_results
32
34
  end
33
35
 
36
+ # Definitions of various special characters, used in regular expressions.
37
+ # For example, `/\h/.examples` will return the value of `Hex` in this module
34
38
  module CharSets
35
39
  Lower = Array('a'..'z')
36
40
  Upper = Array('A'..'Z')
@@ -6,13 +6,12 @@ module RegexpExamples
6
6
  attr_reader :group_id, :subgroups
7
7
  def initialize(result, group_id = nil, subgroups = [])
8
8
  @group_id = group_id
9
- @subgroups = subgroups
10
- @subgroups = result.all_subgroups if result.respond_to?(:group_id)
9
+ @subgroups = result.respond_to?(:group_id) ? result.all_subgroups : subgroups
11
10
  super(result)
12
11
  end
13
12
 
14
13
  def all_subgroups
15
- [self, subgroups].flatten.reject { |subgroup| subgroup.group_id.nil? }
14
+ [self, subgroups].flatten.keep_if(&:group_id)
16
15
  end
17
16
 
18
17
  def swapcase
@@ -132,24 +131,16 @@ module RegexpExamples
132
131
  @group_id = group_id
133
132
  end
134
133
 
135
- def result
136
- result_by_method(:result)
137
- end
138
-
139
- def random_result
140
- result_by_method(:random_result)
141
- end
142
-
143
- private
144
-
145
134
  # Generates the result of each contained group
146
135
  # and adds the filled group of each result to itself
147
- def result_by_method(method)
148
- strings = @groups.map { |repeater| repeater.public_send(method) }
136
+ def result
137
+ strings = @groups.map { |repeater| repeater.public_send(__method__) }
149
138
  RegexpExamples.permutations_of_strings(strings).map do |result|
150
139
  GroupResult.new(result, group_id)
151
140
  end
152
141
  end
142
+
143
+ alias_method :random_result, :result
153
144
  end
154
145
 
155
146
  # A boolean "or" group.
@@ -177,13 +168,10 @@ module RegexpExamples
177
168
  private
178
169
 
179
170
  def result_by_method(method)
180
- repeaters_list.map do |repeaters|
181
- RegexpExamples.public_send(method, repeaters)
182
- end
171
+ repeaters_list
172
+ .map { |repeaters| RegexpExamples.public_send(method, repeaters) }
183
173
  .inject(:concat)
184
- .map do |result|
185
- GroupResult.new(result)
186
- end
174
+ .map { |result| GroupResult.new(result) }
187
175
  .uniq
188
176
  end
189
177
 
@@ -203,13 +191,14 @@ module RegexpExamples
203
191
  # of /\1/ as being "__1__". It later gets updated.
204
192
  class BackReferenceGroup
205
193
  include RandomResultBySample
194
+ PLACEHOLDER_FORMAT = '__%s__'
206
195
  attr_reader :id
207
196
  def initialize(id)
208
197
  @id = id
209
198
  end
210
199
 
211
200
  def result
212
- [GroupResult.new("__#{@id}__")]
201
+ [GroupResult.new(PLACEHOLDER_FORMAT % @id)]
213
202
  end
214
203
  end
215
204
  end
@@ -1,3 +1,4 @@
1
+ # :nodoc:
1
2
  module RegexpExamples
2
3
  # Given an array of arrays of strings, returns all possible perutations
3
4
  # for strings, created by joining one element from each array
@@ -14,15 +15,13 @@ module RegexpExamples
14
15
  end
15
16
 
16
17
  def self.join_preserving_capture_groups(result)
17
- result.flatten!
18
+ # Only save the LAST group from repeated capture groups, e.g. /([ab]){2}/
19
+ # (Hence the need for "reverse"!)
18
20
  subgroups = result
19
- .map(&:all_subgroups)
20
- .flatten
21
+ .flat_map(&:all_subgroups)
22
+ .reverse
23
+ .uniq(&:group_id)
21
24
 
22
- # Only save the LAST group from repeated capture groups, e.g. /([ab]){2}/
23
- subgroups.delete_if do |subgroup|
24
- subgroups.count { |other_subgroup| other_subgroup.group_id == subgroup.group_id } > 1
25
- end
26
25
  GroupResult.new(result.join, nil, subgroups)
27
26
  end
28
27