srl_ruby 0.3.5 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +9 -3
- data/CHANGELOG.md +20 -0
- data/README.md +326 -52
- data/bin/srl2ruby +95 -0
- data/bin/srl2ruby_cli_parser.rb +89 -0
- data/lib/regex/abstract_method.rb +1 -1
- data/lib/regex/alternation.rb +1 -1
- data/lib/regex/anchor.rb +3 -3
- data/lib/regex/atomic_expression.rb +2 -2
- data/lib/regex/capturing_group.rb +3 -3
- data/lib/regex/char_class.rb +5 -5
- data/lib/regex/char_range.rb +5 -5
- data/lib/regex/char_shorthand.rb +2 -2
- data/lib/regex/character.rb +6 -6
- data/lib/regex/compound_expression.rb +2 -2
- data/lib/regex/concatenation.rb +3 -3
- data/lib/regex/expression.rb +4 -4
- data/lib/regex/lookaround.rb +1 -1
- data/lib/regex/match_option.rb +2 -2
- data/lib/regex/monadic_expression.rb +3 -3
- data/lib/regex/multiplicity.rb +1 -1
- data/lib/regex/non_capturing_group.rb +1 -1
- data/lib/regex/polyadic_expression.rb +3 -3
- data/lib/regex/repetition.rb +2 -2
- data/lib/regex/wildcard.rb +3 -3
- data/lib/srl_ruby/ast_builder.rb +22 -22
- data/lib/srl_ruby/srl_token.rb +1 -1
- data/lib/srl_ruby/tokenizer.rb +7 -7
- data/lib/srl_ruby/version.rb +1 -1
- data/spec/acceptance/srl_test_suite_spec.rb +1 -1
- data/spec/acceptance/support/rule_file_ast_builder.rb +8 -8
- data/spec/acceptance/support/rule_file_token.rb +1 -1
- data/spec/acceptance/support/rule_file_tokenizer.rb +8 -8
- data/spec/regex/character_spec.rb +30 -30
- data/spec/regex/multiplicity_spec.rb +24 -24
- data/srl_ruby.gemspec +8 -5
- data/templates/base.erb +2 -0
- data/templates/srl_and_ruby.erb +9 -0
- data/templates/srl_block_and_ruby.erb +10 -0
- metadata +8 -4
- data/bin/srl_ruby +0 -51
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f476a011aec905f943e525e2fb73770d6fe253b0
|
4
|
+
data.tar.gz: 3a93a4502442762e7101bb46f58be95ac60a7730
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 064d58d3a68227f7f0e636b3228194262bf08b79b07d807edd9c99042fa9e22a39735c4798a9bd918adb24216095849e7e69f5500447e4b2c1fe519823cac5d8
|
7
|
+
data.tar.gz: 63e7d716017d8fc95dfbac33fc1b36e0d4b39bcf0803b192f30b7d772747f5c811619afabcec0dbcb8091788e62d378b982de5e20978bea717f054301b3c4f57
|
data/.rubocop.yml
CHANGED
@@ -27,6 +27,9 @@ Layout/IndentationWidth:
|
|
27
27
|
Layout/IndentationConsistency:
|
28
28
|
Enabled: true
|
29
29
|
|
30
|
+
Layout/IndentHeredoc:
|
31
|
+
Enabled: false
|
32
|
+
|
30
33
|
Layout/MultilineHashBraceLayout:
|
31
34
|
Enabled: true
|
32
35
|
|
@@ -99,7 +102,7 @@ Naming/ClassAndModuleCamelCase:
|
|
99
102
|
Enabled: false
|
100
103
|
|
101
104
|
Naming/UncommunicativeBlockParamName:
|
102
|
-
Enabled:
|
105
|
+
Enabled: true
|
103
106
|
|
104
107
|
Naming/UncommunicativeMethodParamName:
|
105
108
|
Enabled: false
|
@@ -144,7 +147,7 @@ Style/ConditionalAssignment:
|
|
144
147
|
Enabled: false
|
145
148
|
|
146
149
|
Style/DefWithParentheses:
|
147
|
-
Enabled:
|
150
|
+
Enabled: true
|
148
151
|
|
149
152
|
Style/Documentation:
|
150
153
|
Enabled: false
|
@@ -159,7 +162,7 @@ Style/IfUnlessModifier:
|
|
159
162
|
Enabled: false
|
160
163
|
|
161
164
|
Style/InverseMethods:
|
162
|
-
Enabled:
|
165
|
+
Enabled: true
|
163
166
|
|
164
167
|
Style/Next:
|
165
168
|
Enabled: false
|
@@ -178,6 +181,9 @@ Style/RegexpLiteral:
|
|
178
181
|
|
179
182
|
Style/PercentLiteralDelimiters:
|
180
183
|
Enabled: false
|
184
|
+
|
185
|
+
Style/StderrPuts:
|
186
|
+
Enabled: false
|
181
187
|
|
182
188
|
Style/StringLiterals:
|
183
189
|
Enabled: true
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
## [0.4.0] - 2018-05-13
|
2
|
+
Version bump: SrlRuby has a command-line compiler `srl2ruby`
|
3
|
+
|
4
|
+
### Added
|
5
|
+
- File `bin/srl2ruby` A compiler that parses SRL expressions and transform them into regular Regexp.
|
6
|
+
- File `srl2ruby_cli_parser.rb` Implementation of the CLI of the `srl2ruby` compiler.
|
7
|
+
- Directory `templates` contains ERB templates that can be used to format `srl2ruby` output.
|
8
|
+
|
9
|
+
### Changed
|
10
|
+
- File `README.md` vastly expanded in order to cover `srl2ruby` compiler and a couple of SRL examples
|
11
|
+
- File `.rubocop.yml` enabled some of the complaining cops
|
12
|
+
|
13
|
+
### Removed
|
14
|
+
- File `srl_ruby` the previous binary of the gem is now replaced by `srl2ruby`.
|
15
|
+
|
16
|
+
### Fixed
|
17
|
+
- Method `Tokenizer#_next_token` failed to recognize digit or integer value immediately followed by a closing parenthesis ')'.
|
18
|
+
- Many classes lightly refactored in order to please Rubocop.
|
19
|
+
|
20
|
+
|
1
21
|
## [0.3.5] - 2018-04-29
|
2
22
|
|
3
23
|
### Added
|
data/README.md
CHANGED
@@ -5,72 +5,362 @@
|
|
5
5
|
[![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](https://github.com/famished-tiger/SRL-Ruby/blob/master/LICENSE.txt)
|
6
6
|
|
7
7
|
|
8
|
-
|
8
|
+
Welcome to the first Ruby implementation of a [Simple Regex Language](https://simple-regex.com) (SRL) parser and compiler.
|
9
|
+
It allows you to write __highly-readable__ text patterns in SRL and then generate their Ruby `Regexp` counterparts.
|
10
|
+
Ever wanted to write challenging regular expressions but were intimided by their arcane, cryptic syntax?
|
11
|
+
With **srl_ruby** you can easily design your patterns in SRL and let *srl_ruby* transform them into terse `Regexp`.
|
12
|
+
|
13
|
+
### Features:
|
14
|
+
- Command-line SRL-to-Ruby compiler with customizable output.
|
15
|
+
- Ruby API for integrating a SRL parser or compiler with your code.
|
16
|
+
- 100% pure Ruby with clean design (_not a port from some other language_)
|
17
|
+
- Minimal runtime dependency ([Rley](https://rubygems.org/gems/rley) gem). Won't drag a bunch of gems...
|
18
|
+
- Compatibility: works with 2.x+ MRI, JRuby
|
19
|
+
- Portability: tested on both Linux and Windows,...
|
9
20
|
|
10
|
-
##
|
11
|
-
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
### ...with Bundler
|
24
|
+
Add this line to your application's Gemfile:
|
25
|
+
|
26
|
+
gem 'srl_ruby'
|
27
|
+
|
28
|
+
And then execute:
|
29
|
+
|
30
|
+
$ bundle
|
31
|
+
|
32
|
+
### ...with Rubygem
|
33
|
+
Or install it directly yourself with the command line:
|
34
|
+
|
35
|
+
$ gem install srl_ruby
|
36
|
+
|
37
|
+
## Usage
|
38
|
+
Let's test the installation by launching the __srl2ruby__ (SRL-to-Ruby) command-line compiler with the help option:
|
39
|
+
|
40
|
+
$ srl2ruby --help
|
41
|
+
|
42
|
+
It should output something similar to:
|
43
|
+
|
44
|
+
```
|
45
|
+
Usage: srl2ruby SRL_FILE [options]
|
46
|
+
|
47
|
+
Description:
|
48
|
+
Parses a SRL file and compiles it into a Ruby Regexp literal.
|
49
|
+
Simple Regex Language (SRL) website: https://simple-regex.com
|
50
|
+
|
51
|
+
Examples:
|
52
|
+
srl2ruby example.srl
|
53
|
+
srl2ruby example.srl -o example_re.rb -t srl_and_ruby.erb
|
54
|
+
|
55
|
+
Options:
|
56
|
+
-o, --output-file PATH Output to a file with specified name.
|
57
|
+
-t, --template-file TEMPLATE Use given ERB template for the Ruby code generation. srl2ruby looks for
|
58
|
+
the template file in current dir first then in its gem's /templates dir.
|
59
|
+
|
60
|
+
--version Display the program version then quit.
|
61
|
+
-?, -h, --help Display this help then quit.
|
62
|
+
```
|
63
|
+
|
64
|
+
## A quick intro to SRL and srl2ruby
|
65
|
+
### What is SRL?
|
66
|
+
SRL is a small language that lets you write pattern matching expressions
|
12
67
|
in a readable syntax that bears some resemblance with English.
|
68
|
+
For SRL documentation and examples, we cannot but recommend you to jump to the [official SRL website](https://simple-regex.com).
|
69
|
+
|
70
|
+
### Why SRL?
|
71
|
+
It is a well-known fact: regexes can be really hard to write and even harder to read ('decipher' verb is closer to reality).
|
72
|
+
Alas, the path of creating and maintaining regexes can be full of frustration.
|
73
|
+
|
74
|
+
There comes SRL. The intent is to let developers define self-documenting patterns with an easy syntax.
|
75
|
+
And then let your computer translate SRL expressions into terse regular expressions.
|
76
|
+
|
77
|
+
### Our first SRL pattern
|
78
|
+
Let's succumb to the traditional 'hello world' example. True, it is a contrived example that doesn't make justice to SRL expressiveness. On the other hand, it is a starting point good enough to learn the compile cycle.
|
79
|
+
|
80
|
+
As a first step, let's create a file named `hello.srl` with just the following line:
|
81
|
+
```
|
82
|
+
begin with literally "Hello world!"
|
83
|
+
```
|
84
|
+
|
85
|
+
It should read as 'Match any text that begins with the exact text "Hello world!"'
|
86
|
+
Now, if one invokes the `srl2ruby` compiler with the command line...
|
87
|
+
|
88
|
+
$ srl2ruby hello.srl
|
89
|
+
|
90
|
+
|
91
|
+
... one gets the following output:
|
92
|
+
```
|
93
|
+
Parsing file 'hello.srl'
|
94
|
+
/^Hello world!/
|
95
|
+
```
|
96
|
+
|
97
|
+
The last displayed line is the Ruby `Regexp` representation of the above SRL line. It can be pasted as such in your Ruby code, like in the following Ruby snippet:
|
98
|
+
```ruby
|
99
|
+
subject = 'Hello world! Welcome to SRL...'
|
100
|
+
puts 'It matches!' if subject =~ /^Hello world!/
|
101
|
+
```
|
102
|
+
|
103
|
+
As expected the snippet results in the message:
|
104
|
+
```
|
105
|
+
It matches!
|
106
|
+
```
|
107
|
+
|
108
|
+
**Quick recap:**
|
109
|
+
- `srl2ruby` expects a SRL file (typically with a .srl extension)
|
110
|
+
- It parses the _Simple Regex Language_ content...
|
111
|
+
- ... then generates the `Regexp` that is equivalent to the SRL input
|
112
|
+
- Finally, it prints the results to the console.
|
113
|
+
|
114
|
+
Feature: with the command-line `-o` option the compiler will send the output to a file with specified name.
|
115
|
+
|
116
|
+
|
117
|
+
### Gears up
|
118
|
+
Let's admit it, our first example wasn't really impressive.
|
119
|
+
So, let's try with a more imposing example inspired from the [official SRL website](https://simple-regex.com): an email validation pattern.
|
120
|
+
|
121
|
+
```
|
122
|
+
begin with any of (digit, letter, one of "._%+-") once or more,
|
123
|
+
literally "@",
|
124
|
+
any of (digit, letter, one of ".-") once or more,
|
125
|
+
( literally ".",
|
126
|
+
letter at least 2 times
|
127
|
+
) optional,
|
128
|
+
must end,
|
129
|
+
case insensitive
|
130
|
+
```
|
131
|
+
|
132
|
+
Assume that the previous SRL pattern was put in a file named `email_validation.srl` and that we invoked `srl2ruby` with the following command-line:
|
133
|
+
|
134
|
+
$ srl2ruby email_validation.srl
|
135
|
+
|
136
|
+
Then the output should be:
|
137
|
+
```
|
138
|
+
Parsing file 'email_validation.srl'
|
139
|
+
/(?i-mx:^(?:\d|[a-z]|[._%+\-])+@(?:\d|[a-z]|[.\-])+(?:\.[a-z]{2,})?$)/
|
140
|
+
```
|
141
|
+
|
142
|
+
The resulting regexp isn't for the fainted hearts: who's ready to maintain it? In addition, the above pattern covers only the most frequent cases.
|
143
|
+
If you were asked to cover more exotic case, and knowing that it means an expression at least twice as complex, which version are you willing to update the SRL or the Regexp one?
|
144
|
+
|
145
|
+
#### Good to know: customizable output
|
146
|
+
In fact, if one wants to update or maintain a pattern, it would be practical to have the SRL expression and its equivalent Regexp next to each other in our Ruby source code.
|
147
|
+
Can the `srl2ruby` compiler help there? The answer is ... yes.
|
148
|
+
First, it is good to know that the output of the `srl2ruby` compiler can be tailored with an ERB template. For instance, the output of all the previous examples is relying on a default template called `base.erb`. It is bundled in the `srl_ruby` gem as another template called `srl_and_ruby.erb`. This second template will emit the SRL code (in Ruby comments) followed by the Regexp literal.
|
149
|
+
So let's use it with our email validation example:
|
150
|
+
|
151
|
+
$ srl2ruby email_validation.srl --template-file srl_and_ruby.erb
|
152
|
+
|
153
|
+
The shorter option `-t` syntax is also possible:
|
154
|
+
|
155
|
+
$ srl2ruby email_validation.srl -t srl_and_ruby.erb
|
156
|
+
|
157
|
+
The compiler's output contains now the original SRL expression in comments:
|
158
|
+
|
159
|
+
```
|
160
|
+
Parsing file 'email_validation.srl'
|
161
|
+
# SRL expression follows:
|
162
|
+
# begin with any of (digit, letter, one of "._%+-") once or more,
|
163
|
+
# literally "@",
|
164
|
+
# any of (digit, letter, one of ".-") once or more,
|
165
|
+
# ( literally ".",
|
166
|
+
# letter at least 2 times
|
167
|
+
# ) optional,
|
168
|
+
# must end,
|
169
|
+
# case insensitive
|
170
|
+
#
|
171
|
+
# ... and its Regexp equivalent:
|
172
|
+
/(?i-mx:^(?:\d|[a-z]|[._%+\-])+@(?:\d|[a-z]|[.\-])+(?:\.[a-z]{2,})?$)/
|
173
|
+
```
|
174
|
+
The above SRL code in comments can be safely inserted in a Ruby file.
|
175
|
+
|
176
|
+
**Quick recap:**
|
177
|
+
- SRL can be used to specify much more challenging patterns than our boring 'Hello world!'.
|
178
|
+
- The `srl2ruby` compiler uses a ERB template to format its output.
|
179
|
+
- It is possible to choose a specific template via the `-t` option.
|
180
|
+
|
181
|
+
_Feature_: When given the name of a template via the `-t` option, the compiler will look first for such a template in the current directory, then, if not found, in its `templates` directory. This gives the opportunity to use customized local template files.
|
182
|
+
|
183
|
+
## Time for yet another example
|
13
184
|
|
14
|
-
As an example, let's assume that
|
15
|
-
|
16
|
-
|
185
|
+
As an example, let's assume that we are asked to create a regular expression that matches the time in 12 hour clock format (say, _hh:mm AM/PM_).
|
186
|
+
In addition, the hour and minute values must be put (= captured) in a variable named `hour` and `min` respectively.
|
187
|
+
|
188
|
+
We will proceed in multiple iterations of increasing complexity.
|
189
|
+
However, for those that are always in a hurry and like movie spoils, here is the requested `Regexp`:
|
190
|
+
```ruby
|
191
|
+
/(?i-mx:^(?<hour>(?:(?:0?\d)|(?:1[01]))):(?<min>(?:0?|[1-5])\d)\s?[AP]M$)/
|
192
|
+
```
|
193
|
+
Want to jump directly to the latest [iteration](#iteration-5)?...
|
194
|
+
|
195
|
+
|
196
|
+
### Iteration 1
|
197
|
+
Here is a very naive SRL expression that matches the requested time format:
|
17
198
|
```
|
18
|
-
|
19
|
-
digit optional,
|
199
|
+
begin with digit twice,
|
20
200
|
literally ":",
|
21
|
-
digit
|
201
|
+
digit twice
|
22
202
|
literally " ",
|
23
|
-
one of "AP", literally "M"
|
203
|
+
one of "AP", literally "M",
|
204
|
+
must end
|
24
205
|
```
|
25
206
|
|
26
|
-
|
27
|
-
|
28
|
-
[
|
29
|
-
|
207
|
+
If one compiles the above SRL expression with `srl2ruby` as explained earlier in ['Our first SRL pattern'](#our-first-srl-pattern) section, it will generate the following Regexp literal:
|
208
|
+
```ruby
|
209
|
+
/^\d{2}:\d{2} [AP]M$/
|
210
|
+
```
|
30
211
|
|
212
|
+
When I want to test regular expressions, one of my favorite tool is the
|
213
|
+
[Rubular website](http://rubular.com/). Tom Lovitt created a great Regexp editor and tester specifically for the Ruby community.
|
31
214
|
|
32
|
-
|
33
|
-
|
34
|
-
This is where SRL shines over the traditional regular expressions: high readability.
|
35
|
-
For instance, a regex for the clock format problem may look like this:
|
215
|
+
By the way, perhaps, some lynx-eyed readers spotted a small "mistake" on the third line of the SRL snippet: it doesn't end with a comma.
|
216
|
+
My apologies... For style consistency this line should be written as:
|
36
217
|
```
|
37
|
-
|
218
|
+
digit twice,
|
38
219
|
```
|
220
|
+
In reality, SRL happily ignores comma. Well..., most of the time. There is one exception: for the `any of` construct commas are used to separate alternatives (see example in [Iteration 3](#iteration-3)
|
39
221
|
|
40
|
-
|
41
|
-
|
222
|
+
### Iteration 2
|
223
|
+
Tests won't take a long time to show that the previous pattern is much too 'lenient' and will accept grossly incorrect entries such as 45:67 PM.
|
42
224
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
225
|
+
For our next iteration, we keep note that:
|
226
|
+
- The first digit (from the left) can take the values 0 or 1 only.
|
227
|
+
- The third digit may run from 0 to 5 since the highest value for the minutes is 59.
|
228
|
+
|
229
|
+
Here is the improved SRL version:
|
230
|
+
```
|
231
|
+
begin with digit from 0 to 1,
|
232
|
+
digit,
|
233
|
+
literally ":",
|
234
|
+
digit from 0 to 5,
|
235
|
+
digit,
|
236
|
+
literally " ",
|
237
|
+
one of "AP", literally "M"
|
238
|
+
must end
|
239
|
+
```
|
240
|
+
|
241
|
+
`srl2ruby` will swallow the SRL file and will spit out the next Regexp:
|
242
|
+
```ruby
|
243
|
+
/^[0-1]\d:[0-5]\d [AP]M$/
|
244
|
+
```
|
245
|
+
### Iteration 3
|
246
|
+
Erroneous values like 45:67 PM are no more accepted this time. That's definitively better...
|
247
|
+
But other tests will reveal that our pattern is still too permissive since it accepts values like 17:23 PM. A hour value of 17 is OK in 24 hour format but here we fail meeting our requirements...
|
248
|
+
|
249
|
+
So, for our third try, we keep note that:
|
250
|
+
- If the first hour digit is 1, then the second digit can take the values 0 or 1 only.
|
47
251
|
|
252
|
+
Let's refactor our pattern:
|
253
|
+
```
|
254
|
+
begin with any of (
|
255
|
+
(literally "0", digit),
|
256
|
+
(literally "1", one of "01")
|
257
|
+
)
|
258
|
+
literally ":",
|
259
|
+
digit from 0 to 5,
|
260
|
+
digit,
|
261
|
+
literally " ",
|
262
|
+
one of "AP", literally "M"
|
263
|
+
must end
|
264
|
+
```
|
48
265
|
|
49
|
-
|
50
|
-
The
|
51
|
-
zero or one.
|
266
|
+
Remarks:
|
267
|
+
- The indentation isn't required by SRL, but I find that it contributes to the readability...
|
52
268
|
|
53
|
-
|
269
|
+
`srl2ruby` will transform this into:
|
270
|
+
```ruby
|
271
|
+
/^(?:(?:0\d)|(?:1[01])):[0-5]\d [AP]M$/
|
54
272
|
```
|
55
|
-
|
273
|
+
|
274
|
+
### Iteration 4
|
275
|
+
This time the pattern works correctly. But in the meantime, our customer changed his requirements (*of course, such things never happen in real life...*). He asks for more flexibility in the pattern:
|
276
|
+
- If the most significant digit value is zero, it is optional (i.e. some clock models won't display it).
|
277
|
+
- The space between the minute value and the AM/PM indicator is now optional.
|
278
|
+
- The AM/PM indicator can sometimes be written in small letters (am/pm).
|
279
|
+
|
280
|
+
Let's go for another tour:
|
281
|
+
```
|
282
|
+
begin with any of (
|
56
283
|
(literally "0" optional, digit),
|
57
284
|
(literally "1", one of "01")
|
285
|
+
)
|
286
|
+
literally ":",
|
287
|
+
any of (
|
288
|
+
literally "0" optional,
|
289
|
+
digit from 1 to 5
|
58
290
|
),
|
291
|
+
digit,
|
292
|
+
whitespace optional,
|
293
|
+
one of "AP", literally "M"
|
294
|
+
must end,
|
295
|
+
case insensitive
|
296
|
+
```
|
297
|
+
|
298
|
+
Here is the Regexp counterpart:
|
299
|
+
```ruby
|
300
|
+
/(?i-mx:^(?:(?:0?\d)|(?:1[01])):(?:0?|[1-5])\d\s?[AP]M$)/
|
301
|
+
```
|
302
|
+
### Iteration 5
|
303
|
+
Are we done? No: we were asked to capture the values of hours and minutes.
|
304
|
+
|
305
|
+
SRL allows for named captures, so here is the updated version:
|
306
|
+
```
|
307
|
+
begin with capture(
|
308
|
+
any of (
|
309
|
+
(literally "0" optional, digit),
|
310
|
+
(literally "1", one of "01")
|
311
|
+
)
|
312
|
+
) as "hour",
|
59
313
|
literally ":",
|
60
|
-
|
61
|
-
|
314
|
+
capture(
|
315
|
+
any of (
|
316
|
+
literally "0" optional,
|
317
|
+
digit from 1 to 5
|
318
|
+
),
|
319
|
+
digit
|
320
|
+
) as "min",
|
321
|
+
whitespace optional,
|
62
322
|
one of "AP", literally "M"
|
323
|
+
must end,
|
324
|
+
case insensitive
|
63
325
|
```
|
64
326
|
|
65
|
-
|
327
|
+
`srl2ruby` will swiftly swallow the above SRL pattern and generate the following Regexp:
|
328
|
+
```ruby
|
329
|
+
/(?i-mx:^(?<hour>(?:(?:0?\d)|(?:1[01]))):(?<min>(?:0?|[1-5])\d)\s?[AP]M$)/
|
66
330
|
```
|
67
|
-
|
331
|
+
|
332
|
+
That's becoming insane...
|
333
|
+
|
334
|
+
|
335
|
+
#### Does this last Regexp really work?
|
336
|
+
Glad you asked... Here is a Ruby snippet that can be used to test the last generated Regexp:
|
337
|
+
|
338
|
+
```ruby
|
339
|
+
# Next Regexp was copy-pasted from srl2ruby output
|
340
|
+
pattern = /(?i-mx:^(?<hour>(?:(?:0?\d)|(?:1[01]))):(?<min>(?:0?|[1-5])\d)\s?[AP]M$)/
|
341
|
+
text = '1:43am'
|
342
|
+
|
343
|
+
matching = pattern.match(text)
|
344
|
+
if matching
|
345
|
+
print 'Capture names: '; p(matching.names) # => Capture names: ["hour", "min"]
|
346
|
+
puts "Value of 'hour': #{matching[:hour]}" # => Value of 'hour': 1
|
347
|
+
puts "Value of 'min': #{matching[:min]}" # => Value of 'min': 43
|
348
|
+
else
|
349
|
+
puts "Text '#{text}' doesn't match."
|
350
|
+
end
|
68
351
|
```
|
69
352
|
|
353
|
+
Running this snippet, gives the following output:
|
354
|
+
```
|
355
|
+
Capture names: ["hour", "min"]
|
356
|
+
Value of 'hour': 1
|
357
|
+
Value of 'min': 43
|
358
|
+
```
|
359
|
+
As one can see, from the input '1:43am', the Regexp captured the hour and minute values in the appropriate capture variable. Mission accomplished...
|
70
360
|
|
71
|
-
##
|
361
|
+
## srl_ruby API
|
72
362
|
|
73
|
-
The method `SrlRuby#parse` accepts a Simple Regex Language string as input, and returns the corresponding regular expression.
|
363
|
+
The method `SrlRuby#parse` accepts a Simple Regex Language string as input, and returns the corresponding regular expression as a `Regexp` instance.
|
74
364
|
|
75
365
|
For instance, the following snippet...
|
76
366
|
|
@@ -106,22 +396,6 @@ puts 'Equivalent regexp: /' + result + '/'
|
|
106
396
|
Equivalent regexp: /(?:19|20)\d{2}-(?:(?:0\d)|(?:1[012]))-(?:(?:0\d)|(?:[12]\d)|(?:3[01]))/
|
107
397
|
```
|
108
398
|
|
109
|
-
## Installation
|
110
|
-
|
111
|
-
Add this line to your application's Gemfile:
|
112
|
-
|
113
|
-
```ruby
|
114
|
-
gem 'srl_ruby'
|
115
|
-
```
|
116
|
-
|
117
|
-
And then execute:
|
118
|
-
|
119
|
-
$ bundle
|
120
|
-
|
121
|
-
Or install it yourself as:
|
122
|
-
|
123
|
-
$ gem install srl_ruby
|
124
|
-
|
125
399
|
|
126
400
|
## Contributing
|
127
401
|
|
data/bin/srl2ruby
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'erb'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
require_relative '../lib/srl_ruby'
|
7
|
+
require_relative 'srl2ruby_cli_parser'
|
8
|
+
|
9
|
+
class Srl2RubyProg
|
10
|
+
DefaultSRLExtension = 'srl'.freeze
|
11
|
+
DefDirname = '/templates'.freeze
|
12
|
+
DefTemplate = 'base.erb'.freeze
|
13
|
+
attr_reader(:cli_options)
|
14
|
+
attr_reader(:template)
|
15
|
+
|
16
|
+
def initialize(prog_name, args)
|
17
|
+
my_version = SrlRuby::VERSION
|
18
|
+
cli = Srl2RubyCLIParser.new(prog_name, my_version)
|
19
|
+
@cli_options = cli.parse!(args)
|
20
|
+
@template = valid_template
|
21
|
+
end
|
22
|
+
|
23
|
+
def run!(file_names)
|
24
|
+
file_names.each do |srl_file|
|
25
|
+
fname = validate_filename(srl_file)
|
26
|
+
next unless file_exist?(fname)
|
27
|
+
puts "Parsing file '#{fname}'"
|
28
|
+
srl_source = File.read(fname)
|
29
|
+
result = SrlRuby.parse(srl_source)
|
30
|
+
destination = $stdout
|
31
|
+
if cli_options.include?(:output)
|
32
|
+
filepath = cli_options[:output]
|
33
|
+
destination = File.open(filepath, 'w')
|
34
|
+
end
|
35
|
+
puts "Writing to file '#{filepath}'" unless destination == $stdout
|
36
|
+
destination.puts emit_code(template, srl_source, result)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def valid_template
|
43
|
+
t_filepath = nil
|
44
|
+
bindir = Gem.bin_path('srl_ruby', File.basename(__FILE__))
|
45
|
+
gem_dir = Pathname.new(bindir.sub(/(?<=\/)[^\/]+$/, '')).parent
|
46
|
+
def_template_dir = gem_dir.expand_path.to_s + DefDirname
|
47
|
+
|
48
|
+
if cli_options.include?(:template)
|
49
|
+
fname = cli_options[:template]
|
50
|
+
exists = File.exist?(fname)
|
51
|
+
if exists
|
52
|
+
t_filepath = Dir.getwd + '/' + fname
|
53
|
+
else
|
54
|
+
t_filepath = def_template_dir + '/' + fname
|
55
|
+
exit(1) unless file_exist?(t_filepath)
|
56
|
+
end
|
57
|
+
else
|
58
|
+
t_filepath = def_template_dir + '/' + DefTemplate
|
59
|
+
end
|
60
|
+
path = Pathname.new(t_filepath)
|
61
|
+
erb_template = ERB.new(path.read, nil, '<>')
|
62
|
+
|
63
|
+
erb_template
|
64
|
+
end
|
65
|
+
|
66
|
+
def validate_filename(raw_fname)
|
67
|
+
# When necessary add extension to file name
|
68
|
+
fname = raw_fname.dup
|
69
|
+
basename = File.basename(fname)
|
70
|
+
has_extension = basename =~ /(?<=[^.])\.[^.]+$/
|
71
|
+
fname << '.' << DefaultSRLExtension unless has_extension
|
72
|
+
|
73
|
+
fname
|
74
|
+
end
|
75
|
+
|
76
|
+
def file_exist?(fname)
|
77
|
+
exists = File.exist?(fname)
|
78
|
+
$stderr.puts "No such file '#{fname}'" unless exists
|
79
|
+
|
80
|
+
exists
|
81
|
+
end
|
82
|
+
|
83
|
+
def emit_code(template, srl_source, regexp)
|
84
|
+
template.result(binding)
|
85
|
+
end
|
86
|
+
end # class
|
87
|
+
|
88
|
+
########################################
|
89
|
+
# ENTRY POINT
|
90
|
+
########################################
|
91
|
+
program = Srl2RubyProg.new(File.basename(__FILE__), ARGV)
|
92
|
+
|
93
|
+
# All options from CLI gobbled from ARGV, remains only file name
|
94
|
+
program.run!(ARGV)
|
95
|
+
# End of file
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'optparse' # Use standard OptionParser class for command-line parsing
|
2
|
+
|
3
|
+
# A command-line option parser for the srl2ruby compiler.
|
4
|
+
# It is a specialisation of the OptionParser class.
|
5
|
+
class Srl2RubyCLIParser < OptionParser
|
6
|
+
# @return [Hash{Symbol=>String, Array}]
|
7
|
+
attr_reader(:parsed_options)
|
8
|
+
|
9
|
+
# Constructor.
|
10
|
+
def initialize(prog_name, ver)
|
11
|
+
super()
|
12
|
+
reset(prog_name, ver)
|
13
|
+
|
14
|
+
heading
|
15
|
+
separator 'Options:'
|
16
|
+
add_o_option
|
17
|
+
add_t_option
|
18
|
+
separator ''
|
19
|
+
add_tail_options
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse!(args)
|
23
|
+
super
|
24
|
+
parsed_options
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def reset(prog_name, ver)
|
30
|
+
@program_name = prog_name
|
31
|
+
@version = ver
|
32
|
+
@banner = "Usage: #{prog_name} SRL_FILE [options]"
|
33
|
+
@parsed_options = {}
|
34
|
+
end
|
35
|
+
|
36
|
+
def description
|
37
|
+
descr = <<-DESCR
|
38
|
+
Description:
|
39
|
+
Parses a SRL file and compiles it into a Ruby Regexp literal.
|
40
|
+
Simple Regex Language (SRL) website: https://simple-regex.com
|
41
|
+
|
42
|
+
Examples:
|
43
|
+
#{program_name} example.srl
|
44
|
+
#{program_name} example.srl -o example_re.rb -t srl_and_ruby.erb
|
45
|
+
DESCR
|
46
|
+
|
47
|
+
descr
|
48
|
+
end
|
49
|
+
|
50
|
+
def heading
|
51
|
+
banner
|
52
|
+
separator ''
|
53
|
+
separator description
|
54
|
+
separator ''
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_o_option
|
58
|
+
explanation = 'Output to a file with specified name.'
|
59
|
+
|
60
|
+
on '-o', '--output-file PATH', explanation do |pathname|
|
61
|
+
@parsed_options[:output] = pathname
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_t_option
|
66
|
+
explanation = <<-EXPLANATION
|
67
|
+
Use given ERB template for the Ruby code generation. srl2ruby looks for
|
68
|
+
the template file in current dir first then in its gem's /templates dir.
|
69
|
+
EXPLANATION
|
70
|
+
|
71
|
+
on '-t', '--template-file TEMPLATE', explanation do |template|
|
72
|
+
@parsed_options[:template] = template
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def add_tail_options
|
77
|
+
on_tail('--version', 'Display the program version then quit.') do
|
78
|
+
puts version
|
79
|
+
exit(0)
|
80
|
+
end
|
81
|
+
|
82
|
+
on_tail('-?', '-h', '--help', 'Display this help then quit.') do
|
83
|
+
puts help
|
84
|
+
exit(0)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end # class
|
88
|
+
|
89
|
+
# End of file
|