summaryrefslogtreecommitdiff
path: root/re.c
diff options
context:
space:
mode:
authorVladimir Dementyev <dementiev.vm@gmail.com>2022-08-05 20:13:09 -0400
committerKazuki Tsujimoto <kazuki@callcc.net>2022-10-10 12:41:13 +0900
commit4954c9fc0f9d06aa4e3e8deb33b41f3fae294adc (patch)
tree5874913d47d954cb471e3fba98f534e54903d2f9 /re.c
parent546566d34b37fb5a028bcbe6bafa8248877f06ca (diff)
downloadruby-4954c9fc0f9d06aa4e3e8deb33b41f3fae294adc.tar.gz
Add MatchData#deconstruct/deconstruct_keys
Diffstat (limited to 're.c')
-rw-r--r--re.c85
1 files changed, 85 insertions, 0 deletions
diff --git a/re.c b/re.c
index 5091f9a124..ff9d5bccc4 100644
--- a/re.c
+++ b/re.c
@@ -2283,8 +2283,14 @@ match_named_captures_iter(const OnigUChar *name, const OnigUChar *name_end,
struct MEMO *memo = MEMO_CAST(arg);
VALUE hash = memo->v1;
VALUE match = memo->v2;
+ long symbolize = memo->u3.state;
VALUE key = rb_enc_str_new((const char *)name, name_end-name, regex->enc);
+
+ if (symbolize > 0) {
+ key = rb_str_intern(key);
+ }
+
VALUE value;
int i;
@@ -2350,6 +2356,83 @@ match_named_captures(VALUE match)
/*
* call-seq:
+ * deconstruct_keys(array_of_names) -> hash
+ *
+ * Returns a hash of the named captures for the given names.
+ *
+ * m = /(?<hours>\d{2}):(?<minutes>\d{2}):(?<seconds>\d{2})/.match("18:37:22")
+ * m.deconstruct_keys([:hours, :minutes]) # => {:hours => "18", :minutes => "37"}
+ * m.deconstruct_keys(nil) # => {:hours => "18", :minutes => "37", :seconds => "22"}
+ *
+ * Returns an empty hash of no named captures were defined:
+ *
+ * m = /(\d{2}):(\d{2}):(\d{2})/.match("18:37:22")
+ * m.deconstruct_keys(nil) # => {}
+ *
+ */
+static VALUE
+match_deconstruct_keys(VALUE match, VALUE keys)
+{
+ VALUE h;
+ long i;
+
+ match_check(match);
+
+ if (NIL_P(RMATCH(match)->regexp)) {
+ return rb_hash_new_with_size(0);
+ }
+
+ if (NIL_P(keys)) {
+ h = rb_hash_new_with_size(onig_number_of_names(RREGEXP_PTR(RMATCH(match)->regexp)));
+
+ struct MEMO *memo;
+ memo = MEMO_NEW(h, match, 1);
+
+ onig_foreach_name(RREGEXP_PTR(RMATCH(match)->regexp), match_named_captures_iter, (void*)memo);
+
+ return h;
+ }
+
+ if (UNLIKELY(!RB_TYPE_P(keys, T_ARRAY))) {
+ rb_raise(rb_eTypeError,
+ "wrong argument type %"PRIsVALUE" (expected Array or nil)",
+ rb_obj_class(keys));
+
+ }
+
+ if (onig_number_of_names(RREGEXP_PTR(RMATCH(match)->regexp)) < RARRAY_LEN(keys)) {
+ return rb_hash_new_with_size(0);
+ }
+
+ h = rb_hash_new_with_size(RARRAY_LEN(keys));
+
+ for (i=0; i<RARRAY_LEN(keys); i++) {
+ VALUE key = RARRAY_AREF(keys, i);
+ VALUE name;
+
+ if (UNLIKELY(!SYMBOL_P(key))) {
+ rb_raise(rb_eTypeError,
+ "wrong argument type %"PRIsVALUE" (expected Symbol)",
+ rb_obj_class(key));
+ }
+
+ name = rb_sym2str(key);
+
+ int num = NAME_TO_NUMBER(RMATCH_REGS(match), RMATCH(match)->regexp, RMATCH(match)->regexp,
+ RSTRING_PTR(name), RSTRING_END(name));
+
+ if (num >= 0) {
+ rb_hash_aset(h, key, rb_reg_nth_match(num, match));
+ } else {
+ return h;
+ }
+ }
+
+ return h;
+}
+
+/*
+ * call-seq:
* string -> string
*
* Returns the target string if it was frozen;
@@ -4542,7 +4625,9 @@ Init_Regexp(void)
rb_define_method(rb_cMatch, "to_a", match_to_a, 0);
rb_define_method(rb_cMatch, "[]", match_aref, -1);
rb_define_method(rb_cMatch, "captures", match_captures, 0);
+ rb_define_alias(rb_cMatch, "deconstruct", "captures");
rb_define_method(rb_cMatch, "named_captures", match_named_captures, 0);
+ rb_define_method(rb_cMatch, "deconstruct_keys", match_deconstruct_keys, 1);
rb_define_method(rb_cMatch, "values_at", match_values_at, -1);
rb_define_method(rb_cMatch, "pre_match", rb_reg_match_pre, 0);
rb_define_method(rb_cMatch, "post_match", rb_reg_match_post, 0);