なかだです。

Pythonの"%(x)s"形式のフォーマット(正式にはなんて言うんだか?)を
見て、実装してみました。

それと"%2$s%s"としたときに、現状では二番目と三番目の引数を使っ
てますが、glibcなどでは二番目と最初のが使われるようです。この議
論はなんか昔あったような気がしますが、"*"の後の"$"ではカウンタ
が進まないことと一貫してないように思えるので、揃えてみました。


Index: sprintf.c =================================================================== RCS file: /cvs/ruby/src/ruby/sprintf.c,v retrieving revision 1.19 diff -u -2 -p -r1.19 sprintf.c --- sprintf.c 2002/02/01 10:23:22 1.19 +++ sprintf.c 2002/03/12 14:34:05 @@ -20,4 +20,5 @@ static void fmt_setup _((char*,int,int,int,int)); +static ID keyid _((const char *p, int n)); static char* @@ -128,29 +129,62 @@ remove_sign_bits(str, base) } -#define GETARG() \ - ((nextarg >= argc) ? (rb_raise(rb_eArgError, "too few argument."), 0) : argv[nextarg++]) +#define GETARG() (nextvalue == Qundef ? GETNTHARG(nextarg) : nextvalue) +#define GETNTHARG(nth) \ + ((nth >= argc) ? (rb_raise(rb_eArgError, "too few argument."), 0) : argv[nth++]) + #define GETASTER(val) { \ t = p++; \ n = 0; \ - for (; p < end && ISDIGIT(*p); p++) { \ - n = 10 * n + (*p - '0'); \ - } \ - if (p >= end) { \ - rb_raise(rb_eArgError, "malformed format string - %%*[0-9]"); \ + if (*p == '(') { \ + GETKEY(tmp); \ } \ - if (*p == '$') { \ - int curarg = nextarg; \ - nextarg = n; \ - tmp = GETARG(); \ - nextarg = curarg; \ - } \ else { \ - tmp = GETARG(); \ - p = t; \ + for (; p < end && ISDIGIT(*p); p++) { \ + n = 10 * n + (*p - '0'); \ + } \ + if (p >= end) { \ + rb_raise(rb_eArgError, "malformed format string - %%*[0-9]"); \ + } \ + if (*p == '$') { \ + tmp = GETNTHARG(n); \ + } \ + else { \ + tmp = GETARG(); \ + p = t; \ + } \ } \ val = NUM2INT(tmp); \ } +#define GETKEY(val) { \ + VALUE id; \ + t = ++p; \ + while ((p < end) ? (*p != ')') : \ + (rb_raise(rb_eArgError, "incomplete format key: %.*s", \ + end - t, t), 1)) { \ + p++; \ + } \ + id = keyid(t, p - t); \ + if (!keyargs) { \ + if (nextarg >= argc) rb_raise(rb_eArgError, "format require mapping."); \ + keyargs = argv[--argc]; \ + Check_Type(keyargs, T_HASH); \ + } \ + if (!fetch) fetch = rb_intern("fetch"); \ + val = rb_funcall3(keyargs, fetch, 1, &id); \ +} + +static VALUE +keyid(p, n) + const char *p; + int n; +{ + char *str = ALLOCA_N(char, n + 1); + MEMCPY(str, p, char, n); + str[n] = '\0'; + return ID2SYM(rb_intern(str)); +} + VALUE rb_f_sprintf(argc, argv) @@ -158,4 +192,5 @@ rb_f_sprintf(argc, argv) VALUE *argv; { + static ID fetch; VALUE fmt; char *buf, *p, *end; @@ -166,8 +201,10 @@ rb_f_sprintf(argc, argv) int nextarg = 0; int tainted = 0; + VALUE nextvalue; VALUE tmp; VALUE str; + VALUE keyargs = 0; - fmt = GETARG(); + fmt = GETNTHARG(nextarg); if (OBJ_TAINTED(fmt)) tainted = 1; StringValue(fmt); @@ -191,4 +228,5 @@ rb_f_sprintf(argc, argv) width = prec = -1; + nextvalue = Qundef; retry: switch (*p) { @@ -235,5 +273,8 @@ rb_f_sprintf(argc, argv) } if (*p == '$') { - nextarg = n; + if (nextvalue != Qundef) { + rb_raise(rb_eArgError, "value given twice"); + } + nextvalue = GETNTHARG(n); p++; goto retry; @@ -241,4 +282,12 @@ rb_f_sprintf(argc, argv) width = n; flags |= FWIDTH; + goto retry; + + case '(': + if (nextvalue != Qundef) { + rb_raise(rb_eArgError, "value given twice"); + } + GETKEY(nextvalue); + ++p; goto retry;
-- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