Coverage Report

Created: 2026-08-31 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython3/Parser/lexer/number.c
Line
Count
Source
1
#include "Python.h"
2
#include "pycore_token.h"
3
4
#include "lexer_internal.h"
5
#include "../tokenizer/helpers.h"
6
7
1.07M
#define MAKE_TOKEN(token_type) _PyLexer_token_setup(tok, token, token_type, p_start, p_end)
8
9
static int
10
lookahead(struct tok_state *tok, const char *test)
11
19.4k
{
12
19.4k
    const char *s = test;
13
19.4k
    int res = 0;
14
56.4k
    while (1) {
15
56.4k
        int c = tok_nextc(tok);
16
56.4k
        if (*s == 0) {
17
19.2k
            res = !is_potential_identifier_char(c);
18
19.2k
        }
19
37.2k
        else if (c == *s) {
20
37.0k
            s++;
21
37.0k
            continue;
22
37.0k
        }
23
24
19.4k
        tok_backup(tok, c);
25
56.4k
        while (s != test) {
26
37.0k
            tok_backup(tok, *--s);
27
37.0k
        }
28
19.4k
        return res;
29
56.4k
    }
30
19.4k
}
31
32
static int
33
1.07M
verify_end_of_number(struct tok_state *tok, int c, const char *kind) {
34
1.07M
    if (tok->tok_extra_tokens) {
35
        // When we are parsing extra tokens, we don't want to emit warnings
36
        // about invalid literals, because we want to be a bit more liberal.
37
0
        return 1;
38
0
    }
39
    /* Emit a deprecation warning only if the numeric literal is immediately
40
     * followed by one of keywords which can occur after a numeric literal
41
     * in valid code: "and", "else", "for", "if", "in", "is" and "or".
42
     * It allows to gradually deprecate existing valid code without adding
43
     * warning before error in most cases of invalid numeric literal (which
44
     * would be confusing and break existing tests).
45
     * Raise a syntax error with slightly better message than plain
46
     * "invalid syntax" if the numeric literal is immediately followed by
47
     * other keyword or identifier.
48
     */
49
1.07M
    int r = 0;
50
1.07M
    if (c == 'a') {
51
7.23k
        r = lookahead(tok, "nd");
52
7.23k
    }
53
1.06M
    else if (c == 'e') {
54
3.37k
        r = lookahead(tok, "lse");
55
3.37k
    }
56
1.06M
    else if (c == 'f') {
57
3.58k
        r = lookahead(tok, "or");
58
3.58k
    }
59
1.05M
    else if (c == 'i') {
60
12.5k
        int c2 = tok_nextc(tok);
61
12.5k
        if (c2 == 'f' || c2 == 'n' || c2 == 's') {
62
12.5k
            r = 1;
63
12.5k
        }
64
12.5k
        tok_backup(tok, c2);
65
12.5k
    }
66
1.04M
    else if (c == 'o') {
67
4.83k
        r = lookahead(tok, "r");
68
4.83k
    }
69
1.03M
    else if (c == 'n') {
70
393
        r = lookahead(tok, "ot");
71
393
    }
72
1.07M
    if (r) {
73
31.7k
        tok_backup(tok, c);
74
31.7k
        if (_PyTokenizer_parser_warn(tok, PyExc_SyntaxWarning,
75
31.7k
                "invalid %s literal", kind))
76
0
        {
77
0
            return 0;
78
0
        }
79
31.7k
        tok_nextc(tok);
80
31.7k
    }
81
1.03M
    else /* In future releases, only error will remain. */
82
1.03M
    if (c < 128 && is_potential_identifier_char(c)) {
83
502
        tok_backup(tok, c);
84
502
        _PyTokenizer_syntaxerror(tok, "invalid %s literal", kind);
85
502
        return 0;
86
502
    }
87
1.07M
    return 1;
88
1.07M
}
89
90
static int
91
tok_decimal_tail(struct tok_state *tok)
92
1.08M
{
93
1.08M
    int c;
94
95
1.10M
    while (1) {
96
5.58M
        do {
97
5.58M
            c = tok_nextc(tok);
98
5.58M
        } while (Py_ISDIGIT(c));
99
1.10M
        if (c != '_') {
100
1.08M
            break;
101
1.08M
        }
102
13.6k
        c = tok_nextc(tok);
103
13.6k
        if (!Py_ISDIGIT(c)) {
104
20
            tok_backup(tok, c);
105
20
            _PyTokenizer_syntaxerror(tok, "invalid decimal literal");
106
20
            return 0;
107
20
        }
108
13.6k
    }
109
1.08M
    return c;
110
1.08M
}
111
112
int
113
_PyLexer_scan_number(struct tok_state *tok, struct token *token, int c,
114
                     int leading_dot)
