From 4f9ef0cd590747a7587e0803e3569f7ec3063387 Mon Sep 17 00:00:00 2001 From: Paul Mucur Date: Fri, 21 Oct 2022 19:29:53 +0100 Subject: [PATCH] Add RE2::MatchData#deconstruct GitHub: https://github.com/mudge/re2/issues/57 So that RE2::MatchData can be used with Ruby's pattern matching (https://docs.ruby-lang.org/en/3.0/syntax/pattern_matching_rdoc.html), implement a deconstruct method that returns only the capturing groups. This enables the following example: case RE2('(\d)(\d)?').match("1") in x, y puts "Matched #{x} #{y}" end --- ext/re2/re2.cc | 42 +++++++++++++++++++++++++++++++++++++ spec/re2/match_data_spec.rb | 14 +++++++++++++ 2 files changed, 56 insertions(+) diff --git a/ext/re2/re2.cc b/ext/re2/re2.cc index 5ffad68..96f7218 100644 --- a/ext/re2/re2.cc +++ b/ext/re2/re2.cc @@ -687,6 +687,46 @@ static VALUE re2_matchdata_inspect(VALUE self) { return result; } +/* + * Returns the array of submatches for pattern matching. + * + * @return [Array] the array of submatches + * @example + * m = RE2::Regexp.new('(\d+)').match("bob 123") + * m.deconstruct #=> ["123"] + * + * case RE2::Regexp.new('(\d+) (\d+)').match("bob 123 456") + * in x, y + * puts "Matched #{x} #{y}" + * else + * puts "Unrecognised match" + * end + */ +static VALUE re2_matchdata_deconstruct(VALUE self) { + int i; + re2_matchdata *m; + re2_pattern *p; + re2::StringPiece *match; + VALUE array; + + Data_Get_Struct(self, re2_matchdata, m); + Data_Get_Struct(m->regexp, re2_pattern, p); + + array = rb_ary_new2(m->number_of_matches - 1); + for (i = 1; i < m->number_of_matches; i++) { + match = &m->matches[i]; + + if (match->empty()) { + rb_ary_push(array, Qnil); + } else { + rb_ary_push(array, ENCODED_STR_NEW(match->data(), match->size(), + p->pattern->options().encoding() == RE2::Options::EncodingUTF8 ? "UTF-8" : "ISO-8859-1")); + } + } + + return array; +} + /* * Returns a new RE2 object with a compiled version of * +pattern+ stored inside. Equivalent to +RE2.new+. @@ -1666,6 +1706,8 @@ void Init_re2(void) { RUBY_METHOD_FUNC(re2_matchdata_to_s), 0); rb_define_method(re2_cMatchData, "inspect", RUBY_METHOD_FUNC(re2_matchdata_inspect), 0); + rb_define_method(re2_cMatchData, "deconstruct", + RUBY_METHOD_FUNC(re2_matchdata_deconstruct), 0); rb_define_method(re2_cScanner, "string", RUBY_METHOD_FUNC(re2_scanner_string), 0); diff --git a/spec/re2/match_data_spec.rb b/spec/re2/match_data_spec.rb index 245dd8b..2c77a25 100644 --- a/spec/re2/match_data_spec.rb +++ b/spec/re2/match_data_spec.rb @@ -241,4 +241,18 @@ expect(md.end(:foo)).to be_nil end end + + describe "#deconstruct" do + it "returns all capturing groups" do + md = RE2::Regexp.new('w(o)(o)').match('woo') + + expect(md.deconstruct).to eq(['o', 'o']) + end + + it "includes optional capturing groups as nil" do + md = RE2::Regexp.new('w(.)(.)(.)?').match('woo') + + expect(md.deconstruct).to eq(['o', 'o', nil]) + end + end end