summaryrefslogtreecommitdiff
path: root/Python/pystrtod.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/pystrtod.c')
-rw-r--r--Python/pystrtod.c75
1 files changed, 71 insertions, 4 deletions
diff --git a/Python/pystrtod.c b/Python/pystrtod.c
index 209c9086c8..64d0c52e48 100644
--- a/Python/pystrtod.c
+++ b/Python/pystrtod.c
@@ -370,6 +370,72 @@ PyOS_string_to_double(const char *s,
return result;
}
+/* Remove underscores that follow the underscore placement rule from
+ the string and then call the `innerfunc` function on the result.
+ It should return a new object or NULL on exception.
+
+ `what` is used for the error message emitted when underscores are detected
+ that don't follow the rule. `arg` is an opaque pointer passed to the inner
+ function.
+
+ This is used to implement underscore-agnostic conversion for floats
+ and complex numbers.
+*/
+PyObject *
+_Py_string_to_number_with_underscores(
+ const char *s, Py_ssize_t orig_len, const char *what, PyObject *obj, void *arg,
+ PyObject *(*innerfunc)(const char *, Py_ssize_t, void *))
+{
+ char prev;
+ const char *p, *last;
+ char *dup, *end;
+ PyObject *result;
+
+ if (strchr(s, '_') == NULL) {
+ return innerfunc(s, orig_len, arg);
+ }
+
+ dup = PyMem_Malloc(orig_len + 1);
+ end = dup;
+ prev = '\0';
+ last = s + orig_len;
+ for (p = s; *p; p++) {
+ if (*p == '_') {
+ /* Underscores are only allowed after digits. */
+ if (!(prev >= '0' && prev <= '9')) {
+ goto error;
+ }
+ }
+ else {
+ *end++ = *p;
+ /* Underscores are only allowed before digits. */
+ if (prev == '_' && !(*p >= '0' && *p <= '9')) {
+ goto error;
+ }
+ }
+ prev = *p;
+ }
+ /* Underscores are not allowed at the end. */
+ if (prev == '_') {
+ goto error;
+ }
+ /* No embedded NULs allowed. */
+ if (p != last) {
+ goto error;
+ }
+ *end = '\0';
+ result = innerfunc(dup, end - dup, arg);
+ PyMem_Free(dup);
+ return result;
+
+ error:
+ PyMem_Free(dup);
+ PyErr_Format(PyExc_ValueError,
+ "could not convert string to %s: "
+ "%R", what, obj);
+ return NULL;
+}
+
#ifdef PY_NO_SHORT_FLOAT_REPR
/* Given a string that may have a decimal point in the current
@@ -881,12 +947,12 @@ PyAPI_FUNC(char *) PyOS_double_to_string(double val,
#define OFS_E 2
/* The lengths of these are known to the code below, so don't change them */
-static char *lc_float_strings[] = {
+static const char * const lc_float_strings[] = {
"inf",
"nan",
"e",
};
-static char *uc_float_strings[] = {
+static const char * const uc_float_strings[] = {
"INF",
"NAN",
"E",
@@ -925,7 +991,8 @@ static char *
format_float_short(double d, char format_code,
int mode, int precision,
int always_add_sign, int add_dot_0_if_integer,
- int use_alt_formatting, char **float_strings, int *type)
+ int use_alt_formatting, const char * const *float_strings,
+ int *type)
{
char *buf = NULL;
char *p = NULL;
@@ -1176,7 +1243,7 @@ PyAPI_FUNC(char *) PyOS_double_to_string(double val,
int flags,
int *type)
{
- char **float_strings = lc_float_strings;
+ const char * const *float_strings = lc_float_strings;
int mode;
/* Validate format_code, and map upper and lower case. Compute the