Coverage Report

Created: 2026-10-31 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/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
107k
#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
8.90k
{
12
8.90k
    const char *s = test;
13
8.90k
    int res = 0;
14
23.4k
    while (1) {
15
23.4k
        int c = tok_nextc(tok);
16
23.4k
        if (*s == 0) {
17
8.78k
            res = !is_potential_identifier_char(c);
18
8.78k
        }
19
14.6k
        else if (c == *s) {
20
14.5k
            s++;
21
14.5k
            continue;
22
14.5k
        }
23
24
8.90k
        tok_backup(tok, c);
25
23.4k
        while (s != test) {
26
14.5k
            tok_backup(tok, *--s);
27
14.5k
        }
28
8.90k
        return res;
29
23.4k
    }
30
8.90k
}
31
32
static int
33
107k
verify_end_of_number(struct tok_state *tok, int c, const char *kind) {
34
107k
    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
56
        return 1;
38
56
    }
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
107k
    int r = 0;
50
107k
    if (c == 'a') {
51
1.03k
        r = lookahead(tok, "nd");
52
1.03k
    }
53
106k
    else if (c == 'e') {
54
932
        r = lookahead(tok, "lse");
55
932
    }
56
105k
    else if (c == 'f') {
57
2.67k
        r = lookahead(tok, "or");
58
2.67k
    }
59
102k
    else if (c == 'i') {
60
1.62k
        int c2 = tok_nextc(tok);
61
1.62k
        if (c2 == 'f' || c2 == 'n' || c2 == 's') {
62
1.60k
            r = 1;
63
1.60k
        }
64
1.62k
        tok_backup(tok, c2);
65
1.62k
    }
66
101k
    else if (c == 'o') {
67
3.94k
        r = lookahead(tok, "r");
68
3.94k
    }
69
97.1k
    else if (c == 'n') {
70
316
        r = lookahead(tok, "ot");
71
316
    }
72
107k
    if (r) {
73
10.3k
        tok_backup(tok, c);
74
10.3k
        if (_PyTokenizer_parser_warn(tok, PyExc_SyntaxWarning,
75
10.3k
                "invalid %s literal", kind))
76
0
        {
77
0
            return 0;
78
0
        }
79
10.3k
        tok_nextc(tok);
80
10.3k
    }
81
96.9k
    else /* In future releases, only error will remain. */
82
96.9k
    if (c < 128 && is_potential_identifier_char(c)) {
83
274
        tok_backup(tok, c);
84
274
        _PyTokenizer_syntaxerror(tok, "invalid %s literal", kind);
85
274
        return 0;
86
274
    }
87
107k
    return 1;
88
107k
}
89
90
static int
91
tok_decimal_tail(struct tok_state *tok)
92
92.3k
{
93
92.3k
    int c;
94
95
92.9k
    while (1) {
96
321k
        do {
97
321k
            c = tok_nextc(tok);
98
321k
        } while (Py_ISDIGIT(c));
99
92.9k
        if (c != '_') {
100
92.3k
            break;
101
92.3k
        }
102
540
        c = tok_nextc(tok);
103
540
        if (!Py_ISDIGIT(c)) {
104
15
            tok_backup(tok, c);
105
15
            _PyTokenizer_syntaxerror(tok, "invalid decimal literal");
106
15
            return 0;
107
15
        }
108
540
    }
109
92.3k
    return c;
110
92.3k
}
111
112
int
113
_PyLexer_scan_number(struct tok_state *tok, struct token *token, int c,
114
                     int leading_dot)
