From dd50d5ebb5651aef0507086bd50508d3038fdc18 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 11 Jan 2015 23:26:26 +0000 Subject: [PATCH] Backreferences for NAMED capture groups (v0.1.0) e.g. /(?foo) \k/.examples Bumped the version to 0.1.0 as this is a big feature update! --- README.md | 5 ++--- lib/regexp-examples/backreferences.rb | 4 ++-- lib/regexp-examples/groups.rb | 8 ++++---- lib/regexp-examples/parser.rb | 10 +++++++--- lib/regexp-examples/regexp_extensions.rb | 22 +++++++++++----------- lib/regexp-examples/version.rb | 2 +- spec/regexp-examples_spec.rb | 3 ++- 7 files changed, 29 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 1f7b33b..5eab848 100644 --- a/README.md +++ b/README.md @@ -29,10 +29,9 @@ or a huge number of possible matches, such as `/.\w/`, then only a subset of the * Boolean "Or" groups, e.g. `/a|b|c/` * Character sets (inluding ranges and negation!), e.g. `/[abc]/`, `/[A-Z0-9]/`, `/[^a-z]/` * Escaped characters, e.g. `/\n/`, `/\w/`, `/\D/` (and so on...) -* Capture groups, and backreferences(!!), e.g. `/(this|that) \1/` -* Named capture groups, e.g. `(?bar)/`(Warning: Named capture group backreferences not yet implemented!) +* Capture groups, including named groups and backreferences(!!), e.g. `/(this|that) \1/` `/(?foo) \k/` * Non-capture groups, e.g. `/(?:foo)/` -* Arbitrarily complex combinations of all the above! +* '''Arbitrarily complex combinations of all the above!''' ## Not-Yet-Supported syntax diff --git a/lib/regexp-examples/backreferences.rb b/lib/regexp-examples/backreferences.rb index 4bb192a..b5629b8 100644 --- a/lib/regexp-examples/backreferences.rb +++ b/lib/regexp-examples/backreferences.rb @@ -23,8 +23,8 @@ def substitute_backreferences(full_examples) [full_example] else full_example.map! do |partial_example| - partial_example.gsub(/__(\d+)__/) do |match| - find_backref_for(full_example, $1.to_i) + partial_example.gsub(/__(\w+)__/) do |match| + find_backref_for(full_example, $1) end end end diff --git a/lib/regexp-examples/groups.rb b/lib/regexp-examples/groups.rb index 2c416b6..2605d83 100644 --- a/lib/regexp-examples/groups.rb +++ b/lib/regexp-examples/groups.rb @@ -109,13 +109,13 @@ def result end class BackReferenceGroup - attr_reader :num - def initialize(num) - @num = num + attr_reader :id + def initialize(id) + @id = id end def result - ["__#{@num}__"] + ["__#{@id}__"] end end diff --git a/lib/regexp-examples/parser.rb b/lib/regexp-examples/parser.rb index 8a862e7..bd17a5c 100644 --- a/lib/regexp-examples/parser.rb +++ b/lib/regexp-examples/parser.rb @@ -46,7 +46,11 @@ def parse_after_backslash_group @current_position += 1 case when rest_of_string =~ /\A(\d+)/ - group = parse_backreference_group($&) + @current_position += ($1.length - 1) # In case of 10+ backrefs! + group = parse_backreference_group($1) + when rest_of_string =~ /\Ak<([^>]+)>/ # Named capture group + @current_position += ($1.length + 2) + group = parse_backreference_group($1) when BackslashCharMap.keys.include?(regexp_string[@current_position]) group = CharGroup.new( BackslashCharMap[regexp_string[@current_position]]) @@ -83,7 +87,7 @@ def parse_multi_group rest_of_string.match(/\A(\?)?(:|!|=|<(!|=|[^!=][^>]*))?/) do |match| case when match[1].nil? # e.g. /(normal)/ - group_id = @num_groups + group_id = @num_groups.to_s when match[2] == ':' # e.g. /(?:nocapture)/ @current_position += 2 group_id = nil @@ -141,7 +145,7 @@ def parse_single_char_group(char) end def parse_backreference_group(match) - BackReferenceGroup.new(match.to_i) + BackReferenceGroup.new(match) end def parse_star_repeater(group) diff --git a/lib/regexp-examples/regexp_extensions.rb b/lib/regexp-examples/regexp_extensions.rb index a0017db..c4c22a3 100644 --- a/lib/regexp-examples/regexp_extensions.rb +++ b/lib/regexp-examples/regexp_extensions.rb @@ -1,16 +1,16 @@ class Regexp -module Examples - def examples - partial_examples = - RegexpExamples::Parser.new(source) - .parse - .map {|repeater| repeater.result} - full_examples = RegexpExamples::permutations_of_strings(partial_examples.dup, no_join: true) - full_examples_with_backrefs = \ - RegexpExamples::BackReferenceReplacer.new.substitute_backreferences(full_examples) - full_examples_with_backrefs.map(&:join) + module Examples + def examples + partial_examples = + RegexpExamples::Parser.new(source) + .parse + .map {|repeater| repeater.result} + full_examples = RegexpExamples::permutations_of_strings(partial_examples.dup, no_join: true) + full_examples_with_backrefs = \ + RegexpExamples::BackReferenceReplacer.new.substitute_backreferences(full_examples) + full_examples_with_backrefs.map(&:join) + end end -end include Examples end diff --git a/lib/regexp-examples/version.rb b/lib/regexp-examples/version.rb index 2a7d8d9..780869f 100644 --- a/lib/regexp-examples/version.rb +++ b/lib/regexp-examples/version.rb @@ -1,3 +1,3 @@ module RegexpExamples - VERSION = '0.0.2' + VERSION = '0.1.0' end diff --git a/spec/regexp-examples_spec.rb b/spec/regexp-examples_spec.rb index ad43c69..ce088ee 100644 --- a/spec/regexp-examples_spec.rb +++ b/spec/regexp-examples_spec.rb @@ -56,7 +56,8 @@ def self.examples_exist_and_match(*regexps) examples_exist_and_match( /(normal)/, /(?:nocapture)/, - /(?namedgroup)/ + /(?namedgroup)/, + /(?namedgroup) \k/ ) # TODO: These are not yet implemented # (expect to raise exception)