115
1.07M
{
116
1.07M
    const char *p_start = NULL;
117
1.07M
    const char *p_end = NULL;
118
119
1.07M
    if (leading_dot) {
120
15.9k
        goto fraction;
121
15.9k
    }
122
1.05M
    if (c == '0') {
123
        /* Hex, octal or binary -- maybe. */
124
69.2k
        c = tok_nextc(tok);
125
69.2k
        if (c == 'x' || c == 'X') {
126
            /* Hex */
127
2.10k
            c = tok_nextc(tok);
128
3.44k
            do {
129
3.44k
                if (c == '_') {
130
1.34k
                    c = tok_nextc(tok);
131
1.34k
                }
132
3.44k
                if (!Py_ISXDIGIT(c)) {
133
9
                    tok_backup(tok, c);
134
9
                    return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid hexadecimal literal"));
135
9
                }
136
93.0k
                do {
137
93.0k
                    c = tok_nextc(tok);
138
93.0k
                } while (Py_ISXDIGIT(c));
139
3.43k
            } while (c == '_');
140
2.09k
            if (!verify_end_of_number(tok, c, "hexadecimal")) {
141
2
                return MAKE_TOKEN(ERRORTOKEN);
142
2
            }
143
2.09k
        }
144
67.1k
        else if (c == 'o' || c == 'O') {
145
            /* Octal */
146
1.51k
            c = tok_nextc(tok);
147
2.20k
            do {
148
2.20k
                if (c == '_') {
149
692
                    c = tok_nextc(tok);
150
692
                }
151
2.20k
                if (c < '0' || c >= '8') {
152
12
                    if (Py_ISDIGIT(c)) {
153
1
                        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok,
154
1
                                "invalid digit '%c' in octal literal", c));
155
1
                    }
156
11
                    else {
157
11
                        tok_backup(tok, c);
158
11
                        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid octal literal"));
159
11
                    }
160
12
                }
161
19.7k
                do {
162
19.7k
                    c = tok_nextc(tok);
163
19.7k
                } while ('0' <= c && c < '8');
164
2.19k
            } while (c == '_');
165
1.50k
            if (Py_ISDIGIT(c)) {
166
1
                return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok,
167
1
                        "invalid digit '%c' in octal literal", c));
168
1
            }
169
1.50k
            if (!verify_end_of_number(tok, c, "octal")) {
170
7
                return MAKE_TOKEN(ERRORTOKEN);
171
7
            }
172
1.50k
        }
173
65.6k
        else if (c == 'b' || c == 'B') {
174
            /* Binary */
175
1.69k
            c = tok_nextc(tok);
176
2.40k
            do {
177
2.40k
                if (c == '_') {
178
1.66k
                    c = tok_nextc(tok);
179
1.66k
                }
180
2.40k
                if (c != '0' && c != '1') {
181
8
                    if (Py_ISDIGIT(c)) {
182
1
                        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid digit '%c' in binary literal", c));
183
1
                    }
184
7
                    else {
185
7
                        tok_backup(tok, c);
186
7
                        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid binary literal"));
187
7
                    }
188
8
                }
189
57.1k
                do {
190
57.1k
                    c = tok_nextc(tok);
191
57.1k
                } while (c == '0' || c == '1');
192
2.39k
            } while (c == '_');
193
1.69k
            if (Py_ISDIGIT(c)) {
194
3
                return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid digit '%c' in binary literal", c));
195
3
            }
196
1.68k
            if (!verify_end_of_number(tok, c, "binary")) {
197
4
                return MAKE_TOKEN(ERRORTOKEN);
198
4
            }
199
1.68k
        }
