diff --git a/ChangeLog b/ChangeLog index a04f9e67a8..5baff4a096 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Tue May 21 22:47:06 2013 NARUSE, Yui + + * ext/strscan/strscan.c (strscan_aref): support named captures. + patched by Konstantin Haase [ruby-core:54664] [Feature #8343] + Tue May 21 21:48:44 2013 Kouhei Sutou * test/ruby/test_dir_m17n.rb (TestDir_M17N#test_entries_compose): diff --git a/ext/strscan/strscan.c b/ext/strscan/strscan.c index b81e1f1833..4623994612 100644 --- a/ext/strscan/strscan.c +++ b/ext/strscan/strscan.c @@ -38,6 +38,9 @@ struct strscanner /* the regexp register; legal only when MATCHED_P(s) */ struct re_registers regs; + + /* regexp used for last scan */ + VALUE regex; }; #define MATCHED_P(s) ((s)->flags & FLAG_MATCHED) @@ -456,6 +459,8 @@ strscan_do_scan(VALUE self, VALUE regex, int succptr, int getstr, int headonly) if (S_RESTLEN(p) < 0) { return Qnil; } + + p->regex = regex; re = rb_reg_prepare_re(regex, p->str); tmpreg = re != RREGEXP(regex)->ptr; if (!tmpreg) RREGEXP(regex)->usecnt++; @@ -983,17 +988,45 @@ strscan_matched_size(VALUE self) * s[3] # -> "12" * s.post_match # -> "1975 14:39" * s.pre_match # -> "" + * + * s.reset + * s.scan(/(?\w+) (?\w+) (?\d+) /) # -> "Fri Dec 12 " + * s[0] # -> "Fri Dec 12 " + * s[1] # -> "Fri" + * s[2] # -> "Dec" + * s[3] # -> "12" + * s[:wday] # -> "Fri" + * s[:month] # -> "Dec" + * s[:day] # -> "12" + * s.post_match # -> "1975 14:39" + * s.pre_match # -> "" */ static VALUE strscan_aref(VALUE self, VALUE idx) { + const char *name, *name_end; struct strscanner *p; long i; GET_SCANNER(self, p); if (! MATCHED_P(p)) return Qnil; - i = NUM2LONG(idx); + switch (TYPE(idx)) { + case T_SYMBOL: + name = rb_id2name(SYM2ID(idx)); + goto name_to_backref; + break; + case T_STRING: + name = StringValuePtr(idx); + name_to_backref: + name_end = name + strlen(name); + i = onig_name_to_backref_number(RREGEXP(p->regex)->ptr, + (const unsigned char* )name, (const unsigned char* )name_end, &(p->regs)); + break; + default: + i = NUM2LONG(idx); + } + if (i < 0) i += p->regs.num_regs; if (i < 0) return Qnil; diff --git a/test/strscan/test_stringscanner.rb b/test/strscan/test_stringscanner.rb index 2c4cf90828..7f2e5c1ccd 100644 --- a/test/strscan/test_stringscanner.rb +++ b/test/strscan/test_stringscanner.rb @@ -457,6 +457,17 @@ class TestStringScanner < Test::Unit::TestCase assert_equal true, s[2].tainted? assert_equal true, s[3].tainted? assert_equal true, s[4].tainted? + + s = StringScanner.new("foo bar") + s.scan /(?(\w+)) (?(\w+))/ + assert_equal 'foo', s[1] + assert_equal 'bar', s[2] + assert_equal 'foo', s[:a] + assert_equal 'bar', s[:b] + assert_equal nil, s[:c] + assert_equal 'foo', s['a'] + assert_equal 'bar', s['b'] + assert_equal nil, s['c'] end def test_pre_match