grex_ruby 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/.gitattributes +1 -0
- data/CHANGELOG.md +8 -0
- data/Cargo.lock +1 -1
- data/README.md +91 -0
- data/exe/grex-ruby +97 -0
- data/ext/grex_ruby/Cargo.toml +1 -1
- data/lib/grex_ruby/version.rb +1 -1
- data/lib/grex_ruby.rb +71 -66
- data/sig/grex_ruby.rbs +20 -0
- metadata +25 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 781efb31741fa9806bf76e010c0414b06bdd4dfaad5ef213ad4eba27d60ca976
|
|
4
|
+
data.tar.gz: d902f4f544a50bf54c71eb5ecbd2b743628ea195b13c282b1baef7aff54f7e69
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 06d66538fd78d54b19d8f4c79c4b57c9e50afe65dd225b866354a8b057e753703b388a5aa81003d2dbbfbc96f4dd497ef33183427abcd028f1e78a4e4e4ef554
|
|
7
|
+
data.tar.gz: 26d1802d580e715d89431471ea6744fad9290cc651cc8a9802b4704def599525c8bb6577f83c9e862b363773cf4bf70d6073d393e96fbe54bebe87643a119683
|
data/.gitattributes
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
exe/* text eol=lf
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
- Add `Grex.generate_from_file` with UTF-8 input, LF/CRLF line handling and keyword options.
|
|
6
|
+
- Add the `grex-ruby` command with flags for every generation option.
|
|
7
|
+
- Add command-line help, version output and readable errors with nonzero exit status.
|
|
8
|
+
- Document file input and command-line usage, including local development commands.
|
|
9
|
+
- Add integration tests for the CLI and file-based generation.
|
|
10
|
+
|
|
3
11
|
## 0.2.0
|
|
4
12
|
|
|
5
13
|
- Expose the public API as `Grex`, keeping `GrexRuby` as a compatibility alias.
|
data/Cargo.lock
CHANGED
data/README.md
CHANGED
|
@@ -401,6 +401,97 @@ An empty array raises `ArgumentError` with the message `samples must not be empt
|
|
|
401
401
|
Passing a string instead of an array, or an array containing an integer, raises
|
|
402
402
|
`TypeError`.
|
|
403
403
|
|
|
404
|
+
## Reading examples from a file
|
|
405
|
+
|
|
406
|
+
Starting with 0.3.0, `Grex.generate_from_file` reads a UTF-8 file with one example
|
|
407
|
+
per line and accepts the same keyword options as `Grex.generate`:
|
|
408
|
+
|
|
409
|
+
```ruby
|
|
410
|
+
# samples.txt contains two lines: 123 and 234.
|
|
411
|
+
Grex.generate_from_file('samples.txt', convert_digits: true)
|
|
412
|
+
# => "^\\p{Nd}\\p{Nd}\\p{Nd}$"
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
LF and Windows CRLF line endings are removed; other whitespace is preserved.
|
|
416
|
+
The last line does not need a newline. An empty file raises `ArgumentError`;
|
|
417
|
+
a missing file raises `Errno::ENOENT`.
|
|
418
|
+
|
|
419
|
+
## Command-line usage
|
|
420
|
+
|
|
421
|
+
Version 0.3.0 adds the `grex-ruby` command. After installing the gem:
|
|
422
|
+
|
|
423
|
+
```text
|
|
424
|
+
grex-ruby --help
|
|
425
|
+
grex-ruby --version
|
|
426
|
+
grex-ruby samples.txt
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
Given a UTF-8 `samples.txt` containing:
|
|
430
|
+
|
|
431
|
+
```text
|
|
432
|
+
123
|
|
433
|
+
234
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
Run:
|
|
437
|
+
|
|
438
|
+
```text
|
|
439
|
+
grex-ruby samples.txt --convert-digits
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
Output:
|
|
443
|
+
|
|
444
|
+
```text
|
|
445
|
+
^\p{Nd}\p{Nd}\p{Nd}$
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
Flags may appear before or after the file path, and can be combined:
|
|
449
|
+
|
|
450
|
+
```text
|
|
451
|
+
grex-ruby samples.txt --convert-digits --convert-repetitions --no-anchors
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
Output:
|
|
455
|
+
|
|
456
|
+
```text
|
|
457
|
+
\p{Nd}{3}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
All generation options are available:
|
|
461
|
+
|
|
462
|
+
| CLI flag | Ruby keyword |
|
|
463
|
+
| --- | --- |
|
|
464
|
+
| `--convert-digits` | `convert_digits: true` |
|
|
465
|
+
| `--convert-non-digits` | `convert_non_digits: true` |
|
|
466
|
+
| `--convert-whitespace` | `convert_whitespace: true` |
|
|
467
|
+
| `--convert-non-whitespace` | `convert_non_whitespace: true` |
|
|
468
|
+
| `--convert-words` | `convert_words: true` |
|
|
469
|
+
| `--convert-non-words` | `convert_non_words: true` |
|
|
470
|
+
| `--convert-repetitions` | `convert_repetitions: true` |
|
|
471
|
+
| `--minimum-repetitions N` | `minimum_repetitions: N` |
|
|
472
|
+
| `--minimum-substring-length N` | `minimum_substring_length: N` |
|
|
473
|
+
| `--no-anchors` | `anchors: false` |
|
|
474
|
+
| `--no-start-anchor` | `start_anchor: false` |
|
|
475
|
+
| `--no-end-anchor` | `end_anchor: false` |
|
|
476
|
+
| `-i`, `--ignore-case` | `ignore_case: true` |
|
|
477
|
+
| `--capturing-groups` | `capturing_groups: true` |
|
|
478
|
+
| `-x`, `--verbose` | `verbose: true` |
|
|
479
|
+
| `--escape-non-ascii` | `escape_non_ascii: true` |
|
|
480
|
+
|
|
481
|
+
Anchors are enabled by default. The positive forms `--anchors`, `--start-anchor`
|
|
482
|
+
and `--end-anchor` are also accepted. As in the Ruby API, `--no-anchors` overrides
|
|
483
|
+
the individual anchor settings. Numeric minimums must be positive 32-bit integers.
|
|
484
|
+
|
|
485
|
+
The command accepts exactly one file path. Use `--` before a path that starts
|
|
486
|
+
with `-`. Patterns are written to stdout; errors are written to stderr with exit
|
|
487
|
+
status `1`. Successful generation, `--help`, and `--version` exit with status `0`.
|
|
488
|
+
|
|
489
|
+
To run the CLI from a checkout before installing it:
|
|
490
|
+
|
|
491
|
+
```bash
|
|
492
|
+
bundle exec ruby -Ilib exe/grex-ruby samples.txt --convert-digits
|
|
493
|
+
```
|
|
494
|
+
|
|
404
495
|
## Development
|
|
405
496
|
|
|
406
497
|
### Linux
|
data/exe/grex-ruby
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'grex_ruby'
|
|
5
|
+
require 'optparse'
|
|
6
|
+
|
|
7
|
+
options = {}
|
|
8
|
+
|
|
9
|
+
parser = OptionParser.new do |opts|
|
|
10
|
+
opts.banner = 'Usage: grex-ruby FILE [options]'
|
|
11
|
+
|
|
12
|
+
opts.on('--convert-digits', 'Convert digits to a Unicode digit class') do
|
|
13
|
+
options[:convert_digits] = true
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
opts.on('--convert-non-digits', 'Convert non-digits to a Unicode character class') do
|
|
17
|
+
options[:convert_non_digits] = true
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
opts.on('--convert-whitespace', 'Convert whitespace to a Unicode character class') do
|
|
21
|
+
options[:convert_whitespace] = true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
opts.on('--convert-non-whitespace', 'Convert non-whitespace to a Unicode character class') do
|
|
25
|
+
options[:convert_non_whitespace] = true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
opts.on('--convert-words', 'Convert Unicode word characters') do
|
|
29
|
+
options[:convert_words] = true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
opts.on('--convert-non-words', 'Convert characters outside the Unicode word class') do
|
|
33
|
+
options[:convert_non_words] = true
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
opts.on('--convert-repetitions', 'Convert repeated substrings to quantifiers') do
|
|
37
|
+
options[:convert_repetitions] = true
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
opts.on('--minimum-repetitions N', Integer, 'Minimum repetitions after the first occurrence (default: 1)') do |value|
|
|
41
|
+
options[:minimum_repetitions] = value
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
opts.on('--minimum-substring-length N', Integer, 'Minimum repeated fragment length (default: 1)') do |value|
|
|
45
|
+
options[:minimum_substring_length] = value
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
opts.on('--[no-]anchors', 'Keep both line anchors (default: enabled)') do |value|
|
|
49
|
+
options[:anchors] = value
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
opts.on('--[no-]start-anchor', 'Keep the initial line anchor (default: enabled)') do |value|
|
|
53
|
+
options[:start_anchor] = value
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
opts.on('--[no-]end-anchor', 'Keep the final line anchor (default: enabled)') do |value|
|
|
57
|
+
options[:end_anchor] = value
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
opts.on('-i', '--ignore-case', 'Match without regard to letter case') do
|
|
61
|
+
options[:ignore_case] = true
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
opts.on('--capturing-groups', 'Use capturing groups') do
|
|
65
|
+
options[:capturing_groups] = true
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
opts.on('-x', '--verbose', 'Format the pattern across multiple lines') do
|
|
69
|
+
options[:verbose] = true
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
opts.on('--escape-non-ascii', 'Escape non-ASCII characters as Unicode code points') do
|
|
73
|
+
options[:escape_non_ascii] = true
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
opts.on('-V', '--version', 'Show version') do
|
|
77
|
+
puts Grex::VERSION
|
|
78
|
+
exit
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
opts.on('-h', '--help', 'Show help') do
|
|
82
|
+
puts opts
|
|
83
|
+
exit
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
begin
|
|
88
|
+
parser.parse!
|
|
89
|
+
raise OptionParser::MissingArgument, 'FILE' if ARGV.empty?
|
|
90
|
+
raise OptionParser::InvalidArgument, 'expected exactly one FILE' unless ARGV.length == 1
|
|
91
|
+
|
|
92
|
+
puts Grex.generate_from_file(ARGV.first, **options)
|
|
93
|
+
rescue OptionParser::ParseError, ArgumentError, RangeError, SystemCallError, EncodingError => error
|
|
94
|
+
warn "grex-ruby: #{error.message}"
|
|
95
|
+
warn 'Run grex-ruby --help for usage.'
|
|
96
|
+
exit 1
|
|
97
|
+
end
|
data/ext/grex_ruby/Cargo.toml
CHANGED
data/lib/grex_ruby/version.rb
CHANGED
data/lib/grex_ruby.rb
CHANGED
|
@@ -1,66 +1,71 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'grex_ruby/version'
|
|
4
|
-
require 'grex_ruby/grex_ruby'
|
|
5
|
-
|
|
6
|
-
module Grex
|
|
7
|
-
class Error < StandardError; end
|
|
8
|
-
|
|
9
|
-
def self.generate(samples,
|
|
10
|
-
convert_digits: false,
|
|
11
|
-
convert_repetitions: false,
|
|
12
|
-
minimum_substring_length: 1,
|
|
13
|
-
minimum_repetitions: 1,
|
|
14
|
-
anchors: true,
|
|
15
|
-
ignore_case: false,
|
|
16
|
-
capturing_groups: false,
|
|
17
|
-
verbose: false,
|
|
18
|
-
convert_non_digits: false,
|
|
19
|
-
convert_whitespace: false,
|
|
20
|
-
convert_non_whitespace: false,
|
|
21
|
-
convert_words: false,
|
|
22
|
-
convert_non_words: false,
|
|
23
|
-
start_anchor: true,
|
|
24
|
-
end_anchor: true,
|
|
25
|
-
escape_non_ascii: false)
|
|
26
|
-
pattern = generate_native(samples,
|
|
27
|
-
{ convert_digits:, convert_repetitions:, minimum_substring_length:, minimum_repetitions:,
|
|
28
|
-
anchors:, ignore_case:, capturing_groups:, verbose:, convert_non_digits:, convert_whitespace:, convert_non_whitespace:, convert_words:, convert_non_words:, start_anchor:, end_anchor:, escape_non_ascii: })
|
|
29
|
-
adapt_pattern(pattern)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def self.adapt_pattern(pattern)
|
|
33
|
-
chars = pattern.chars
|
|
34
|
-
result = +''
|
|
35
|
-
index = 0
|
|
36
|
-
|
|
37
|
-
while index < chars.length
|
|
38
|
-
if chars[index] == '\\'
|
|
39
|
-
result << case chars[index + 1]
|
|
40
|
-
when 'd'
|
|
41
|
-
'\p{Nd}'
|
|
42
|
-
when 'D'
|
|
43
|
-
'\P{Nd}'
|
|
44
|
-
when 's'
|
|
45
|
-
'\p{Space}'
|
|
46
|
-
when 'S'
|
|
47
|
-
'\P{Space}'
|
|
48
|
-
when 'w'
|
|
49
|
-
'[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_Control}]'
|
|
50
|
-
when 'W'
|
|
51
|
-
'[^\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_Control}]'
|
|
52
|
-
else
|
|
53
|
-
chars[index, 2].join
|
|
54
|
-
end
|
|
55
|
-
index += 2
|
|
56
|
-
else
|
|
57
|
-
result << chars[index]
|
|
58
|
-
index += 1
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
result
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'grex_ruby/version'
|
|
4
|
+
require 'grex_ruby/grex_ruby'
|
|
5
|
+
|
|
6
|
+
module Grex
|
|
7
|
+
class Error < StandardError; end
|
|
8
|
+
|
|
9
|
+
def self.generate(samples,
|
|
10
|
+
convert_digits: false,
|
|
11
|
+
convert_repetitions: false,
|
|
12
|
+
minimum_substring_length: 1,
|
|
13
|
+
minimum_repetitions: 1,
|
|
14
|
+
anchors: true,
|
|
15
|
+
ignore_case: false,
|
|
16
|
+
capturing_groups: false,
|
|
17
|
+
verbose: false,
|
|
18
|
+
convert_non_digits: false,
|
|
19
|
+
convert_whitespace: false,
|
|
20
|
+
convert_non_whitespace: false,
|
|
21
|
+
convert_words: false,
|
|
22
|
+
convert_non_words: false,
|
|
23
|
+
start_anchor: true,
|
|
24
|
+
end_anchor: true,
|
|
25
|
+
escape_non_ascii: false)
|
|
26
|
+
pattern = generate_native(samples,
|
|
27
|
+
{ convert_digits:, convert_repetitions:, minimum_substring_length:, minimum_repetitions:,
|
|
28
|
+
anchors:, ignore_case:, capturing_groups:, verbose:, convert_non_digits:, convert_whitespace:, convert_non_whitespace:, convert_words:, convert_non_words:, start_anchor:, end_anchor:, escape_non_ascii: })
|
|
29
|
+
adapt_pattern(pattern)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.adapt_pattern(pattern)
|
|
33
|
+
chars = pattern.chars
|
|
34
|
+
result = +''
|
|
35
|
+
index = 0
|
|
36
|
+
|
|
37
|
+
while index < chars.length
|
|
38
|
+
if chars[index] == '\\'
|
|
39
|
+
result << case chars[index + 1]
|
|
40
|
+
when 'd'
|
|
41
|
+
'\p{Nd}'
|
|
42
|
+
when 'D'
|
|
43
|
+
'\P{Nd}'
|
|
44
|
+
when 's'
|
|
45
|
+
'\p{Space}'
|
|
46
|
+
when 'S'
|
|
47
|
+
'\P{Space}'
|
|
48
|
+
when 'w'
|
|
49
|
+
'[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_Control}]'
|
|
50
|
+
when 'W'
|
|
51
|
+
'[^\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_Control}]'
|
|
52
|
+
else
|
|
53
|
+
chars[index, 2].join
|
|
54
|
+
end
|
|
55
|
+
index += 2
|
|
56
|
+
else
|
|
57
|
+
result << chars[index]
|
|
58
|
+
index += 1
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
result
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.generate_from_file(path, **options)
|
|
66
|
+
lines = File.readlines(path, encoding: 'UTF-8', chomp: true)
|
|
67
|
+
generate(lines, **options)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private_class_method :generate_native, :adapt_pattern
|
|
71
|
+
end
|
data/sig/grex_ruby.rbs
CHANGED
|
@@ -20,6 +20,26 @@ module Grex
|
|
|
20
20
|
?end_anchor: bool,
|
|
21
21
|
?escape_non_ascii: bool,
|
|
22
22
|
) -> String
|
|
23
|
+
|
|
24
|
+
def self.generate_from_file: (
|
|
25
|
+
String,
|
|
26
|
+
?convert_digits: bool,
|
|
27
|
+
?convert_repetitions: bool,
|
|
28
|
+
?minimum_substring_length: Integer,
|
|
29
|
+
?minimum_repetitions: Integer,
|
|
30
|
+
?anchors: bool,
|
|
31
|
+
?ignore_case: bool,
|
|
32
|
+
?capturing_groups: bool,
|
|
33
|
+
?verbose: bool,
|
|
34
|
+
?convert_non_digits: bool,
|
|
35
|
+
?convert_whitespace: bool,
|
|
36
|
+
?convert_non_whitespace: bool,
|
|
37
|
+
?convert_words: bool,
|
|
38
|
+
?convert_non_words: bool,
|
|
39
|
+
?start_anchor: bool,
|
|
40
|
+
?end_anchor: bool,
|
|
41
|
+
?escape_non_ascii: bool,
|
|
42
|
+
) -> String
|
|
23
43
|
end
|
|
24
44
|
|
|
25
45
|
module GrexRuby = Grex
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: grex_ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nelly Simkova
|
|
@@ -23,13 +23,35 @@ dependencies:
|
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: 0.9.128
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: optparse
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0.3'
|
|
33
|
+
- - "<"
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: '1.0'
|
|
36
|
+
type: :runtime
|
|
37
|
+
prerelease: false
|
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - ">="
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '0.3'
|
|
43
|
+
- - "<"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '1.0'
|
|
26
46
|
email:
|
|
27
47
|
- sk8high3r@ya.ru
|
|
28
|
-
executables:
|
|
48
|
+
executables:
|
|
49
|
+
- grex-ruby
|
|
29
50
|
extensions:
|
|
30
51
|
- ext/grex_ruby/extconf.rb
|
|
31
52
|
extra_rdoc_files: []
|
|
32
53
|
files:
|
|
54
|
+
- ".gitattributes"
|
|
33
55
|
- ".github/workflows/ci.yml"
|
|
34
56
|
- CHANGELOG.md
|
|
35
57
|
- Cargo.lock
|
|
@@ -38,6 +60,7 @@ files:
|
|
|
38
60
|
- README.md
|
|
39
61
|
- Rakefile
|
|
40
62
|
- THIRD_PARTY_NOTICES.md
|
|
63
|
+
- exe/grex-ruby
|
|
41
64
|
- ext/grex_ruby/Cargo.toml
|
|
42
65
|
- ext/grex_ruby/build.rs
|
|
43
66
|
- ext/grex_ruby/extconf.rb
|