あおきです。ruby-dev に移します。

  In mail "[ruby-list:31213] Re: [REQ] String#slice(re, n)"
    matz / ruby-lang.org (Yukihiro Matsumoto) wrote:

> まつもと ゆきひろです

> |> str.slice(re, n)はマッチしていない、あるいは対応するかっこが
> |> ないときにはnilを返すということ?

> そうなると(そうでなくてもsliceという名前なら)整合性のために、
> []や[]=やslice!も(re, n)というパターンに対応する必要がありま
> すね。大仕事だなあ。

実装してみようかと思ったんですが、その途中で、/a(b)c/ で b に
「マッチ」するような「サブパターンオブジェクト」があるといろいろな
面でうまくいくのではないかと思いつきました。つまり

    str = 'abc'
    re = /a(b)c/
    n = 1

    pat = re.sub_pattern(n)  # または RegexpSubPattern.new(re,n)
    p str.slice(pat)         #=> "b"
    p str.slice!(pat)        #=> "ac"
    p str[pat]               #=> "b"
    p str[pat] = 'REPLACE'   #=> "aREPLACEc"

という感じです。C からも部分文字列取り出しを透過にしておけば、
string.c は全くいじらずに slice(!) [] []= sub(!) に対応できて
しまいます (意見ころころ変えてすみません〜)。

# ただ gsub は offset==BEG[0] を仮定してるらしいので直す必要あり


で、それを実装してみたのが添付のパッチです。re.c のレベルで
m[i] を m[i-n] に置きかえることで実現してます。struct RRegexp に
メンバを増やしてるのは試験実装ってことで許してください。
-------------------------------------------------------------------
青木峰郎

Index: ruby.h =================================================================== RCS file: /src/ruby/ruby.h,v retrieving revision 1.51 diff -u -r1.51 ruby.h --- ruby.h 2001/07/19 02:46:28 1.51 +++ ruby.h 2001/09/03 12:21:37 @@ -287,6 +287,7 @@ struct re_pattern_buffer *ptr; long len; char *str; + int shift; }; struct RHash { Index: re.c =================================================================== RCS file: /src/ruby/re.c,v retrieving revision 1.44 diff -u -r1.44 re.c --- re.c 2001/07/24 09:07:33 1.44 +++ re.c 2001/09/03 12:21:36 @@ -603,6 +603,9 @@ pos, range); } +static int reg_subpattern_n_get _((VALUE)); +static void match_shift_registers _((VALUE, int)); + int rb_reg_search(re, str, pos, reverse) VALUE re, str; @@ -655,6 +658,7 @@ match = match_alloc(); } re_copy_registers(RMATCH(match)->regs, &regs); + match_shift_registers(match, reg_subpattern_n_get(re)); RMATCH(match)->str = rb_str_new4(str); rb_backref_set(match); @@ -815,6 +819,7 @@ } VALUE rb_cRegexp; +static void reg_subpattern_n_set _((VALUE, int)); static void rb_reg_initialize(obj, s, len, options) @@ -867,6 +872,7 @@ memcpy(re->str, s, len); re->str[len] = '\0'; re->len = len; + reg_subpattern_n_set((VALUE)re, 0); if (options & ~0xf) { kcode_reset_option(); } @@ -1353,6 +1359,55 @@ return match_getter(); } +VALUE rb_cRegexpSubPattern; + +static void +reg_subpattern_n_set(re, nth) + VALUE re; + int nth; +{ + RREGEXP(re)->shift = nth; +} + +static int +reg_subpattern_n_get(re) + VALUE re; +{ + return RREGEXP(re)->shift; +} + +static VALUE +rb_reg_sub_pattern(re, nth) + VALUE re, nth; +{ + VALUE obj; + + obj = rb_reg_clone(re); + RREGEXP(obj)->basic.klass = rb_cRegexpSubPattern; + reg_subpattern_n_set(obj, NUM2INT(nth)); + return obj; +} + +static void +match_shift_registers(match, n) + VALUE match; + int n; +{ + int i; + int len = RMATCH(match)->regs->num_regs; + + if (n > len) n = len; + for (i = 0; i < len - n; i++) { + RMATCH(match)->regs->beg[i] = RMATCH(match)->regs->beg[i+n]; + RMATCH(match)->regs->end[i] = RMATCH(match)->regs->end[i+n]; + } + for (i = len - n; i < len; i++) { + RMATCH(match)->regs->beg[i] = -1; + RMATCH(match)->regs->end[i] = -1; + } + RMATCH(match)->regs->num_regs -= n; +} + void Init_Regexp() { @@ -1408,6 +1463,9 @@ rb_define_const(rb_cRegexp, "MULTILINE", INT2FIX(RE_OPTION_MULTILINE)); rb_global_variable(&reg_cache); + + rb_cRegexpSubPattern = rb_define_class("RegexpSubPattern", rb_cRegexp); + rb_define_method(rb_cRegexp, "sub_pattern", rb_reg_sub_pattern, 1); rb_cMatch = rb_define_class("MatchData", rb_cObject); rb_define_global_const("MatchingData", rb_cMatch);