summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rational.c245
-rw-r--r--test/ruby/test_rational.rb16
2 files changed, 104 insertions, 157 deletions
diff --git a/rational.c b/rational.c
index 742f594f65..53e136ff13 100644
--- a/rational.c
+++ b/rational.c
@@ -2260,11 +2260,11 @@ 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)++;
}
@@ -2272,93 +2272,57 @@ read_sign(const char **s)
}
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)
{
return (c == 'e' || c == 'E');
}
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);
+ }
+ 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);
+ }
}
- 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);
+ 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;
+ if (!canonicalization || den != ONE)
+ 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");
diff --git a/test/ruby/test_rational.rb b/test/ruby/test_rational.rb
index ff2d189eff..43bd750615 100644
--- a/test/ruby/test_rational.rb
+++ b/test/ruby/test_rational.rb
@@ -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']