From: Yusuke ENDOH Date: 2008-08-15T13:08:12+09:00 Subject: [ruby-dev:35851] [Feature:1.9] name referencing in sprintf 遠藤です。 1.9 では正規表現で名前を使った参照ができるので、sprintf でも 名前を使った参照ができると便利じゃないでしょうか。 $ ./ruby -e 'puts "%d : %f" % { :foo => 1, :bar => 2 }' 1 : 2.000000 $ ./ruby -e 'printf("%d : %f\n", :foo => 1, :bar => 2)' 1 : 2.000000 あまりテストしていませんが、たたき台の実装です。 Index: sprintf.c =================================================================== --- sprintf.c (revision 18638) +++ sprintf.c (working copy) @@ -103,18 +103,28 @@ } while (0) #define GETARG() (nextvalue != Qundef ? nextvalue : \ - posarg < 0 ? \ + posarg == -1 ? \ (rb_raise(rb_eArgError, "unnumbered(%d) mixed with numbered", nextarg), 0) : \ + posarg == -2 ? \ + (rb_raise(rb_eArgError, "unnumbered(%d) mixed with named", nextarg), 0) : \ (posarg = nextarg++, GETNTHARG(posarg))) #define GETPOSARG(n) (posarg > 0 ? \ (rb_raise(rb_eArgError, "numbered(%d) after unnumbered(%d)", n, posarg), 0) : \ + posarg == -2 ? \ + (rb_raise(rb_eArgError, "numbered(%d) after named", n), 0) : \ ((n < 1) ? (rb_raise(rb_eArgError, "invalid index - %d$", n), 0) : \ (posarg = -1, GETNTHARG(n)))) #define GETNTHARG(nth) \ ((nth >= argc) ? (rb_raise(rb_eArgError, "too few arguments"), 0) : argv[nth]) +#define GETNAMEARG(id) (posarg > 0 ? \ + (rb_raise(rb_eArgError, "named after unnumbered(%d)", posarg), 0) : \ + posarg == -1 ? \ + (rb_raise(rb_eArgError, "named after numbered"), 0) : \ + rb_hash_aref(get_hash(&hash, argc, argv), id)) + #define GETNUM(n, val) \ for (; p < end && rb_enc_isdigit(*p, enc); p++) { \ int next_n = 10 * n + (*p - '0'); \ @@ -141,7 +151,22 @@ val = NUM2INT(tmp); \ } while (0) +static VALUE +get_hash(volatile VALUE *hash, int argc, const VALUE *argv) +{ + VALUE tmp; + if (*hash != Qundef) return *hash; + if (argc != 2) { + rb_raise(rb_eArgError, "one hash required"); + } + tmp = rb_check_convert_type(argv[1], T_HASH, "Hash", "to_hash"); + if (NIL_P(tmp)) { + rb_raise(rb_eArgError, "one hash required"); + } + return (*hash = tmp); +} + /* * call-seq: * format(format_string [, arguments...] ) => string @@ -412,6 +437,7 @@ VALUE nextvalue; VALUE tmp; VALUE str; + volatile VALUE hash = Qundef; #define CHECK_FOR_WIDTH(f) \ if ((f) & FWIDTH) { \ @@ -513,6 +539,22 @@ flags |= FWIDTH; goto retry; + case '<': + { + const char *start = p; + ID id; + for (; p < end && *p != '>'; ) { + p += rb_enc_mbclen(p, end, enc); + } + if (p >= end) { + rb_raise(rb_eArgError, "malformed name - unmatched parenthesis"); + } + id = rb_intern3(start + 1, p - start - 1, enc); + nextvalue = GETNAMEARG(ID2SYM(id)); + p++; + goto retry; + } + case '*': CHECK_FOR_WIDTH(flags); flags |= FWIDTH; -- Yusuke ENDOH