Skip to content

Commit dfbafab

Browse files
committed
py: Improve mp_parse_num_integer; make it self contained.
1 parent 6e48f7f commit dfbafab

1 file changed

Lines changed: 63 additions & 56 deletions

File tree

py/parsenum.c

Lines changed: 63 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -13,76 +13,83 @@
1313
#include <math.h>
1414
#endif
1515

16-
#if defined(UNIX)
17-
18-
#include <ctype.h>
19-
#include <errno.h>
20-
2116
mp_obj_t mp_parse_num_integer(const char *restrict str, uint len, int base) {
22-
// TODO at the moment we ignore len; we should honour it!
23-
// TODO detect integer overflow and return bignum
24-
25-
int c, neg = 0;
26-
const char *p = str;
27-
char *num;
28-
long found;
17+
const char *restrict top = str + len;
18+
bool neg = false;
2919

3020
// check radix base
3121
if ((base != 0 && base < 2) || base > 36) {
3222
nlr_jump(mp_obj_new_exception_msg(&mp_type_ValueError, "ValueError: int() arg 2 must be >=2 and <= 36"));
3323
}
34-
// skip surrounded whitespace
35-
while (isspace((c = *(p++))));
36-
if (c == 0) {
37-
goto value_error;
38-
}
39-
// preced sign
40-
if (c == '+' || c == '-') {
41-
neg = - (c == '-');
42-
} else {
43-
p--;
24+
25+
// skip leading space
26+
for (; str < top && unichar_isspace(*str); str++) {
4427
}
4528

46-
len -= p - str;
47-
int skip = mp_parse_num_base(p, len, &base);
48-
p += skip;
49-
len -= skip;
50-
51-
errno = 0;
52-
found = strtol(p, &num, base);
53-
if (errno) {
54-
goto value_error;
55-
} else if (found && *(num) == 0) {
56-
goto done;
57-
} else if (found || num != p) {
58-
goto check_tail_space;
59-
} else {
60-
goto value_error;
29+
// parse optional sign
30+
if (str < top) {
31+
if (*str == '+') {
32+
str++;
33+
} else if (*str == '-') {
34+
str++;
35+
neg = true;
36+
}
6137
}
6238

63-
check_tail_space:
64-
if (*(num) != 0) {
65-
while (isspace((c = *(num++))));
66-
if (c != 0) {
67-
goto value_error;
39+
// parse optional base prefix
40+
str += mp_parse_num_base(str, top - str, &base);
41+
42+
// string should be an integer number
43+
machine_int_t int_val = 0;
44+
for (; str < top; str++) {
45+
machine_int_t old_val = int_val;
46+
int dig = *str;
47+
if (unichar_isdigit(dig) && dig - '0' < base) {
48+
// 0-9 digit
49+
int_val = base * int_val + dig - '0';
50+
} else if (base == 16) {
51+
dig |= 0x20;
52+
if ('a' <= dig && dig <= 'f') {
53+
// a-f hex digit
54+
int_val = base * int_val + dig - 'a' + 10;
55+
} else {
56+
// unknown character
57+
break;
58+
}
59+
} else {
60+
// unknown character
61+
break;
62+
}
63+
if (int_val < old_val) {
64+
// If new value became less than previous, it's overflow
65+
goto overflow;
66+
} else if ((old_val ^ int_val) & WORD_MSBIT_HIGH) {
67+
// If signed number changed sign - it's overflow
68+
goto overflow;
6869
}
6970
}
7071

71-
done:
72-
return MP_OBJ_NEW_SMALL_INT((found ^ neg) - neg);
72+
// negate value if needed
73+
if (neg) {
74+
int_val = -int_val;
75+
}
7376

74-
value_error:
75-
nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "invalid literal for int() with base %d: '%s'", base, str));
76-
}
77+
// skip trailing space
78+
for (; str < top && unichar_isspace(*str); str++) {
79+
}
7780

78-
#else /* defined(UNIX) */
81+
// check we reached the end of the string
82+
if (str != top) {
83+
nlr_jump(mp_obj_new_exception_msg(&mp_type_SyntaxError, "invalid syntax for number"));
84+
}
7985

80-
mp_obj_t mp_parse_num_integer(const char *restrict str, uint len, int base) {
81-
// TODO port strtol to stm
82-
return MP_OBJ_NEW_SMALL_INT(0);
83-
}
86+
// return the object
87+
return MP_OBJ_NEW_SMALL_INT(int_val);
8488

85-
#endif /* defined(UNIX) */
89+
overflow:
90+
// TODO reparse using bignum
91+
nlr_jump(mp_obj_new_exception_msg(&mp_type_ValueError, "overflow parsing integer"));
92+
}
8693

8794
#define PARSE_DEC_IN_INTG (1)
8895
#define PARSE_DEC_IN_FRAC (2)
@@ -96,10 +103,10 @@ mp_obj_t mp_parse_num_decimal(const char *str, uint len, bool allow_imag, bool f
96103
bool imag = false;
97104

98105
// skip leading space
99-
for (; str < top && isspace(*str); str++) {
106+
for (; str < top && unichar_isspace(*str); str++) {
100107
}
101108

102-
// get optional sign
109+
// parse optional sign
103110
if (str < top) {
104111
if (*str == '+') {
105112
str++;
@@ -187,7 +194,7 @@ mp_obj_t mp_parse_num_decimal(const char *str, uint len, bool allow_imag, bool f
187194
}
188195

189196
// skip trailing space
190-
for (; str < top && isspace(*str); str++) {
197+
for (; str < top && unichar_isspace(*str); str++) {
191198
}
192199

193200
// check we reached the end of the string

0 commit comments

Comments
 (0)