115
107k
{
116
107k
    _PyTok_Off p_start = -1;
117
107k
    _PyTok_Off p_end = -1;
118
119
107k
    if (leading_dot) {
120
4.18k
        goto fraction;
121
4.18k
    }
122
103k
    if (c == '0') {
123
        /* Hex, octal or binary -- maybe. */
124
32.8k
        c = tok_nextc(tok);
125
32.8k
        if (c == 'x' || c == 'X') {
126
            /* Hex */
127
15.7k
            c = tok_nextc(tok);
128
15.9k
            do {
129
15.9k
                if (c == '_') {
130
203
                    c = tok_nextc(tok);
131
203
                }
132
15.9k
                if (!Py_ISXDIGIT(c)) {
133
17
                    tok_backup(tok, c);
134
17
                    return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid hexadecimal literal"));
135
17
                }
136
78.8k
                do {
137
78.8k
                    c = tok_nextc(tok);
138
78.8k
                } while (Py_ISXDIGIT(c));
139
15.9k
            } while (c == '_');
140
15.7k
            if (!verify_end_of_number(tok, c, "hexadecimal")) {
141
1
                return MAKE_TOKEN(ERRORTOKEN);
142
1
            }
143
15.7k
        }
144
17.1k
        else if (c == 'o' || c == 'O') {
145
            /* Octal */
146
1.12k
            c = tok_nextc(tok);
147
3.16k
            do {
148
3.16k
                if (c == '_') {
149
2.04k
                    c = tok_nextc(tok);
150
2.04k
                }
151
3.16k
                if (c < '0' || c >= '8') {
152
20
                    if (Py_ISDIGIT(c)) {
153
1
                        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok,
154
1
                                "invalid digit '%c' in octal literal", c));
155
1
                    }
156
19
                    else {
157
19
                        tok_backup(tok, c);
158
19
                        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid octal literal"));
159
19
                    }
160
20
                }
161
20.4k
                do {
162
20.4k
                    c = tok_nextc(tok);
163
20.4k
                } while ('0' <= c && c < '8');
164
3.14k
            } while (c == '_');
165
1.10k
            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.09k
            if (!verify_end_of_number(tok, c, "octal")) {
170
4
                return MAKE_TOKEN(ERRORTOKEN);
171
4
            }
172
1.09k
        }
173
16.0k
        else if (c == 'b' || c == 'B') {
174
            /* Binary */
175
711
            c = tok_nextc(tok);
176
1.07k
            do {
177
1.07k
                if (c == '_') {
178
370
                    c = tok_nextc(tok);
179
370
                }
180
1.07k
                if (c != '0' && c != '1') {
181
27
                    if (Py_ISDIGIT(c)) {
182
2
                        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid digit '%c' in binary literal", c));
183
2
                    }
184
25
                    else {
185
25
                        tok_backup(tok, c);
186
25
                        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid binary literal"));
187
25
                    }
188
27
                }
189
7.88k
                do {
190
7.88k
                    c = tok_nextc(tok);
191
7.88k
                } while (c == '0' || c == '1');
192
1.04k
            } while (c == '_');
193
684
            if (Py_ISDIGIT(c)) {
194
1
                return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid digit '%c' in binary literal", c));
195
1
            }
196
683
            if (!verify_end_of_number(tok, c, "binary")) {
197
2
                return MAKE_TOKEN(ERRORTOKEN);
198
2
            }
199
683
        }
