rational.c: read_num
* rational.c (read_num): use rb_int_parse_cstr to parse integer parts, and make String#to_r consistent with #to_i and #to_f. [ruby-core:80098] [Bug #13105] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@57989 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
92e2a7048e
commit
62fb6147cd
245
rational.c
245
rational.c
@ -2260,67 +2260,17 @@ issign(int c)
|
||||
}
|
||||
|
||||
static int
|
||||
read_sign(const char **s)
|
||||
read_sign(const char **s, const char *const e)
|
||||
{
|
||||
int sign = '?';
|
||||
|
||||
if (issign(**s)) {
|
||||
if (*s < e && issign(**s)) {
|
||||
sign = **s;
|
||||
(*s)++;
|
||||
}
|
||||
return sign;
|
||||
}
|
||||
|
||||
inline static int
|
||||
isdecimal(int c)
|
||||
{
|
||||
return isdigit((unsigned char)c);
|
||||
}
|
||||
|
||||
static int
|
||||
read_digits(const char **s, int strict,
|
||||
VALUE *num, int *count)
|
||||
{
|
||||
char *b, *bb;
|
||||
int us = 1, ret = 1;
|
||||
VALUE tmp;
|
||||
|
||||
if (!isdecimal(**s)) {
|
||||
*num = ZERO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bb = b = ALLOCV_N(char, tmp, strlen(*s) + 1);
|
||||
|
||||
while (isdecimal(**s) || **s == '_') {
|
||||
if (**s == '_') {
|
||||
if (strict) {
|
||||
if (us) {
|
||||
ret = 0;
|
||||
goto conv;
|
||||
}
|
||||
}
|
||||
us = 1;
|
||||
}
|
||||
else {
|
||||
if (count)
|
||||
(*count)++;
|
||||
*b++ = **s;
|
||||
us = 0;
|
||||
}
|
||||
(*s)++;
|
||||
}
|
||||
if (us)
|
||||
do {
|
||||
(*s)--;
|
||||
} while (**s == '_');
|
||||
conv:
|
||||
*b = '\0';
|
||||
*num = rb_cstr_to_inum(bb, 10, 0);
|
||||
ALLOCV_END(tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline static int
|
||||
islettere(int c)
|
||||
{
|
||||
@ -2328,37 +2278,51 @@ islettere(int c)
|
||||
}
|
||||
|
||||
static int
|
||||
read_num(const char **s, int strict, VALUE *num, VALUE *div)
|
||||
read_num(const char **s, const char *const end, VALUE *num, VALUE *div)
|
||||
{
|
||||
VALUE fp = ONE, exp, fn = ZERO;
|
||||
int expsign = 0;
|
||||
VALUE fp = ONE, exp, fn = ZERO, n;
|
||||
int expsign = 0, ok = 0;
|
||||
char *e;
|
||||
|
||||
*div = ONE;
|
||||
*num = ZERO;
|
||||
if (**s != '.') {
|
||||
if (!read_digits(s, strict, num, NULL))
|
||||
if (*s < end && **s != '.') {
|
||||
n = rb_int_parse_cstr(*s, end-*s, &e, NULL,
|
||||
10, RB_INT_PARSE_UNDERSCORE);
|
||||
if (NIL_P(n))
|
||||
return 0;
|
||||
*s = e;
|
||||
*num = n;
|
||||
ok = 1;
|
||||
}
|
||||
|
||||
if (**s == '.') {
|
||||
int count = 0;
|
||||
if (*s < end && **s == '.') {
|
||||
size_t count = 0;
|
||||
|
||||
(*s)++;
|
||||
if (!read_digits(s, strict, &fp, &count))
|
||||
return 0;
|
||||
fp = rb_int_parse_cstr(*s, end-*s, &e, &count,
|
||||
10, RB_INT_PARSE_UNDERSCORE);
|
||||
if (NIL_P(fp))
|
||||
return 1;
|
||||
*s = e;
|
||||
{
|
||||
VALUE l = f_expt10(INT2NUM(count));
|
||||
*num = *num == ZERO ? fp : rb_int_plus(rb_int_mul(*num, l), fp);
|
||||
VALUE l = f_expt10(SIZET2NUM(count));
|
||||
n = n == ZERO ? fp : rb_int_plus(rb_int_mul(*num, l), fp);
|
||||
*num = n;
|
||||
*div = l;
|
||||
fn = INT2NUM(count);
|
||||
fn = SIZET2NUM(count);
|
||||
}
|
||||
ok = 1;
|
||||
}
|
||||
|
||||
if (islettere(**s)) {
|
||||
if (ok && *s + 1 < end && islettere(**s)) {
|
||||
(*s)++;
|
||||
expsign = read_sign(s);
|
||||
if (!read_digits(s, strict, &exp, NULL))
|
||||
return 0;
|
||||
expsign = read_sign(s, end);
|
||||
exp = rb_int_parse_cstr(*s, end-*s, &e, NULL,
|
||||
10, RB_INT_PARSE_UNDERSCORE);
|
||||
if (NIL_P(exp))
|
||||
return 1;
|
||||
*s = e;
|
||||
if (exp != ZERO) {
|
||||
if (expsign == '-') {
|
||||
if (fn != ZERO) exp = rb_int_plus(exp, fn);
|
||||
@ -2370,109 +2334,88 @@ read_num(const char **s, int strict, VALUE *num, VALUE *div)
|
||||
*div = f_expt10(exp);
|
||||
}
|
||||
else {
|
||||
*num = rb_int_mul(*num, f_expt10(exp));
|
||||
*num = rb_int_mul(n, f_expt10(exp));
|
||||
*div = ONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int
|
||||
read_rat_nos(const char **s, int sign, int strict, VALUE *num)
|
||||
inline static const char *
|
||||
skip_ws(const char *s, const char *e)
|
||||
{
|
||||
VALUE den = ONE, div;
|
||||
while (s < e && isspace((unsigned char)*s))
|
||||
++s;
|
||||
return s;
|
||||
}
|
||||
|
||||
if (!read_num(s, strict, num, &div)) {
|
||||
failed:
|
||||
if (!canonicalization && !strict)
|
||||
*num = rb_rational_raw(*num, div);
|
||||
return 0;
|
||||
static VALUE
|
||||
parse_rat(const char *s, const char *const e, int strict)
|
||||
{
|
||||
int sign;
|
||||
VALUE num, den, ndiv;
|
||||
|
||||
s = skip_ws(s, e);
|
||||
sign = read_sign(&s, e);
|
||||
|
||||
if (!read_num(&s, e, &num, &ndiv)) {
|
||||
if (strict) return Qnil;
|
||||
return canonicalization ? ZERO : nurat_s_alloc(rb_cRational);
|
||||
}
|
||||
if (div != ONE) nurat_reduce(num, &div);
|
||||
den = div;
|
||||
if (**s == '/') {
|
||||
(*s)++;
|
||||
if (!read_digits(s, strict, &den, NULL)) goto failed;
|
||||
if (den == ZERO) rb_num_zerodiv();
|
||||
nurat_reduce(num, &den);
|
||||
if (div != ONE && den != ONE)
|
||||
den = rb_int_mul(den, div);
|
||||
nurat_reduce(&num, &ndiv);
|
||||
den = ndiv;
|
||||
if (s < e && *s == '/') {
|
||||
char *t;
|
||||
s++;
|
||||
den = rb_int_parse_cstr(s, e-s, &t, NULL,
|
||||
10, RB_INT_PARSE_UNDERSCORE);
|
||||
s = t;
|
||||
if (NIL_P(den)) {
|
||||
if (strict) return Qnil;
|
||||
den = ndiv;
|
||||
}
|
||||
else if (den == ZERO) {
|
||||
rb_num_zerodiv();
|
||||
}
|
||||
else if (strict && skip_ws(s, e) != e) {
|
||||
return Qnil;
|
||||
}
|
||||
else {
|
||||
nurat_reduce(&num, &den);
|
||||
den = rb_int_mul(den, ndiv);
|
||||
}
|
||||
}
|
||||
else if (strict && skip_ws(s, e) != e) {
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
if (sign == '-') {
|
||||
if (FIXNUM_P(*num)) {
|
||||
*num = rb_int_uminus(*num);
|
||||
if (FIXNUM_P(num)) {
|
||||
num = rb_int_uminus(num);
|
||||
}
|
||||
else {
|
||||
BIGNUM_NEGATE(*num);
|
||||
*num = rb_big_norm(*num);
|
||||
BIGNUM_NEGATE(num);
|
||||
num = rb_big_norm(num);
|
||||
}
|
||||
}
|
||||
|
||||
if (!canonicalization || den != ONE)
|
||||
*num = rb_rational_raw(*num, den);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
read_rat(const char **s, int strict,
|
||||
VALUE *num)
|
||||
{
|
||||
int sign;
|
||||
|
||||
sign = read_sign(s);
|
||||
if (!read_rat_nos(s, sign, strict, num))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline static void
|
||||
skip_ws(const char **s)
|
||||
{
|
||||
while (isspace((unsigned char)**s))
|
||||
(*s)++;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_rat(const char *s, int strict,
|
||||
VALUE *num)
|
||||
{
|
||||
skip_ws(&s);
|
||||
if (!read_rat(&s, strict, num))
|
||||
return 0;
|
||||
skip_ws(&s);
|
||||
|
||||
if (strict)
|
||||
if (*s != '\0')
|
||||
return 0;
|
||||
return 1;
|
||||
num = rb_rational_raw(num, den);
|
||||
return num;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
string_to_r_strict(VALUE self)
|
||||
{
|
||||
char *s;
|
||||
VALUE num;
|
||||
|
||||
rb_must_asciicompat(self);
|
||||
|
||||
s = RSTRING_PTR(self);
|
||||
|
||||
if (!s || memchr(s, '\0', RSTRING_LEN(self)))
|
||||
rb_raise(rb_eArgError, "string contains null byte");
|
||||
|
||||
if (s && s[RSTRING_LEN(self)]) {
|
||||
rb_str_modify(self);
|
||||
s = RSTRING_PTR(self);
|
||||
s[RSTRING_LEN(self)] = '\0';
|
||||
}
|
||||
|
||||
if (!s)
|
||||
s = (char *)"";
|
||||
|
||||
if (!parse_rat(s, 1, &num)) {
|
||||
num = parse_rat(RSTRING_PTR(self), RSTRING_END(self), 1);
|
||||
if (NIL_P(num)) {
|
||||
rb_raise(rb_eArgError, "invalid value for convert(): %+"PRIsVALUE,
|
||||
self);
|
||||
}
|
||||
@ -2508,23 +2451,11 @@ string_to_r_strict(VALUE self)
|
||||
static VALUE
|
||||
string_to_r(VALUE self)
|
||||
{
|
||||
char *s;
|
||||
VALUE num;
|
||||
|
||||
rb_must_asciicompat(self);
|
||||
|
||||
s = RSTRING_PTR(self);
|
||||
|
||||
if (s && s[RSTRING_LEN(self)]) {
|
||||
rb_str_modify(self);
|
||||
s = RSTRING_PTR(self);
|
||||
s[RSTRING_LEN(self)] = '\0';
|
||||
}
|
||||
|
||||
if (!s)
|
||||
s = (char *)"";
|
||||
|
||||
(void)parse_rat(s, 0, &num);
|
||||
num = parse_rat(RSTRING_PTR(self), RSTRING_END(self), 0);
|
||||
|
||||
if (RB_FLOAT_TYPE_P(num))
|
||||
rb_raise(rb_eFloatDomainError, "Infinity");
|
||||
@ -2536,7 +2467,7 @@ rb_cstr_to_rat(const char *s, int strict) /* for complex's internal */
|
||||
{
|
||||
VALUE num;
|
||||
|
||||
(void)parse_rat(s, strict, &num);
|
||||
num = parse_rat(s, s + strlen(s), strict);
|
||||
|
||||
if (RB_FLOAT_TYPE_P(num))
|
||||
rb_raise(rb_eFloatDomainError, "Infinity");
|
||||
|
@ -695,27 +695,43 @@ class Rational_Test < Test::Unit::TestCase
|
||||
ok[-5, 1, '-5']
|
||||
ok[ 5, 3, '5/3']
|
||||
ok[-5, 3, '-5/3']
|
||||
ok[ 5, 3, '5_5/33']
|
||||
ok[ 5,33, '5/3_3']
|
||||
ng[ 5, 1, '5__5/33']
|
||||
ng[ 5, 3, '5/3__3']
|
||||
|
||||
ok[ 5, 1, '5.0']
|
||||
ok[-5, 1, '-5.0']
|
||||
ok[ 5, 3, '5.0/3']
|
||||
ok[-5, 3, '-5.0/3']
|
||||
ok[ 501,100, '5.0_1']
|
||||
ok[ 501,300, '5.0_1/3']
|
||||
ok[ 5,33, '5.0/3_3']
|
||||
ng[ 5, 1, '5.0__1/3']
|
||||
ng[ 5, 3, '5.0/3__3']
|
||||
|
||||
ok[ 5, 1, '5e0']
|
||||
ok[-5, 1, '-5e0']
|
||||
ok[ 5, 3, '5e0/3']
|
||||
ok[-5, 3, '-5e0/3']
|
||||
ok[550, 1, '5_5e1']
|
||||
ng[ 5, 1, '5_e1']
|
||||
|
||||
ok[ 5e1, 1, '5e1']
|
||||
ok[-5e2, 1, '-5e2']
|
||||
ok[ 5e3, 3, '5e003/3']
|
||||
ok[-5e4, 3, '-5e004/3']
|
||||
ok[ 5e3, 1, '5e0_3']
|
||||
ok[ 5e1,33, '5e1/3_3']
|
||||
ng[ 5e0, 1, '5e0__3/3']
|
||||
ng[ 5e1, 3, '5e1/3__3']
|
||||
|
||||
ok[ 33, 100, '.33']
|
||||
ok[ 33, 100, '0.33']
|
||||
ok[-33, 100, '-.33']
|
||||
ok[-33, 100, '-0.33']
|
||||
ok[-33, 100, '-0.3_3']
|
||||
ng[ -3, 10, '-0.3__3']
|
||||
|
||||
ok[ 1, 2, '5e-1']
|
||||
ok[50, 1, '5e+1']
|
||||
|
Loading…
x
Reference in New Issue
Block a user