diff --git a/ChangeLog b/ChangeLog index 75ee2564be..db9ecafbfe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Sun Nov 18 00:14:46 2012 Tadayoshi Funaba + + * complex.c (string_to_c_strict, string_to_c): rewrote without regexp. + * rational.c (string_to_r_strict, string_to_r): ditto. + Sat Nov 17 23:53:05 2012 Tadayoshi Funaba * complex.c (make_patterns): should not accept extra sign. diff --git a/complex.c b/complex.c index d515e59061..5cebf96522 100644 --- a/complex.c +++ b/complex.c @@ -1496,159 +1496,263 @@ numeric_to_c(VALUE self) return rb_complex_new1(self); } -static VALUE comp_pat0, comp_pat1, comp_pat2, a_slash, a_dot_and_an_e, - null_string, underscores_pat, an_underscore; +#include -#define WS "\\s*" -#define DIGITS "(?:[0-9](?:_[0-9]|[0-9])*)" -#define NUMERATOR "(?:" DIGITS "?\\.)?" DIGITS "(?:[eE][-+]?" DIGITS ")?" -#define DENOMINATOR DIGITS -#define NUMBER "[-+]?" NUMERATOR "(?:\\/" DENOMINATOR ")?" -#define NUMBERNOS NUMERATOR "(?:\\/" DENOMINATOR ")?" -#define PATTERN0 "\\A" WS "(" NUMBER ")@(" NUMBER ")" WS -#define PATTERN1 "\\A" WS "([-+])?(" NUMBERNOS ")?[iIjJ]" WS -#define PATTERN2 "\\A" WS "(" NUMBER ")(([-+])(" NUMBERNOS ")?[iIjJ])?" WS - -static void -make_patterns(void) +static int +read_sign(const char **s, + char **b) { - static const char comp_pat0_source[] = PATTERN0; - static const char comp_pat1_source[] = PATTERN1; - static const char comp_pat2_source[] = PATTERN2; - static const char underscores_pat_source[] = "_+"; + int sign = '?'; - if (comp_pat0) return; - - comp_pat0 = rb_reg_new(comp_pat0_source, sizeof comp_pat0_source - 1, 0); - rb_gc_register_mark_object(comp_pat0); - - comp_pat1 = rb_reg_new(comp_pat1_source, sizeof comp_pat1_source - 1, 0); - rb_gc_register_mark_object(comp_pat1); - - comp_pat2 = rb_reg_new(comp_pat2_source, sizeof comp_pat2_source - 1, 0); - rb_gc_register_mark_object(comp_pat2); - - a_slash = rb_usascii_str_new2("/"); - rb_gc_register_mark_object(a_slash); - - a_dot_and_an_e = rb_usascii_str_new2(".eE"); - rb_gc_register_mark_object(a_dot_and_an_e); - - null_string = rb_usascii_str_new2(""); - rb_gc_register_mark_object(null_string); - - underscores_pat = rb_reg_new(underscores_pat_source, - sizeof underscores_pat_source - 1, 0); - rb_gc_register_mark_object(underscores_pat); - - an_underscore = rb_usascii_str_new2("_"); - rb_gc_register_mark_object(an_underscore); + if (**s == '-' || **s == '+') { + sign = **b = **s; + (*s)++; + (*b)++; + } + return sign; } -#define id_match rb_intern("match") -#define f_match(x,y) rb_funcall((x), id_match, 1, (y)) +static int +read_digits(const char **s, int strict, + char **b) +{ + int us = 1; -#define id_gsub_bang rb_intern("gsub!") -#define f_gsub_bang(x,y,z) rb_funcall((x), id_gsub_bang, 2, (y), (z)) + if (!isdigit((unsigned char)**s)) + return 0; + + while (isdigit((unsigned char)**s) || **s == '_') { + if (**s == '_') { + if (strict) { + if (us) + return 0; + } + us = 1; + } + else { + **b = **s; + (*b)++; + us = 0; + } + (*s)++; + } + if (us) + do { + (*s)--; + } while (**s == '_'); + return 1; +} + +static int +read_num(const char **s, int strict, + char **b) +{ + if (**s != '.') { + if (!read_digits(s, strict, b)) + return 0; + } + + if (**s == '.') { + **b = **s; + (*s)++; + (*b)++; + if (!read_digits(s, strict, b)) { + (*b)--; + return 0; + } + } + + if (**s == 'e' || **s == 'E') { + **b = **s; + (*s)++; + (*b)++; + read_sign(s, b); + if (!read_digits(s, strict, b)) { + (*b)--; + return 0; + } + } + return 1; +} + +static int +read_den(const char **s, int strict, + char **b) +{ + if (!read_digits(s, strict, b)) + return 0; + return 1; +} + +static int +read_rat_nos(const char **s, int strict, + char **b) +{ + if (!read_num(s, strict, b)) + return 0; + if (**s == '/') { + **b = **s; + (*s)++; + (*b)++; + if (!read_den(s, strict, b)) { + (*b)--; + return 0; + } + } + return 1; +} + +static int +read_rat(const char **s, int strict, + char **b) +{ + read_sign(s, b); + if (!read_rat_nos(s, strict, b)) + return 0; + return 1; +} + +static int +isimagunit(int c) +{ + return (c == 'i' || c == 'I' || + c == 'j' || c == 'J'); +} + +VALUE rb_cstr_to_rat(const char *, int); static VALUE -string_to_c_internal(VALUE self) +str2num(char *s) { - VALUE s; - - s = self; - - if (RSTRING_LEN(s) == 0) - return rb_assoc_new(Qnil, self); - - { - VALUE m, sr, si, re, r, i; - int po; - - m = f_match(comp_pat0, s); - if (!NIL_P(m)) { - sr = rb_reg_nth_match(1, m); - si = rb_reg_nth_match(2, m); - re = rb_reg_match_post(m); - po = 1; - } - if (NIL_P(m)) { - m = f_match(comp_pat1, s); - if (!NIL_P(m)) { - sr = Qnil; - si = rb_reg_nth_match(1, m); - if (NIL_P(si)) - si = rb_usascii_str_new2(""); - { - VALUE t; - - t = rb_reg_nth_match(2, m); - if (NIL_P(t)) - t = rb_usascii_str_new2("1"); - rb_str_concat(si, t); - } - re = rb_reg_match_post(m); - po = 0; - } - } - if (NIL_P(m)) { - m = f_match(comp_pat2, s); - if (NIL_P(m)) - return rb_assoc_new(Qnil, self); - sr = rb_reg_nth_match(1, m); - if (NIL_P(rb_reg_nth_match(2, m))) - si = Qnil; - else { - VALUE t; - - si = rb_reg_nth_match(3, m); - t = rb_reg_nth_match(4, m); - if (NIL_P(t)) - t = rb_usascii_str_new2("1"); - rb_str_concat(si, t); - } - re = rb_reg_match_post(m); - po = 0; - } - r = INT2FIX(0); - i = INT2FIX(0); - if (!NIL_P(sr)) { - if (strchr(RSTRING_PTR(sr), '/')) - r = f_to_r(sr); - else if (strpbrk(RSTRING_PTR(sr), ".eE")) - r = f_to_f(sr); - else - r = f_to_i(sr); - } - if (!NIL_P(si)) { - if (strchr(RSTRING_PTR(si), '/')) - i = f_to_r(si); - else if (strpbrk(RSTRING_PTR(si), ".eE")) - i = f_to_f(si); - else - i = f_to_i(si); - } - if (po) - return rb_assoc_new(rb_complex_polar(r, i), re); - else - return rb_assoc_new(rb_complex_new2(r, i), re); + if (strchr(s, '/')) + return rb_cstr_to_rat(s, 0); + if (strpbrk(s, ".eE")) { + double d = rb_cstr_to_dbl(s, 0); + return DBL2NUM(d); } + return rb_cstr_to_inum(s, 10, 0); +} + +static int +read_comp(const char **s, int strict, + VALUE *ret, char **b) +{ + char *bb; + int sign; + VALUE num, num2; + + bb = *b; + + sign = read_sign(s, b); + + if (isimagunit(**s)) { + (*s)++; + num = INT2FIX((sign == '-') ? -1 : + 1); + *ret = rb_complex_raw2(ZERO, num); + return 1; /* e.g. "i" */ + } + + if (!read_rat_nos(s, strict, b)) { + **b = '\0'; + num = str2num(bb); + *ret = rb_complex_raw2(num, ZERO); + return 0; /* e.g. "1/" */ + } + **b = '\0'; + num = str2num(bb); + + if (isimagunit(**s)) { + (*s)++; + *ret = rb_complex_raw2(ZERO, num); + return 1; /* e.g. "3i" */ + } + + if (**s == '@') { + (*s)++; + bb = *b; + if (!read_rat(s, strict, b)) { + num = rb_complex_raw2(num, ZERO); + return 0; /* e.g. "1@x" */ + } + **b = '\0'; + num2 = str2num(bb); + *ret = rb_complex_polar(num, num2); + return 1; /* e.g. "1@2" */ + } + + if (**s == '-' || **s == '+') { + bb = *b; + sign = read_sign(s, b); + if (isimagunit(**s)) + num2 = INT2FIX((sign == '-') ? -1 : + 1); + else { + if (!read_rat_nos(s, strict, b)) { + *ret = rb_complex_raw2(num, ZERO); + return 0; /* e.g. "1+xi" */ + } + **b = '\0'; + num2 = str2num(bb); + } + if (!isimagunit(**s)) { + *ret = rb_complex_raw2(num, ZERO); + return 0; /* e.g. "1+3x" */ + } + (*s)++; + *ret = rb_complex_raw2(num, num2); + return 1; /* e.g. "1+2i" */ + } + /* !(@, - or +) */ + { + *ret = rb_complex_raw2(num, ZERO); + return 1; /* e.g. "3" */ + } +} + +static int +parse_comp(const char *s, int strict, + VALUE *num) +{ + char *buf, *b; + + buf = ALLOCA_N(char, strlen(s) + 1); + b = buf; + + while (isspace((unsigned char)*s)) + s++; + + if (!read_comp(&s, strict, num, &b)) + return 0; + + while (isspace((unsigned char)*s)) + s++; + + if (strict) + if (*s != '\0') + return 0; + return 1; } static VALUE string_to_c_strict(VALUE self) { - VALUE a = string_to_c_internal(self); - if (NIL_P(RARRAY_PTR(a)[0]) || RSTRING_LEN(RARRAY_PTR(a)[1]) > 0) { - VALUE s = f_inspect(self); - rb_raise(rb_eArgError, "invalid value for convert(): %s", - StringValuePtr(s)); - } - return RARRAY_PTR(a)[0]; -} + const char *s; + VALUE num; -#define id_gsub rb_intern("gsub") -#define f_gsub(x,y,z) rb_funcall((x), id_gsub, 2, (y), (z)) + rb_must_asciicompat(self); + + s = RSTRING_PTR(self); + + if (memchr(s, 0, RSTRING_LEN(self))) + rb_raise(rb_eArgError, "string contains null byte"); + + if (!parse_comp(s, 1, &num)) { + VALUE ins = f_inspect(self); + rb_raise(rb_eArgError, "invalid value for convert(): %s", + StringValuePtr(ins)); + } + + return num; +} /* * call-seq: @@ -1674,19 +1778,16 @@ string_to_c_strict(VALUE self) static VALUE string_to_c(VALUE self) { - VALUE s, a, backref; + const char *s; + VALUE num; - backref = rb_backref_get(); - rb_match_busy(backref); + rb_must_asciicompat(self); - s = f_gsub(self, underscores_pat, an_underscore); - a = string_to_c_internal(s); + s = RSTRING_PTR(self); - rb_backref_set(backref); + (void)parse_comp(s, 0, &num); - if (!NIL_P(RARRAY_PTR(a)[0])) - return RARRAY_PTR(a)[0]; - return rb_complex_new1(INT2FIX(0)); + return num; } static VALUE @@ -2054,8 +2155,6 @@ Init_Complex(void) rb_define_method(rb_cNilClass, "to_c", nilclass_to_c, 0); rb_define_method(rb_cNumeric, "to_c", numeric_to_c, 0); - make_patterns(); - rb_define_method(rb_cString, "to_c", string_to_c, 0); rb_define_private_method(CLASS_OF(rb_cComplex), "convert", nucomp_s_convert, -1); diff --git a/rational.c b/rational.c index 845b4d80b4..88f4083341 100644 --- a/rational.c +++ b/rational.c @@ -1956,145 +1956,185 @@ float_rationalize(int argc, VALUE *argv, VALUE self) return rb_rational_new2(p, q); } -static VALUE rat_pat, an_e_pat, a_dot_pat, underscores_pat, an_underscore; - -#define WS "\\s*" -#define DIGITS "(?:[0-9](?:_[0-9]|[0-9])*)" -#define NUMERATOR "(?:" DIGITS "?\\.)?" DIGITS "(?:[eE][-+]?" DIGITS ")?" -#define DENOMINATOR DIGITS -#define PATTERN "\\A" WS "([-+])?(" NUMERATOR ")(?:\\/(" DENOMINATOR "))?" WS - -static void -make_patterns(void) -{ - static const char rat_pat_source[] = PATTERN; - static const char an_e_pat_source[] = "[eE]"; - static const char a_dot_pat_source[] = "\\."; - static const char underscores_pat_source[] = "_+"; - - if (rat_pat) return; - - rat_pat = rb_reg_new(rat_pat_source, sizeof rat_pat_source - 1, 0); - rb_gc_register_mark_object(rat_pat); - - an_e_pat = rb_reg_new(an_e_pat_source, sizeof an_e_pat_source - 1, 0); - rb_gc_register_mark_object(an_e_pat); - - a_dot_pat = rb_reg_new(a_dot_pat_source, sizeof a_dot_pat_source - 1, 0); - rb_gc_register_mark_object(a_dot_pat); - - underscores_pat = rb_reg_new(underscores_pat_source, - sizeof underscores_pat_source - 1, 0); - rb_gc_register_mark_object(underscores_pat); - - an_underscore = rb_usascii_str_new2("_"); - rb_gc_register_mark_object(an_underscore); -} - -#define id_match rb_intern("match") -#define f_match(x,y) rb_funcall((x), id_match, 1, (y)) - -#define id_split rb_intern("split") -#define f_split(x,y) rb_funcall((x), id_split, 1, (y)) - #include -static VALUE -string_to_r_internal(VALUE self) +static int +read_sign(const char **s) { - VALUE s, m; + int sign = '?'; - s = self; - - if (RSTRING_LEN(s) == 0) - return rb_assoc_new(Qnil, self); - - m = f_match(rat_pat, s); - - if (!NIL_P(m)) { - VALUE v, ifp, exp, ip, fp; - VALUE si = rb_reg_nth_match(1, m); - VALUE nu = rb_reg_nth_match(2, m); - VALUE de = rb_reg_nth_match(3, m); - VALUE re = rb_reg_match_post(m); - - { - VALUE a; - - if (!strpbrk(RSTRING_PTR(nu), "eE")) { - ifp = nu; /* not a copy */ - exp = Qnil; - } - else { - a = f_split(nu, an_e_pat); - ifp = RARRAY_PTR(a)[0]; - if (RARRAY_LEN(a) != 2) - exp = Qnil; - else - exp = RARRAY_PTR(a)[1]; - } - - if (!strchr(RSTRING_PTR(ifp), '.')) { - ip = ifp; /* not a copy */ - fp = Qnil; - } - else { - a = f_split(ifp, a_dot_pat); - ip = RARRAY_PTR(a)[0]; - if (RARRAY_LEN(a) != 2) - fp = Qnil; - else - fp = RARRAY_PTR(a)[1]; - } - } - - v = rb_rational_new1(f_to_i(ip)); - - if (!NIL_P(fp)) { - char *p = RSTRING_PTR(fp); - long count = 0; - VALUE l; - - while (*p) { - if (rb_isdigit(*p)) - count++; - p++; - } - l = f_expt10(LONG2NUM(count)); - v = f_mul(v, l); - v = f_add(v, f_to_i(fp)); - v = f_div(v, l); - } - if (!NIL_P(si) && *RSTRING_PTR(si) == '-') - v = f_negate(v); - if (!NIL_P(exp)) - v = f_mul(v, f_expt10(f_to_i(exp))); -#if 0 - if (!NIL_P(de) && (!NIL_P(fp) || !NIL_P(exp))) - return rb_assoc_new(v, rb_usascii_str_new2("dummy")); -#endif - if (!NIL_P(de)) - v = f_div(v, f_to_i(de)); - - return rb_assoc_new(v, re); + if (**s == '-' || **s == '+') { + sign = **s; + (*s)++; } - return rb_assoc_new(Qnil, self); + return sign; +} + +static int +read_digits(const char **s, int strict, + VALUE *num, int *count) +{ + int us = 1; + + if (!isdigit((unsigned char)**s)) + return 0; + + *num = ZERO; + + while (isdigit((unsigned char)**s) || **s == '_') { + if (**s == '_') { + if (strict) { + if (us) + return 0; + } + us = 1; + } + else { + *num = f_mul(*num, INT2FIX(10)); + *num = f_add(*num, INT2FIX(**s - '0')); + if (count) + (*count)++; + us = 0; + } + (*s)++; + } + if (us) + do { + (*s)--; + } while (**s == '_'); + return 1; +} + +static int +read_num(const char **s, int numsign, int strict, + VALUE *num) +{ + VALUE ip, fp, exp; + + *num = rb_rational_raw2(ZERO, ONE); + exp = Qnil; + + if (**s != '.') { + if (!read_digits(s, strict, &ip, NULL)) + return 0; + *num = rb_rational_raw2(ip, ONE); + } + + if (**s == '.') { + int count = 0; + + (*s)++; + if (!read_digits(s, strict, &fp, &count)) + return 0; + { + VALUE l = f_expt10(INT2NUM(count)); + *num = f_mul(*num, l); + *num = f_add(*num, fp); + *num = f_div(*num, l); + } + } + + if (**s == 'e' || **s == 'E') { + int expsign; + + (*s)++; + expsign = read_sign(s); + if (!read_digits(s, strict, &exp, NULL)) + return 0; + if (expsign == '-') + exp = f_negate(exp); + } + + if (numsign == '-') + *num = f_negate(*num); + if (!NIL_P(exp)) { + VALUE l = f_expt10(exp); + *num = f_mul(*num, l); + } + return 1; +} + +static int +read_den(const char **s, int strict, + VALUE *num) +{ + if (!read_digits(s, strict, num, NULL)) + return 0; + return 1; +} + +static int +read_rat_nos(const char **s, int sign, int strict, + VALUE *num) +{ + VALUE den; + + if (!read_num(s, sign, strict, num)) + return 0; + if (**s == '/') { + (*s)++; + if (!read_den(s, strict, &den)) + return 0; + if (!(FIXNUM_P(den) && FIX2LONG(den) == 1)) + *num = f_div(*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; +} + +static int +parse_rat(const char *s, int strict, + VALUE *num) +{ + while (isspace((unsigned char)*s)) + s++; + + if (!read_rat(&s, strict, num)) + return 0; + + while (isspace((unsigned char)*s)) + s++; + + if (strict) + if (*s != '\0') + return 0; + return 1; } static VALUE string_to_r_strict(VALUE self) { - VALUE a = string_to_r_internal(self); - if (NIL_P(RARRAY_PTR(a)[0]) || RSTRING_LEN(RARRAY_PTR(a)[1]) > 0) { - VALUE s = f_inspect(self); - rb_raise(rb_eArgError, "invalid value for convert(): %s", - StringValuePtr(s)); - } - return RARRAY_PTR(a)[0]; -} + const char *s; + VALUE num; -#define id_gsub rb_intern("gsub") -#define f_gsub(x,y,z) rb_funcall((x), id_gsub, 2, (y), (z)) + rb_must_asciicompat(self); + + s = RSTRING_PTR(self); + + if (memchr(s, 0, RSTRING_LEN(self))) + rb_raise(rb_eArgError, "string contains null byte"); + + if (!parse_rat(s, 1, &num)) { + VALUE ins = f_inspect(self); + rb_raise(rb_eArgError, "invalid value for convert(): %s", + StringValuePtr(ins)); + } + + if (RB_TYPE_P(num, T_FLOAT)) + rb_raise(rb_eFloatDomainError, "Infinity"); + return num; +} /* * call-seq: @@ -2120,27 +2160,31 @@ string_to_r_strict(VALUE self) static VALUE string_to_r(VALUE self) { - VALUE s, a, a1, backref; + const char *s; + VALUE num; - backref = rb_backref_get(); - rb_match_busy(backref); + rb_must_asciicompat(self); - s = f_gsub(self, underscores_pat, an_underscore); - a = string_to_r_internal(s); + s = RSTRING_PTR(self); - rb_backref_set(backref); + (void)parse_rat(s, 0, &num); - a1 = RARRAY_PTR(a)[0]; - if (!NIL_P(a1)) { - if (RB_TYPE_P(a1, T_FLOAT)) - rb_raise(rb_eFloatDomainError, "Infinity"); - return a1; - } - return rb_rational_new1(INT2FIX(0)); + if (RB_TYPE_P(num, T_FLOAT)) + rb_raise(rb_eFloatDomainError, "Infinity"); + return num; } -#define id_to_r rb_intern("to_r") -#define f_to_r(x) rb_funcall((x), id_to_r, 0) +VALUE +rb_cstr_to_rat(const char *s, int strict) /* for complex's internal */ +{ + VALUE num; + + (void)parse_rat(s, strict, &num); + + if (RB_TYPE_P(num, T_FLOAT)) + rb_raise(rb_eFloatDomainError, "Infinity"); + return num; +} static VALUE nurat_s_convert(int argc, VALUE *argv, VALUE klass) @@ -2369,8 +2413,6 @@ Init_Rational(void) rb_define_method(rb_cFloat, "to_r", float_to_r, 0); rb_define_method(rb_cFloat, "rationalize", float_rationalize, -1); - make_patterns(); - rb_define_method(rb_cString, "to_r", string_to_r, 0); rb_define_private_method(CLASS_OF(rb_cRational), "convert", nurat_s_convert, -1);