regexp-examples 0.5.1 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -5
- data/lib/regexp-examples/groups.rb +2 -4
- data/lib/regexp-examples/parser.rb +46 -3
- data/lib/regexp-examples/version.rb +1 -1
- data/spec/regexp-examples_spec.rb +20 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0d34e8d62dc70d3b7b1ba17930f94a3799e9f669
|
4
|
+
data.tar.gz: 62b828f1e6207cf7198ad96a2b61ab5f08dea5d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e41e5d5a41dfaa44c67c3e99f28b0b37675774b3509f22d417f7bc2e9c664cccb9b480c8218a72ba63ce9876d2c7d09cbfc0bf8ab2c04d773f0e9e8d3818657
|
7
|
+
data.tar.gz: 82393a8ad2d76ebd853845a9f1de46cf1aa2175d56e0883004dc770fb9eaf5253dc23750a9eafb4cb36a8a751f5794071bd42fda0d8a2f4f68989486f31dccdc
|
data/README.md
CHANGED
@@ -38,6 +38,7 @@ For more detail on this, see [configuration options](#configuration-options).
|
|
38
38
|
* ...And backreferences(!!!), e.g. `/(this|that) \1/` `/(?<name>foo) \k<name>/`
|
39
39
|
* Groups work fine, even if nested or optional e.g. `/(even(this(works?))) \1 \2 \3/`, `/what about (this)? \1/`
|
40
40
|
* Non-capture groups, e.g. `/(?:foo)/`
|
41
|
+
* Comment groups, e.g. `/foo(?#comment)bar/`
|
41
42
|
* Control characters, e.g. `/\ca/`, `/\cZ/`, `/\C-9/`
|
42
43
|
* Escape sequences, e.g. `/\x42/`, `/\x5word/`, `/#{"\x80".force_encoding("ASCII-8BIT")}/`
|
43
44
|
* Unicode characters, e.g. `/\u0123/`, `/\uabcd/`, `/\u{789}/`
|
@@ -45,8 +46,9 @@ For more detail on this, see [configuration options](#configuration-options).
|
|
45
46
|
|
46
47
|
* Regexp options can also be used:
|
47
48
|
* Case insensitive examples: `/cool/i.examples #=> ["cool", "cooL", "coOl", "coOL", ...]`
|
48
|
-
* Multiline examples: `/./m.examples
|
49
|
+
* Multiline examples: `/./m.examples #=> ["\n", "a", "b", "c", "d"]`
|
49
50
|
* Extended form examples: `/line1 #comment \n line2/x.examples #=> ["line1line2"]`
|
51
|
+
* Options toggling supported: `/before(?imx-imx)after/`, `/before(?imx-imx:subexpr)after/`
|
50
52
|
|
51
53
|
## Bugs and Not-Yet-Supported syntax
|
52
54
|
|
@@ -54,10 +56,7 @@ For more detail on this, see [configuration options](#configuration-options).
|
|
54
56
|
* `/[[abc]]/.examples` (which _should_ return `["a", "b", "c"]`)
|
55
57
|
* `/[[a-d]&&[c-f]]/.examples` (which _should_ return: `["c", "d"]`)
|
56
58
|
|
57
|
-
*
|
58
|
-
* Including comments inside the pattern, i.e. `/(?#...)/`
|
59
|
-
* Conditional capture groups, such as `/(group1) (?(1)yes|no)`
|
60
|
-
* Options toggling, i.e. `/(?imx)/`, `/(?-imx)/`, `/(?imx: re)/` and `/(?-imx: re)/`
|
59
|
+
* Conditional capture groups, such as `/(group1) (?(1)yes|no)`
|
61
60
|
|
62
61
|
* The patterns: `/\10/` ... `/\77/` should match the octal representation of their character code, if there is no nth grouped subexpression. For example, `/\10/.examples` should return `["\x08"]`. Funnily enough, I did not think of this when writing my regexp parser.
|
63
62
|
|
@@ -126,7 +126,7 @@ module RegexpExamples
|
|
126
126
|
|
127
127
|
def result
|
128
128
|
chars = CharSets::Any
|
129
|
-
chars
|
129
|
+
chars = (["\n"] | chars) if multiline
|
130
130
|
chars.map do |result|
|
131
131
|
GroupResult.new(result)
|
132
132
|
end
|
@@ -134,12 +134,10 @@ module RegexpExamples
|
|
134
134
|
end
|
135
135
|
|
136
136
|
class MultiGroup
|
137
|
-
prepend GroupWithIgnoreCase
|
138
137
|
attr_reader :group_id
|
139
|
-
def initialize(groups, group_id
|
138
|
+
def initialize(groups, group_id)
|
140
139
|
@groups = groups
|
141
140
|
@group_id = group_id
|
142
|
-
@ignorecase = ignorecase
|
143
141
|
end
|
144
142
|
|
145
143
|
# Generates the result of each contained group
|
@@ -146,13 +146,44 @@ module RegexpExamples
|
|
146
146
|
@current_position += 1
|
147
147
|
@num_groups += 1
|
148
148
|
group_id = nil # init
|
149
|
-
|
149
|
+
previous_ignorecase = @ignorecase
|
150
|
+
previous_multiline = @multiline
|
151
|
+
previous_extended = @extended
|
152
|
+
rest_of_string.match(
|
153
|
+
/
|
154
|
+
\A
|
155
|
+
(\?)? # Is it a "special" group, i.e. starts with a "?"?
|
156
|
+
(
|
157
|
+
: # Non capture group
|
158
|
+
|! # Neglookahead
|
159
|
+
|= # Lookahead
|
160
|
+
|\# # Comment group
|
161
|
+
|< # Lookbehind or named capture
|
162
|
+
(
|
163
|
+
! # Neglookbehind
|
164
|
+
|= # Lookbehind
|
165
|
+
|[^>]+ # Named capture
|
166
|
+
)
|
167
|
+
|[mix]*-?[mix]* # Option toggle
|
168
|
+
)?
|
169
|
+
/x
|
170
|
+
) do |match|
|
150
171
|
case
|
151
172
|
when match[1].nil? # e.g. /(normal)/
|
152
173
|
group_id = @num_groups.to_s
|
153
174
|
when match[2] == ':' # e.g. /(?:nocapture)/
|
154
175
|
@current_position += 2
|
155
|
-
|
176
|
+
when match[2] == '#' # e.g. /(?#comment)/
|
177
|
+
comment_group = rest_of_string.match(/.*?[^\\](?:\\{2})*\)/)[0]
|
178
|
+
@current_position += comment_group.length
|
179
|
+
when match[2] =~ /\A(?=[mix-]+)([mix]*)-?([mix]*)/ # e.g. /(?i-mx)/
|
180
|
+
regexp_options_toggle($1, $2)
|
181
|
+
@current_position += $&.length + 1
|
182
|
+
if next_char == ':' # e.g. /(?i:subexpr)/
|
183
|
+
@current_position += 1
|
184
|
+
else
|
185
|
+
return parse_single_char_group('')
|
186
|
+
end
|
156
187
|
when %w(! =).include?(match[2]) # e.g. /(?=lookahead)/, /(?!neglookahead)/
|
157
188
|
raise IllegalSyntaxError, "Lookaheads are not regular; cannot generate examples"
|
158
189
|
when %w(! =).include?(match[3]) # e.g. /(?<=lookbehind)/, /(?<!neglookbehind)/
|
@@ -163,7 +194,19 @@ module RegexpExamples
|
|
163
194
|
end
|
164
195
|
end
|
165
196
|
groups = parse
|
166
|
-
|
197
|
+
@ignorecase = previous_ignorecase
|
198
|
+
@multiline = previous_multiline
|
199
|
+
@extended = previous_extended
|
200
|
+
MultiGroup.new(groups, group_id)
|
201
|
+
end
|
202
|
+
|
203
|
+
def regexp_options_toggle(on, off)
|
204
|
+
@ignorecase = true if (on.include? "i")
|
205
|
+
@ignorecase = false if (off.include? "i")
|
206
|
+
@multiline = true if (on.include? "m")
|
207
|
+
@multiline = false if (off.include? "m")
|
208
|
+
@extended = true if (on.include? "x")
|
209
|
+
@extended = false if (off.include? "x")
|
167
210
|
end
|
168
211
|
|
169
212
|
def parse_multi_end_group
|
@@ -231,6 +231,13 @@ RSpec.describe Regexp, "#examples" do
|
|
231
231
|
)
|
232
232
|
end
|
233
233
|
|
234
|
+
context "comment group" do
|
235
|
+
examples_exist_and_match(
|
236
|
+
/a(?#comment)b/,
|
237
|
+
/a(?#ugly backslashy\ comment\\\))b/
|
238
|
+
)
|
239
|
+
end
|
240
|
+
|
234
241
|
context "exact examples match" do
|
235
242
|
# More rigorous tests to assert that ALL examples are being listed
|
236
243
|
context "default config options" do
|
@@ -284,6 +291,19 @@ RSpec.describe Regexp, "#examples" do
|
|
284
291
|
).to eq %w(line1line2)
|
285
292
|
end
|
286
293
|
end
|
294
|
+
|
295
|
+
context "options toggling" do
|
296
|
+
context "rest of string" do
|
297
|
+
it { expect(/a(?i)b(?-i)c/.examples).to eq %w{abc aBc}}
|
298
|
+
it { expect(/a(?x) b(?-x) c/.examples).to eq %w{ab\ c}}
|
299
|
+
it { expect(/(?m)./.examples(max_group_results: 999)).to include "\n" }
|
300
|
+
end
|
301
|
+
context "subexpression" do
|
302
|
+
it { expect(/a(?i:b)c/.examples).to eq %w{abc aBc}}
|
303
|
+
it { expect(/a(?i:b(?-i:c))/.examples).to eq %w{abc aBc}}
|
304
|
+
it { expect(/a(?-i:b)c/i.examples).to eq %w{abc abC Abc AbC}}
|
305
|
+
end
|
306
|
+
end
|
287
307
|
end
|
288
308
|
|
289
309
|
end
|