1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* 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
This commit is contained in:
knu 2010-09-10 07:51:58 +00:00
parent 4f77c495cd
commit 82abe79b9f
8 changed files with 130 additions and 83 deletions

View file

@ -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

View file

@ -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

View file

@ -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メソッド呼び出し

29
class.c
View file

@ -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 *);

3
dir.c
View file

@ -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;

View file

@ -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);

72
io.c
View file

@ -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;

View file

@ -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;
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 if (!NIL_P(flags_v = rb_check_to_integer(opt, "to_int"))) {
flags = NUM2INT(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))