aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorknu <knu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-09-10 07:51:58 +0000
committerknu <knu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-09-10 07:51:58 +0000
commit82abe79b9fa3b5a648f453076a28ed9566d02ec8 (patch)
tree38c67940337c200adc83b83669049492fb4a3534
parent4f77c495cddcdfcbb15e363ba21d09e5a9ddfd37 (diff)
downloadruby-82abe79b9fa3b5a648f453076a28ed9566d02ec8.tar.gz
* class.c (rb_scan_args): Add support for optional keyword
argument hash. * README.EXT, README.EXT.ja: Update documentation accordingly. * dir.c (dir_initialize): Make use of the new rb_scan_args() feature. * io.c (rb_io_s_popen, rb_scan_open_args, rb_io_initialize) (rb_io_s_pipe, open_key_args, io_s_foreach, io_s_readlines) (rb_io_s_read, rb_io_set_encoding): Ditto. * transcode.c (str_transcode, econv_args) (econv_primitive_convert): Ditto. * ext/zlib/zlib.c (rb_gzreader_initialize): Ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@29214 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog19
-rw-r--r--README.EXT19
-rw-r--r--README.EXT.ja16
-rw-r--r--class.c29
-rw-r--r--dir.c3
-rw-r--r--ext/zlib/zlib.c6
-rw-r--r--io.c72
-rw-r--r--transcode.c49
8 files changed, 130 insertions, 83 deletions
diff --git a/ChangeLog b/ChangeLog
index 01c46b56c2..15330acac7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+Fri Sep 10 16:49:20 2010 Akinori MUSHA <knu@iDaemons.org>
+
+ * class.c (rb_scan_args): Add support for optional keyword
+ argument hash.
+
+ * README.EXT, README.EXT.ja: Update documentation accordingly.
+
+ * dir.c (dir_initialize): Make use of the new rb_scan_args()
+ feature.
+
+ * io.c (rb_io_s_popen, rb_scan_open_args, rb_io_initialize)
+ (rb_io_s_pipe, open_key_args, io_s_foreach, io_s_readlines)
+ (rb_io_s_read, rb_io_set_encoding): Ditto.
+
+ * transcode.c (str_transcode, econv_args)
+ (econv_primitive_convert): Ditto.
+
+ * ext/zlib/zlib.c (rb_gzreader_initialize): Ditto.
+
Fri Sep 10 10:33:18 2010 NARUSE, Yui <naruse@ruby-lang.org>
* random.c (rb_genrand_ulong_limited): renamed from
diff --git a/README.EXT b/README.EXT
index ac0d7cc98d..c2b2d9d52d 100644
--- a/README.EXT
+++ b/README.EXT
@@ -1105,12 +1105,13 @@ according to the format string. The format can be described in ABNF
as follows:
--
-scan-arg-spec := param-arg-spec [block-arg-spec]
+scan-arg-spec := param-arg-spec [option-hash-arg-spec] [block-arg-spec]
param-arg-spec := pre-arg-spec [post-arg-spec] / post-arg-spec / pre-opt-post-arg-spec
pre-arg-spec := num-of-leading-mandatory-args [num-of-optional-args]
post-arg-spec := sym-for-variable-length-args [num-of-trailing-mandatory-args]
pre-opt-post-arg-spec := num-of-leading-mandatory-args num-of-optional-args num-of-trailing-mandatory-args
+option-hash-arg-spec := sym-for-option-hash-arg
block-arg-spec := sym-for-block-arg
num-of-leading-mandatory-args := DIGIT ; The number of leading
@@ -1122,6 +1123,18 @@ sym-for-variable-length-args := "*" ; Indicates that variable
; captured as a ruby array
num-of-trailing-mandatory-args := DIGIT ; The number of trailing
; mandatory arguments
+sym-for-option-hash-arg := ":" ; Indicates that an option
+ ; hash is captured if the last
+ ; argument is a hash or can be
+ ; converted to a hash with
+ ; #to_hash. When the last
+ ; argument is nil, it is
+ ; captured if it is not
+ ; ambiguous to take it as
+ ; empty option hash; i.e. '*'
+ ; is not specified and
+ ; arguments are given more
+ ; than sufficient.
sym-for-block-arg := "&" ; Indicates that an iterator
; block should be captured if
; given
@@ -1134,8 +1147,8 @@ assigned to captured arguments. For omitted arguments, variables are
set to Qnil. NULL can be put in place of a variable reference, which
means the corresponding captured argument(s) should be just dropped.
-The number of given arguments, excluding an iterator block, is
-returned.
+The number of given arguments, excluding an option hash or iterator
+block, is returned.
** Invoking Ruby method
diff --git a/README.EXT.ja b/README.EXT.ja
index 95c4ee2fb9..54ab449f2b 100644
--- a/README.EXT.ja
+++ b/README.EXT.ja
@@ -1198,12 +1198,13 @@ rb_scan_args(int argc, VALUE *argv, const char *fmt, ...)
トは,ABNFで記述すると以下の通りです.
--
-scan-arg-spec := param-arg-spec [block-arg-spec]
+scan-arg-spec := param-arg-spec [option-hash-arg-spec] [block-arg-spec]
param-arg-spec := pre-arg-spec [post-arg-spec] / post-arg-spec / pre-opt-post-arg-spec
pre-arg-spec := num-of-leading-mandatory-args [num-of-optional-args]
post-arg-spec := sym-for-variable-length-args [num-of-trailing-mandatory-args]
pre-opt-post-arg-spec := num-of-leading-mandatory-args num-of-optional-args num-of-trailing-mandatory-args
+option-hash-arg-spec := sym-for-option-hash-arg
block-arg-spec := sym-for-block-arg
num-of-leading-mandatory-args := DIGIT ; 先頭に置かれる省略不能な引数の数
@@ -1211,6 +1212,15 @@ num-of-optional-args := DIGIT ; 続いて置かれる省略可能な引数の数
sym-for-variable-length-args := "*" ; 続いて置かれる可変長引数を
; Rubyの配列で取得するための指定
num-of-trailing-mandatory-args := DIGIT ; 終端に置かれる省略不能な引数の数
+sym-for-option-hash-arg := ":" ; オプションハッシュを取得する
+ ; ための指定; 省略不能な引数の
+ ; 数よりも多くの引数が指定され,
+ ; 最後の引数がハッシュ(または
+ ; #to_hashで変換可能)の場合に
+ ; 取得される.最後の引数がnilの
+ ; 場合,可変長引数指定がなく,
+ ; 省略不能引数の数よりも多くの
+ ; 引数が指定された場合に取得される
sym-for-block-arg := "&" ; イテレータブロックを取得するための
; 指定
--
@@ -1223,8 +1233,8 @@ sym-for-block-arg := "&" ; イテレータブロックを取得するための
省略可能引数が省略された時の変数の値はnil(C言語のレベルでは
Qnil)になります.
- 返り値は与えられた引数の数です.イテレータブロックは数えま
- せん.
+ 返り値は与えられた引数の数です.オプションハッシュおよびイ
+ テレータブロックは数えません.
** Rubyメソッド呼び出し
diff --git a/class.c b/class.c
index 9ad8ec8539..1d4695bc6c 100644
--- a/class.c
+++ b/class.c
@@ -1376,9 +1376,10 @@ rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...)
const char *p = fmt;
VALUE *var;
va_list vargs;
- int f_var = 0, f_block = 0;
+ int f_var = 0, f_hash = 0, f_block = 0;
int n_lead = 0, n_opt = 0, n_trail = 0, n_mand;
int argi = 0;
+ VALUE hash = Qnil;
if (ISDIGIT(*p)) {
n_lead = *p - '0';
@@ -1402,6 +1403,10 @@ rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...)
}
}
block_arg:
+ if (*p == ':') {
+ f_hash = 1;
+ p++;
+ }
if (*p == '&') {
f_block = 1;
p++;
@@ -1416,6 +1421,23 @@ rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...)
va_start(vargs, fmt);
+ /* capture an option hash - phase 1: pop */
+ if (f_hash && n_mand < argc) {
+ VALUE last = argv[argc - 1];
+
+ if (NIL_P(last)) {
+ /* nil is taken as an empty option hash only if it is not
+ ambiguous; i.e. '*' is not specified and arguments are
+ given more than sufficient */
+ if (!f_var && n_mand + n_opt < argc)
+ argc--;
+ }
+ else {
+ hash = rb_check_convert_type(last, T_HASH, "Hash", "to_hash");
+ if (!NIL_P(hash))
+ argc--;
+ }
+ }
/* capture leading mandatory arguments */
for (i = n_lead; i-- > 0; ) {
var = va_arg(vargs, VALUE *);
@@ -1452,6 +1474,11 @@ rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...)
if (var) *var = argv[argi];
argi++;
}
+ /* capture an option hash - phase 2: assignment */
+ if (f_hash) {
+ var = va_arg(vargs, VALUE *);
+ if (var) *var = hash;
+ }
/* capture iterator block */
if (f_block) {
var = va_arg(vargs, VALUE *);
diff --git a/dir.c b/dir.c
index 483c8beafc..f9867e44d7 100644
--- a/dir.c
+++ b/dir.c
@@ -390,11 +390,10 @@ dir_initialize(int argc, VALUE *argv, VALUE dir)
}
fsenc = rb_filesystem_encoding();
- rb_scan_args(argc, argv, "11", &dirname, &opt);
+ argc = rb_scan_args(argc, argv, "1:", &dirname, &opt);
if (!NIL_P(opt)) {
VALUE v, enc=Qnil;
- opt = rb_convert_type(opt, T_HASH, "Hash", "to_hash");
v = rb_hash_aref(opt, sym_enc);
if (!NIL_P(v)) enc = v;
diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c
index 20218a962c..970b85713e 100644
--- a/ext/zlib/zlib.c
+++ b/ext/zlib/zlib.c
@@ -3067,11 +3067,7 @@ rb_gzreader_initialize(int argc, VALUE *argv, VALUE obj)
int err;
Data_Get_Struct(obj, struct gzfile, gz);
- if (argc > 1) {
- opt = rb_check_convert_type(argv[argc-1], T_HASH, "Hash", "to_hash");
- if (!NIL_P(opt)) argc--;
- }
- rb_scan_args(argc, argv, "1", &io);
+ rb_scan_args(argc, argv, "1:", &io, &opt);
/* this is undocumented feature of zlib */
err = inflateInit2(&gz->z.stream, -MAX_WBITS);
diff --git a/io.c b/io.c
index 169158b454..7a4be8a447 100644
--- a/io.c
+++ b/io.c
@@ -5238,21 +5238,6 @@ pipe_open_s(VALUE prog, const char *modestr, int fmode, convconfig_t *convconfig
return pipe_open(&earg, prog, modestr, fmode, convconfig);
}
-static VALUE
-pop_last_hash(int *argc_p, VALUE *argv)
-{
- VALUE last, tmp;
- if (*argc_p == 0)
- return Qnil;
- last = argv[*argc_p-1];
- if (NIL_P(last)) return Qnil;
- tmp = rb_check_convert_type(last, T_HASH, "Hash", "to_hash");
- if (NIL_P(tmp))
- return Qnil;
- (*argc_p)--;
- return tmp;
-}
-
/*
* call-seq:
* IO.popen(cmd, mode="r" [, opt]) -> io
@@ -5344,8 +5329,7 @@ rb_io_s_popen(int argc, VALUE *argv, VALUE klass)
int oflags, fmode;
convconfig_t convconfig;
- opt = pop_last_hash(&argc, argv);
- rb_scan_args(argc, argv, "11", &pname, &pmode);
+ argc = rb_scan_args(argc, argv, "11:", &pname, &pmode, &opt);
rb_io_extract_modeenc(&pmode, 0, opt, &oflags, &fmode, &convconfig);
modestr = rb_io_oflags_modestr(oflags);
@@ -5389,12 +5373,11 @@ rb_scan_open_args(int argc, VALUE *argv,
VALUE *fname_p, int *oflags_p, int *fmode_p,
convconfig_t *convconfig_p, mode_t *perm_p)
{
- VALUE opt=Qnil, fname, vmode, vperm;
+ VALUE opt, fname, vmode, vperm;
int oflags, fmode;
mode_t perm;
- opt = pop_last_hash(&argc, argv);
- rb_scan_args(argc, argv, "12", &fname, &vmode, &vperm);
+ argc = rb_scan_args(argc, argv, "12:", &fname, &vmode, &vperm, &opt);
FilePathValue(fname);
rb_io_extract_modeenc(&vmode, &vperm, opt, &oflags, &fmode, convconfig_p);
@@ -6462,8 +6445,7 @@ rb_io_initialize(int argc, VALUE *argv, VALUE io)
rb_secure(4);
- opt = pop_last_hash(&argc, argv);
- rb_scan_args(argc, argv, "11", &fnum, &vmode);
+ argc = rb_scan_args(argc, argv, "11:", &fnum, &vmode, &opt);
rb_io_extract_modeenc(&vmode, 0, opt, &oflags, &fmode, &convconfig);
fd = NUM2INT(fnum);
@@ -7776,8 +7758,7 @@ rb_io_s_pipe(int argc, VALUE *argv, VALUE klass)
int fmode = 0;
VALUE ret;
- opt = pop_last_hash(&argc, argv);
- rb_scan_args(argc, argv, "02", &v1, &v2);
+ argc = rb_scan_args(argc, argv, "02:", &v1, &v2, &opt);
if (rb_pipe(pipes) == -1)
rb_sys_fail(0);
@@ -7824,22 +7805,20 @@ struct foreach_arg {
};
static void
-open_key_args(int argc, VALUE *argv, struct foreach_arg *arg)
+open_key_args(int argc, VALUE *argv, VALUE opt, struct foreach_arg *arg)
{
- VALUE opt, v;
+ VALUE path, v;
- FilePathValue(argv[0]);
+ path = *argv++;
+ argc--;
+ FilePathValue(path);
arg->io = 0;
- arg->argc = argc - 1;
- arg->argv = argv + 1;
- if (argc == 1) {
- no_key:
- arg->io = rb_io_open(argv[0], INT2NUM(O_RDONLY), INT2FIX(0666), Qnil);
+ arg->argc = argc;
+ arg->argv = argv;
+ if (NIL_P(opt)) {
+ arg->io = rb_io_open(path, INT2NUM(O_RDONLY), INT2FIX(0666), Qnil);
return;
}
- opt = pop_last_hash(&arg->argc, arg->argv);
- if (NIL_P(opt)) goto no_key;
-
v = rb_hash_aref(opt, sym_open_args);
if (!NIL_P(v)) {
VALUE args;
@@ -7853,13 +7832,13 @@ open_key_args(int argc, VALUE *argv, struct foreach_arg *arg)
}
#endif
args = rb_ary_tmp_new(n);
- rb_ary_push(args, argv[0]);
+ rb_ary_push(args, path);
rb_ary_concat(args, v);
arg->io = rb_io_open_with_args((int)n, RARRAY_PTR(args));
rb_ary_clear(args); /* prevent from GC */
return;
}
- arg->io = rb_io_open(argv[0], Qnil, Qnil, opt);
+ arg->io = rb_io_open(path, Qnil, Qnil, opt);
}
static VALUE
@@ -7902,11 +7881,12 @@ io_s_foreach(struct foreach_arg *arg)
static VALUE
rb_io_s_foreach(int argc, VALUE *argv, VALUE self)
{
+ VALUE opt;
struct foreach_arg arg;
- rb_scan_args(argc, argv, "13", NULL, NULL, NULL, NULL);
+ argc = rb_scan_args(argc, argv, "13:", NULL, NULL, NULL, NULL, &opt);
RETURN_ENUMERATOR(self, argc, argv);
- open_key_args(argc, argv, &arg);
+ open_key_args(argc, argv, opt, &arg);
if (NIL_P(arg.io)) return Qnil;
return rb_ensure(io_s_foreach, (VALUE)&arg, rb_io_close, arg.io);
}
@@ -7938,10 +7918,11 @@ io_s_readlines(struct foreach_arg *arg)
static VALUE
rb_io_s_readlines(int argc, VALUE *argv, VALUE io)
{
+ VALUE opt;
struct foreach_arg arg;
- rb_scan_args(argc, argv, "13", NULL, NULL, NULL, NULL);
- open_key_args(argc, argv, &arg);
+ argc = rb_scan_args(argc, argv, "13:", NULL, NULL, NULL, NULL, &opt);
+ open_key_args(argc, argv, opt, &arg);
if (NIL_P(arg.io)) return Qnil;
return rb_ensure(io_s_readlines, (VALUE)&arg, rb_io_close, arg.io);
}
@@ -8001,11 +7982,11 @@ seek_before_access(VALUE argp)
static VALUE
rb_io_s_read(int argc, VALUE *argv, VALUE io)
{
- VALUE offset;
+ VALUE opt, offset;
struct foreach_arg arg;
- rb_scan_args(argc, argv, "13", NULL, NULL, &offset, NULL);
- open_key_args(argc, argv, &arg);
+ argc = rb_scan_args(argc, argv, "13:", NULL, NULL, &offset, NULL, &opt);
+ open_key_args(argc, argv, opt, &arg);
if (NIL_P(arg.io)) return Qnil;
if (!NIL_P(offset)) {
struct seek_arg sarg;
@@ -8684,8 +8665,7 @@ rb_io_set_encoding(int argc, VALUE *argv, VALUE io)
rb_io_t *fptr;
VALUE v1, v2, opt;
- opt = pop_last_hash(&argc, argv);
- rb_scan_args(argc, argv, "11", &v1, &v2);
+ argc = rb_scan_args(argc, argv, "11:", &v1, &v2, &opt);
GetOpenFile(io, fptr);
io_encoding_set(fptr, v1, v2, opt);
return io;
diff --git a/transcode.c b/transcode.c
index c718182a14..17bad37e27 100644
--- a/transcode.c
+++ b/transcode.c
@@ -2666,12 +2666,9 @@ str_transcode(int argc, VALUE *argv, VALUE *self)
int ecflags = 0;
VALUE ecopts = Qnil;
- if (0 < argc) {
- opt = rb_check_convert_type(argv[argc-1], T_HASH, "Hash", "to_hash");
- if (!NIL_P(opt)) {
- argc--;
- ecflags = rb_econv_prepare_opts(opt, &ecopts);
- }
+ argc = rb_scan_args(argc, argv, "02:", NULL, NULL, &opt);
+ if (!NIL_P(opt)) {
+ ecflags = rb_econv_prepare_opts(opt, &ecopts);
}
return str_transcode0(argc, argv, self, ecflags, ecopts);
}
@@ -2908,25 +2905,28 @@ econv_args(int argc, VALUE *argv,
int *ecflags_p,
VALUE *ecopts_p)
{
- VALUE opt, opthash, flags_v, ecopts;
+ VALUE opt, flags_v, ecopts;
int sidx, didx;
const char *sname, *dname;
rb_encoding *senc, *denc;
int ecflags;
- rb_scan_args(argc, argv, "21", snamev_p, dnamev_p, &opt);
+ argc = rb_scan_args(argc, argv, "21:", snamev_p, dnamev_p, &flags_v, &opt);
- if (NIL_P(opt)) {
- ecflags = 0;
+ if (!NIL_P(flags_v)) {
+ if (!NIL_P(opt)) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 2..3)",
+ argc + 1);
+ }
+ ecflags = NUM2INT(rb_to_int(flags_v));
ecopts = Qnil;
}
- else if (!NIL_P(flags_v = rb_check_to_integer(opt, "to_int"))) {
- ecflags = NUM2INT(flags_v);
- ecopts = Qnil;
+ else if (!NIL_P(opt)) {
+ ecflags = rb_econv_prepare_opts(opt, &ecopts);
}
else {
- opthash = rb_convert_type(opt, T_HASH, "Hash", "to_hash");
- ecflags = rb_econv_prepare_opts(opthash, &ecopts);
+ ecflags = 0;
+ ecopts = Qnil;
}
senc = NULL;
@@ -3543,7 +3543,7 @@ econv_primitive_convert(int argc, VALUE *argv, VALUE self)
unsigned long output_byteend;
int flags;
- rb_scan_args(argc, argv, "23", &input, &output, &output_byteoffset_v, &output_bytesize_v, &opt);
+ argc = rb_scan_args(argc, argv, "23:", &input, &output, &output_byteoffset_v, &output_bytesize_v, &flags_v, &opt);
if (NIL_P(output_byteoffset_v))
output_byteoffset = 0; /* dummy */
@@ -3555,15 +3555,15 @@ econv_primitive_convert(int argc, VALUE *argv, VALUE self)
else
output_bytesize = NUM2LONG(output_bytesize_v);
- if (NIL_P(opt)) {
- flags = 0;
- }
- else if (!NIL_P(flags_v = rb_check_to_integer(opt, "to_int"))) {
- flags = NUM2INT(flags_v);
+ if (!NIL_P(flags_v)) {
+ if (!NIL_P(opt)) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 2..5)",
+ argc + 1);
+ }
+ flags = NUM2INT(rb_to_int(flags_v));
}
- else {
+ else if (!NIL_P(opt)) {
VALUE v;
- opt = rb_convert_type(opt, T_HASH, "Hash", "to_hash");
flags = 0;
v = rb_hash_aref(opt, sym_partial_input);
if (RTEST(v))
@@ -3572,6 +3572,9 @@ econv_primitive_convert(int argc, VALUE *argv, VALUE self)
if (RTEST(v))
flags |= ECONV_AFTER_OUTPUT;
}
+ else {
+ flags = 0;
+ }
StringValue(output);
if (!NIL_P(input))