regexp-examples 0.3.1 → 0.3.2
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/README.md +12 -5
- data/coverage/.resultset.json +203 -131
- data/coverage/coverage-badge.png +0 -0
- data/coverage/index.html +1264 -832
- data/lib/regexp-examples/constants.rb +17 -12
- data/lib/regexp-examples/groups.rb +21 -9
- data/lib/regexp-examples/helpers.rb +11 -3
- data/lib/regexp-examples/parser.rb +7 -3
- data/lib/regexp-examples/regexp_extensions.rb +3 -5
- data/lib/regexp-examples/repeaters.rb +11 -5
- data/lib/regexp-examples/version.rb +1 -1
- data/spec/regexp-examples_spec.rb +28 -2
- data/spec/spec_helper.rb +0 -1
- metadata +2 -2
@@ -1,6 +1,12 @@
|
|
1
1
|
module RegexpExamples
|
2
|
-
#
|
3
|
-
|
2
|
+
# The maximum variance for any given repeater, to prevent a huge/infinite number of
|
3
|
+
# examples from being listed. For example, if MaxRepeaterVariance = 2 then:
|
4
|
+
# .* is equivalent to .{0,2}
|
5
|
+
# .+ is equivalent to .{1,3}
|
6
|
+
# .{2,} is equivalent to .{2,4}
|
7
|
+
# .{,3} is equivalent to .{0,2}
|
8
|
+
# .{3,8} is equivalent to .{3,5}
|
9
|
+
MaxRepeaterVariance = 2
|
4
10
|
|
5
11
|
# Maximum number of characters returned from a char set, to reduce output spam
|
6
12
|
# For example:
|
@@ -9,14 +15,13 @@ module RegexpExamples
|
|
9
15
|
MaxGroupResults = 5
|
10
16
|
|
11
17
|
module CharSets
|
12
|
-
Lower
|
13
|
-
Upper
|
14
|
-
Digit
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
Any = Lower | Upper | Digit | Punct
|
18
|
+
Lower = Array('a'..'z')
|
19
|
+
Upper = Array('A'..'Z')
|
20
|
+
Digit = Array('0'..'9')
|
21
|
+
Punct = [33..47, 58..64, 91..96, 123..126].map { |r| r.map { |val| val.chr } }.flatten
|
22
|
+
Hex = Array('a'..'f') | Array('A'..'F') | Digit
|
23
|
+
Whitespace = [' ', "\t", "\n", "\r", "\v", "\f"]
|
24
|
+
Any = Lower | Upper | Digit | Punct
|
20
25
|
end
|
21
26
|
|
22
27
|
# Map of special regex characters, to their associated character sets
|
@@ -25,8 +30,8 @@ module RegexpExamples
|
|
25
30
|
'D' => CharSets::Lower | CharSets::Upper | CharSets::Punct,
|
26
31
|
'w' => CharSets::Lower | CharSets::Upper | CharSets::Digit | ['_'],
|
27
32
|
'W' => CharSets::Punct.reject { |val| val == '_' },
|
28
|
-
's' =>
|
29
|
-
'S' => CharSets::Any -
|
33
|
+
's' => CharSets::Whitespace,
|
34
|
+
'S' => CharSets::Any - CharSets::Whitespace,
|
30
35
|
'h' => CharSets::Hex,
|
31
36
|
'H' => CharSets::Any - CharSets::Hex,
|
32
37
|
|
@@ -53,9 +53,16 @@ module RegexpExamples
|
|
53
53
|
last = nil
|
54
54
|
first = @chars.shift if @chars.first == "-"
|
55
55
|
last = @chars.pop if @chars.last == "-"
|
56
|
-
# Replace all instances of e.g. ["a" "-" "z"] with ["a", "b", ..., "z"]
|
56
|
+
# Replace all instances of e.g. ["a", "-", "z"] with ["a", "b", ..., "z"]
|
57
57
|
while i = @chars.index("-")
|
58
|
-
|
58
|
+
# Prevent infinite loops from expanding [",", "-", "."] to itself
|
59
|
+
# (Since ",".ord = 44, "-".ord = 45, ".".ord = 46)
|
60
|
+
if (@chars[i-1] == ',' && @chars[i+1] == '.')
|
61
|
+
first = '-'
|
62
|
+
@chars.delete_at(i)
|
63
|
+
else
|
64
|
+
@chars[i-1..i+1] = (@chars[i-1]..@chars[i+1]).to_a
|
65
|
+
end
|
59
66
|
end
|
60
67
|
# restore them back
|
61
68
|
@chars.unshift(first) if first
|
@@ -66,7 +73,7 @@ module RegexpExamples
|
|
66
73
|
@chars.each_with_index do |char, i|
|
67
74
|
if char == "\\"
|
68
75
|
if BackslashCharMap.keys.include?(@chars[i+1])
|
69
|
-
@chars[i..i+1] = BackslashCharMap[@chars[i+1]]
|
76
|
+
@chars[i..i+1] = move_backslash_to_front( BackslashCharMap[@chars[i+1]] )
|
70
77
|
elsif @chars[i+1] == 'b'
|
71
78
|
@chars[i..i+1] = "\b"
|
72
79
|
elsif @chars[i+1] == "\\"
|
@@ -83,6 +90,14 @@ module RegexpExamples
|
|
83
90
|
GroupResult.new(result)
|
84
91
|
end
|
85
92
|
end
|
93
|
+
|
94
|
+
private
|
95
|
+
def move_backslash_to_front(chars)
|
96
|
+
if index = chars.index { |char| char == '\\' }
|
97
|
+
chars.unshift chars.delete_at(index)
|
98
|
+
end
|
99
|
+
chars
|
100
|
+
end
|
86
101
|
end
|
87
102
|
|
88
103
|
class DotGroup
|
@@ -120,13 +135,10 @@ module RegexpExamples
|
|
120
135
|
@right_repeaters = right_repeaters
|
121
136
|
end
|
122
137
|
|
138
|
+
|
123
139
|
def result
|
124
|
-
left_result = @left_repeaters
|
125
|
-
|
126
|
-
end
|
127
|
-
right_result = @right_repeaters.map do |repeater|
|
128
|
-
RegexpExamples::permutations_of_strings([repeater.result])
|
129
|
-
end
|
140
|
+
left_result = RegexpExamples::map_results(@left_repeaters)
|
141
|
+
right_result = RegexpExamples::map_results(@right_repeaters)
|
130
142
|
left_result.concat(right_result).flatten.uniq.map do |result|
|
131
143
|
GroupResult.new(result)
|
132
144
|
end
|
@@ -5,12 +5,12 @@ module RegexpExamples
|
|
5
5
|
# element from each array
|
6
6
|
#
|
7
7
|
# For example:
|
8
|
-
# permutations_of_strings [ ['a'], ['b'], ['c', 'd', 'e'] ] #=> ['
|
8
|
+
# permutations_of_strings [ ['a'], ['b'], ['c', 'd', 'e'] ] #=> ['abc', 'abd', 'abe']
|
9
9
|
# permutations_of_strings [ ['a', 'b'], ['c', 'd'] ] #=> [ 'ac', 'ad', 'bc', 'bd' ]
|
10
|
-
def self.permutations_of_strings(arrays_of_strings
|
10
|
+
def self.permutations_of_strings(arrays_of_strings)
|
11
11
|
first = arrays_of_strings.shift
|
12
12
|
return first if arrays_of_strings.empty?
|
13
|
-
first.product( permutations_of_strings(arrays_of_strings
|
13
|
+
first.product( permutations_of_strings(arrays_of_strings) ).map do |result|
|
14
14
|
join_preserving_capture_groups(result)
|
15
15
|
end
|
16
16
|
end
|
@@ -22,5 +22,13 @@ module RegexpExamples
|
|
22
22
|
.flatten
|
23
23
|
GroupResult.new(result.join, nil, subgroups)
|
24
24
|
end
|
25
|
+
|
26
|
+
def self.map_results(repeaters)
|
27
|
+
repeaters
|
28
|
+
.map {|repeater| repeater.result}
|
29
|
+
.instance_eval do |partial_results|
|
30
|
+
RegexpExamples::permutations_of_strings(partial_results)
|
31
|
+
end
|
32
|
+
end
|
25
33
|
end
|
26
34
|
|
@@ -12,7 +12,9 @@ module RegexpExamples
|
|
12
12
|
while @current_position < regexp_string.length
|
13
13
|
group = parse_group(repeaters)
|
14
14
|
break if group.is_a? MultiGroupEnd
|
15
|
-
|
15
|
+
if group.is_a? OrGroup
|
16
|
+
return [OneTimeRepeater.new(group)]
|
17
|
+
end
|
16
18
|
@current_position += 1
|
17
19
|
repeaters << parse_repeater(group)
|
18
20
|
end
|
@@ -65,7 +67,10 @@ module RegexpExamples
|
|
65
67
|
group = parse_backreference_group($1)
|
66
68
|
when BackslashCharMap.keys.include?(regexp_string[@current_position])
|
67
69
|
group = CharGroup.new(
|
68
|
-
|
70
|
+
# Note: The `.dup` is important, as it prevents modifying the constant, in
|
71
|
+
# CharGroup#init_ranges (where the '-' is moved to the front)
|
72
|
+
BackslashCharMap[regexp_string[@current_position]].dup
|
73
|
+
)
|
69
74
|
when rest_of_string =~ /\A(c|C-)(.)/ # Control character
|
70
75
|
@current_position += $1.length
|
71
76
|
group = parse_single_char_group( parse_control_character($2) )
|
@@ -98,7 +103,6 @@ module RegexpExamples
|
|
98
103
|
end
|
99
104
|
else
|
100
105
|
group = parse_single_char_group( regexp_string[@current_position] )
|
101
|
-
# TODO: What about cases like \A, \z, \Z ?
|
102
106
|
end
|
103
107
|
group
|
104
108
|
end
|
@@ -1,11 +1,9 @@
|
|
1
1
|
class Regexp
|
2
2
|
module Examples
|
3
3
|
def examples
|
4
|
-
|
5
|
-
RegexpExamples::Parser.new(source)
|
6
|
-
|
7
|
-
.map {|repeater| repeater.result}
|
8
|
-
full_examples = RegexpExamples::permutations_of_strings(partial_examples)
|
4
|
+
full_examples = RegexpExamples::map_results(
|
5
|
+
RegexpExamples::Parser.new(source).parse
|
6
|
+
)
|
9
7
|
RegexpExamples::BackReferenceReplacer.new.substitute_backreferences(full_examples)
|
10
8
|
end
|
11
9
|
end
|
@@ -33,7 +33,7 @@ module RegexpExamples
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def result
|
36
|
-
super(0,
|
36
|
+
super(0, MaxRepeaterVariance)
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
@@ -43,7 +43,7 @@ module RegexpExamples
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def result
|
46
|
-
super(1,
|
46
|
+
super(1, MaxRepeaterVariance + 1)
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
@@ -62,17 +62,23 @@ module RegexpExamples
|
|
62
62
|
super(group)
|
63
63
|
@min = min || 0
|
64
64
|
if max
|
65
|
-
|
65
|
+
# Prevent huge number of results in case of e.g. /.{1,100}/.examples
|
66
|
+
@max = smallest(max, @min + MaxRepeaterVariance)
|
66
67
|
elsif has_comma
|
67
|
-
@max = min +
|
68
|
+
@max = @min + MaxRepeaterVariance
|
68
69
|
else
|
69
|
-
@max = min
|
70
|
+
@max = @min
|
70
71
|
end
|
71
72
|
end
|
72
73
|
|
73
74
|
def result
|
74
75
|
super(@min, @max)
|
75
76
|
end
|
77
|
+
|
78
|
+
private
|
79
|
+
def smallest(x, y)
|
80
|
+
(x < y) ? x : y
|
81
|
+
end
|
76
82
|
end
|
77
83
|
end
|
78
84
|
|
@@ -29,6 +29,14 @@ RSpec.describe Regexp, "#examples" do
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
+
def self.examples_are_empty(*regexps)
|
33
|
+
regexps.each do |regexp|
|
34
|
+
it do
|
35
|
+
expect(regexp.examples).to be_empty
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
32
40
|
context 'returns matching strings' do
|
33
41
|
context "for basic repeaters" do
|
34
42
|
examples_exist_and_match(
|
@@ -54,6 +62,7 @@ RSpec.describe Regexp, "#examples" do
|
|
54
62
|
|
55
63
|
context "for complex char groups (square brackets)" do
|
56
64
|
examples_exist_and_match(
|
65
|
+
|
57
66
|
/[abc]/,
|
58
67
|
/[a-c]/,
|
59
68
|
/[abc-e]/,
|
@@ -65,7 +74,8 @@ RSpec.describe Regexp, "#examples" do
|
|
65
74
|
/[\\\]]/,
|
66
75
|
/[\n-\r]/,
|
67
76
|
/[\-]/,
|
68
|
-
/[%-+]
|
77
|
+
/[%-+]/, # This regex is "supposed to" match some surprising things!!!
|
78
|
+
/['-.]/ # Test to ensure no "infinite loop" on character set expansion
|
69
79
|
)
|
70
80
|
end
|
71
81
|
|
@@ -118,7 +128,8 @@ RSpec.describe Regexp, "#examples" do
|
|
118
128
|
/((a?b*c+)) \1/,
|
119
129
|
/((a?b*c+)?) \1/,
|
120
130
|
/a|b|c|d/,
|
121
|
-
/a+|b*|c
|
131
|
+
/a+|b*|c?/,
|
132
|
+
/one|two|three/
|
122
133
|
)
|
123
134
|
end
|
124
135
|
|
@@ -189,5 +200,20 @@ RSpec.describe Regexp, "#examples" do
|
|
189
200
|
)
|
190
201
|
end
|
191
202
|
|
203
|
+
context "for empty character sets" do
|
204
|
+
examples_are_empty(
|
205
|
+
/[^\d\D]/,
|
206
|
+
/[^\w\W]/,
|
207
|
+
/[^\s\S]/,
|
208
|
+
/[^\h\H]/,
|
209
|
+
/[^\D0-9]/,
|
210
|
+
/[^\Wa-zA-Z0-9_]/,
|
211
|
+
/[^\d\D]*/,
|
212
|
+
/[^\d\D]+/,
|
213
|
+
/[^\d\D]{2}/,
|
214
|
+
/[^\d\D]word/
|
215
|
+
)
|
216
|
+
end
|
217
|
+
|
192
218
|
end
|
193
219
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: regexp-examples
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Lord
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-01-
|
11
|
+
date: 2015-01-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|