srl_ruby 0.3.2 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +29 -1
- data/CHANGELOG.md +10 -0
- data/Rakefile +1 -2
- data/bin/srl_ruby +41 -18
- data/lib/regex/char_range.rb +2 -2
- data/lib/regex/multiplicity.rb +69 -71
- data/lib/srl_ruby/ast_builder.rb +1 -1
- data/lib/srl_ruby/grammar.rb +5 -4
- data/lib/srl_ruby/tokenizer.rb +2 -2
- data/lib/srl_ruby/version.rb +1 -1
- data/lib/srl_ruby.rb +17 -12
- data/spec/acceptance/support/rule_file_nodes.rb +2 -2
- data/spec/regex/multiplicity_spec.rb +55 -57
- data/spec/spec_helper.rb +1 -1
- data/spec/srl_ruby/tokenizer_spec.rb +16 -16
- data/spec/srl_ruby_spec.rb +34 -34
- data/srl_ruby.gemspec +14 -12
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d359b7fdd648df4b9e77e0bf64266b3fec2d474
|
4
|
+
data.tar.gz: 7a8a1efb3f978b8f45b15c47c0b457a6109daf37
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 20c7d24104afefa0371692ca2f0713916b9a6edbc1825c32bb77ff1c29760fa46bd04731ab88914f8dbd1d97f1cbd523f8b5e765d57ad0cf9ea7938110161c95
|
7
|
+
data.tar.gz: 9110435c0ae7bf089c167a46c81f6aabe91a7f9d52926843654972877aef2ccc1838c5a5218e8eac1f628cf95328f1f044a62b9d1fc4cfbcc6db49752b24b9ee
|
data/.rubocop.yml
CHANGED
@@ -1,3 +1,31 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- 'exp/**/*'
|
4
|
+
|
1
5
|
Layout/EndOfLine:
|
2
6
|
Enabled: true
|
3
|
-
EnforcedStyle: lf
|
7
|
+
EnforcedStyle: lf
|
8
|
+
|
9
|
+
Metrics/BlockLength:
|
10
|
+
Enabled: true
|
11
|
+
Max: 300
|
12
|
+
|
13
|
+
Metrics/MethodLength:
|
14
|
+
Enabled: true
|
15
|
+
Max: 30
|
16
|
+
|
17
|
+
Metrics/ModuleLength:
|
18
|
+
Enabled: true
|
19
|
+
Max: 500
|
20
|
+
|
21
|
+
Naming/ConstantName:
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
Naming/VariableName:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
Style/CommentedKeyword:
|
28
|
+
Enabled: false
|
29
|
+
|
30
|
+
Style/RedundantReturn:
|
31
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## [0.3.3] - 2018-04-19
|
2
|
+
### Changed
|
3
|
+
- Binary `srl_ruby` now accepts a SRL filename as command-line argument. Command-line documentation updated.
|
4
|
+
|
5
|
+
### Fixed
|
6
|
+
- Method `Tokenizer#_next_token` failed to recognize single letter followed by a comma
|
7
|
+
- Binary `srl_ruby` now loads source files from correct filepath
|
8
|
+
- Module `SRL` is no more an ancestor of class `Multiplicity`.
|
9
|
+
|
10
|
+
|
1
11
|
## [0.3.2] - 2018-04-13
|
2
12
|
### Added
|
3
13
|
- `Cucumber` feature files are now part of the gem.
|
data/Rakefile
CHANGED
@@ -7,13 +7,12 @@ RSpec::Core::RakeTask.new do |spec|
|
|
7
7
|
spec.pattern = 'spec/**/*_spec.rb'
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
10
|
Cucumber::Rake::Task.new do |_|
|
12
11
|
end
|
13
12
|
|
14
13
|
# Combine RSpec and Cucumber tests
|
15
14
|
desc 'Run tests, with RSpec and Cucumber'
|
16
|
-
task test: [
|
15
|
+
task test: %i[spec cucumber]
|
17
16
|
|
18
17
|
# Default rake task
|
19
18
|
task default: :test
|
data/bin/srl_ruby
CHANGED
@@ -1,28 +1,51 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
require_relative '../lib/srl_ruby
|
2
|
+
require_relative '../lib/srl_ruby'
|
3
3
|
|
4
|
+
my_name = File.basename(__FILE__)
|
5
|
+
my_version = SrlRuby::VERSION
|
6
|
+
puts "#{my_name} v. #{my_version}"
|
4
7
|
|
5
8
|
# Parse the input expression in command-line
|
6
9
|
if ARGV.empty?
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
10
|
+
msg = <<-MSG
|
11
|
+
Simple Regex Language compiler:
|
12
|
+
- Parses an SRL expression and displays the corresponding Regexp literal
|
13
|
+
|
14
|
+
Command-line syntax (I):
|
15
|
+
========================
|
16
|
+
#{my_name} "SRL expression"
|
17
|
+
where:
|
18
|
+
the SRL expression is enclosed between double quotes (")
|
19
|
+
|
20
|
+
Examples:
|
21
|
+
#{my_name} "letter from a to f exactly 4 times"
|
22
|
+
#{my_name} "uppercase letter between 2 and 3 times"
|
23
|
+
#{my_name} "digit from 0 to 7 once or more"
|
24
|
+
|
25
|
+
Command-line syntax (II):
|
26
|
+
========================
|
27
|
+
#{my_name} -f filename
|
28
|
+
where:
|
29
|
+
the filename is a text file containing the SRL expression to compile
|
30
|
+
|
31
|
+
Examples:
|
32
|
+
#{my_name} -f hexadecimal.srl
|
33
|
+
MSG
|
22
34
|
puts msg
|
23
35
|
exit(1)
|
24
36
|
end
|
25
|
-
|
26
|
-
|
37
|
+
|
38
|
+
|
39
|
+
if ARGV[0] =~ /^--?fi?l?e?$/
|
40
|
+
srl_file = ARGV[1]
|
41
|
+
result = SrlRuby.load_file(srl_file)
|
42
|
+
else
|
43
|
+
srl_expr = ARGV[0]
|
44
|
+
puts 'SRL input: ' + srl_expr
|
45
|
+
result = SrlRuby.parse(srl_expr)
|
46
|
+
end
|
47
|
+
|
48
|
+
puts 'Resulting Regexp: /' + result.to_s + '/'
|
49
|
+
|
27
50
|
|
28
51
|
# End of file
|
data/lib/regex/char_range.rb
CHANGED
@@ -7,9 +7,9 @@ module Regex # This module is used as a namespace
|
|
7
7
|
# Assumption: characters are ordered by codepoint
|
8
8
|
class CharRange < PolyadicExpression
|
9
9
|
# Constructor.
|
10
|
-
# [thelowerBound]
|
10
|
+
# [thelowerBound]
|
11
11
|
# A character that will be the lower bound value for the range.
|
12
|
-
# [theUpperBound]
|
12
|
+
# [theUpperBound]
|
13
13
|
# A character that will be the upper bound value for the range.
|
14
14
|
# TODO: optimisation. Build a Character if lower bound == upper bound.
|
15
15
|
def initialize(theLowerBound, theUpperBound)
|
data/lib/regex/multiplicity.rb
CHANGED
@@ -1,91 +1,89 @@
|
|
1
1
|
# File: Multiplicity.rb
|
2
2
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
attr_reader(:lower_bound)
|
3
|
+
module Regex # This module is used as a namespace
|
4
|
+
# The multiplicity specifies by how much a given expression can be repeated.
|
5
|
+
class Multiplicity
|
6
|
+
# The lowest acceptable repetition count
|
7
|
+
attr_reader(:lower_bound)
|
9
8
|
|
10
|
-
|
11
|
-
|
9
|
+
# The highest possible repetition count
|
10
|
+
attr_reader(:upper_bound)
|
12
11
|
|
13
|
-
|
14
|
-
|
12
|
+
# An indicator that specifies how to repeat (:greedy, :lazy, :possessive)
|
13
|
+
attr_reader(:policy)
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
# Purpose: Return the String representation of the multiplicity.
|
26
|
-
def to_str()
|
27
|
-
case upper_bound
|
28
|
-
when :more
|
29
|
-
case lower_bound
|
30
|
-
when 0
|
31
|
-
subresult = '*'
|
32
|
-
when 1
|
33
|
-
subresult = '+'
|
34
|
-
else
|
35
|
-
subresult = "{#{lower_bound},}"
|
36
|
-
end
|
15
|
+
# @param aLowerBound [Integer]
|
16
|
+
# @param anUpperBound [Integer, Symbol] integer or :more symbol
|
17
|
+
# @param aPolicy [Symbol] One of: (:greedy, :lazy, :possessive)
|
18
|
+
def initialize(aLowerBound, anUpperBound, aPolicy)
|
19
|
+
@lower_bound = valid_lower_bound(aLowerBound)
|
20
|
+
@upper_bound = valid_upper_bound(anUpperBound)
|
21
|
+
@policy = valid_policy(aPolicy)
|
22
|
+
end
|
37
23
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
24
|
+
# Purpose: Return the String representation of the multiplicity.
|
25
|
+
def to_str()
|
26
|
+
case upper_bound
|
27
|
+
when :more
|
28
|
+
case lower_bound
|
29
|
+
when 0
|
30
|
+
subresult = '*'
|
31
|
+
when 1
|
32
|
+
subresult = '+'
|
43
33
|
else
|
44
|
-
subresult = "{#{lower_bound}
|
45
|
-
|
46
|
-
end
|
34
|
+
subresult = "{#{lower_bound},}"
|
35
|
+
end
|
47
36
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
'?'
|
53
|
-
|
54
|
-
|
55
|
-
|
37
|
+
when lower_bound
|
38
|
+
subresult = "{#{lower_bound}}"
|
39
|
+
else
|
40
|
+
if [lower_bound, upper_bound] == [0, 1]
|
41
|
+
subresult = '?'
|
42
|
+
else
|
43
|
+
subresult = "{#{lower_bound},#{upper_bound}}"
|
44
|
+
end
|
45
|
+
end
|
56
46
|
|
57
|
-
|
47
|
+
suffix = case policy
|
48
|
+
when :greedy
|
49
|
+
''
|
50
|
+
when :lazy
|
51
|
+
'?'
|
52
|
+
when :possessive
|
53
|
+
'+'
|
58
54
|
end
|
59
55
|
|
60
|
-
|
56
|
+
return subresult + suffix
|
57
|
+
end
|
61
58
|
|
62
|
-
|
63
|
-
def valid_lower_bound(aLowerBound)
|
64
|
-
err_msg = "Invalid lower bound of repetition count #{aLowerBound}"
|
65
|
-
raise StandardError, err_msg unless aLowerBound.kind_of?(Integer)
|
66
|
-
return aLowerBound
|
67
|
-
end
|
59
|
+
private
|
68
60
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
61
|
+
# Validation method. Return the validated lower bound value
|
62
|
+
def valid_lower_bound(aLowerBound)
|
63
|
+
err_msg = "Invalid lower bound of repetition count #{aLowerBound}"
|
64
|
+
raise StandardError, err_msg unless aLowerBound.kind_of?(Integer)
|
65
|
+
return aLowerBound
|
66
|
+
end
|
75
67
|
|
76
|
-
|
68
|
+
# Validation method. Return the validated lower bound value
|
69
|
+
def valid_upper_bound(anUpperBound)
|
70
|
+
err_msg = "Invalid upper bound of repetition count #{anUpperBound}"
|
71
|
+
unless anUpperBound.kind_of?(Integer) || (anUpperBound == :more)
|
72
|
+
raise StandardError, err_msg
|
77
73
|
end
|
78
74
|
|
79
|
-
|
80
|
-
|
81
|
-
err_msg = "Invalid repetition policy '#{aPolicy}'."
|
82
|
-
valid_policies = %i[greedy lazy possessive]
|
83
|
-
raise StandardError, err_msg unless valid_policies.include? aPolicy
|
75
|
+
return anUpperBound
|
76
|
+
end
|
84
77
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
78
|
+
# Validation method. Return the validated policy value.
|
79
|
+
def valid_policy(aPolicy)
|
80
|
+
err_msg = "Invalid repetition policy '#{aPolicy}'."
|
81
|
+
valid_policies = %i[greedy lazy possessive]
|
82
|
+
raise StandardError, err_msg unless valid_policies.include? aPolicy
|
83
|
+
|
84
|
+
return aPolicy
|
85
|
+
end
|
86
|
+
end # class
|
89
87
|
end # module
|
90
88
|
|
91
89
|
# End of file
|
data/lib/srl_ruby/ast_builder.rb
CHANGED
@@ -34,7 +34,7 @@ module SrlRuby
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def multiplicity(lowerBound, upperBound)
|
37
|
-
return
|
37
|
+
return Regex::Multiplicity.new(lowerBound, upperBound, :greedy)
|
38
38
|
end
|
39
39
|
|
40
40
|
def string_literal(aString, to_escape = true)
|
data/lib/srl_ruby/grammar.rb
CHANGED
@@ -6,11 +6,11 @@ module SrlRuby
|
|
6
6
|
builder = Rley::Syntax::GrammarBuilder.new do
|
7
7
|
# Separators...
|
8
8
|
add_terminals('LPAREN', 'RPAREN', 'COMMA')
|
9
|
-
|
9
|
+
|
10
10
|
# Literal values...
|
11
11
|
add_terminals('DIGIT_LIT', 'INTEGER', 'LETTER_LIT', 'CHAR_CLASS')
|
12
12
|
add_terminals('LITERALLY', 'STRING_LIT', 'IDENTIFIER')
|
13
|
-
|
13
|
+
|
14
14
|
# Keywords...
|
15
15
|
add_terminals('BEGIN', 'STARTS', 'WITH')
|
16
16
|
add_terminals('MUST', 'END', 'RAW')
|
@@ -74,8 +74,8 @@ module SrlRuby
|
|
74
74
|
rule('digit_range' => %w[digit_or_number FROM DIGIT_LIT TO DIGIT_LIT]).as 'digits_from_to'
|
75
75
|
rule('character_class' => %w[ANY CHARACTER]).as 'any_character'
|
76
76
|
rule('character_class' => %w[NO CHARACTER]).as 'no_character'
|
77
|
-
rule('character_class' => 'digit_or_number').as 'digit'
|
78
|
-
rule('character_class' => %w[NO DIGIT]).as 'non_digit'
|
77
|
+
rule('character_class' => 'digit_or_number').as 'digit'
|
78
|
+
rule('character_class' => %w[NO DIGIT]).as 'non_digit'
|
79
79
|
rule('character_class' => 'WHITESPACE').as 'whitespace'
|
80
80
|
rule('character_class' => %w[NO WHITESPACE]).as 'no_whitespace'
|
81
81
|
rule('character_class' => 'ANYTHING').as 'anything'
|
@@ -123,5 +123,6 @@ module SrlRuby
|
|
123
123
|
end
|
124
124
|
|
125
125
|
# And now build the grammar and make it accessible via a global constant
|
126
|
+
# [Rley::Syntax::Grammar]
|
126
127
|
Grammar = builder.grammar
|
127
128
|
end # module
|
data/lib/srl_ruby/tokenizer.rb
CHANGED
@@ -124,7 +124,7 @@ module SrlRuby
|
|
124
124
|
elsif (lexeme = scanner.scan(/'(?:\\'|[^'])*'/)) # Single quotes literal?
|
125
125
|
unquoted = lexeme.gsub(/(^')|('$)/, '')
|
126
126
|
token = build_token('STRING_LIT', unquoted)
|
127
|
-
elsif (lexeme = scanner.scan(/[a-zA-Z]((?=\s)|$)/))
|
127
|
+
elsif (lexeme = scanner.scan(/[a-zA-Z]((?=\s|,)|$)/))
|
128
128
|
token = build_token('LETTER_LIT', lexeme)
|
129
129
|
elsif (lexeme = scanner.scan(/[a-zA-Z_][a-zA-Z0-9_]+/))
|
130
130
|
keyw = @@keywords[lexeme.upcase]
|
@@ -133,7 +133,7 @@ module SrlRuby
|
|
133
133
|
elsif (lexeme = scanner.scan(/[^,"\s]{2,}/))
|
134
134
|
token = build_token('CHAR_CLASS', lexeme)
|
135
135
|
else # Unknown token
|
136
|
-
erroneous = curr_ch.nil? ? '' :
|
136
|
+
erroneous = curr_ch.nil? ? '' : scanner.scan(/./)
|
137
137
|
sequel = scanner.scan(/.{1,20}/)
|
138
138
|
erroneous += sequel unless sequel.nil?
|
139
139
|
raise ScanError.new("Unknown token #{erroneous} on line #{lineno}")
|
data/lib/srl_ruby/version.rb
CHANGED
data/lib/srl_ruby.rb
CHANGED
@@ -3,22 +3,29 @@ require_relative './srl_ruby/tokenizer'
|
|
3
3
|
require_relative './srl_ruby/grammar'
|
4
4
|
require_relative './srl_ruby/ast_builder'
|
5
5
|
|
6
|
-
module SrlRuby
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# @
|
10
|
-
# @return [Regexp]
|
6
|
+
module SrlRuby
|
7
|
+
# Compile the SRL expression in given filename into a Regexp object.
|
8
|
+
# @param filename [String] Name of SRL file to parse
|
9
|
+
# @return [Regexp] A regexp object equivalent to the SRL expression.
|
11
10
|
def self.load_file(filename)
|
12
11
|
source = nil
|
13
12
|
File.open(filename, 'r') { |f| source = f.read }
|
14
13
|
return source if source.nil? || source.empty?
|
15
|
-
|
14
|
+
|
16
15
|
return parse(source)
|
17
16
|
end
|
18
|
-
|
19
|
-
#
|
20
|
-
# @param source [String]
|
21
|
-
# @return [Regexp]
|
17
|
+
|
18
|
+
# Compile the given SRL expression into its Regexp equivalent.
|
19
|
+
# @param source [String] SRL expression to parse
|
20
|
+
# @return [Regexp] A regexp object equivalent to the SRL expression.
|
21
|
+
# @example Matching a (signed) integer literal
|
22
|
+
# some_srl =<<-SRL
|
23
|
+
# begin with
|
24
|
+
# (one of "+-") optional,
|
25
|
+
# digit once or more,
|
26
|
+
# must end
|
27
|
+
# SRL
|
28
|
+
# regex = SrlRuby::API.parse(some_srl) # => regex == /^[+\-]?\d+$/
|
22
29
|
def self.parse(source)
|
23
30
|
# Create a Rley facade object
|
24
31
|
engine = Rley::Engine.new { |cfg| cfg.diagnose = true }
|
@@ -42,8 +49,6 @@ module SrlRuby # This module is used as a namespace
|
|
42
49
|
|
43
50
|
# Now output the regexp literal
|
44
51
|
root = ast_ptree.root
|
45
|
-
# puts root.to_str # TODO remove this line
|
46
|
-
# pp root
|
47
52
|
return Regexp.new(root.to_str)
|
48
53
|
end
|
49
54
|
end # module
|
@@ -37,9 +37,9 @@ module Acceptance
|
|
37
37
|
self.srl = aSRLExpression.dup
|
38
38
|
self.match_tests = []
|
39
39
|
self.no_match_tests = []
|
40
|
-
|
40
|
+
self.capture_tests = []
|
41
41
|
end
|
42
|
-
|
42
|
+
end
|
43
43
|
|
44
44
|
MatchTest = Struct.new(:test_string)
|
45
45
|
NoMatchTest = Struct.new(:test_string)
|
@@ -3,77 +3,75 @@
|
|
3
3
|
require_relative '../spec_helper' # Use the RSpec test framework
|
4
4
|
require_relative '../../lib/regex/multiplicity'
|
5
5
|
|
6
|
-
module
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
expect { Multiplicity.new(0, 1, aPolicy) }.not_to raise_error
|
15
|
-
end
|
16
|
-
|
17
|
-
# Invalid case: initialized with invalid policy value
|
18
|
-
err = StandardError
|
19
|
-
msg = "Invalid repetition policy 'KO'."
|
20
|
-
expect { Multiplicity.new(0, :more, 'KO') }.to raise_error(err, msg)
|
6
|
+
# Reopen the module, in order to get rid of fully qualified names
|
7
|
+
module Regex # This module is used as a namespace
|
8
|
+
describe Multiplicity do
|
9
|
+
context 'Creation & initialisation' do
|
10
|
+
it 'should be created with 3 arguments' do
|
11
|
+
# Valid cases: initialized with two integer values and a policy symbol
|
12
|
+
%i[greedy lazy possessive].each do |aPolicy|
|
13
|
+
expect { Multiplicity.new(0, 1, aPolicy) }.not_to raise_error
|
21
14
|
end
|
15
|
+
|
16
|
+
# Invalid case: initialized with invalid policy value
|
17
|
+
err = StandardError
|
18
|
+
msg = "Invalid repetition policy 'KO'."
|
19
|
+
expect { Multiplicity.new(0, :more, 'KO') }.to raise_error(err, msg)
|
22
20
|
end
|
21
|
+
end
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
context 'Provided services' do
|
24
|
+
it 'should know its text representation' do
|
25
|
+
policy2text = { greedy: '', lazy: '?', possessive: '+' }
|
27
26
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
# Case: zero or one
|
28
|
+
policy2text.each_key do |aPolicy|
|
29
|
+
multi = Multiplicity.new(0, 1, aPolicy)
|
30
|
+
expect(multi.to_str).to eq("?#{policy2text[aPolicy]}")
|
31
|
+
end
|
33
32
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
33
|
+
# Case: zero or more
|
34
|
+
policy2text.each_key do |aPolicy|
|
35
|
+
multi = Multiplicity.new(0, :more, aPolicy)
|
36
|
+
expect(multi.to_str).to eq("*#{policy2text[aPolicy]}")
|
37
|
+
end
|
39
38
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
39
|
+
# Case: one or more
|
40
|
+
policy2text.each_key do |aPolicy|
|
41
|
+
multi = Multiplicity.new(1, :more, aPolicy)
|
42
|
+
expect(multi.to_str).to eq("+#{policy2text[aPolicy]}")
|
43
|
+
end
|
45
44
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
end
|
45
|
+
# Case: exactly m times
|
46
|
+
policy2text.each_key do |aPolicy|
|
47
|
+
samples = [1, 2, 5, 100]
|
48
|
+
samples.each do |aCount|
|
49
|
+
multi = Multiplicity.new(aCount, aCount, aPolicy)
|
50
|
+
expect(multi.to_str).to eq("{#{aCount}}#{policy2text[aPolicy]}")
|
53
51
|
end
|
52
|
+
end
|
54
53
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
54
|
+
# Case: m, n times
|
55
|
+
policy2text.each_key do |aPolicy|
|
56
|
+
samples = [1, 2, 5, 100]
|
57
|
+
samples.each do |aCount|
|
58
|
+
upper = aCount + 1 + rand(20)
|
59
|
+
multi = Multiplicity.new(aCount, upper, aPolicy)
|
60
|
+
expectation = "{#{aCount},#{upper}}#{policy2text[aPolicy]}"
|
61
|
+
expect(multi.to_str).to eq(expectation)
|
64
62
|
end
|
63
|
+
end
|
65
64
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
end
|
65
|
+
# Case: m or more
|
66
|
+
policy2text.each_key do |aPolicy|
|
67
|
+
samples = [2, 3, 5, 100]
|
68
|
+
samples.each do |aCount|
|
69
|
+
multi = Multiplicity.new(aCount, :more, aPolicy)
|
70
|
+
expect(multi.to_str).to eq("{#{aCount},}#{policy2text[aPolicy]}")
|
73
71
|
end
|
74
72
|
end
|
75
73
|
end
|
76
74
|
end
|
77
|
-
end
|
75
|
+
end
|
78
76
|
end # module
|
79
77
|
# End of file
|
data/spec/spec_helper.rb
CHANGED
@@ -5,7 +5,7 @@ require_relative '../lib/srl_ruby'
|
|
5
5
|
RSpec.configure do |config|
|
6
6
|
# Enable flags like --only-failures and --next-failure
|
7
7
|
config.example_status_persistence_file_path = '.rspec_status'
|
8
|
-
|
8
|
+
|
9
9
|
config.expect_with :rspec do |c|
|
10
10
|
# Disable the `should` syntax...
|
11
11
|
c.syntax = :expect
|
@@ -10,9 +10,9 @@ module SrlRuby
|
|
10
10
|
expect(token.lexeme).to eq(lexeme)
|
11
11
|
end
|
12
12
|
end
|
13
|
-
|
14
|
-
subject { Tokenizer.new('') }
|
15
|
-
|
13
|
+
|
14
|
+
subject { Tokenizer.new('') }
|
15
|
+
|
16
16
|
context 'Initialization:' do
|
17
17
|
it 'should be initialized with a text to tokenize and a grammar' do
|
18
18
|
expect { Tokenizer.new('anything') }.not_to raise_error
|
@@ -31,7 +31,7 @@ module SrlRuby
|
|
31
31
|
expect(token.terminal).to eq('COMMA')
|
32
32
|
expect(token.lexeme).to eq(',')
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
it 'should tokenize keywords' do
|
36
36
|
sample = 'between Exactly oncE optional TWICE'
|
37
37
|
subject.scanner.string = sample
|
@@ -40,14 +40,14 @@ module SrlRuby
|
|
40
40
|
expect(tok.terminal).to eq(tok.lexeme.upcase)
|
41
41
|
end
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
it 'should tokenize integer values' do
|
45
45
|
subject.scanner.string = ' 123 '
|
46
46
|
token = subject.tokens.first
|
47
47
|
expect(token).to be_kind_of(Rley::Lexical::Token)
|
48
48
|
expect(token.terminal).to eq('INTEGER')
|
49
49
|
expect(token.lexeme).to eq('123')
|
50
|
-
end
|
50
|
+
end
|
51
51
|
|
52
52
|
it 'should tokenize single digits' do
|
53
53
|
subject.scanner.string = ' 1 '
|
@@ -55,9 +55,9 @@ module SrlRuby
|
|
55
55
|
expect(token).to be_kind_of(Rley::Lexical::Token)
|
56
56
|
expect(token.terminal).to eq('DIGIT_LIT')
|
57
57
|
expect(token.lexeme).to eq('1')
|
58
|
-
end
|
58
|
+
end
|
59
59
|
end # context
|
60
|
-
|
60
|
+
|
61
61
|
context 'String literal tokenization:' do
|
62
62
|
it "should recognize 'literally ...'" do
|
63
63
|
input = 'literally "hello"'
|
@@ -67,9 +67,9 @@ module SrlRuby
|
|
67
67
|
%w[STRING_LIT hello]
|
68
68
|
]
|
69
69
|
match_expectations(subject, expectations)
|
70
|
-
end
|
71
|
-
end # context
|
72
|
-
|
70
|
+
end
|
71
|
+
end # context
|
72
|
+
|
73
73
|
context 'Character range tokenization:' do
|
74
74
|
it "should recognize 'letter from ... to ...'" do
|
75
75
|
input = 'letter a to f'
|
@@ -81,9 +81,9 @@ module SrlRuby
|
|
81
81
|
%w[LETTER_LIT f]
|
82
82
|
]
|
83
83
|
match_expectations(subject, expectations)
|
84
|
-
end
|
84
|
+
end
|
85
85
|
end # context
|
86
|
-
|
86
|
+
|
87
87
|
context 'Quantifier tokenization:' do
|
88
88
|
it "should recognize 'exactly ... times'" do
|
89
89
|
input = 'exactly 4 Times'
|
@@ -95,7 +95,7 @@ module SrlRuby
|
|
95
95
|
]
|
96
96
|
match_expectations(subject, expectations)
|
97
97
|
end
|
98
|
-
|
98
|
+
|
99
99
|
it "should recognize 'between ... and ... times'" do
|
100
100
|
input = 'Between 2 AND 4 times'
|
101
101
|
subject.scanner.string = input
|
@@ -129,7 +129,7 @@ module SrlRuby
|
|
129
129
|
%w[MORE more]
|
130
130
|
]
|
131
131
|
match_expectations(subject, expectations)
|
132
|
-
end
|
132
|
+
end
|
133
133
|
|
134
134
|
it "should recognize 'at least ... times'" do
|
135
135
|
input = 'at least 10 times'
|
@@ -141,7 +141,7 @@ module SrlRuby
|
|
141
141
|
%w[TIMES times]
|
142
142
|
]
|
143
143
|
match_expectations(subject, expectations)
|
144
|
-
end
|
144
|
+
end
|
145
145
|
end # context
|
146
146
|
end # describe
|
147
147
|
end # module
|
data/spec/srl_ruby_spec.rb
CHANGED
@@ -44,7 +44,7 @@ describe SrlRuby do
|
|
44
44
|
regexp = SrlRuby.parse("literally '.'")
|
45
45
|
expect(regexp.source).to eq('\.')
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
it 'should parse single quotes literal string' do
|
49
49
|
regexp = SrlRuby.parse('literally "an", whitespace, raw "[a-zA-Z]"')
|
50
50
|
expect(regexp.source).to eq('an\s[a-zA-Z]')
|
@@ -61,7 +61,7 @@ describe SrlRuby do
|
|
61
61
|
regexp = SrlRuby.parse('number')
|
62
62
|
expect(regexp.source).to eq('\d')
|
63
63
|
end
|
64
|
-
|
64
|
+
|
65
65
|
it "should parse 'no digit' syntax" do
|
66
66
|
regexp = SrlRuby.parse('no digit')
|
67
67
|
expect(regexp.source).to eq('\D')
|
@@ -98,41 +98,41 @@ describe SrlRuby do
|
|
98
98
|
# (escapes more characters than required)
|
99
99
|
expect(regexp.source).to eq('[._%+\-]')
|
100
100
|
end
|
101
|
-
|
101
|
+
|
102
102
|
it "should parse 'one of' with unquoted character class syntax" do
|
103
103
|
# Case of digit sequence
|
104
104
|
regexp = SrlRuby.parse('one of 13579, must end')
|
105
105
|
expect(regexp.source).to eq('[13579]$')
|
106
|
-
|
106
|
+
|
107
107
|
# Case of identifier-like character class
|
108
108
|
regexp = SrlRuby.parse('one of abcd, must end')
|
109
109
|
expect(regexp.source).to eq('[abcd]$')
|
110
110
|
|
111
111
|
# Case of arbitrary character class
|
112
112
|
regexp = SrlRuby.parse('one of 12hms:, must end')
|
113
|
-
expect(regexp.source).to eq('[12hms:]$')
|
114
|
-
end
|
115
|
-
|
113
|
+
expect(regexp.source).to eq('[12hms:]$')
|
114
|
+
end
|
115
|
+
|
116
116
|
it "should parse 'none of' syntax" do
|
117
117
|
regexp = SrlRuby.parse('none of "._%+-"')
|
118
118
|
# Remark: reference implementation less readable
|
119
119
|
# (escapes more characters than required)
|
120
120
|
expect(regexp.source).to eq('[^._%+\-]')
|
121
|
-
end
|
121
|
+
end
|
122
122
|
|
123
123
|
it "should parse 'none of' with unquoted character class syntax" do
|
124
124
|
# Case of digit sequence
|
125
125
|
regexp = SrlRuby.parse('none of 13579, must end')
|
126
126
|
expect(regexp.source).to eq('[^13579]$')
|
127
|
-
|
127
|
+
|
128
128
|
# Case of identifier-like character class
|
129
129
|
regexp = SrlRuby.parse('none of abcd, must end')
|
130
130
|
expect(regexp.source).to eq('[^abcd]$')
|
131
131
|
|
132
132
|
# Case of arbitrary character class
|
133
133
|
regexp = SrlRuby.parse('none of 12hms:^, must end')
|
134
|
-
expect(regexp.source).to eq('[^12hms:\^]$')
|
135
|
-
end
|
134
|
+
expect(regexp.source).to eq('[^12hms:\^]$')
|
135
|
+
end
|
136
136
|
end # context
|
137
137
|
|
138
138
|
context 'Parsing special character declarations:' do
|
@@ -140,7 +140,7 @@ describe SrlRuby do
|
|
140
140
|
regexp = SrlRuby.parse('tab')
|
141
141
|
expect(regexp.source).to eq('\t')
|
142
142
|
end
|
143
|
-
|
143
|
+
|
144
144
|
it "should parse 'vertical tab' syntax" do
|
145
145
|
regexp = SrlRuby.parse('vertical tab')
|
146
146
|
expect(regexp.source).to eq('\v')
|
@@ -155,21 +155,21 @@ describe SrlRuby do
|
|
155
155
|
regexp = SrlRuby.parse('new line')
|
156
156
|
expect(regexp.source).to eq('\n')
|
157
157
|
end
|
158
|
-
|
158
|
+
|
159
159
|
it "should parse 'carriage return' syntax" do
|
160
160
|
regexp = SrlRuby.parse('carriage return')
|
161
161
|
expect(regexp.source).to eq('\r')
|
162
|
-
end
|
162
|
+
end
|
163
163
|
|
164
164
|
it "should parse 'word' syntax" do
|
165
165
|
regexp = SrlRuby.parse('word, literally "is"')
|
166
166
|
expect(regexp.source).to eq('\bis')
|
167
|
-
end
|
167
|
+
end
|
168
168
|
|
169
169
|
it "should parse 'no word' syntax" do
|
170
170
|
regexp = SrlRuby.parse('no word, literally "is"')
|
171
171
|
expect(regexp.source).to eq('\Bis')
|
172
|
-
end
|
172
|
+
end
|
173
173
|
end # context
|
174
174
|
|
175
175
|
context 'Parsing alternations:' do
|
@@ -177,25 +177,25 @@ describe SrlRuby do
|
|
177
177
|
source = 'any of (any character, one of "._%-+")'
|
178
178
|
regexp = SrlRuby.parse(source)
|
179
179
|
expect(regexp.source).to eq('(?:\w|[._%\-+])')
|
180
|
-
end
|
180
|
+
end
|
181
181
|
|
182
182
|
it "should parse 'either of' syntax" do
|
183
183
|
source = 'either of (any character, one of "._%-+")'
|
184
184
|
regexp = SrlRuby.parse(source)
|
185
185
|
expect(regexp.source).to eq('(?:\w|[._%\-+])')
|
186
|
-
end
|
187
|
-
|
186
|
+
end
|
187
|
+
|
188
188
|
it 'should anchor as alternative' do
|
189
189
|
regexp = SrlRuby.parse('any of (literally "?", must end)')
|
190
190
|
expect(regexp.source).to eq('(?:\\?|$)')
|
191
|
-
end
|
191
|
+
end
|
192
192
|
end # context
|
193
193
|
|
194
194
|
context 'Parsing concatenation:' do
|
195
195
|
it 'should reject dangling comma' do
|
196
196
|
source = 'literally "a",'
|
197
197
|
err = StandardError
|
198
|
-
msg_pattern = /Premature end of input after ',' at position line 1, column 14/
|
198
|
+
msg_pattern = /Premature end of input after ',' at position line 1, column 14/
|
199
199
|
expect { SrlRuby.parse(source) }.to raise_error(err, msg_pattern)
|
200
200
|
end
|
201
201
|
|
@@ -205,13 +205,13 @@ describe SrlRuby do
|
|
205
205
|
end
|
206
206
|
|
207
207
|
it 'should parse a long sequence of patterns' do
|
208
|
-
source = <<-
|
208
|
+
source = <<-SRL
|
209
209
|
any of (any character, one of "._%-+") once or more,
|
210
210
|
literally "@",
|
211
211
|
any of (digit, letter, one of ".-") once or more,
|
212
212
|
literally ".",
|
213
213
|
letter at least 2 times
|
214
|
-
|
214
|
+
SRL
|
215
215
|
|
216
216
|
regexp = SrlRuby.parse(source)
|
217
217
|
# SRL: (?:\w|[\._%\-\+])+(?:@)(?:[0-9]|[a-z]|[\.\-])+(?:\.)[a-z]{2,}
|
@@ -246,9 +246,9 @@ END_SRL
|
|
246
246
|
it "should parse 'between ... and ... times' syntax" do
|
247
247
|
regexp = SrlRuby.parse(prefix + 'between 2 and 4 times')
|
248
248
|
expect(regexp.source).to eq('[p-t]{2,4}')
|
249
|
-
|
249
|
+
|
250
250
|
# Dropping 'times' keyword is a shorter alternative syntax
|
251
|
-
regexp = SrlRuby.parse(prefix + 'between 2 and 4')
|
251
|
+
regexp = SrlRuby.parse(prefix + 'between 2 and 4')
|
252
252
|
expect(regexp.source).to eq('[p-t]{2,4}')
|
253
253
|
end
|
254
254
|
|
@@ -312,22 +312,22 @@ END_SRL
|
|
312
312
|
regexp = SrlRuby.parse(source)
|
313
313
|
expect(regexp.source).to eq('(?<first>.+)$')
|
314
314
|
end
|
315
|
-
|
315
|
+
|
316
316
|
it 'should parse complex named capturing group' do
|
317
|
-
source = <<-
|
318
|
-
capture(any of (literally "sample", (digit once or more)))
|
317
|
+
source = <<-SRL
|
318
|
+
capture(any of (literally "sample", (digit once or more)))
|
319
319
|
as "foo"
|
320
|
-
|
320
|
+
SRL
|
321
321
|
regexp = SrlRuby.parse(source)
|
322
322
|
expect(regexp.source).to eq('(?<foo>(?:sample|(?:\d+)))')
|
323
323
|
end
|
324
324
|
|
325
325
|
it 'should parse a sequence with named capturing groups' do
|
326
|
-
source = <<-
|
326
|
+
source = <<-SRL
|
327
327
|
capture (anything once or more) as "first",
|
328
328
|
literally " - ",
|
329
329
|
capture literally "second part" as "second"
|
330
|
-
|
330
|
+
SRL
|
331
331
|
regexp = SrlRuby.parse(source)
|
332
332
|
expect(regexp.source).to eq('(?<first>.+) - (?<second>second part)')
|
333
333
|
end
|
@@ -361,16 +361,16 @@ END_SRL
|
|
361
361
|
end
|
362
362
|
|
363
363
|
it 'should accept anchor with a sequence of patterns' do
|
364
|
-
source = <<-
|
364
|
+
source = <<-SRL
|
365
365
|
begin with any of (digit, letter, one of ".-") once or more,
|
366
366
|
literally ".",
|
367
367
|
letter at least 2 times must end
|
368
|
-
|
368
|
+
SRL
|
369
369
|
|
370
370
|
regexp = SrlRuby.parse(source)
|
371
371
|
# SRL: (?:\w|[\._%\-\+])+(?:@)(?:[0-9]|[a-z]|[\.\-])+(?:\.)[a-z]{2,}
|
372
372
|
expect(regexp.source).to eq('^(?:\d|[a-z]|[.\-])+\.[a-z]{2,}$')
|
373
|
-
end
|
373
|
+
end
|
374
374
|
end # context
|
375
375
|
end # describe
|
376
376
|
# End of file
|
data/srl_ruby.gemspec
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
|
2
1
|
lib = File.expand_path('../lib', __FILE__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
require 'srl_ruby/version'
|
5
4
|
|
5
|
+
# Implementation module
|
6
6
|
module PkgExtending
|
7
7
|
def self.pkg_files(aPackage)
|
8
8
|
file_list = Dir[
|
@@ -42,29 +42,31 @@ Gem::Specification.new do |spec|
|
|
42
42
|
spec.authors = ['Dimitri Geshef']
|
43
43
|
spec.email = ['famished.tiger@yahoo.com']
|
44
44
|
|
45
|
-
spec.description =
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
45
|
+
spec.description = <<-DESCR
|
46
|
+
Build easily regular expressions thanks to Simple Regex Language
|
47
|
+
DESCR
|
48
|
+
spec.summary = <<-SUMMARY
|
49
|
+
A parser for the [Simple Regex Language](https://simple-regex.com/).
|
50
|
+
It translates patterns expressed in SRL into plain Ruby Regexp objects
|
51
|
+
or in regex literals. Use self-documenting, human readable, SRL expressions
|
52
|
+
to craft your new awesome regular expressions.
|
53
|
+
SUMMARY
|
52
54
|
spec.homepage = 'https://github.com/famished-tiger/SRL-Ruby'
|
53
55
|
spec.license = 'MIT'
|
54
56
|
|
55
57
|
spec.bindir = 'bin'
|
56
58
|
spec.executables << 'srl_ruby'
|
57
59
|
spec.require_paths = ['lib']
|
58
|
-
PkgExtending
|
59
|
-
PkgExtending
|
60
|
+
PkgExtending.pkg_files(spec)
|
61
|
+
PkgExtending.pkg_documentation(spec)
|
60
62
|
spec.required_ruby_version = '>= 2.1.0'
|
61
|
-
|
63
|
+
|
62
64
|
# Runtime dependencies
|
63
65
|
spec.add_dependency 'rley', '~> 0.6.06'
|
64
66
|
|
65
67
|
# Development dependencies
|
66
68
|
spec.add_development_dependency 'bundler', '~> 1.16'
|
67
|
-
spec.add_development_dependency 'cucumber', '>= 2.2.0'
|
69
|
+
spec.add_development_dependency 'cucumber', '>= 2.2.0'
|
68
70
|
spec.add_development_dependency 'rake', '~> 10.0'
|
69
71
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
70
72
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: srl_ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-04-
|
11
|
+
date: 2018-04-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rley
|
@@ -80,7 +80,8 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '3.0'
|
83
|
-
description:
|
83
|
+
description: |2
|
84
|
+
Build easily regular expressions thanks to Simple Regex Language
|
84
85
|
email:
|
85
86
|
- famished.tiger@yahoo.com
|
86
87
|
executables:
|
@@ -202,9 +203,9 @@ rubygems_version: 2.6.13
|
|
202
203
|
signing_key:
|
203
204
|
specification_version: 4
|
204
205
|
summary: A parser for the [Simple Regex Language](https://simple-regex.com/). It translates
|
205
|
-
patterns expressed in SRL into plain Ruby Regexp objects
|
206
|
-
|
207
|
-
|
206
|
+
patterns expressed in SRL into plain Ruby Regexp objects or in regex literals. Use
|
207
|
+
self-documenting, human readable, SRL expressions to craft your new awesome regular
|
208
|
+
expressions.
|
208
209
|
test_files:
|
209
210
|
- spec/acceptance/srl_test_suite_spec.rb
|
210
211
|
- spec/regex/character_spec.rb
|