Add exception: keyword in Kernel#Complex()

Support `exception:` keyword argument in `Kernel#Complex()`.
If `exception:` is `false`, `Kernel#Complex()` returns `nil` if the given
value cannot be interpreted as a complex 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@62760 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
mrkn 2018-03-15 07:19:48 +00:00
parent 0dc74b94c2
commit c6ab349855
2 changed files with 90 additions and 22 deletions

View File

@ -390,6 +390,7 @@ f_complex_new2(VALUE klass, VALUE x, VALUE y)
return nucomp_s_canonicalize_internal(klass, x, y); return nucomp_s_canonicalize_internal(klass, x, y);
} }
static VALUE nucomp_convert(VALUE klass, VALUE a1, VALUE a2, int raise);
static VALUE nucomp_s_convert(int argc, VALUE *argv, VALUE klass); static VALUE nucomp_s_convert(int argc, VALUE *argv, VALUE klass);
/* /*
@ -428,7 +429,22 @@ static VALUE nucomp_s_convert(int argc, VALUE *argv, VALUE klass);
static VALUE static VALUE
nucomp_f_complex(int argc, VALUE *argv, VALUE klass) nucomp_f_complex(int argc, VALUE *argv, VALUE klass)
{ {
return nucomp_s_convert(argc, argv, rb_cComplex); VALUE a1, a2, opts = Qnil;
int raise = TRUE;
if (rb_scan_args(argc, argv, "11:", &a1, &a2, &opts) == 1) {
a2 = Qundef;
}
if (!NIL_P(opts)) {
static ID kwds[1];
VALUE exception;
if (!kwds[0]) {
kwds[0] = rb_intern_const("exception");
}
rb_get_kwargs(opts, kwds, 0, 1, &exception);
raise = (exception != Qfalse);
}
return nucomp_convert(rb_cComplex, a1, a2, raise);
} }
#define imp1(n) \ #define imp1(n) \
@ -1735,8 +1751,7 @@ skip_ws(const char **s)
} }
static int static int
parse_comp(const char *s, int strict, parse_comp(const char *s, int strict, VALUE *num)
VALUE *num)
{ {
char *buf, *b; char *buf, *b;
VALUE tmp; VALUE tmp;
@ -1747,14 +1762,14 @@ parse_comp(const char *s, int strict,
skip_ws(&s); skip_ws(&s);
if (!read_comp(&s, strict, num, &b)) { if (!read_comp(&s, strict, num, &b)) {
ret = 0; ret = 0;
} }
else { else {
skip_ws(&s); skip_ws(&s);
if (strict) if (strict)
if (*s != '\0') if (*s != '\0')
ret = 0; ret = 0;
} }
ALLOCV_END(tmp); ALLOCV_END(tmp);
@ -1762,7 +1777,7 @@ parse_comp(const char *s, int strict,
} }
static VALUE static VALUE
string_to_c_strict(VALUE self) string_to_c_strict(VALUE self, int raise)
{ {
char *s; char *s;
VALUE num; VALUE num;
@ -1771,8 +1786,10 @@ string_to_c_strict(VALUE self)
s = RSTRING_PTR(self); s = RSTRING_PTR(self);
if (!s || memchr(s, '\0', RSTRING_LEN(self))) if (!s || memchr(s, '\0', RSTRING_LEN(self))) {
if (!raise) return Qnil;
rb_raise(rb_eArgError, "string contains null byte"); rb_raise(rb_eArgError, "string contains null byte");
}
if (s && s[RSTRING_LEN(self)]) { if (s && s[RSTRING_LEN(self)]) {
rb_str_modify(self); rb_str_modify(self);
@ -1784,6 +1801,7 @@ string_to_c_strict(VALUE self)
s = (char *)""; s = (char *)"";
if (!parse_comp(s, 1, &num)) { if (!parse_comp(s, 1, &num)) {
if (!raise) return Qnil;
rb_raise(rb_eArgError, "invalid value for convert(): %+"PRIsVALUE, rb_raise(rb_eArgError, "invalid value for convert(): %+"PRIsVALUE,
self); self);
} }
@ -1839,21 +1857,25 @@ string_to_c(VALUE self)
} }
static VALUE static VALUE
nucomp_s_convert(int argc, VALUE *argv, VALUE klass) to_complex(VALUE val)
{ {
VALUE a1, a2; return rb_convert_type(val, T_COMPLEX, "Complex", "to_c");
}
rb_scan_args(argc, argv, "11", &a1, &a2); static VALUE
nucomp_convert(VALUE klass, VALUE a1, VALUE a2, int raise)
if (NIL_P(a1) || (argc == 2 && NIL_P(a2))) {
if (NIL_P(a1) || NIL_P(a2))
rb_raise(rb_eTypeError, "can't convert nil into Complex"); rb_raise(rb_eTypeError, "can't convert nil into Complex");
if (RB_TYPE_P(a1, T_STRING)) { if (RB_TYPE_P(a1, T_STRING)) {
a1 = string_to_c_strict(a1); a1 = string_to_c_strict(a1, raise);
if (NIL_P(a1)) return Qnil;
} }
if (RB_TYPE_P(a2, T_STRING)) { if (RB_TYPE_P(a2, T_STRING)) {
a2 = string_to_c_strict(a2); a2 = string_to_c_strict(a2, raise);
if (NIL_P(a2)) return Qnil;
} }
if (RB_TYPE_P(a1, T_COMPLEX)) { if (RB_TYPE_P(a1, T_COMPLEX)) {
@ -1875,16 +1897,19 @@ nucomp_s_convert(int argc, VALUE *argv, VALUE klass)
} }
if (RB_TYPE_P(a1, T_COMPLEX)) { if (RB_TYPE_P(a1, T_COMPLEX)) {
if (argc == 1 || (k_exact_zero_p(a2))) if (a2 == Qundef || (k_exact_zero_p(a2)))
return a1; return a1;
} }
if (argc == 1) { if (a2 == Qundef) {
if (k_numeric_p(a1) && !f_real_p(a1)) if (k_numeric_p(a1) && !f_real_p(a1))
return a1; return a1;
/* should raise exception for consistency */ /* should raise exception for consistency */
if (!k_numeric_p(a1)) if (!k_numeric_p(a1)) {
return rb_convert_type(a1, T_COMPLEX, "Complex", "to_c"); if (!raise)
return rb_protect(to_complex, a1, NULL);
return to_complex(a1);
}
} }
else { else {
if ((k_numeric_p(a1) && k_numeric_p(a2)) && if ((k_numeric_p(a1) && k_numeric_p(a2)) &&
@ -1895,13 +1920,35 @@ nucomp_s_convert(int argc, VALUE *argv, VALUE klass)
} }
{ {
int argc;
VALUE argv2[2]; VALUE argv2[2];
argv2[0] = a1; argv2[0] = a1;
argv2[1] = a2; if (a2 == Qundef) {
argv2[1] = Qnil;
argc = 1;
}
else {
if (!raise && !RB_INTEGER_TYPE_P(a2) && !RB_FLOAT_TYPE_P(a2) && !RB_TYPE_P(a2, T_RATIONAL))
return Qnil;
argv2[1] = a2;
argc = 2;
}
return nucomp_s_new(argc, argv2, klass); return nucomp_s_new(argc, argv2, klass);
} }
} }
static VALUE
nucomp_s_convert(int argc, VALUE *argv, VALUE klass)
{
VALUE a1, a2;
if (rb_scan_args(argc, argv, "11", &a1, &a2) == 1) {
a2 = Qundef;
}
return nucomp_convert(klass, a1, a2, TRUE);
}
/* --- */ /* --- */
/* /*

View File

@ -746,6 +746,27 @@ class Complex_Test < Test::Unit::TestCase
end end
def test_Complex_without_exception
assert_nothing_raised(ArgumentError){
assert_equal(nil, Complex('5x', exception: false))
}
assert_nothing_raised(ArgumentError){
assert_equal(nil, Complex(Object.new, exception: false))
}
assert_nothing_raised(ArgumentError){
assert_equal(nil, Complex(1, Object.new, exception: false))
}
o = Object.new
def o.to_c; raise; end
assert_nothing_raised(ArgumentError){
assert_equal(nil, Complex(o, exception: false))
}
assert_nothing_raised(ArgumentError){
assert_equal(nil, Complex(1, o, exception: false))
}
end
def test_respond def test_respond
c = Complex(1,1) c = Complex(1,1)
assert_not_respond_to(c, :%) assert_not_respond_to(c, :%)