summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormrkn <mrkn@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-03-15 07:19:43 +0000
committermrkn <mrkn@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-03-15 07:19:43 +0000
commit2cfc5b03dac80a92de2dc7a17be4b3cfff92adf7 (patch)
tree2f73938d92adf528407d46046d28b961f2d16aab
parent4691daccc156f290ca09effdd2558cd6dd0cec70 (diff)
Add `exception:` keyword in Kernel#Integer()
Support `exception:` keyword argument in Kernel#Integer(). If `exception:` is `false`, `Kernel#Integer()` returns `nil` if the given value cannot be interpreted as an integer value. The default value of `exception:` is `true`. This is part of [Feature #12732]. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62757 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--bignum.c15
-rw-r--r--internal.h1
-rw-r--r--object.c83
-rw-r--r--test/ruby/test_integer.rb34
4 files changed, 103 insertions, 30 deletions
diff --git a/bignum.c b/bignum.c
index 6c40c778de..b4c7560034 100644
--- a/bignum.c
+++ b/bignum.c
@@ -4230,7 +4230,7 @@ rb_cstr_parse_inum(const char *str, ssize_t len, char **endp, int base)
}
VALUE
-rb_str_to_inum(VALUE str, int base, int badcheck)
+rb_str_convert_to_inum(VALUE str, int base, int badcheck, int raise_exception)
{
VALUE ret;
const char *s;
@@ -4242,13 +4242,22 @@ rb_str_to_inum(VALUE str, int base, int badcheck)
RSTRING_GETMEM(str, s, len);
ret = rb_cstr_parse_inum(s, len, (badcheck ? NULL : &end), base);
if (NIL_P(ret)) {
- if (badcheck) invalid_integer(str);
- ret = INT2FIX(0);
+ if (badcheck) {
+ if (!raise_exception) return Qnil;
+ invalid_integer(str);
+ }
+ ret = INT2FIX(0);
}
return ret;
}
VALUE
+rb_str_to_inum(VALUE str, int base, int badcheck)
+{
+ return rb_str_convert_to_inum(str, base, badcheck, TRUE);
+}
+
+VALUE
rb_str2big_poweroftwo(VALUE arg, int base, int badcheck)
{
int positive_p = 1;
diff --git a/internal.h b/internal.h
index ff7a89a2e4..9b6a213151 100644
--- a/internal.h
+++ b/internal.h
@@ -1101,6 +1101,7 @@ size_t rb_big_size(VALUE);
VALUE rb_integer_float_cmp(VALUE x, VALUE y);
VALUE rb_integer_float_eq(VALUE x, VALUE y);
VALUE rb_cstr_parse_inum(const char *str, ssize_t len, char **endp, int base);
+VALUE rb_str_convert_to_inum(VALUE str, int base, int badcheck, int raise_exception);
VALUE rb_big_comp(VALUE x);
VALUE rb_big_aref(VALUE x, VALUE y);
VALUE rb_big_abs(VALUE x);
diff --git a/object.c b/object.c
index 59a8131bdb..7a730ef132 100644
--- a/object.c
+++ b/object.c
@@ -3108,40 +3108,57 @@ rb_check_to_int(VALUE val)
}
static VALUE
-rb_convert_to_integer(VALUE val, int base)
+rb_check_to_i(VALUE val)
+{
+ if (RB_INTEGER_TYPE_P(val)) return val;
+ val = try_to_int(val, idTo_i, FALSE);
+ if (RB_INTEGER_TYPE_P(val)) return val;
+ return Qnil;
+}
+
+static VALUE
+rb_convert_to_integer(VALUE val, int base, int raise_exception)
{
VALUE tmp;
if (RB_FLOAT_TYPE_P(val)) {
- double f;
- if (base != 0) goto arg_error;
- f = RFLOAT_VALUE(val);
- if (FIXABLE(f)) return LONG2FIX((long)f);
- return rb_dbl2big(f);
+ double f;
+ if (base != 0) goto arg_error;
+ f = RFLOAT_VALUE(val);
+ if (FIXABLE(f)) return LONG2FIX((long)f);
+ return rb_dbl2big(f);
}
else if (RB_INTEGER_TYPE_P(val)) {
- if (base != 0) goto arg_error;
- return val;
+ if (base != 0) goto arg_error;
+ return val;
}
else if (RB_TYPE_P(val, T_STRING)) {
- return rb_str_to_inum(val, base, TRUE);
+ return rb_str_convert_to_inum(val, base, TRUE, raise_exception);
}
else if (NIL_P(val)) {
- if (base != 0) goto arg_error;
- rb_raise(rb_eTypeError, "can't convert nil into Integer");
+ if (base != 0) goto arg_error;
+ if (!raise_exception) return Qnil;
+ rb_raise(rb_eTypeError, "can't convert nil into Integer");
}
if (base != 0) {
- tmp = rb_check_string_type(val);
- if (!NIL_P(tmp)) return rb_str_to_inum(tmp, base, TRUE);
+ tmp = rb_check_string_type(val);
+ if (!NIL_P(tmp)) return rb_str_convert_to_inum(tmp, base, TRUE, raise_exception);
arg_error:
- rb_raise(rb_eArgError, "base specified for non string value");
+ if (!raise_exception) return Qnil;
+ rb_raise(rb_eArgError, "base specified for non string value");
}
- tmp = convert_type_with_id(val, "Integer", idTo_int, FALSE, -1);
- if (!RB_INTEGER_TYPE_P(tmp)) {
- return rb_to_integer(val, "to_i", idTo_i);
+
+ tmp = rb_protect(rb_check_to_int, val, NULL);
+ if (RB_INTEGER_TYPE_P(tmp)) return tmp;
+ rb_set_errinfo(Qnil);
+
+ if (!raise_exception) {
+ VALUE result = rb_protect(rb_check_to_i, val, NULL);
+ rb_set_errinfo(Qnil);
+ return result;
}
- return tmp;
+ return rb_to_integer(val, "to_i", idTo_i);
}
/**
@@ -3153,7 +3170,19 @@ rb_convert_to_integer(VALUE val, int base)
VALUE
rb_Integer(VALUE val)
{
- return rb_convert_to_integer(val, 0);
+ return rb_convert_to_integer(val, 0, TRUE);
+}
+
+static int
+opts_exception_p(VALUE opts)
+{
+ static ID kwds[1];
+ VALUE exception;
+ if (!kwds[0]) {
+ kwds[0] = rb_intern_const("exception");
+ }
+ rb_get_kwargs(opts, kwds, 0, 1, &exception);
+ return exception != Qfalse;
}
/*
@@ -3183,20 +3212,20 @@ rb_Integer(VALUE val)
static VALUE
rb_f_integer(int argc, VALUE *argv, VALUE obj)
{
- VALUE arg = Qnil;
+ VALUE arg = Qnil, opts = Qnil;
int base = 0;
- switch (argc) {
+ switch (rb_scan_args(argc, argv, "11:", NULL, NULL, &opts)) {
case 2:
- base = NUM2INT(argv[1]);
+ base = NUM2INT(argv[1]);
case 1:
- arg = argv[0];
- break;
+ arg = argv[0];
+ break;
default:
- /* should cause ArgumentError */
- rb_scan_args(argc, argv, "11", NULL, NULL);
+ UNREACHABLE;
}
- return rb_convert_to_integer(arg, base);
+
+ return rb_convert_to_integer(arg, base, opts_exception_p(opts));
}
/*!
diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb
index 92ad38d39a..0b9aa4191d 100644
--- a/test/ruby/test_integer.rb
+++ b/test/ruby/test_integer.rb
@@ -127,6 +127,40 @@ class TestInteger < Test::Unit::TestCase
end;
end
+ def test_Integer_with_exception_keyword
+ assert_nothing_raised(ArgumentError) {
+ assert_equal(nil, Integer("1z", exception: false))
+ }
+ assert_nothing_raised(ArgumentError) {
+ assert_equal(nil, Integer(Object.new, exception: false))
+ }
+ assert_nothing_raised(ArgumentError) {
+ o = Object.new
+ def o.to_i; 42.5; end
+ assert_equal(nil, Integer(o, exception: false))
+ }
+ assert_nothing_raised(ArgumentError) {
+ o = Object.new
+ def o.to_i; raise; end
+ assert_equal(nil, Integer(o, exception: false))
+ }
+ assert_nothing_raised(ArgumentError) {
+ o = Object.new
+ def o.to_int; raise; end
+ assert_equal(nil, Integer(o, exception: false))
+ }
+
+ assert_raise(ArgumentError) {
+ Integer("1z", exception: true)
+ }
+ assert_raise(TypeError) {
+ Integer(nil, exception: true)
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, Integer(nil, exception: false))
+ }
+ end
+
def test_int_p
assert_not_predicate(1.0, :integer?)
assert_predicate(1, :integer?)