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
|
static int
|
||||||
read_sign(const char **s)
|
read_sign(const char **s, const char *const e)
|
||||||
{
|
{
|
||||||
int sign = '?';
|
int sign = '?';
|
||||||
|
|
||||||
if (issign(**s)) {
|
if (*s < e && issign(**s)) {
|
||||||
sign = **s;
|
sign = **s;
|
||||||
(*s)++;
|
(*s)++;
|
||||||
}
|
}
|
||||||
return sign;
|
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
|
inline static int
|
||||||
islettere(int c)
|
islettere(int c)
|
||||||
{
|
{
|
||||||
@ -2328,37 +2278,51 @@ islettere(int c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
VALUE fp = ONE, exp, fn = ZERO, n;
|
||||||
int expsign = 0;
|
int expsign = 0, ok = 0;
|
||||||
|
char *e;
|
||||||
|
|
||||||
*div = ONE;
|
*div = ONE;
|
||||||
*num = ZERO;
|
*num = ZERO;
|
||||||
if (**s != '.') {
|
if (*s < end && **s != '.') {
|
||||||
if (!read_digits(s, strict, num, NULL))
|
n = rb_int_parse_cstr(*s, end-*s, &e, NULL,
|
||||||
|
10, RB_INT_PARSE_UNDERSCORE);
|
||||||
|
if (NIL_P(n))
|
||||||
return 0;
|
return 0;
|
||||||
|
*s = e;
|
||||||
|
*num = n;
|
||||||
|
ok = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (**s == '.') {
|
if (*s < end && **s == '.') {
|
||||||
int count = 0;
|
size_t count = 0;
|
||||||
|
|
||||||
(*s)++;
|
(*s)++;
|
||||||
if (!read_digits(s, strict, &fp, &count))
|
fp = rb_int_parse_cstr(*s, end-*s, &e, &count,
|
||||||
return 0;
|
10, RB_INT_PARSE_UNDERSCORE);
|
||||||
|
if (NIL_P(fp))
|
||||||
|
return 1;
|
||||||
|
*s = e;
|
||||||
{
|
{
|
||||||
VALUE l = f_expt10(INT2NUM(count));
|
VALUE l = f_expt10(SIZET2NUM(count));
|
||||||
*num = *num == ZERO ? fp : rb_int_plus(rb_int_mul(*num, l), fp);
|
n = n == ZERO ? fp : rb_int_plus(rb_int_mul(*num, l), fp);
|
||||||
|
*num = n;
|
||||||
*div = l;
|
*div = l;
|
||||||
fn = INT2NUM(count);
|
fn = SIZET2NUM(count);
|
||||||
}
|
}
|
||||||
|
ok = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (islettere(**s)) {
|
if (ok && *s + 1 < end && islettere(**s)) {
|
||||||
(*s)++;
|
(*s)++;
|
||||||
expsign = read_sign(s);
|
expsign = read_sign(s, end);
|
||||||
if (!read_digits(s, strict, &exp, NULL))
|
exp = rb_int_parse_cstr(*s, end-*s, &e, NULL,
|
||||||
return 0;
|
10, RB_INT_PARSE_UNDERSCORE);
|
||||||
|
if (NIL_P(exp))
|
||||||
|
return 1;
|
||||||
|
*s = e;
|
||||||
if (exp != ZERO) {
|
if (exp != ZERO) {
|
||||||
if (expsign == '-') {
|
if (expsign == '-') {
|
||||||
if (fn != ZERO) exp = rb_int_plus(exp, fn);
|
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);
|
*div = f_expt10(exp);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
*num = rb_int_mul(*num, f_expt10(exp));
|
*num = rb_int_mul(n, f_expt10(exp));
|
||||||
*div = ONE;
|
*div = ONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
inline static const char *
|
||||||
read_rat_nos(const char **s, int sign, int strict, VALUE *num)
|
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)) {
|
static VALUE
|
||||||
failed:
|
parse_rat(const char *s, const char *const e, int strict)
|
||||||
if (!canonicalization && !strict)
|
{
|
||||||
*num = rb_rational_raw(*num, div);
|
int sign;
|
||||||
return 0;
|
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);
|
nurat_reduce(&num, &ndiv);
|
||||||
den = div;
|
den = ndiv;
|
||||||
if (**s == '/') {
|
if (s < e && *s == '/') {
|
||||||
(*s)++;
|
char *t;
|
||||||
if (!read_digits(s, strict, &den, NULL)) goto failed;
|
s++;
|
||||||
if (den == ZERO) rb_num_zerodiv();
|
den = rb_int_parse_cstr(s, e-s, &t, NULL,
|
||||||
nurat_reduce(num, &den);
|
10, RB_INT_PARSE_UNDERSCORE);
|
||||||
if (div != ONE && den != ONE)
|
s = t;
|
||||||
den = rb_int_mul(den, div);
|
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 (sign == '-') {
|
||||||
if (FIXNUM_P(*num)) {
|
if (FIXNUM_P(num)) {
|
||||||
*num = rb_int_uminus(*num);
|
num = rb_int_uminus(num);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
BIGNUM_NEGATE(*num);
|
BIGNUM_NEGATE(num);
|
||||||
*num = rb_big_norm(*num);
|
num = rb_big_norm(num);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!canonicalization || den != ONE)
|
if (!canonicalization || den != ONE)
|
||||||
*num = rb_rational_raw(*num, den);
|
num = rb_rational_raw(num, den);
|
||||||
return 1;
|
return num;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
string_to_r_strict(VALUE self)
|
string_to_r_strict(VALUE self)
|
||||||
{
|
{
|
||||||
char *s;
|
|
||||||
VALUE num;
|
VALUE num;
|
||||||
|
|
||||||
rb_must_asciicompat(self);
|
rb_must_asciicompat(self);
|
||||||
|
|
||||||
s = RSTRING_PTR(self);
|
num = parse_rat(RSTRING_PTR(self), RSTRING_END(self), 1);
|
||||||
|
if (NIL_P(num)) {
|
||||||
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)) {
|
|
||||||
rb_raise(rb_eArgError, "invalid value for convert(): %+"PRIsVALUE,
|
rb_raise(rb_eArgError, "invalid value for convert(): %+"PRIsVALUE,
|
||||||
self);
|
self);
|
||||||
}
|
}
|
||||||
@ -2508,23 +2451,11 @@ string_to_r_strict(VALUE self)
|
|||||||
static VALUE
|
static VALUE
|
||||||
string_to_r(VALUE self)
|
string_to_r(VALUE self)
|
||||||
{
|
{
|
||||||
char *s;
|
|
||||||
VALUE num;
|
VALUE num;
|
||||||
|
|
||||||
rb_must_asciicompat(self);
|
rb_must_asciicompat(self);
|
||||||
|
|
||||||
s = RSTRING_PTR(self);
|
num = parse_rat(RSTRING_PTR(self), RSTRING_END(self), 0);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (RB_FLOAT_TYPE_P(num))
|
if (RB_FLOAT_TYPE_P(num))
|
||||||
rb_raise(rb_eFloatDomainError, "Infinity");
|
rb_raise(rb_eFloatDomainError, "Infinity");
|
||||||
@ -2536,7 +2467,7 @@ rb_cstr_to_rat(const char *s, int strict) /* for complex's internal */
|
|||||||
{
|
{
|
||||||
VALUE num;
|
VALUE num;
|
||||||
|
|
||||||
(void)parse_rat(s, strict, &num);
|
num = parse_rat(s, s + strlen(s), strict);
|
||||||
|
|
||||||
if (RB_FLOAT_TYPE_P(num))
|
if (RB_FLOAT_TYPE_P(num))
|
||||||
rb_raise(rb_eFloatDomainError, "Infinity");
|
rb_raise(rb_eFloatDomainError, "Infinity");
|
||||||
|
@ -695,27 +695,43 @@ class Rational_Test < Test::Unit::TestCase
|
|||||||
ok[-5, 1, '-5']
|
ok[-5, 1, '-5']
|
||||||
ok[ 5, 3, '5/3']
|
ok[ 5, 3, '5/3']
|
||||||
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, 1, '-5.0']
|
ok[-5, 1, '-5.0']
|
||||||
ok[ 5, 3, '5.0/3']
|
ok[ 5, 3, '5.0/3']
|
||||||
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, 1, '-5e0']
|
ok[-5, 1, '-5e0']
|
||||||
ok[ 5, 3, '5e0/3']
|
ok[ 5, 3, '5e0/3']
|
||||||
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[ 5e1, 1, '5e1']
|
||||||
ok[-5e2, 1, '-5e2']
|
ok[-5e2, 1, '-5e2']
|
||||||
ok[ 5e3, 3, '5e003/3']
|
ok[ 5e3, 3, '5e003/3']
|
||||||
ok[-5e4, 3, '-5e004/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, '.33']
|
||||||
ok[ 33, 100, '0.33']
|
ok[ 33, 100, '0.33']
|
||||||
ok[-33, 100, '-.33']
|
ok[-33, 100, '-.33']
|
||||||
ok[-33, 100, '-0.33']
|
ok[-33, 100, '-0.33']
|
||||||
ok[-33, 100, '-0.3_3']
|
ok[-33, 100, '-0.3_3']
|
||||||
|
ng[ -3, 10, '-0.3__3']
|
||||||
|
|
||||||
ok[ 1, 2, '5e-1']
|
ok[ 1, 2, '5e-1']
|
||||||
ok[50, 1, '5e+1']
|
ok[50, 1, '5e+1']
|
||||||
|
Loading…
x
Reference in New Issue
Block a user