200
15.3k
        else {
201
15.3k
            int nonzero = 0;
202
            /* maybe old-style octal; c is first char of it */
203
            /* in any case, allow '0' as a literal */
204
17.7k
            while (1) {
205
17.7k
                if (c == '_') {
206
323
                    c = tok_nextc(tok);
207
323
                    if (!Py_ISDIGIT(c)) {
208
4
                        tok_backup(tok, c);
209
4
                        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid decimal literal"));
210
4
                    }
211
323
                }
212
17.6k
                if (c != '0') {
213
15.3k
                    break;
214
15.3k
                }
215
2.37k
                c = tok_nextc(tok);
216
2.37k
            }
217
15.3k
            _PyTok_Off zeros_end = tok->cur;
218
15.3k
            if (Py_ISDIGIT(c)) {
219
784
                nonzero = 1;
220
784
                c = tok_decimal_tail(tok);
221
784
                if (c == 0) {
222
1
                    return MAKE_TOKEN(ERRORTOKEN);
223
1
                }
224
784
            }
225
15.3k
            if (c == '.') {
226
1.18k
                c = tok_nextc(tok);
227
1.18k
                goto fraction;
228
1.18k
            }
229
14.1k
            else if (c == 'e' || c == 'E') {
230
949
                goto exponent;
231
949
            }
232
13.1k
            else if (c == 'j' || c == 'J') {
233
1.04k
                goto imaginary;
234
1.04k
            }
235
12.1k
            else if (nonzero && !tok->tok_extra_tokens) {
236
                /* Old-style octal: now disallowed. */
237
49
                tok_backup(tok, c);
238
49
                return MAKE_TOKEN(_PyTokenizer_syntaxerror_known_range(
239
49
                        tok, (int)(tok->start + 1 - tok->line_start),
240
49
                        (int)(zeros_end - tok->line_start),
241
49
                        "leading zeros in decimal integer "
242
49
                        "literals are not permitted; "
243
49
                        "use an 0o prefix for octal integers"));
244
49
            }
245
12.0k
            if (!verify_end_of_number(tok, c, "decimal")) {
246
30
                return MAKE_TOKEN(ERRORTOKEN);
247
30
            }
248
12.0k
        }
249
32.8k
    }
250
70.4k
    else {
251
        /* Decimal */
252
70.4k
        c = tok_decimal_tail(tok);
253
70.4k
        if (c == 0) {
254
11
            return MAKE_TOKEN(ERRORTOKEN);
255
11
        }
256
70.4k
        {
257
            /* Accept floating-point numbers. */
258
70.4k
            if (c == '.') {
259
5.08k
                c = tok_nextc(tok);
260
10.4k
    fraction:
261
                /* Fraction */
262
10.4k
                if (Py_ISDIGIT(c)) {
263
7.60k
                    c = tok_decimal_tail(tok);
264
7.60k
                    if (c == 0) {
265
2
                        return MAKE_TOKEN(ERRORTOKEN);
266
2
                    }
267
7.60k
                }
268
10.4k
            }
269
75.8k
            if (c == 'e' || c == 'E') {
270
13.5k
                int e;
271
14.4k
              exponent:
272
14.4k
                e = c;
273
                /* Exponent part */
274
14.4k
                c = tok_nextc(tok);
275
14.4k
                if (c == '+' || c == '-') {
276
6.38k
                    c = tok_nextc(tok);
277
6.38k
                    if (!Py_ISDIGIT(c)) {
278
11
                        tok_backup(tok, c);
279
11
                        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid decimal literal"));
280
11
                    }
281
8.09k
                } else if (!Py_ISDIGIT(c)) {
282
920
                    tok_backup(tok, c);
283
920
                    if (!verify_end_of_number(tok, e, "decimal")) {
284
59
                        return MAKE_TOKEN(ERRORTOKEN);
285
59
                    }
286
861
                    tok_backup(tok, e);
287
861
                    p_start = tok->start;
288
861
                    p_end = tok->cur;
289
861
                    return MAKE_TOKEN(NUMBER);
290
920
                }
291
13.5k
                c = tok_decimal_tail(tok);
292
13.5k
                if (c == 0) {
293
1
                    return MAKE_TOKEN(ERRORTOKEN);
294
1
                }
295
13.5k
            }
296
75.8k
            if (c == 'j' || c == 'J') {
297
                /* Imaginary part */
298
5.41k
    imaginary:
299
5.41k
                c = tok_nextc(tok);
300
5.41k
                if (!verify_end_of_number(tok, c, "imaginary")) {
301
15
                    return MAKE_TOKEN(ERRORTOKEN);
302
15
                }
303
5.41k
            }
304
71.4k
            else if (!verify_end_of_number(tok, c, "decimal")) {
305
163
                return MAKE_TOKEN(ERRORTOKEN);
306
163
            }
307
75.8k
        }
308
75.8k
    }
309
106k
    tok_backup(tok, c);
310
106k
    p_start = tok->start;
311
106k
    p_end = tok->cur;
312
106k
    return MAKE_TOKEN(NUMBER);
313
103k
}