rrrex 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +21 -0
- data/README.md +57 -0
- data/Rakefile +79 -0
- data/lib/method_missing_conversion.rb +23 -0
- data/lib/rrrex.rb +1 -0
- data/lib/rrrex/composite_match.rb +16 -0
- data/lib/rrrex/concat_match.rb +10 -0
- data/lib/rrrex/core_ext.rb +3 -0
- data/lib/rrrex/core_ext/fixnum.rb +13 -0
- data/lib/rrrex/core_ext/range.rb +10 -0
- data/lib/rrrex/core_ext/string.rb +23 -0
- data/lib/rrrex/dsl_context.rb +47 -0
- data/lib/rrrex/group_match.rb +20 -0
- data/lib/rrrex/match.rb +52 -0
- data/lib/rrrex/match_data.rb +36 -0
- data/lib/rrrex/not_match.rb +10 -0
- data/lib/rrrex/number_match.rb +17 -0
- data/lib/rrrex/or_match.rb +10 -0
- data/lib/rrrex/range_match.rb +14 -0
- data/lib/rrrex/regexp.rb +14 -0
- data/lib/rrrex/single_atom_match.rb +26 -0
- data/lib/rrrex/string_match.rb +14 -0
- data/lib/rrrex/unescaped_string_match.rb +8 -0
- data/test/match_test.rb +230 -0
- metadata +104 -0
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2011 Ian Young
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
Really Readable Regular Expressions
|
2
|
+
================
|
3
|
+
|
4
|
+
rrrex is a new syntax for regular expressions. It trades compactness for readability by real humans, and picks up a couple nice perks
|
5
|
+
along the way.
|
6
|
+
|
7
|
+
Crash Course
|
8
|
+
============
|
9
|
+
|
10
|
+
"The string you'd like to search".rmatch? { "string" }
|
11
|
+
|
12
|
+
"abc".rmatch? { "ab" + "c" }
|
13
|
+
|
14
|
+
"abc".rmatch? { "xyz".or "abc" }
|
15
|
+
|
16
|
+
You don't have to worry about escaping special characters in your strings any more:
|
17
|
+
"{symbols} .*&+ [galore]".rmatch? { "{symbols} .*&+ [galore]" }
|
18
|
+
|
19
|
+
You can combine operations and get the expected precedence:
|
20
|
+
"abc".rmatch? { "ab" + ( "z".or "c" ) }
|
21
|
+
|
22
|
+
Repetition:
|
23
|
+
"aaabc".rmatch? { 1.or_more "a" }
|
24
|
+
"aaabc".rmatch? { 5.or_less "a" }
|
25
|
+
"aaabc".rmatch? { 3.exactly "a" }
|
26
|
+
"aaabc".rmatch? { (1..5).of "a" }
|
27
|
+
These are equivalent:
|
28
|
+
"aaabc".rmatch? { 0.or_more "a" }
|
29
|
+
"aaabc".rmatch? { any "a" }
|
30
|
+
And these are equivalent:
|
31
|
+
"aaabc".rmatch? { 1.or_more "a" }
|
32
|
+
"aaabc".rmatch? { some "a" }
|
33
|
+
|
34
|
+
Special character sets:
|
35
|
+
"abc1234.&*".rmatch? { 10.exactly any_char }
|
36
|
+
"abc1234".rmatch? { 3.exactly letter }
|
37
|
+
"abc1234".rmatch? { 4.exactly digit }
|
38
|
+
"abc_123".rmatch? { 7.exactly word_char }
|
39
|
+
" ".rmatch? { whitespace }
|
40
|
+
Or create your own:
|
41
|
+
"abc".rmatch? { 3.exactly "a".."c" }
|
42
|
+
|
43
|
+
Two types of negation:
|
44
|
+
"x".rmatch? { word_char.not "x" } # => nil
|
45
|
+
"y".rmatch? { word_char.not "x" }
|
46
|
+
"x".rmatch? { _not "x" } # => nil
|
47
|
+
"y".rmatch? { _not "x" }
|
48
|
+
|
49
|
+
Groups:
|
50
|
+
match = "1234567890 Central Processing".rmatch? do
|
51
|
+
group :serial do
|
52
|
+
some digit
|
53
|
+
end + some whitespace + group :source do
|
54
|
+
any any_char
|
55
|
+
end
|
56
|
+
end
|
57
|
+
match
|
data/Rakefile
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "rake/gempackagetask"
|
3
|
+
require "rake/rdoctask"
|
4
|
+
|
5
|
+
require "rake/testtask"
|
6
|
+
Rake::TestTask.new do |t|
|
7
|
+
t.libs << "test"
|
8
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
9
|
+
t.verbose = true
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
task :default => ["test"]
|
14
|
+
|
15
|
+
spec = Gem::Specification.new do |s|
|
16
|
+
|
17
|
+
s.name = "rrrex"
|
18
|
+
s.version = "0.1.0"
|
19
|
+
s.author = "Ian Young"
|
20
|
+
s.email = "ian.greenleaf+github@gmail.com"
|
21
|
+
|
22
|
+
s.summary = "Really Readable Regexps"
|
23
|
+
s.description = <<-EOF
|
24
|
+
Rrrex is a new syntax for regular expressions.
|
25
|
+
Less compact, but human-readable. By regular humans.
|
26
|
+
EOF
|
27
|
+
|
28
|
+
s.files = Dir[ 'lib/**/*.rb', 'test/**/*', '[A-Z]*' ]
|
29
|
+
|
30
|
+
s.has_rdoc = true
|
31
|
+
s.extra_rdoc_files = %w(README.md)
|
32
|
+
s.rdoc_options = %w(--main README.md)
|
33
|
+
|
34
|
+
s.add_development_dependency("mocha")
|
35
|
+
end
|
36
|
+
|
37
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
38
|
+
pkg.gem_spec = spec
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "Build the gemspec file #{spec.name}.gemspec"
|
42
|
+
task :gemspec do
|
43
|
+
file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
|
44
|
+
File.open(file, "w") {|f| f << spec.to_ruby }
|
45
|
+
end
|
46
|
+
|
47
|
+
task :package => :gemspec
|
48
|
+
|
49
|
+
Rake::RDocTask.new do |rd|
|
50
|
+
rd.main = "README.md"
|
51
|
+
rd.rdoc_files.include("README.md", "lib/**/*.rb")
|
52
|
+
rd.rdoc_dir = "rdoc"
|
53
|
+
end
|
54
|
+
|
55
|
+
desc 'Clear out RDoc and generated packages'
|
56
|
+
task :clean => [:clobber_rdoc, :clobber_package] do
|
57
|
+
rm "#{spec.name}.gemspec"
|
58
|
+
end
|
59
|
+
|
60
|
+
desc 'Tag the repository in git with gem version number'
|
61
|
+
task :tag => [:gemspec, :package] do
|
62
|
+
if `git diff --cached`.empty?
|
63
|
+
if `git tag`.split("\n").include?("v#{spec.version}")
|
64
|
+
raise "Version #{spec.version} has already been released"
|
65
|
+
end
|
66
|
+
`git add #{File.expand_path("../#{spec.name}.gemspec", __FILE__)}`
|
67
|
+
`git commit -m "Released version #{spec.version}"`
|
68
|
+
`git tag v#{spec.version}`
|
69
|
+
`git push --tags`
|
70
|
+
`git push`
|
71
|
+
else
|
72
|
+
raise "Unstaged changes still waiting to be committed"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
desc "Tag and publish the gem to rubygems.org"
|
77
|
+
task :publish => :tag do
|
78
|
+
`gem push pkg/#{spec.name}-#{spec.version}.gem`
|
79
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module MethodMissingConversion
|
2
|
+
def self.included receiver
|
3
|
+
receiver.extend ClassMethods
|
4
|
+
end
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def sends_methods_to methods, convert_to
|
8
|
+
define_method :method_missing_helper do |name, args, block|
|
9
|
+
if methods.include? name
|
10
|
+
convert_to.new( self ).send name, *args
|
11
|
+
else
|
12
|
+
method_missing_without_regexp name, *args, &block
|
13
|
+
end
|
14
|
+
end
|
15
|
+
alias_method :method_missing_without_regexp, :method_missing
|
16
|
+
alias_method :method_missing, :method_missing_with_regexp
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def method_missing_with_regexp( name, *args, &block )
|
21
|
+
method_missing_helper name, args, block
|
22
|
+
end
|
23
|
+
end
|
data/lib/rrrex.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'rrrex/core_ext'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rrrex/composite_match'
|
2
|
+
module Rrrex
|
3
|
+
module CompositeMatch
|
4
|
+
def initialize(*args)
|
5
|
+
@atoms = args.collect do |a|
|
6
|
+
input a
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def group_names
|
11
|
+
@atoms.inject( [] ) do |memo,a|
|
12
|
+
memo + a.group_names
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'method_missing_conversion'
|
2
|
+
require 'rrrex/string_match'
|
3
|
+
require 'rrrex/match'
|
4
|
+
require 'rrrex/dsl_context'
|
5
|
+
class String
|
6
|
+
include MethodMissingConversion
|
7
|
+
sends_methods_to [ :or ], Rrrex::StringMatch
|
8
|
+
|
9
|
+
def plus_with_regexp( str2 )
|
10
|
+
if str2.kind_of? Rrrex::Match
|
11
|
+
Rrrex::StringMatch.new( self ) + str2
|
12
|
+
else
|
13
|
+
self.plus_without_regexp str2
|
14
|
+
end
|
15
|
+
end
|
16
|
+
alias_method :plus_without_regexp, :+
|
17
|
+
alias_method :+, :plus_with_regexp
|
18
|
+
|
19
|
+
def rmatch?( &block )
|
20
|
+
pattern = Rrrex::Match.convert Rrrex::DslContext.module_exec &block
|
21
|
+
pattern.match self
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rrrex/unescaped_string_match'
|
2
|
+
require 'rrrex/group_match'
|
3
|
+
require 'rrrex/number_match'
|
4
|
+
module Rrrex
|
5
|
+
module DslContext
|
6
|
+
|
7
|
+
WORD_CHAR = '\w'
|
8
|
+
DIGIT = '\d'
|
9
|
+
WHITESPACE = '\s'
|
10
|
+
LETTER = '[[:alpha:]]'
|
11
|
+
ANY_CHAR = '.'
|
12
|
+
|
13
|
+
constants.each do |const|
|
14
|
+
( class << self; self; end ).instance_eval do
|
15
|
+
define_method const.downcase.to_sym do
|
16
|
+
UnescapedStringMatch.new const_get( const )
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.any r
|
22
|
+
NumberMatch.new r, 0, nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.some r
|
26
|
+
NumberMatch.new r, 1, nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def self._not r
|
30
|
+
any_char.not r
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.group( name_or_atom=nil, atom=nil, &block )
|
34
|
+
if name_or_atom.kind_of? Symbol
|
35
|
+
name = name_or_atom
|
36
|
+
atom = atom || DslContext.module_exec( &block )
|
37
|
+
elsif name_or_atom.kind_of? Hash
|
38
|
+
name = name_or_atom.keys.first
|
39
|
+
atom = name_or_atom[ name ]
|
40
|
+
else
|
41
|
+
name = nil
|
42
|
+
atom = name_or_atom
|
43
|
+
end
|
44
|
+
GroupMatch.new atom, name
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rrrex/match'
|
2
|
+
require 'rrrex/single_atom_match'
|
3
|
+
module Rrrex
|
4
|
+
class GroupMatch < Match
|
5
|
+
include SingleAtomMatch
|
6
|
+
def initialize atom, name
|
7
|
+
@name = name
|
8
|
+
super atom
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_regexp_string
|
12
|
+
"(#{atom.to_regexp_string})"
|
13
|
+
end
|
14
|
+
|
15
|
+
def group_names
|
16
|
+
names = @atom.group_names || []
|
17
|
+
names.unshift @name
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/rrrex/match.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
module Rrrex
|
2
|
+
class Match
|
3
|
+
end
|
4
|
+
end
|
5
|
+
require 'rrrex/regexp'
|
6
|
+
require 'rrrex/string_match'
|
7
|
+
require 'rrrex/range_match'
|
8
|
+
require 'rrrex/or_match'
|
9
|
+
require 'rrrex/concat_match'
|
10
|
+
require 'rrrex/not_match'
|
11
|
+
module Rrrex
|
12
|
+
class Match
|
13
|
+
def self.convert( atom )
|
14
|
+
if atom.kind_of? Match
|
15
|
+
atom
|
16
|
+
elsif atom.kind_of? Range
|
17
|
+
RangeMatch.new atom
|
18
|
+
else
|
19
|
+
StringMatch.new atom
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def wrap( s )
|
24
|
+
"(?:#{s})"
|
25
|
+
end
|
26
|
+
|
27
|
+
def match(str)
|
28
|
+
Regexp.new( self ).match str
|
29
|
+
end
|
30
|
+
|
31
|
+
def or(atom)
|
32
|
+
OrMatch.new self, atom
|
33
|
+
end
|
34
|
+
|
35
|
+
def +(p)
|
36
|
+
ConcatMatch.new self, p
|
37
|
+
end
|
38
|
+
|
39
|
+
def not(atom)
|
40
|
+
ConcatMatch.new NotMatch.new( atom ), self
|
41
|
+
end
|
42
|
+
|
43
|
+
def group_names
|
44
|
+
[]
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
def input( atom )
|
49
|
+
self.class.convert atom
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Rrrex
|
2
|
+
class MatchData
|
3
|
+
def initialize( atom, match_data )
|
4
|
+
@atom = atom
|
5
|
+
@match_data = match_data
|
6
|
+
end
|
7
|
+
|
8
|
+
def []( i )
|
9
|
+
if i.is_a? Symbol
|
10
|
+
named_groups[ i ]
|
11
|
+
else
|
12
|
+
@match_data[ i ]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def named_groups
|
17
|
+
@named_groups ||=
|
18
|
+
begin
|
19
|
+
result = {}
|
20
|
+
names = @atom.group_names
|
21
|
+
names.each_index do |i|
|
22
|
+
result[ names[ i ] ] = @match_data[ i + 1 ]
|
23
|
+
end
|
24
|
+
result
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_a
|
29
|
+
@match_data.to_a
|
30
|
+
end
|
31
|
+
|
32
|
+
def method_missing( name, *args )
|
33
|
+
@match_data.send name, *args
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rrrex/match'
|
2
|
+
require 'rrrex/single_atom_match'
|
3
|
+
module Rrrex
|
4
|
+
class NumberMatch < Match
|
5
|
+
include SingleAtomMatch
|
6
|
+
def initialize( a, min, max )
|
7
|
+
super a
|
8
|
+
@min = min
|
9
|
+
@max = max
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_regexp_string
|
13
|
+
# Subtle: when nil, we want min to convert to 0, but max to convert to ""
|
14
|
+
wrap atom.to_regexp_string + "{#{@min.to_i},#{@max}}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rrrex/match'
|
2
|
+
require 'rrrex/single_atom_match'
|
3
|
+
module Rrrex
|
4
|
+
class RangeMatch < Match
|
5
|
+
include SingleAtomMatch
|
6
|
+
def initialize( range )
|
7
|
+
@range = range
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_regexp_string
|
11
|
+
wrap "[#{@range.first}-#{@range.last}]"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/rrrex/regexp.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Rrrex
|
2
|
+
module SingleAtomMatch
|
3
|
+
|
4
|
+
attr_reader :atom
|
5
|
+
|
6
|
+
def initialize( a )
|
7
|
+
self.atom = a
|
8
|
+
end
|
9
|
+
|
10
|
+
def atom=( a )
|
11
|
+
@atom = input a
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_regexp_string
|
15
|
+
wrap atom.to_regexp_string
|
16
|
+
end
|
17
|
+
|
18
|
+
def group_names
|
19
|
+
if @atom.respond_to? :group_names
|
20
|
+
@atom.group_names if @atom.respond_to? :group_names
|
21
|
+
else
|
22
|
+
[]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/test/match_test.rb
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'mocha'
|
4
|
+
require 'rrrex'
|
5
|
+
|
6
|
+
class MatchTest < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def test_match_simple_string
|
9
|
+
[ ["a", "a"], ["bc", "babb bc"], ["úñícode", "i like úñícode"] ].each do |pattern,string|
|
10
|
+
m = Rrrex::StringMatch.new pattern
|
11
|
+
assert m.match string
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_dont_match_simple_string
|
16
|
+
[ ["a", "b"], ["bc", "bac def"], ["úñícode", "i like unicode"] ].each do |pattern,string|
|
17
|
+
m = Rrrex::StringMatch.new pattern
|
18
|
+
assert_nil m.match string
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_inline_match_triggers_module
|
23
|
+
rxp_stub = stub "Rrrex::Match", { :match => true }
|
24
|
+
Rrrex::StringMatch.expects(:new).with("oo").returns(rxp_stub)
|
25
|
+
"foobar".rmatch? do "oo" end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_match_simple_string_inline
|
29
|
+
assert_match "oo", "foobar" do "oo" end
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_dont_match_simple_string_inline
|
33
|
+
assert_no_match "foobar" do "xy" end
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_special_characters_escaped_in_string
|
37
|
+
assert_no_match "foobar" do "o+" end
|
38
|
+
assert_match "o+", "+hello+" do "o+" end
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_match_or
|
42
|
+
assert_match "foo", "foobar" do "xy".or "foo" end
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_match_any
|
46
|
+
assert_match "x", "x" do any "x" end
|
47
|
+
assert_match "aaaaa", "aaaaab" do any "a" end
|
48
|
+
assert_match "abab", "ababaab" do any "ab" end
|
49
|
+
assert_match "", "xxxx" do any "y" end
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_match_some
|
53
|
+
assert_match "x", "x" do some "x" end
|
54
|
+
assert_match "aaaaa", "aaaaab" do some "a" end
|
55
|
+
assert_match "abab", "ababaab" do some "ab" end
|
56
|
+
assert_no_match "xxxx" do some "y" end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_match_not
|
60
|
+
assert_match "x", "x" do _not "y" end
|
61
|
+
assert_no_match "x" do _not "x" end
|
62
|
+
assert_match "cdef", "abcdefab" do some _not( "a".or "b" ) end
|
63
|
+
assert_match "defa", "abcdefaab" do some _not( "ab".or "b".or "c" ) end
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_match_lookahead_not
|
67
|
+
assert_match "y", "xy" do letter.not "x" end
|
68
|
+
assert_match "1234", "123456789" do some( digit.not "5" ) end
|
69
|
+
assert_match "1234", "1234abc" do some( digit.not "5" ) end
|
70
|
+
assert_match "abb", "abcabb" do ( "ab" + letter ).not "abc" end
|
71
|
+
assert_no_match "abbbc" do "a" + ( (1..6).of "a" ).not( "aaa" ) + "c" end
|
72
|
+
assert_match "21", "123321" do 2.or_more digit.not( "12".or "3" ) end
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_match_concat
|
76
|
+
assert_match "foobar", "foobar" do "foo" + "bar" end
|
77
|
+
assert_match "foobarbaz", "foobarbazbar" do "foo" + "bar" + "baz" end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_match_num_exactly
|
81
|
+
assert_match "oo", "foobar" do 2.exactly "o" end
|
82
|
+
assert_match "oo", "foooobar" do 2.exactly "o" end
|
83
|
+
assert_no_match "foobar" do 3.exactly "o" end
|
84
|
+
assert_match "foobar", "foobar" do "f" + 2.exactly( "o" ) + "bar" end
|
85
|
+
assert_no_match "foobar" do "f" + 1.exactly( "o" ) + "bar" end
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_match_num_or_more
|
89
|
+
assert_match "oo", "foobar" do 2.or_more "o" end
|
90
|
+
assert_match "foooo", "foooobar" do "f" + 2.or_more( "o" ) end
|
91
|
+
assert_no_match "foobar" do 3.or_more "o" end
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_match_num_or_less
|
95
|
+
assert_match "xx", "xx" do 2.or_less "x" end
|
96
|
+
assert_match "xx", "xxxxxxxx" do 2.or_less "x" end
|
97
|
+
assert_match "xx", "xx" do 100.or_less "x" end
|
98
|
+
assert_match "foobar", "foobar" do "f" + 2.or_less( "o" ) + "bar" end
|
99
|
+
assert_match "fbar", "fbar" do "f" + 2.or_less( "o" ) + "bar" end
|
100
|
+
assert_no_match "foooobar" do "f" + 3.or_less( "o" ) + "bar" end
|
101
|
+
assert_match "", "xxxxx" do 3.or_less "y" end
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_range_of_matches
|
105
|
+
assert_match "xx", "xx" do (2..4).of "x" end
|
106
|
+
assert_match "xxx", "xxx" do (2..100).of "x" end
|
107
|
+
assert_match "xxxx", "xxxxxxxx" do (2..4).of "x" end
|
108
|
+
assert_no_match "foobar" do "f" + (3..4).of( "o" ) + "bar" end
|
109
|
+
assert_no_match "foooooooobar" do "f" + (3..4).of( "o" ) + "bar" end
|
110
|
+
assert_no_match "xxxx" do (1..100).of( "y" ) + "bar" end
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_char_range
|
114
|
+
assert_match "b", "b" do "a".."c" end
|
115
|
+
assert_no_match "b" do "A".."C" end
|
116
|
+
assert_match "abc", "abcdefg" do 1.or_more "a".."c" end
|
117
|
+
assert_match "123", "123456789" do (1..4).of 1..3 end
|
118
|
+
assert_match "x8", "ax87" do 1.or_more( ("q".."z").or(8..9) ) end
|
119
|
+
assert_match "az", "az" do ("a".."c") + ("w".."z") end
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_grouping
|
123
|
+
assert_match "foobar", "foobar" do "foo" + ( "xyz".or "bar" ) end
|
124
|
+
assert_match "bar", "foobar" do ( "xyz" + "foo" ).or "bar" end
|
125
|
+
assert_match "fo", "foobar" do ( "xyz" + "foo" ).or( "xyz".or "fo" ).or( "foo" + "xyz" ) end
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_dont_add_extra_backreferences
|
129
|
+
mdata = "foobar".rmatch? do "foo" + ( "xyz".or "bar" ) end
|
130
|
+
assert_equal 1, mdata.length
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_any_characters
|
134
|
+
assert_match "f", "foobar" do any_char end
|
135
|
+
chars = "#\t?/\<>.,;:\"'!@\#$%^&*()[]{} bar"
|
136
|
+
assert_match chars, chars do some any_char end
|
137
|
+
assert_no_match "\n" do any_char end
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_word_characters
|
141
|
+
assert_match "f", "foobar" do word_char end
|
142
|
+
assert_match "foo_bar2", "### foo_bar2 baz bar" do some word_char end
|
143
|
+
assert_no_match '?/\<>.,;:"\'!@#$%^&*()[]{}' do word_char end
|
144
|
+
assert_no_match 'a,b,c,d' do 2.or_more word_char end
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_digit_characters
|
148
|
+
assert_match "1", "12345" do digit end
|
149
|
+
assert_match "654321", "### abc654321baz123" do some digit end
|
150
|
+
assert_no_match 'abc_DEF *&".' do digit end
|
151
|
+
assert_no_match '1,2,3' do 2.or_more digit end
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_letter_characters
|
155
|
+
assert_match "f", "foobar" do letter end
|
156
|
+
assert_match "foo", "### foo_bar2 baz bar" do some letter end
|
157
|
+
assert_no_match '?/."()123456_' do letter end
|
158
|
+
assert_no_match 'a1b2c3' do 2.or_more letter end
|
159
|
+
end
|
160
|
+
|
161
|
+
def test_whitespace_characters
|
162
|
+
assert_match " ", " " do whitespace end
|
163
|
+
assert_match " \t ", "### \t baz bar" do some whitespace end
|
164
|
+
assert_no_match 'abc_123-+=().?!' do whitespace end
|
165
|
+
assert_no_match 'a b c d' do 2.or_more whitespace end
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_numeric_group
|
169
|
+
assert_match_backreferences ["a", "a"], "abc" do group "a" end
|
170
|
+
assert_match_backreferences ["ab", "a", "b"], "abc" do group( "a" ) + group( "b" ) end
|
171
|
+
assert_match_backreferences ["ab", "ab", "b"], "abc" do group( "a" + group( "b" ) ) end
|
172
|
+
assert_match_backreferences ["abcde", "abcde"], "abcde" do group( any word_char ) end
|
173
|
+
assert_match_backreferences ["abcde", "e"], "abcde" do any group( word_char ) end
|
174
|
+
assert_match_backreferences ["a", "a", nil], "abc" do group( "a" ).or group( "b" ) end
|
175
|
+
end
|
176
|
+
|
177
|
+
def test_named_groups
|
178
|
+
assert_match_named_groups( { :my_a => "a" }, "abc" ) do group :my_a, "a" end
|
179
|
+
assert_match_named_groups( { :my_b => "b" }, "abc" ) do "a" + group( :my_b, "b" ) + "c" end
|
180
|
+
assert_match_named_groups( { :a => "a", :b => nil }, "abc" ) do group( :a, "a" ).or group( :b, "b" ) end
|
181
|
+
assert_match_named_groups( { :a => "ab", :b => "ab" }, "abc" ) do group( :a, group( :b, "ab" ) ) end
|
182
|
+
assert_match_named_groups( { :a => "ab", :b => "b" }, "abc" ) do group( :a, "a" + group( :b, "b" ) ) end
|
183
|
+
assert_match_named_groups( { :word => "abcde" }, "abcde" ) do group( :word, any( word_char ) ) end
|
184
|
+
assert_match_named_groups( { :letter => "e" }, "abcde" ) do any group( :letter, word_char ) end
|
185
|
+
end
|
186
|
+
|
187
|
+
def test_named_groups_block_syntax
|
188
|
+
assert_match_named_groups( { :my_a => "a" }, "abc" ) do group :my_a do "a" end end
|
189
|
+
assert_match_named_groups( { :my_a => "a" }, "abc" ) do group( :my_a ) { "a" } end
|
190
|
+
assert_match_named_groups( { :full_match => "abc", :last_part => "bc" }, "abc" ) do
|
191
|
+
group :full_match do
|
192
|
+
"a" + group( :last_part ) do
|
193
|
+
"bz".or "bc".or "b"
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def test_named_groups_cached
|
200
|
+
assert( matches = "a".rmatch? do group :a, "a" end )
|
201
|
+
Rrrex::GroupMatch.any_instance.expects( :group_names ).times( 1 ).returns( [] )
|
202
|
+
matches[ :a ]
|
203
|
+
matches[ :a ]
|
204
|
+
matches.named_groups
|
205
|
+
matches.named_groups
|
206
|
+
end
|
207
|
+
|
208
|
+
def assert_no_match( string, &block )
|
209
|
+
assert_nil( string.rmatch?( &block ) )
|
210
|
+
end
|
211
|
+
|
212
|
+
def assert_match( expected, string, &block )
|
213
|
+
assert( matches = string.rmatch?( &block ) )
|
214
|
+
assert_equal expected, matches[0]
|
215
|
+
end
|
216
|
+
|
217
|
+
def assert_match_backreferences( expected, string, &block )
|
218
|
+
assert( matches = string.rmatch?( &block ) )
|
219
|
+
assert_equal expected, matches.to_a
|
220
|
+
end
|
221
|
+
|
222
|
+
def assert_match_named_groups( expected, string, &block )
|
223
|
+
assert( matches = string.rmatch?( &block ) )
|
224
|
+
assert_equal expected, matches.named_groups
|
225
|
+
expected.each do |k,v|
|
226
|
+
assert_equal v, matches[ k ]
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rrrex
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Ian Young
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-03-11 00:00:00 -08:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: mocha
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
description: " Rrrex is a new syntax for regular expressions.\n Less compact, but human-readable. By regular humans.\n"
|
36
|
+
email: ian.greenleaf+github@gmail.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README.md
|
43
|
+
files:
|
44
|
+
- lib/rrrex.rb
|
45
|
+
- lib/rrrex/match_data.rb
|
46
|
+
- lib/rrrex/or_match.rb
|
47
|
+
- lib/rrrex/single_atom_match.rb
|
48
|
+
- lib/rrrex/regexp.rb
|
49
|
+
- lib/rrrex/dsl_context.rb
|
50
|
+
- lib/rrrex/match.rb
|
51
|
+
- lib/rrrex/not_match.rb
|
52
|
+
- lib/rrrex/group_match.rb
|
53
|
+
- lib/rrrex/number_match.rb
|
54
|
+
- lib/rrrex/core_ext.rb
|
55
|
+
- lib/rrrex/range_match.rb
|
56
|
+
- lib/rrrex/unescaped_string_match.rb
|
57
|
+
- lib/rrrex/string_match.rb
|
58
|
+
- lib/rrrex/core_ext/string.rb
|
59
|
+
- lib/rrrex/core_ext/fixnum.rb
|
60
|
+
- lib/rrrex/core_ext/range.rb
|
61
|
+
- lib/rrrex/composite_match.rb
|
62
|
+
- lib/rrrex/concat_match.rb
|
63
|
+
- lib/method_missing_conversion.rb
|
64
|
+
- test/match_test.rb
|
65
|
+
- LICENSE
|
66
|
+
- Rakefile
|
67
|
+
- README.md
|
68
|
+
has_rdoc: true
|
69
|
+
homepage:
|
70
|
+
licenses: []
|
71
|
+
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options:
|
74
|
+
- --main
|
75
|
+
- README.md
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
hash: 3
|
84
|
+
segments:
|
85
|
+
- 0
|
86
|
+
version: "0"
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
hash: 3
|
93
|
+
segments:
|
94
|
+
- 0
|
95
|
+
version: "0"
|
96
|
+
requirements: []
|
97
|
+
|
98
|
+
rubyforge_project:
|
99
|
+
rubygems_version: 1.3.7
|
100
|
+
signing_key:
|
101
|
+
specification_version: 3
|
102
|
+
summary: Really Readable Regexps
|
103
|
+
test_files: []
|
104
|
+
|