diff -r 1d9f730717b8 Lib/test/test_complex.py --- a/Lib/test/test_complex.py Wed Dec 31 05:08:55 2008 +0100 +++ b/Lib/test/test_complex.py Wed Dec 31 20:17:49 2008 +0900 @@ -215,11 +215,34 @@ self.assertAlmostEqual(complex(0.0, 3.14), 3.14j) self.assertAlmostEqual(complex("1"), 1+0j) self.assertAlmostEqual(complex("1j"), 1j) + self.assertAlmostEqual(complex("j"), 1j) + self.assertAlmostEqual(complex("J"), 1j) + self.assertAlmostEqual(complex("-j"), -1j) + self.assertAlmostEqual(complex("-J"), -1j) self.assertAlmostEqual(complex(), 0) self.assertAlmostEqual(complex("-1"), -1) self.assertAlmostEqual(complex("+1"), +1) self.assertAlmostEqual(complex("(1+2j)"), 1+2j) self.assertAlmostEqual(complex("(1.3+2.2j)"), 1.3+2.2j) + self.assertAlmostEqual(complex("1.3+j"), 1.3+1.0j) + + # Test for nan and inf + self.assertEqual(str(complex('inf')), '(inf+0j)') + self.assertEqual(str(complex('infj')), 'inf*j') + self.assertEqual(str(complex('inf+j')), '(inf+1j)') + self.assertEqual(str(complex('inf+infj')), '(inf+inf*j)') + self.assertEqual(str(complex('inf-infj')), '(inf-inf*j)') + self.assertEqual(str(complex('1-infj')), '(1-inf*j)') + self.assertEqual(str(complex('inf-j')), '(inf-1j)') + self.assertEqual(str(complex('infinite-j')), '(inf-1j)') + self.assertEqual(str(complex('1-infinitej')), '(1-inf*j)') + self.assertEqual(str(complex('nan')), '(nan+0j)') + self.assertEqual(str(complex('nanj')), 'nan*j') + self.assertEqual(str(complex('nan+j')), '(nan+1j)') + self.assertEqual(str(complex('nan+nanj')), '(nan+nan*j)') + self.assertEqual(str(complex('nan-nanj')), '(nan+nan*j)') + self.assertEqual(str(complex('1+nanj')), '(1+nan*j)') + self.assertEqual(str(complex('nan+j')), '(nan+1j)') class complex2(complex): pass self.assertAlmostEqual(complex(complex2(1+1j)), 1+1j) @@ -266,12 +289,19 @@ self.assertRaises(TypeError, complex, "1", 42) self.assertRaises(TypeError, complex, 1, "2") self.assertRaises(ValueError, complex, "1+") + self.assertRaises(ValueError, complex, "jj") + self.assertRaises(ValueError, complex, "1+jj") self.assertRaises(ValueError, complex, "1+1j+1j") self.assertRaises(ValueError, complex, "--") self.assertRaises(ValueError, complex, "(1+2j") self.assertRaises(ValueError, complex, "1+2j)") self.assertRaises(ValueError, complex, "1+(2j)") self.assertRaises(ValueError, complex, "(1+2j)123") + self.assertRaises(ValueError, complex, "inff") + self.assertRaises(ValueError, complex, "infinitejj") + self.assertRaises(ValueError, complex, "inf+inf") + self.assertRaises(ValueError, complex, "inf\0nite") + self.assertRaises(ValueError, complex, "nan\0") if test_support.have_unicode: self.assertRaises(ValueError, complex, unicode("1"*500)) self.assertRaises(ValueError, complex, unicode("x")) diff -r 1d9f730717b8 Objects/complexobject.c --- a/Objects/complexobject.c Wed Dec 31 05:08:55 2008 +0100 +++ b/Objects/complexobject.c Wed Dec 31 20:17:49 2008 +0900 @@ -861,21 +861,115 @@ {0}, }; +/* + * Parse sign in string s, and put the result in sign + * + * Return the string at position just after the sign if successfull, NULL + * otherwise + */ +static const char* +parse_sign(const char *s, int *sign) +{ + *sign = 1; + + switch (*s) { + case '-': + *sign = -1; + /* Fallthrough */ + case '+': + s++; + if (*s=='\0'||*s=='+'||*s=='-'||*s==')'|| + isspace(Py_CHARMASK(*s))) { + return NULL; + } + break; + default: + break; + } + + return s; +} + +/* + * Parse one part (imaginary or real) of a complex number. Assumes the string + * starts right at the beginning of the number (number including sign). + * + * Return the string at position just after the number if successfull, NULL + * otherwise, and the value into val + * + * ebuf is a buffer for error: if an error happened, ebuf may contain some + * informations about it. ebuf is not touched otherwise. ebuflen is the max number + * of bytes which can be written to ebuf (including '\0'). + * + * Handle nan and inf + */ +static const char* +parse_part(const char* s, double *val, char *ebuf, size_t ebuflen) +{ + int digit_or_dot = 0; + const char *ret; + char *end; + int sign; + + ret = parse_sign(s, &sign); + if (ret == NULL) { + goto parse_error; + } + s = ret; + + if (isspace(Py_CHARMASK(*s))) { + goto parse_error; + } else if (PyOS_strnicmp(s, "inf", 3) == 0) { + s += 3; + if (PyOS_strnicmp(s, "inite", 5) == 0) { + s += 5; + } + *val = sign * Py_HUGE_VAL; + } else if (PyOS_strnicmp(s, "nan", 3) == 0) { + s += 3; + *val = Py_NAN; + } else if (*s == 'j' || *s == 'J') { + *val = 1. * sign; + } else { + /* Parse 'normal number' */ + digit_or_dot = + (*s=='.' || isdigit(Py_CHARMASK(*s))); + if (!digit_or_dot) { + goto parse_error; + } + errno = 0; + PyFPE_START_PROTECT("strtod", goto parse_error) + *val = PyOS_ascii_strtod(s, &end) ; + PyFPE_END_PROTECT(*val) + if (errno != 0) { + PyOS_snprintf(ebuf, ebuflen, + "float() out of range: %.150s", s); + goto parse_error; + } + *val *= sign; + + s=end; + } + + return s; + +parse_error: + return NULL; +} + static PyObject * complex_subtype_from_string(PyTypeObject *type, PyObject *v) { - const char *s, *start; - char *end; - double x=0.0, y=0.0, z; - int got_re=0, got_im=0, got_bracket=0, done=0; - int digit_or_dot; - int sw_error=0; - int sign; + const char *s, *start, *ret; + double x, y, z; + int got_bracket=0; char buffer[256]; /* For errors */ #ifdef Py_USING_UNICODE char s_buffer[256]; #endif Py_ssize_t len; + + buffer[0] = '\0'; if (PyString_Check(v)) { s = PyString_AS_STRING(v); @@ -920,120 +1014,75 @@ s++; } - z = -1.0; - sign = 1; - do { + /* Parse first number */ + ret = parse_part(s, &z, buffer, sizeof(buffer)); + if (ret == NULL) { + goto parse_error; + } + s = ret; - switch (*s) { + if (*s == 'j' || *s == 'J') { + x = 0; + y = z; + s += 1; + } else { + x = z; - case '\0': - if (s-start != len) { - PyErr_SetString( - PyExc_ValueError, - "complex() arg contains a null byte"); - return NULL; - } - if(!done) sw_error=1; - break; + if (*s != '\0') { + /* Parse 2nd number */ - case ')': - if (!got_bracket || !(got_re || got_im)) { - sw_error=1; - break; - } - got_bracket=0; - done=1; - s++; - while (*s && isspace(Py_CHARMASK(*s))) - s++; - if (*s) sw_error=1; - break; + ret = parse_part(s, &z, buffer, sizeof(buffer)); + if (ret == NULL) { + goto parse_error; + } + s = ret; - case '-': - sign = -1; - /* Fallthrough */ - case '+': - if (done) sw_error=1; - s++; - if ( *s=='\0'||*s=='+'||*s=='-'||*s==')'|| - isspace(Py_CHARMASK(*s)) ) sw_error=1; - break; + /* Check it was an imaginary number */ + if (*s == 'j' || *s == 'J') { + s += 1; + } else { + goto parse_error; + } + y = z; + } else { + y = 0; + } + } - case 'J': - case 'j': - if (got_im || done) { - sw_error = 1; - break; - } - if (z<0.0) { - y=sign; - } - else{ - y=sign*z; - } - got_im=1; - s++; - if (*s!='+' && *s!='-' ) - done=1; - break; + if (*s == ')') { + if (!got_bracket) { + goto parse_error; + } + s += 1; + } + /* Got ( without ) */ + else if (got_bracket) { + goto parse_error; + } - default: - if (isspace(Py_CHARMASK(*s))) { - while (*s && isspace(Py_CHARMASK(*s))) - s++; - if (*s && *s != ')') - sw_error=1; - else - done = 1; - break; - } - digit_or_dot = - (*s=='.' || isdigit(Py_CHARMASK(*s))); - if (done||!digit_or_dot) { - sw_error=1; - break; - } - errno = 0; - PyFPE_START_PROTECT("strtod", return 0) - z = PyOS_ascii_strtod(s, &end) ; - PyFPE_END_PROTECT(z) - if (errno != 0) { - PyOS_snprintf(buffer, sizeof(buffer), - "float() out of range: %.150s", s); - PyErr_SetString( - PyExc_ValueError, - buffer); - return NULL; - } - s=end; - if (*s=='J' || *s=='j') { + /* Skip trailing spaces */ + while (*s && isspace(Py_CHARMASK(*s))) + s++; - break; - } - if (got_re) { - sw_error=1; - break; - } + if (*s != '\0') { + goto parse_error; + } - /* accept a real part */ - x=sign*z; - got_re=1; - if (got_im) done=1; - z = -1.0; - sign = 1; - break; - - } /* end of switch */ - - } while (s - start < len && !sw_error); - - if (sw_error || got_bracket) { - PyErr_SetString(PyExc_ValueError, - "complex() arg is a malformed string"); - return NULL; - } + /* Check that we went through the whole string */ + if (s-start != len) { + goto parse_error; + } return complex_subtype_from_doubles(type, x, y); + +parse_error: + if (buffer[0] != '\0') { + PyErr_SetString(PyExc_ValueError, buffer); + } else { + PyErr_SetString(PyExc_ValueError, + "complex() arg is a malformed string"); + } + return NULL; } static PyObject *