200
63.9k
        else {
201
63.9k
            int nonzero = 0;
202
            /* maybe old-style octal; c is first char of it */
203
            /* in any case, allow '0' as a literal */
204
132k
            while (1) {
205
132k
                if (c == '_') {
206
427
                    c = tok_nextc(tok);
207
427
                    if (!Py_ISDIGIT(c)) {
208
4
                        tok_backup(tok, c);
209
4
                        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid decimal literal"));
210
4
                    }
211
427
                }
212
132k
                if (c != '0') {
213
63.9k
                    break;
214
63.9k
                }
215
68.5k
                c = tok_nextc(tok);
216
68.5k
            }
217
63.9k
            char* zeros_end = tok->cur;
218
63.9k
            if (Py_ISDIGIT(c)) {
219
2.97k
                nonzero = 1;
220
2.97k
                c = tok_decimal_tail(tok);
221
2.97k
                if (c == 0) {
222
2
                    return MAKE_TOKEN(ERRORTOKEN);
223
2
                }
224
2.97k
            }
225
63.9k
            if (c == '.') {
226
7.69k
                c = tok_nextc(tok);
227
7.69k
                goto fraction;
228
7.69k
            }
229
56.2k
            else if (c == 'e' || c == 'E') {
230
1.93k
                goto exponent;
231
1.93k
            }
232
54.3k
            else if (c == 'j' || c == 'J') {
233
11.9k
                goto imaginary;
234
11.9k
            }
235
42.3k
            else if (nonzero && !tok->tok_extra_tokens) {
236
                /* Old-style octal: now disallowed. */
237
84
                tok_backup(tok, c);
238
84
                return MAKE_TOKEN(_PyTokenizer_syntaxerror_known_range(
239
84
                        tok, (int)(tok->start + 1 - tok->line_start),
240
84
                        (int)(zeros_end - tok->line_start),
241
84
                        "leading zeros in decimal integer "
242
84
                        "literals are not permitted; "
243
84
                        "use an 0o prefix for octal integers"));
244
84
            }
245
42.2k
            if (!verify_end_of_number(tok, c, "decimal")) {
246
69
                return MAKE_TOKEN(ERRORTOKEN);
247
69
            }
248
42.2k
        }
249
69.2k
    }
250
985k
    else {
251
        /* Decimal */
252
985k
        c = tok_decimal_tail(tok);
253
985k
        if (c == 0) {
254
12
            return MAKE_TOKEN(ERRORTOKEN);
255
12
        }
256
985k
        {
257
            /* Accept floating-point numbers. */
258
985k
            if (c == '.') {
259
49.9k
                c = tok_nextc(tok);
260
73.5k
    fraction:
261
                /* Fraction */
262
73.5k
                if (Py_ISDIGIT(c)) {
263
41.3k
                    c = tok_decimal_tail(tok);
264
41.3k
                    if (c == 0) {
265
4
                        return MAKE_TOKEN(ERRORTOKEN);
266
4
                    }
267
41.3k
                }
268
73.5k
            }
269
1.00M
            if (c == 'e' || c == 'E') {
270
59.4k
                int e;
271
61.3k
              exponent:
272
61.3k
                e = c;
273
                /* Exponent part */
274
61.3k
                c = tok_nextc(tok);
275
61.3k
                if (c == '+' || c == '-') {
276
10.3k
                    c = tok_nextc(tok);
277
10.3k
                    if (!Py_ISDIGIT(c)) {
278
8
                        tok_backup(tok, c);
279
8
                        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid decimal literal"));
280
8
                    }
281
51.0k
                } else if (!Py_ISDIGIT(c)) {
282
3.37k
                    tok_backup(tok, c);
283
3.37k
                    if (!verify_end_of_number(tok, e, "decimal")) {
284
58
                        return MAKE_TOKEN(ERRORTOKEN);
285
58
                    }
286
3.31k
                    tok_backup(tok, e);
287
3.31k
                    p_start = tok->start;
288
3.31k
                    p_end = tok->cur;
289
3.31k
                    return MAKE_TOKEN(NUMBER);
290
3.37k
                }
291
58.0k
                c = tok_decimal_tail(tok);
292
58.0k
                if (c == 0) {
293
2
                    return MAKE_TOKEN(ERRORTOKEN);
294
2
                }
295
58.0k
            }
296
1.00M
            if (c == 'j' || c == 'J') {
297
                /* Imaginary part */
298
93.1k
    imaginary:
299
93.1k
                c = tok_nextc(tok);
300
93.1k
                if (!verify_end_of_number(tok, c, "imaginary")) {
301
21
                    return MAKE_TOKEN(ERRORTOKEN);
302
21
                }
303
93.1k
            }
304
926k
            else if (!verify_end_of_number(tok, c, "decimal")) {
305
341
                return MAKE_TOKEN(ERRORTOKEN);
306
341
            }
307
1.00M
        }
308
1.00M
    }
309
1.06M
    tok_backup(tok, c);
310
1.06M
    p_start = tok->start;
311
1.06M
    p_end = tok->cur;
312
1.06M
    return MAKE_TOKEN(NUMBER);
313
1.05M
}