From dc60f9a45653c872044a69d39bcfab038a19205d Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Sun, 28 Apr 2013 16:30:14 +0200 Subject: [PATCH] make StringScanner#[] accept names of named captures --- ext/strscan/strscan.c | 28 ++++++++++++++++++++++++++-- test/strscan/test_stringscanner.rb | 11 +++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/ext/strscan/strscan.c b/ext/strscan/strscan.c index b81e1f1..2826f96 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++; @@ -976,24 +981,43 @@ strscan_matched_size(VALUE self) * Return the n-th subgroup in the most recent match. * * s = StringScanner.new("Fri Dec 12 1975 14:39") - * s.scan(/(\w+) (\w+) (\d+) /) # -> "Fri Dec 12 " + * 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 2c4cf90..7f2e5c1 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 -- 1.8.2