Coverage Report

Created: 2025-09-05 07:10

/src/cpython/Parser/lexer/lexer.c
Line
Count
Source (jump to first uncovered line)
1
#include "Python.h"
2
#include "pycore_token.h"
3
#include "pycore_unicodeobject.h"
4
#include "errcode.h"
5
6
#include "state.h"
7
#include "../tokenizer/helpers.h"
8
9
/* Alternate tab spacing */
10
1.64k
#define ALTTABSIZE 1
11
12
1.75M
#define is_potential_identifier_start(c) (\
13
1.75M
              (c >= 'a' && c <= 'z')\
14
1.75M
               || (c >= 'A' && c <= 'Z')\
15
1.75M
               || c == '_'\
16
1.75M
               || (c >= 128))
17
18
2.44M
#define is_potential_identifier_char(c) (\
19
2.44M
              (c >= 'a' && c <= 'z')\
20
2.44M
               || (c >= 'A' && c <= 'Z')\
21
2.44M
               || (c >= '0' && c <= '9')\
22
2.44M
               || c == '_'\
23
2.44M
               || (c >= 128))
24
25
#ifdef Py_DEBUG
26
static inline tokenizer_mode* TOK_GET_MODE(struct tok_state* tok) {
27
    assert(tok->tok_mode_stack_index >= 0);
28
    assert(tok->tok_mode_stack_index < MAXFSTRINGLEVEL);
29
    return &(tok->tok_mode_stack[tok->tok_mode_stack_index]);
30
}
31
static inline tokenizer_mode* TOK_NEXT_MODE(struct tok_state* tok) {
32
    assert(tok->tok_mode_stack_index >= 0);
33
    assert(tok->tok_mode_stack_index + 1 < MAXFSTRINGLEVEL);
34
    return &(tok->tok_mode_stack[++tok->tok_mode_stack_index]);
35
}
36
#else
37
1.88M
#define TOK_GET_MODE(tok) (&(tok->tok_mode_stack[tok->tok_mode_stack_index]))
38
16.5k
#define TOK_NEXT_MODE(tok) (&(tok->tok_mode_stack[++tok->tok_mode_stack_index]))
39
#endif
40
41
#define FTSTRING_MIDDLE(tok_mode) (tok_mode->string_kind == TSTRING ? TSTRING_MIDDLE : FSTRING_MIDDLE)
42
#define FTSTRING_END(tok_mode) (tok_mode->string_kind == TSTRING ? TSTRING_END : FSTRING_END)
43
33
#define TOK_GET_STRING_PREFIX(tok) (TOK_GET_MODE(tok)->string_kind == TSTRING ? 't' : 'f')
44
1.76M
#define MAKE_TOKEN(token_type) _PyLexer_token_setup(tok, token, token_type, p_start, p_end)
45
0
#define MAKE_TYPE_COMMENT_TOKEN(token_type, col_offset, end_col_offset) (\
46
0
                _PyLexer_type_comment_token_setup(tok, token, token_type, col_offset, end_col_offset, p_start, p_end))
47
48
/* Spaces in this constant are treated as "zero or more spaces or tabs" when
49
   tokenizing. */
50
static const char* type_comment_prefix = "# type: ";
51
52
static inline int
53
contains_null_bytes(const char* str, size_t size)
54
232k
{
55
232k
    return memchr(str, 0, size) != NULL;
56
232k
}
57
58
/* Get next char, updating state; error code goes into tok->done */
59
static int
60
tok_nextc(struct tok_state *tok)
61
10.9M
{
62
10.9M
    int rc;
63
11.1M
    for (;;) {
64
11.1M
        if (tok->cur != tok->inp) {
65
10.8M
            if ((unsigned int) tok->col_offset >= (unsigned int) INT_MAX) {
66
0
                tok->done = E_COLUMNOVERFLOW;
67
0
                return EOF;
68
0
            }
69
10.8M
            tok->col_offset++;
70
10.8M
            return Py_CHARMASK(*tok->cur++); /* Fast path */
71
10.8M
        }
72
283k
        if (tok->done != E_OK) {
73
33.6k
            return EOF;
74
33.6k
        }
75
249k
        rc = tok->underflow(tok);
76
#if defined(Py_DEBUG)
77
        if (tok->debug) {
78
            fprintf(stderr, "line[%d] = ", tok->lineno);
79
            _PyTokenizer_print_escape(stderr, tok->cur, tok->inp - tok->cur);
80
            fprintf(stderr, "  tok->done = %d\n", tok->done);
81
        }
82
#endif
83
249k
        if (!rc) {
84
17.0k
            tok->cur = tok->inp;
85
17.0k
            return EOF;
86
17.0k
        }
87
232k
        tok->line_start = tok->cur;
88
89
232k
        if (contains_null_bytes(tok->line_start, tok->inp - tok->line_start)) {
90
0
            _PyTokenizer_syntaxerror(tok, "source code cannot contain null bytes");
91
0
            tok->cur = tok->inp;
92
0
            return EOF;
93
0
        }
94
232k
    }
95
10.9M
    Py_UNREACHABLE();
96
10.9M
}
97
98
/* Back-up one character */
99
static void
100
tok_backup(struct tok_state *tok, int c)
101
3.71M
{
102
3.71M
    if (c != EOF) {
103
3.67M
        if (--tok->cur < tok->buf) {
104
0
            Py_FatalError("tokenizer beginning of buffer");
105
0
        }
106
3.67M
        if ((int)(unsigned char)*tok->cur != Py_CHARMASK(c)) {
107
0
            Py_FatalError("tok_backup: wrong character");
108
0
        }
109
3.67M
        tok->col_offset--;
110
3.67M
    }
111
3.71M
}
112
113
static int
114
22.6k
set_ftstring_expr(struct tok_state* tok, struct token *token, char c) {
115
22.6k
    assert(token != NULL);
116
22.6k
    assert(c == '}' || c == ':' || c == '!');
117
22.6k
    tokenizer_mode *tok_mode = TOK_GET_MODE(tok);
118
119
22.6k
    if (!(tok_mode->in_debug || tok_mode->string_kind == TSTRING) || token->metadata) {
120
13.1k
        return 0;
121
13.1k
    }
122
9.44k
    PyObject *res = NULL;
123
124
    // Look for a # character outside of string literals
125
9.44k
    int hash_detected = 0;
126
9.44k
    int in_string = 0;
127
9.44k
    char quote_char = 0;
128
129
1.46M
    for (Py_ssize_t i = 0; i < tok_mode->last_expr_size - tok_mode->last_expr_end; i++) {
130
1.45M
        char ch = tok_mode->last_expr_buffer[i];
131
132
        // Skip escaped characters
133
1.45M
        if (ch == '\\') {
134
28.8k
            i++;
135
28.8k
            continue;
136
28.8k
        }
137
138
        // Handle quotes
139
1.42M
        if (ch == '"' || ch == '\'') {
140
            // The following if/else block works becase there is an off number
141
            // of quotes in STRING tokens and the lexer only ever reaches this
142
            // function with valid STRING tokens.
143
            // For example: """hello"""
144
            // First quote: in_string = 1
145
            // Second quote: in_string = 0
146
            // Third quote: in_string = 1
147
188k
            if (!in_string) {
148
66.8k
                in_string = 1;
149
66.8k
                quote_char = ch;
150
66.8k
            }
151
121k
            else if (ch == quote_char) {
152
65.9k
                in_string = 0;
153
65.9k
            }
154
188k
            continue;
155
188k
        }
156
157
        // Check for # outside strings
158
1.23M
        if (ch == '#' && !in_string) {
159
873
            hash_detected = 1;
160
873
            break;
161
873
        }
162
1.23M
    }
163
    // If we found a # character in the expression, we need to handle comments
164
9.44k
    if (hash_detected) {
165
        // Allocate buffer for processed result
166
873
        char *result = (char *)PyMem_Malloc((tok_mode->last_expr_size - tok_mode->last_expr_end + 1) * sizeof(char));
167
873
        if (!result) {
168
0
            return -1;
169
0
        }
170
171
873
        Py_ssize_t i = 0;  // Input position
172
873
        Py_ssize_t j = 0;  // Output position
173
873
        in_string = 0;     // Whether we're in a string
174
873
        quote_char = 0;    // Current string quote char
175
176
        // Process each character
177
55.0k
        while (i < tok_mode->last_expr_size - tok_mode->last_expr_end) {
178
54.1k
            char ch = tok_mode->last_expr_buffer[i];
179
180
            // Handle string quotes
181
54.1k
            if (ch == '"' || ch == '\'') {
182
                // See comment above to understand this part
183
8.19k
                if (!in_string) {
184
3.35k
                    in_string = 1;
185
3.35k
                    quote_char = ch;
186
4.83k
                } else if (ch == quote_char) {
187
3.33k
                    in_string = 0;
188
3.33k
                }
189
8.19k
                result[j++] = ch;
190
8.19k
            }
191
            // Skip comments
192
46.0k
            else if (ch == '#' && !in_string) {
193
49.6k
                while (i < tok_mode->last_expr_size - tok_mode->last_expr_end &&
194
49.6k
                       tok_mode->last_expr_buffer[i] != '\n') {
195
48.5k
                    i++;
196
48.5k
                }
197
1.07k
                if (i < tok_mode->last_expr_size - tok_mode->last_expr_end) {
198
303
                    result[j++] = '\n';
199
303
                }
200
1.07k
            }
201
            // Copy other chars
202
44.9k
            else {
203
44.9k
                result[j++] = ch;
204
44.9k
            }
205
54.1k
            i++;
206
54.1k
        }
207
208
873
        result[j] = '\0';  // Null-terminate the result string
209
873
        res = PyUnicode_DecodeUTF8(result, j, NULL);
210
873
        PyMem_Free(result);
211
8.57k
    } else {
212
8.57k
        res = PyUnicode_DecodeUTF8(
213
8.57k
            tok_mode->last_expr_buffer,
214
8.57k
            tok_mode->last_expr_size - tok_mode->last_expr_end,
215
8.57k
            NULL
216
8.57k
        );
217
8.57k
    }
218
219
9.44k
    if (!res) {
220
9
        return -1;
221
9
    }
222
9.43k
    token->metadata = res;
223
9.43k
    return 0;
224
9.44k
}
225
226
int
227
_PyLexer_update_ftstring_expr(struct tok_state *tok, char cur)
228
63.5k
{
229
63.5k
    assert(tok->cur != NULL);
230
231
63.5k
    Py_ssize_t size = strlen(tok->cur);
232
63.5k
    tokenizer_mode *tok_mode = TOK_GET_MODE(tok);
233
234
63.5k
    switch (cur) {
235
0
       case 0:
236
0
            if (!tok_mode->last_expr_buffer || tok_mode->last_expr_end >= 0) {
237
0
                return 1;
238
0
            }
239
0
            char *new_buffer = PyMem_Realloc(
240
0
                tok_mode->last_expr_buffer,
241
0
                tok_mode->last_expr_size + size
242
0
            );
243
0
            if (new_buffer == NULL) {
244
0
                PyMem_Free(tok_mode->last_expr_buffer);
245
0
                goto error;
246
0
            }
247
0
            tok_mode->last_expr_buffer = new_buffer;
248
0
            strncpy(tok_mode->last_expr_buffer + tok_mode->last_expr_size, tok->cur, size);
249
0
            tok_mode->last_expr_size += size;
250
0
            break;
251
40.9k
        case '{':
252
40.9k
            if (tok_mode->last_expr_buffer != NULL) {
253
29.5k
                PyMem_Free(tok_mode->last_expr_buffer);
254
29.5k
            }
255
40.9k
            tok_mode->last_expr_buffer = PyMem_Malloc(size);
256
40.9k
            if (tok_mode->last_expr_buffer == NULL) {
257
0
                goto error;
258
0
            }
259
40.9k
            tok_mode->last_expr_size = size;
260
40.9k
            tok_mode->last_expr_end = -1;
261
40.9k
            strncpy(tok_mode->last_expr_buffer, tok->cur, size);
262
40.9k
            break;
263
18.2k
        case '}':
264
19.9k
        case '!':
265
19.9k
            tok_mode->last_expr_end = strlen(tok->start);
266
19.9k
            break;
267
2.67k
        case ':':
268
2.67k
            if (tok_mode->last_expr_end == -1) {
269
2.40k
               tok_mode->last_expr_end = strlen(tok->start);
270
2.40k
            }
271
2.67k
            break;
272
0
        default:
273
0
            Py_UNREACHABLE();
274
63.5k
    }
275
63.5k
    return 1;
276
0
error:
277
0
    tok->done = E_NOMEM;
278
0
    return 0;
279
63.5k
}
280
281
static int
282
lookahead(struct tok_state *tok, const char *test)
283
8.31k
{
284
8.31k
    const char *s = test;
285
8.31k
    int res = 0;
286
21.8k
    while (1) {
287
21.8k
        int c = tok_nextc(tok);
288
21.8k
        if (*s == 0) {
289
8.21k
            res = !is_potential_identifier_char(c);
290
8.21k
        }
291
13.6k
        else if (c == *s) {
292
13.5k
            s++;
293
13.5k
            continue;
294
13.5k
        }
295
296
8.31k
        tok_backup(tok, c);
297
21.8k
        while (s != test) {
298
13.5k
            tok_backup(tok, *--s);
299
13.5k
        }
300
8.31k
        return res;
301
21.8k
    }
302
8.31k
}
303
304
static int
305
100k
verify_end_of_number(struct tok_state *tok, int c, const char *kind) {
306
100k
    if (tok->tok_extra_tokens) {
307
        // When we are parsing extra tokens, we don't want to emit warnings
308
        // about invalid literals, because we want to be a bit more liberal.
309
0
        return 1;
310
0
    }
311
    /* Emit a deprecation warning only if the numeric literal is immediately
312
     * followed by one of keywords which can occur after a numeric literal
313
     * in valid code: "and", "else", "for", "if", "in", "is" and "or".
314
     * It allows to gradually deprecate existing valid code without adding
315
     * warning before error in most cases of invalid numeric literal (which
316
     * would be confusing and break existing tests).
317
     * Raise a syntax error with slightly better message than plain
318
     * "invalid syntax" if the numeric literal is immediately followed by
319
     * other keyword or identifier.
320
     */
321
100k
    int r = 0;
322
100k
    if (c == 'a') {
323
742
        r = lookahead(tok, "nd");
324
742
    }
325
99.8k
    else if (c == 'e') {
326
439
        r = lookahead(tok, "lse");
327
439
    }
328
99.4k
    else if (c == 'f') {
329
3.48k
        r = lookahead(tok, "or");
330
3.48k
    }
331
95.9k
    else if (c == 'i') {
332
2.41k
        int c2 = tok_nextc(tok);
333
2.41k
        if (c2 == 'f' || c2 == 'n' || c2 == 's') {
334
2.40k
            r = 1;
335
2.40k
        }
336
2.41k
        tok_backup(tok, c2);
337
2.41k
    }
338
93.5k
    else if (c == 'o') {
339
3.35k
        r = lookahead(tok, "r");
340
3.35k
    }
341
90.1k
    else if (c == 'n') {
342
291
        r = lookahead(tok, "ot");
343
291
    }
344
100k
    if (r) {
345
10.6k
        tok_backup(tok, c);
346
10.6k
        if (_PyTokenizer_parser_warn(tok, PyExc_SyntaxWarning,
347
10.6k
                "invalid %s literal", kind))
348
0
        {
349
0
            return 0;
350
0
        }
351
10.6k
        tok_nextc(tok);
352
10.6k
    }
353
90.0k
    else /* In future releases, only error will remain. */
354
90.0k
    if (c < 128 && is_potential_identifier_char(c)) {
355
194
        tok_backup(tok, c);
356
194
        _PyTokenizer_syntaxerror(tok, "invalid %s literal", kind);
357
194
        return 0;
358
194
    }
359
100k
    return 1;
360
100k
}
361
362
/* Verify that the identifier follows PEP 3131. */
363
static int
364
verify_identifier(struct tok_state *tok)
365
13.4k
{
366
13.4k
    if (tok->tok_extra_tokens) {
367
0
        return 1;
368
0
    }
369
13.4k
    PyObject *s;
370
13.4k
    if (tok->decoding_erred)
371
0
        return 0;
372
13.4k
    s = PyUnicode_DecodeUTF8(tok->start, tok->cur - tok->start, NULL);
373
13.4k
    if (s == NULL) {
374
995
        if (PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) {
375
995
            tok->done = E_DECODE;
376
995
        }
377
0
        else {
378
0
            tok->done = E_ERROR;
379
0
        }
380
995
        return 0;
381
995
    }
382
12.4k
    Py_ssize_t invalid = _PyUnicode_ScanIdentifier(s);
383
12.4k
    assert(invalid >= 0);
384
12.4k
    assert(PyUnicode_GET_LENGTH(s) > 0);
385
12.4k
    if (invalid < PyUnicode_GET_LENGTH(s)) {
386
682
        Py_UCS4 ch = PyUnicode_READ_CHAR(s, invalid);
387
682
        if (invalid + 1 < PyUnicode_GET_LENGTH(s)) {
388
            /* Determine the offset in UTF-8 encoded input */
389
473
            Py_SETREF(s, PyUnicode_Substring(s, 0, invalid + 1));
390
473
            if (s != NULL) {
391
473
                Py_SETREF(s, PyUnicode_AsUTF8String(s));
392
473
            }
393
473
            if (s == NULL) {
394
0
                tok->done = E_ERROR;
395
0
                return 0;
396
0
            }
397
473
            tok->cur = (char *)tok->start + PyBytes_GET_SIZE(s);
398
473
        }
399
682
        Py_DECREF(s);
400
682
        if (Py_UNICODE_ISPRINTABLE(ch)) {
401
387
            _PyTokenizer_syntaxerror(tok, "invalid character '%c' (U+%04X)", ch, ch);
402
387
        }
403
295
        else {
404
295
            _PyTokenizer_syntaxerror(tok, "invalid non-printable character U+%04X", ch);
405
295
        }
406
682
        return 0;
407
682
    }
408
11.7k
    Py_DECREF(s);
409
11.7k
    return 1;
410
12.4k
}
411
412
static int
413
tok_decimal_tail(struct tok_state *tok)
414
80.6k
{
415
80.6k
    int c;
416
417
81.1k
    while (1) {
418
231k
        do {
419
231k
            c = tok_nextc(tok);
420
231k
        } while (Py_ISDIGIT(c));
421
81.1k
        if (c != '_') {
422
80.6k
            break;
423
80.6k
        }
424
494
        c = tok_nextc(tok);
425
494
        if (!Py_ISDIGIT(c)) {
426
16
            tok_backup(tok, c);
427
16
            _PyTokenizer_syntaxerror(tok, "invalid decimal literal");
428
16
            return 0;
429
16
        }
430
494
    }
431
80.6k
    return c;
432
80.6k
}
433
434
static inline int
435
1.07k
tok_continuation_line(struct tok_state *tok) {
436
1.07k
    int c = tok_nextc(tok);
437
1.07k
    if (c == '\r') {
438
71
        c = tok_nextc(tok);
439
71
    }
440
1.07k
    if (c != '\n') {
441
56
        tok->done = E_LINECONT;
442
56
        return -1;
443
56
    }
444
1.02k
    c = tok_nextc(tok);
445
1.02k
    if (c == EOF) {
446
47
        tok->done = E_EOF;
447
47
        tok->cur = tok->inp;
448
47
        return -1;
449
974
    } else {
450
974
        tok_backup(tok, c);
451
974
    }
452
974
    return c;
453
1.02k
}
454
455
static int
456
maybe_raise_syntax_error_for_string_prefixes(struct tok_state *tok,
457
                                             int saw_b, int saw_r, int saw_u,
458
21.8k
                                             int saw_f, int saw_t) {
459
    // Supported: rb, rf, rt (in any order)
460
    // Unsupported: ub, ur, uf, ut, bf, bt, ft (in any order)
461
462
21.8k
#define RETURN_SYNTAX_ERROR(PREFIX1, PREFIX2)                             \
463
21.8k
    do {                                                                  \
464
7
        (void)_PyTokenizer_syntaxerror_known_range(                       \
465
7
            tok, (int)(tok->start + 1 - tok->line_start),                 \
466
7
            (int)(tok->cur - tok->line_start),                            \
467
7
            "'" PREFIX1 "' and '" PREFIX2 "' prefixes are incompatible"); \
468
7
        return -1;                                                        \
469
7
    } while (0)
470
471
21.8k
    if (saw_u && saw_b) {
472
1
        RETURN_SYNTAX_ERROR("u", "b");
473
1
    }
474
21.8k
    if (saw_u && saw_r) {
475
1
        RETURN_SYNTAX_ERROR("u", "r");
476
1
    }
477
21.8k
    if (saw_u && saw_f) {
478
1
        RETURN_SYNTAX_ERROR("u", "f");
479
1
    }
480
21.8k
    if (saw_u && saw_t) {
481
1
        RETURN_SYNTAX_ERROR("u", "t");
482
1
    }
483
484
21.8k
    if (saw_b && saw_f) {
485
1
        RETURN_SYNTAX_ERROR("b", "f");
486
1
    }
487
21.8k
    if (saw_b && saw_t) {
488
1
        RETURN_SYNTAX_ERROR("b", "t");
489
1
    }
490
491
21.8k
    if (saw_f && saw_t) {
492
1
        RETURN_SYNTAX_ERROR("f", "t");
493
1
    }
494
495
21.8k
#undef RETURN_SYNTAX_ERROR
496
497
21.8k
    return 0;
498
21.8k
}
499
500
static int
501
tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct token *token)
502
1.72M
{
503
1.72M
    int c;
504
1.72M
    int blankline, nonascii;
505
506
1.72M
    const char *p_start = NULL;
507
1.72M
    const char *p_end = NULL;
508
1.81M
  nextline:
509
1.81M
    tok->start = NULL;
510
1.81M
    tok->starting_col_offset = -1;
511
1.81M
    blankline = 0;
512
513
514
    /* Get indentation level */
515
1.81M
    if (tok->atbol) {
516
231k
        int col = 0;
517
231k
        int altcol = 0;
518
231k
        tok->atbol = 0;
519
231k
        int cont_line_col = 0;
520
925k
        for (;;) {
521
925k
            c = tok_nextc(tok);
522
925k
            if (c == ' ') {
523
690k
                col++, altcol++;
524
690k
            }
525
234k
            else if (c == '\t') {
526
820
                col = (col / tok->tabsize + 1) * tok->tabsize;
527
820
                altcol = (altcol / ALTTABSIZE + 1) * ALTTABSIZE;
528
820
            }
529
233k
            else if (c == '\014')  {/* Control-L (formfeed) */
530
1.79k
                col = altcol = 0; /* For Emacs users */
531
1.79k
            }
532
232k
            else if (c == '\\') {
533
                // Indentation cannot be split over multiple physical lines
534
                // using backslashes. This means that if we found a backslash
535
                // preceded by whitespace, **the first one we find** determines
536
                // the level of indentation of whatever comes next.
537
652
                cont_line_col = cont_line_col ? cont_line_col : col;
538
652
                if ((c = tok_continuation_line(tok)) == -1) {
539
39
                    return MAKE_TOKEN(ERRORTOKEN);
540
39
                }
541
652
            }
542
231k
            else {
543
231k
                break;
544
231k
            }
545
925k
        }
546
231k
        tok_backup(tok, c);
547
231k
        if (c == '#' || c == '\n' || c == '\r') {
548
            /* Lines with only whitespace and/or comments
549
               shouldn't affect the indentation and are
550
               not passed to the parser as NEWLINE tokens,
551
               except *totally* empty lines in interactive
552
               mode, which signal the end of a command group. */
553
48.1k
            if (col == 0 && c == '\n' && tok->prompt != NULL) {
554
0
                blankline = 0; /* Let it through */
555
0
            }
556
48.1k
            else if (tok->prompt != NULL && tok->lineno == 1) {
557
                /* In interactive mode, if the first line contains
558
                   only spaces and/or a comment, let it through. */
559
0
                blankline = 0;
560
0
                col = altcol = 0;
561
0
            }
562
48.1k
            else {
563
48.1k
                blankline = 1; /* Ignore completely */
564
48.1k
            }
565
            /* We can't jump back right here since we still
566
               may need to skip to the end of a comment */
567
48.1k
        }
568
231k
        if (!blankline && tok->level == 0) {
569
142k
            col = cont_line_col ? cont_line_col : col;
570
142k
            altcol = cont_line_col ? cont_line_col : altcol;
571
142k
            if (col == tok->indstack[tok->indent]) {
572
                /* No change */
573
103k
                if (altcol != tok->altindstack[tok->indent]) {
574
1
                    return MAKE_TOKEN(_PyTokenizer_indenterror(tok));
575
1
                }
576
103k
            }
577
38.9k
            else if (col > tok->indstack[tok->indent]) {
578
                /* Indent -- always one */
579
21.8k
                if (tok->indent+1 >= MAXINDENT) {
580
0
                    tok->done = E_TOODEEP;
581
0
                    tok->cur = tok->inp;
582
0
                    return MAKE_TOKEN(ERRORTOKEN);
583
0
                }
584
21.8k
                if (altcol <= tok->altindstack[tok->indent]) {
585
3
                    return MAKE_TOKEN(_PyTokenizer_indenterror(tok));
586
3
                }
587
21.7k
                tok->pendin++;
588
21.7k
                tok->indstack[++tok->indent] = col;
589
21.7k
                tok->altindstack[tok->indent] = altcol;
590
21.7k
            }
591
17.1k
            else /* col < tok->indstack[tok->indent] */ {
592
                /* Dedent -- any number, must be consistent */
593
38.1k
                while (tok->indent > 0 &&
594
38.1k
                    col < tok->indstack[tok->indent]) {
595
20.9k
                    tok->pendin--;
596
20.9k
                    tok->indent--;
597
20.9k
                }
598
17.1k
                if (col != tok->indstack[tok->indent]) {
599
8
                    tok->done = E_DEDENT;
600
8
                    tok->cur = tok->inp;
601
8
                    return MAKE_TOKEN(ERRORTOKEN);
602
8
                }
603
17.1k
                if (altcol != tok->altindstack[tok->indent]) {
604
1
                    return MAKE_TOKEN(_PyTokenizer_indenterror(tok));
605
1
                }
606
17.1k
            }
607
142k
        }
608
231k
    }
609
610
1.81M
    tok->start = tok->cur;
611
1.81M
    tok->starting_col_offset = tok->col_offset;
612
613
    /* Return pending indents/dedents */
614
1.81M
    if (tok->pendin != 0) {
615
42.7k
        if (tok->pendin < 0) {
616
20.9k
            if (tok->tok_extra_tokens) {
617
0
                p_start = tok->cur;
618
0
                p_end = tok->cur;
619
0
            }
620
20.9k
            tok->pendin++;
621
20.9k
            return MAKE_TOKEN(DEDENT);
622
20.9k
        }
623
21.7k
        else {
624
21.7k
            if (tok->tok_extra_tokens) {
625
0
                p_start = tok->buf;
626
0
                p_end = tok->cur;
627
0
            }
628
21.7k
            tok->pendin--;
629
21.7k
            return MAKE_TOKEN(INDENT);
630
21.7k
        }
631
42.7k
    }
632
633
    /* Peek ahead at the next character */
634
1.77M
    c = tok_nextc(tok);
635
1.77M
    tok_backup(tok, c);
636
637
1.77M
 again:
638
1.77M
    tok->start = NULL;
639
    /* Skip spaces */
640
2.12M
    do {
641
2.12M
        c = tok_nextc(tok);
642
2.12M
    } while (c == ' ' || c == '\t' || c == '\014');
643
644
    /* Set start of current token */
645
1.77M
    tok->start = tok->cur == NULL ? NULL : tok->cur - 1;
646
1.77M
    tok->starting_col_offset = tok->col_offset - 1;
647
648
    /* Skip comment, unless it's a type comment */
649
1.77M
    if (c == '#') {
650
651
42.3k
        const char* p = NULL;
652
42.3k
        const char *prefix, *type_start;
653
42.3k
        int current_starting_col_offset;
654
655
1.30M
        while (c != EOF && c != '\n' && c != '\r') {
656
1.26M
            c = tok_nextc(tok);
657
1.26M
        }
658
659
42.3k
        if (tok->tok_extra_tokens) {
660
0
            p = tok->start;
661
0
        }
662
663
42.3k
        if (tok->type_comments) {
664
0
            p = tok->start;
665
0
            current_starting_col_offset = tok->starting_col_offset;
666
0
            prefix = type_comment_prefix;
667
0
            while (*prefix && p < tok->cur) {
668
0
                if (*prefix == ' ') {
669
0
                    while (*p == ' ' || *p == '\t') {
670
0
                        p++;
671
0
                        current_starting_col_offset++;
672
0
                    }
673
0
                } else if (*prefix == *p) {
674
0
                    p++;
675
0
                    current_starting_col_offset++;
676
0
                } else {
677
0
                    break;
678
0
                }
679
680
0
                prefix++;
681
0
            }
682
683
            /* This is a type comment if we matched all of type_comment_prefix. */
684
0
            if (!*prefix) {
685
0
                int is_type_ignore = 1;
686
                // +6 in order to skip the word 'ignore'
687
0
                const char *ignore_end = p + 6;
688
0
                const int ignore_end_col_offset = current_starting_col_offset + 6;
689
0
                tok_backup(tok, c);  /* don't eat the newline or EOF */
690
691
0
                type_start = p;
692
693
                /* A TYPE_IGNORE is "type: ignore" followed by the end of the token
694
                 * or anything ASCII and non-alphanumeric. */
695
0
                is_type_ignore = (
696
0
                    tok->cur >= ignore_end && memcmp(p, "ignore", 6) == 0
697
0
                    && !(tok->cur > ignore_end
698
0
                         && ((unsigned char)ignore_end[0] >= 128 || Py_ISALNUM(ignore_end[0]))));
699
700
0
                if (is_type_ignore) {
701
0
                    p_start = ignore_end;
702
0
                    p_end = tok->cur;
703
704
                    /* If this type ignore is the only thing on the line, consume the newline also. */
705
0
                    if (blankline) {
706
0
                        tok_nextc(tok);
707
0
                        tok->atbol = 1;
708
0
                    }
709
0
                    return MAKE_TYPE_COMMENT_TOKEN(TYPE_IGNORE, ignore_end_col_offset, tok->col_offset);
710
0
                } else {
711
0
                    p_start = type_start;
712
0
                    p_end = tok->cur;
713
0
                    return MAKE_TYPE_COMMENT_TOKEN(TYPE_COMMENT, current_starting_col_offset, tok->col_offset);
714
0
                }
715
0
            }
716
0
        }
717
42.3k
        if (tok->tok_extra_tokens) {
718
0
            tok_backup(tok, c);  /* don't eat the newline or EOF */
719
0
            p_start = p;
720
0
            p_end = tok->cur;
721
0
            tok->comment_newline = blankline;
722
0
            return MAKE_TOKEN(COMMENT);
723
0
        }
724
42.3k
    }
725
726
1.77M
    if (tok->done == E_INTERACT_STOP) {
727
0
        return MAKE_TOKEN(ENDMARKER);
728
0
    }
729
730
    /* Check for EOF and errors now */
731
1.77M
    if (c == EOF) {
732
16.8k
        if (tok->level) {
733
4.09k
            return MAKE_TOKEN(ERRORTOKEN);
734
4.09k
        }
735
12.7k
        return MAKE_TOKEN(tok->done == E_EOF ? ENDMARKER : ERRORTOKEN);
736
16.8k
    }
737
738
    /* Identifier (most frequent token!) */
739
1.75M
    nonascii = 0;
740
1.75M
    if (is_potential_identifier_start(c)) {
741
        /* Process the various legal combinations of b"", r"", u"", and f"". */
742
528k
        int saw_b = 0, saw_r = 0, saw_u = 0, saw_f = 0, saw_t = 0;
743
649k
        while (1) {
744
649k
            if (!saw_b && (c == 'b' || c == 'B')) {
745
20.8k
                saw_b = 1;
746
20.8k
            }
747
            /* Since this is a backwards compatibility support literal we don't
748
               want to support it in arbitrary order like byte literals. */
749
628k
            else if (!saw_u && (c == 'u'|| c == 'U')) {
750
6.93k
                saw_u = 1;
751
6.93k
            }
752
            /* ur"" and ru"" are not supported */
753
622k
            else if (!saw_r && (c == 'r' || c == 'R')) {
754
38.1k
                saw_r = 1;
755
38.1k
            }
756
583k
            else if (!saw_f && (c == 'f' || c == 'F')) {
757
44.6k
                saw_f = 1;
758
44.6k
            }
759
539k
            else if (!saw_t && (c == 't' || c == 'T')) {
760
32.1k
                saw_t = 1;
761
32.1k
            }
762
507k
            else {
763
507k
                break;
764
507k
            }
765
142k
            c = tok_nextc(tok);
766
142k
            if (c == '"' || c == '\'') {
767
                // Raise error on incompatible string prefixes:
768
21.8k
                int status = maybe_raise_syntax_error_for_string_prefixes(
769
21.8k
                    tok, saw_b, saw_r, saw_u, saw_f, saw_t);
770
21.8k
                if (status < 0) {
771
7
                    return MAKE_TOKEN(ERRORTOKEN);
772
7
                }
773
774
                // Handle valid f or t string creation:
775
21.8k
                if (saw_f || saw_t) {
776
16.5k
                    goto f_string_quote;
777
16.5k
                }
778
5.27k
                goto letter_quote;
779
21.8k
            }
780
142k
        }
781
2.34M
        while (is_potential_identifier_char(c)) {
782
1.83M
            if (c >= 128) {
783
180k
                nonascii = 1;
784
180k
            }
785
1.83M
            c = tok_nextc(tok);
786
1.83M
        }
787
507k
        tok_backup(tok, c);
788
507k
        if (nonascii && !verify_identifier(tok)) {
789
1.67k
            return MAKE_TOKEN(ERRORTOKEN);
790
1.67k
        }
791
792
505k
        p_start = tok->start;
793
505k
        p_end = tok->cur;
794
795
505k
        return MAKE_TOKEN(NAME);
796
507k
    }
797
798
1.22M
    if (c == '\r') {
799
438
        c = tok_nextc(tok);
800
438
    }
801
802
    /* Newline */
803
1.22M
    if (c == '\n') {
804
210k
        tok->atbol = 1;
805
210k
        if (blankline || tok->level > 0) {
806
89.0k
            if (tok->tok_extra_tokens) {
807
0
                if (tok->comment_newline) {
808
0
                    tok->comment_newline = 0;
809
0
                }
810
0
                p_start = tok->start;
811
0
                p_end = tok->cur;
812
0
                return MAKE_TOKEN(NL);
813
0
            }
814
89.0k
            goto nextline;
815
89.0k
        }
816
121k
        if (tok->comment_newline && tok->tok_extra_tokens) {
817
0
            tok->comment_newline = 0;
818
0
            p_start = tok->start;
819
0
            p_end = tok->cur;
820
0
            return MAKE_TOKEN(NL);
821
0
        }
822
121k
        p_start = tok->start;
823
121k
        p_end = tok->cur - 1; /* Leave '\n' out of the string */
824
121k
        tok->cont_line = 0;
825
121k
        return MAKE_TOKEN(NEWLINE);
826
121k
    }
827
828
    /* Period or number starting with period? */
829
1.01M
    if (c == '.') {
830
34.8k
        c = tok_nextc(tok);
831
34.8k
        if (Py_ISDIGIT(c)) {
832
2.92k
            goto fraction;
833
31.9k
        } else if (c == '.') {
834
3.14k
            c = tok_nextc(tok);
835
3.14k
            if (c == '.') {
836
2.49k
                p_start = tok->start;
837
2.49k
                p_end = tok->cur;
838
2.49k
                return MAKE_TOKEN(ELLIPSIS);
839
2.49k
            }
840
654
            else {
841
654
                tok_backup(tok, c);
842
654
            }
843
654
            tok_backup(tok, '.');
844
654
        }
845
28.8k
        else {
846
28.8k
            tok_backup(tok, c);
847
28.8k
        }
848
29.4k
        p_start = tok->start;
849
29.4k
        p_end = tok->cur;
850
29.4k
        return MAKE_TOKEN(DOT);
851
34.8k
    }
852
853
    /* Number */
854
981k
    if (Py_ISDIGIT(c)) {
855
97.8k
        if (c == '0') {
856
            /* Hex, octal or binary -- maybe. */
857
34.8k
            c = tok_nextc(tok);
858
34.8k
            if (c == 'x' || c == 'X') {
859
                /* Hex */
860
15.8k
                c = tok_nextc(tok);
861
16.0k
                do {
862
16.0k
                    if (c == '_') {
863
204
                        c = tok_nextc(tok);
864
204
                    }
865
16.0k
                    if (!Py_ISXDIGIT(c)) {
866
19
                        tok_backup(tok, c);
867
19
                        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid hexadecimal literal"));
868
19
                    }
869
78.6k
                    do {
870
78.6k
                        c = tok_nextc(tok);
871
78.6k
                    } while (Py_ISXDIGIT(c));
872
15.9k
                } while (c == '_');
873
15.7k
                if (!verify_end_of_number(tok, c, "hexadecimal")) {
874
2
                    return MAKE_TOKEN(ERRORTOKEN);
875
2
                }
876
15.7k
            }
877
19.0k
            else if (c == 'o' || c == 'O') {
878
                /* Octal */
879
626
                c = tok_nextc(tok);
880
1.07k
                do {
881
1.07k
                    if (c == '_') {
882
456
                        c = tok_nextc(tok);
883
456
                    }
884
1.07k
                    if (c < '0' || c >= '8') {
885
20
                        if (Py_ISDIGIT(c)) {
886
1
                            return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok,
887
1
                                    "invalid digit '%c' in octal literal", c));
888
1
                        }
889
19
                        else {
890
19
                            tok_backup(tok, c);
891
19
                            return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid octal literal"));
892
19
                        }
893
20
                    }
894
2.85k
                    do {
895
2.85k
                        c = tok_nextc(tok);
896
2.85k
                    } while ('0' <= c && c < '8');
897
1.05k
                } while (c == '_');
898
606
                if (Py_ISDIGIT(c)) {
899
1
                    return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok,
900
1
                            "invalid digit '%c' in octal literal", c));
901
1
                }
902
605
                if (!verify_end_of_number(tok, c, "octal")) {
903
2
                    return MAKE_TOKEN(ERRORTOKEN);
904
2
                }
905
605
            }
906
18.4k
            else if (c == 'b' || c == 'B') {
907
                /* Binary */
908
555
                c = tok_nextc(tok);
909
920
                do {
910
920
                    if (c == '_') {
911
374
                        c = tok_nextc(tok);
912
374
                    }
913
920
                    if (c != '0' && c != '1') {
914
18
                        if (Py_ISDIGIT(c)) {
915
1
                            return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid digit '%c' in binary literal", c));
916
1
                        }
917
17
                        else {
918
17
                            tok_backup(tok, c);
919
17
                            return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid binary literal"));
920
17
                        }
921
18
                    }
922
3.94k
                    do {
923
3.94k
                        c = tok_nextc(tok);
924
3.94k
                    } while (c == '0' || c == '1');
925
902
                } while (c == '_');
926
537
                if (Py_ISDIGIT(c)) {
927
2
                    return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid digit '%c' in binary literal", c));
928
2
                }
929
535
                if (!verify_end_of_number(tok, c, "binary")) {
930
2
                    return MAKE_TOKEN(ERRORTOKEN);
931
2
                }
932
535
            }
933
17.8k
            else {
934
17.8k
                int nonzero = 0;
935
                /* maybe old-style octal; c is first char of it */
936
                /* in any case, allow '0' as a literal */
937
19.2k
                while (1) {
938
19.2k
                    if (c == '_') {
939
93
                        c = tok_nextc(tok);
940
93
                        if (!Py_ISDIGIT(c)) {
941
3
                            tok_backup(tok, c);
942
3
                            return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid decimal literal"));
943
3
                        }
944
93
                    }
945
19.2k
                    if (c != '0') {
946
17.8k
                        break;
947
17.8k
                    }
948
1.32k
                    c = tok_nextc(tok);
949
1.32k
                }
950
17.8k
                char* zeros_end = tok->cur;
951
17.8k
                if (Py_ISDIGIT(c)) {
952
630
                    nonzero = 1;
953
630
                    c = tok_decimal_tail(tok);
954
630
                    if (c == 0) {
955
1
                        return MAKE_TOKEN(ERRORTOKEN);
956
1
                    }
957
630
                }
958
17.8k
                if (c == '.') {
959
892
                    c = tok_nextc(tok);
960
892
                    goto fraction;
961
892
                }
962
16.9k
                else if (c == 'e' || c == 'E') {
963
995
                    goto exponent;
964
995
                }
965
15.9k
                else if (c == 'j' || c == 'J') {
966
906
                    goto imaginary;
967
906
                }
968
15.0k
                else if (nonzero && !tok->tok_extra_tokens) {
969
                    /* Old-style octal: now disallowed. */
970
16
                    tok_backup(tok, c);
971
16
                    return MAKE_TOKEN(_PyTokenizer_syntaxerror_known_range(
972
16
                            tok, (int)(tok->start + 1 - tok->line_start),
973
16
                            (int)(zeros_end - tok->line_start),
974
16
                            "leading zeros in decimal integer "
975
16
                            "literals are not permitted; "
976
16
                            "use an 0o prefix for octal integers"));
977
16
                }
978
15.0k
                if (!verify_end_of_number(tok, c, "decimal")) {
979
29
                    return MAKE_TOKEN(ERRORTOKEN);
980
29
                }
981
15.0k
            }
982
34.8k
        }
983
62.9k
        else {
984
            /* Decimal */
985
62.9k
            c = tok_decimal_tail(tok);
986
62.9k
            if (c == 0) {
987
13
                return MAKE_TOKEN(ERRORTOKEN);
988
13
            }
989
62.9k
            {
990
                /* Accept floating-point numbers. */
991
62.9k
                if (c == '.') {
992
4.11k
                    c = tok_nextc(tok);
993
7.93k
        fraction:
994
                    /* Fraction */
995
7.93k
                    if (Py_ISDIGIT(c)) {
996
5.97k
                        c = tok_decimal_tail(tok);
997
5.97k
                        if (c == 0) {
998
1
                            return MAKE_TOKEN(ERRORTOKEN);
999
1
                        }
1000
5.97k
                    }
1001
7.93k
                }
1002
66.7k
                if (c == 'e' || c == 'E') {
1003
10.5k
                    int e;
1004
11.5k
                  exponent:
1005
11.5k
                    e = c;
1006
                    /* Exponent part */
1007
11.5k
                    c = tok_nextc(tok);
1008
11.5k
                    if (c == '+' || c == '-') {
1009
4.15k
                        c = tok_nextc(tok);
1010
4.15k
                        if (!Py_ISDIGIT(c)) {
1011
14
                            tok_backup(tok, c);
1012
14
                            return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid decimal literal"));
1013
14
                        }
1014
7.41k
                    } else if (!Py_ISDIGIT(c)) {
1015
443
                        tok_backup(tok, c);
1016
443
                        if (!verify_end_of_number(tok, e, "decimal")) {
1017
37
                            return MAKE_TOKEN(ERRORTOKEN);
1018
37
                        }
1019
406
                        tok_backup(tok, e);
1020
406
                        p_start = tok->start;
1021
406
                        p_end = tok->cur;
1022
406
                        return MAKE_TOKEN(NUMBER);
1023
443
                    }
1024
11.1k
                    c = tok_decimal_tail(tok);
1025
11.1k
                    if (c == 0) {
1026
1
                        return MAKE_TOKEN(ERRORTOKEN);
1027
1
                    }
1028
11.1k
                }
1029
67.2k
                if (c == 'j' || c == 'J') {
1030
                    /* Imaginary part */
1031
4.60k
        imaginary:
1032
4.60k
                    c = tok_nextc(tok);
1033
4.60k
                    if (!verify_end_of_number(tok, c, "imaginary")) {
1034
10
                        return MAKE_TOKEN(ERRORTOKEN);
1035
10
                    }
1036
4.60k
                }
1037
63.5k
                else if (!verify_end_of_number(tok, c, "decimal")) {
1038
112
                    return MAKE_TOKEN(ERRORTOKEN);
1039
112
                }
1040
67.2k
            }
1041
67.2k
        }
1042
100k
        tok_backup(tok, c);
1043
100k
        p_start = tok->start;
1044
100k
        p_end = tok->cur;
1045
100k
        return MAKE_TOKEN(NUMBER);
1046
97.8k
    }
1047
1048
900k
  f_string_quote:
1049
900k
    if (((Py_TOLOWER(*tok->start) == 'f' || Py_TOLOWER(*tok->start) == 'r' || Py_TOLOWER(*tok->start) == 't')
1050
900k
        && (c == '\'' || c == '"'))) {
1051
1052
16.5k
        int quote = c;
1053
16.5k
        int quote_size = 1;             /* 1 or 3 */
1054
1055
        /* Nodes of type STRING, especially multi line strings
1056
           must be handled differently in order to get both
1057
           the starting line number and the column offset right.
1058
           (cf. issue 16806) */
1059
16.5k
        tok->first_lineno = tok->lineno;
1060
16.5k
        tok->multi_line_start = tok->line_start;
1061
1062
        /* Find the quote size and start of string */
1063
16.5k
        int after_quote = tok_nextc(tok);
1064
16.5k
        if (after_quote == quote) {
1065
2.37k
            int after_after_quote = tok_nextc(tok);
1066
2.37k
            if (after_after_quote == quote) {
1067
765
                quote_size = 3;
1068
765
            }
1069
1.61k
            else {
1070
                // TODO: Check this
1071
1.61k
                tok_backup(tok, after_after_quote);
1072
1.61k
                tok_backup(tok, after_quote);
1073
1.61k
            }
1074
2.37k
        }
1075
16.5k
        if (after_quote != quote) {
1076
14.1k
            tok_backup(tok, after_quote);
1077
14.1k
        }
1078
1079
1080
16.5k
        p_start = tok->start;
1081
16.5k
        p_end = tok->cur;
1082
16.5k
        if (tok->tok_mode_stack_index + 1 >= MAXFSTRINGLEVEL) {
1083
2
            return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "too many nested f-strings or t-strings"));
1084
2
        }
1085
16.5k
        tokenizer_mode *the_current_tok = TOK_NEXT_MODE(tok);
1086
16.5k
        the_current_tok->kind = TOK_FSTRING_MODE;
1087
16.5k
        the_current_tok->quote = quote;
1088
16.5k
        the_current_tok->quote_size = quote_size;
1089
16.5k
        the_current_tok->start = tok->start;
1090
16.5k
        the_current_tok->multi_line_start = tok->line_start;
1091
16.5k
        the_current_tok->first_line = tok->lineno;
1092
16.5k
        the_current_tok->start_offset = -1;
1093
16.5k
        the_current_tok->multi_line_start_offset = -1;
1094
16.5k
        the_current_tok->last_expr_buffer = NULL;
1095
16.5k
        the_current_tok->last_expr_size = 0;
1096
16.5k
        the_current_tok->last_expr_end = -1;
1097
16.5k
        the_current_tok->in_format_spec = 0;
1098
16.5k
        the_current_tok->in_debug = 0;
1099
1100
16.5k
        enum string_kind_t string_kind = FSTRING;
1101
16.5k
        switch (*tok->start) {
1102
923
            case 'T':
1103
4.56k
            case 't':
1104
4.56k
                the_current_tok->raw = Py_TOLOWER(*(tok->start + 1)) == 'r';
1105
4.56k
                string_kind = TSTRING;
1106
4.56k
                break;
1107
1.73k
            case 'F':
1108
11.4k
            case 'f':
1109
11.4k
                the_current_tok->raw = Py_TOLOWER(*(tok->start + 1)) == 'r';
1110
11.4k
                break;
1111
126
            case 'R':
1112
522
            case 'r':
1113
522
                the_current_tok->raw = 1;
1114
522
                if (Py_TOLOWER(*(tok->start + 1)) == 't') {
1115
200
                    string_kind = TSTRING;
1116
200
                }
1117
522
                break;
1118
0
            default:
1119
0
                Py_UNREACHABLE();
1120
16.5k
        }
1121
1122
16.5k
        the_current_tok->string_kind = string_kind;
1123
16.5k
        the_current_tok->curly_bracket_depth = 0;
1124
16.5k
        the_current_tok->curly_bracket_expr_start_depth = -1;
1125
16.5k
        return string_kind == TSTRING ? MAKE_TOKEN(TSTRING_START) : MAKE_TOKEN(FSTRING_START);
1126
16.5k
    }
1127
1128
889k
  letter_quote:
1129
    /* String */
1130
889k
    if (c == '\'' || c == '"') {
1131
59.2k
        int quote = c;
1132
59.2k
        int quote_size = 1;             /* 1 or 3 */
1133
59.2k
        int end_quote_size = 0;
1134
59.2k
        int has_escaped_quote = 0;
1135
1136
        /* Nodes of type STRING, especially multi line strings
1137
           must be handled differently in order to get both
1138
           the starting line number and the column offset right.
1139
           (cf. issue 16806) */
1140
59.2k
        tok->first_lineno = tok->lineno;
1141
59.2k
        tok->multi_line_start = tok->line_start;
1142
1143
        /* Find the quote size and start of string */
1144
59.2k
        c = tok_nextc(tok);
1145
59.2k
        if (c == quote) {
1146
10.6k
            c = tok_nextc(tok);
1147
10.6k
            if (c == quote) {
1148
2.57k
                quote_size = 3;
1149
2.57k
            }
1150
8.04k
            else {
1151
8.04k
                end_quote_size = 1;     /* empty string found */
1152
8.04k
            }
1153
10.6k
        }
1154
59.2k
        if (c != quote) {
1155
56.6k
            tok_backup(tok, c);
1156
56.6k
        }
1157
1158
        /* Get rest of string */
1159
1.17M
        while (end_quote_size != quote_size) {
1160
1.11M
            c = tok_nextc(tok);
1161
1.11M
            if (tok->done == E_ERROR) {
1162
0
                return MAKE_TOKEN(ERRORTOKEN);
1163
0
            }
1164
1.11M
            if (tok->done == E_DECODE) {
1165
0
                break;
1166
0
            }
1167
1.11M
            if (c == EOF || (quote_size == 1 && c == '\n')) {
1168
398
                assert(tok->multi_line_start != NULL);
1169
                // shift the tok_state's location into
1170
                // the start of string, and report the error
1171
                // from the initial quote character
1172
398
                tok->cur = (char *)tok->start;
1173
398
                tok->cur++;
1174
398
                tok->line_start = tok->multi_line_start;
1175
398
                int start = tok->lineno;
1176
398
                tok->lineno = tok->first_lineno;
1177
1178
398
                if (INSIDE_FSTRING(tok)) {
1179
                    /* When we are in an f-string, before raising the
1180
                     * unterminated string literal error, check whether
1181
                     * does the initial quote matches with f-strings quotes
1182
                     * and if it is, then this must be a missing '}' token
1183
                     * so raise the proper error */
1184
28
                    tokenizer_mode *the_current_tok = TOK_GET_MODE(tok);
1185
28
                    if (the_current_tok->quote == quote &&
1186
28
                        the_current_tok->quote_size == quote_size) {
1187
17
                        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok,
1188
17
                            "%c-string: expecting '}'", TOK_GET_STRING_PREFIX(tok)));
1189
17
                    }
1190
28
                }
1191
1192
381
                if (quote_size == 3) {
1193
16
                    _PyTokenizer_syntaxerror(tok, "unterminated triple-quoted string literal"
1194
16
                                     " (detected at line %d)", start);
1195
16
                    if (c != '\n') {
1196
16
                        tok->done = E_EOFS;
1197
16
                    }
1198
16
                    return MAKE_TOKEN(ERRORTOKEN);
1199
16
                }
1200
365
                else {
1201
365
                    if (has_escaped_quote) {
1202
10
                        _PyTokenizer_syntaxerror(
1203
10
                            tok,
1204
10
                            "unterminated string literal (detected at line %d); "
1205
10
                            "perhaps you escaped the end quote?",
1206
10
                            start
1207
10
                        );
1208
355
                    } else {
1209
355
                        _PyTokenizer_syntaxerror(
1210
355
                            tok, "unterminated string literal (detected at line %d)", start
1211
355
                        );
1212
355
                    }
1213
365
                    if (c != '\n') {
1214
14
                        tok->done = E_EOLS;
1215
14
                    }
1216
365
                    return MAKE_TOKEN(ERRORTOKEN);
1217
365
                }
1218
381
            }
1219
1.11M
            if (c == quote) {
1220
57.8k
                end_quote_size += 1;
1221
57.8k
            }
1222
1.06M
            else {
1223
1.06M
                end_quote_size = 0;
1224
1.06M
                if (c == '\\') {
1225
28.7k
                    c = tok_nextc(tok);  /* skip escaped char */
1226
28.7k
                    if (c == quote) {  /* but record whether the escaped char was a quote */
1227
1.48k
                        has_escaped_quote = 1;
1228
1.48k
                    }
1229
28.7k
                    if (c == '\r') {
1230
204
                        c = tok_nextc(tok);
1231
204
                    }
1232
28.7k
                }
1233
1.06M
            }
1234
1.11M
        }
1235
1236
58.8k
        p_start = tok->start;
1237
58.8k
        p_end = tok->cur;
1238
58.8k
        return MAKE_TOKEN(STRING);
1239
59.2k
    }
1240
1241
    /* Line continuation */
1242
829k
    if (c == '\\') {
1243
425
        if ((c = tok_continuation_line(tok)) == -1) {
1244
64
            return MAKE_TOKEN(ERRORTOKEN);
1245
64
        }
1246
361
        tok->cont_line = 1;
1247
361
        goto again; /* Read next line */
1248
425
    }
1249
1250
    /* Punctuation character */
1251
829k
    int is_punctuation = (c == ':' || c == '}' || c == '!' || c == '{');
1252
829k
    if (is_punctuation && INSIDE_FSTRING(tok) && INSIDE_FSTRING_EXPR(current_tok)) {
1253
        /* This code block gets executed before the curly_bracket_depth is incremented
1254
         * by the `{` case, so for ensuring that we are on the 0th level, we need
1255
         * to adjust it manually */
1256
52.6k
        int cursor = current_tok->curly_bracket_depth - (c != '{');
1257
52.6k
        int in_format_spec = current_tok->in_format_spec;
1258
52.6k
         int cursor_in_format_with_debug =
1259
52.6k
             cursor == 1 && (current_tok->in_debug || in_format_spec);
1260
52.6k
         int cursor_valid = cursor == 0 || cursor_in_format_with_debug;
1261
52.6k
        if ((cursor_valid) && !_PyLexer_update_ftstring_expr(tok, c)) {
1262
0
            return MAKE_TOKEN(ENDMARKER);
1263
0
        }
1264
52.6k
        if ((cursor_valid) && c != '{' && set_ftstring_expr(tok, token, c)) {
1265
9
            return MAKE_TOKEN(ERRORTOKEN);
1266
9
        }
1267
1268
52.5k
        if (c == ':' && cursor == current_tok->curly_bracket_expr_start_depth) {
1269
3.64k
            current_tok->kind = TOK_FSTRING_MODE;
1270
3.64k
            current_tok->in_format_spec = 1;
1271
3.64k
            p_start = tok->start;
1272
3.64k
            p_end = tok->cur;
1273
3.64k
            return MAKE_TOKEN(_PyToken_OneChar(c));
1274
3.64k
        }
1275
52.5k
    }
1276
1277
    /* Check for two-character token */
1278
825k
    {
1279
825k
        int c2 = tok_nextc(tok);
1280
825k
        int current_token = _PyToken_TwoChars(c, c2);
1281
825k
        if (current_token != OP) {
1282
22.6k
            int c3 = tok_nextc(tok);
1283
22.6k
            int current_token3 = _PyToken_ThreeChars(c, c2, c3);
1284
22.6k
            if (current_token3 != OP) {
1285
992
                current_token = current_token3;
1286
992
            }
1287
21.6k
            else {
1288
21.6k
                tok_backup(tok, c3);
1289
21.6k
            }
1290
22.6k
            p_start = tok->start;
1291
22.6k
            p_end = tok->cur;
1292
22.6k
            return MAKE_TOKEN(current_token);
1293
22.6k
        }
1294
803k
        tok_backup(tok, c2);
1295
803k
    }
1296
1297
    /* Keep track of parentheses nesting level */
1298
0
    switch (c) {
1299
89.0k
    case '(':
1300
123k
    case '[':
1301
169k
    case '{':
1302
169k
        if (tok->level >= MAXLEVEL) {
1303
1
            return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "too many nested parentheses"));
1304
1
        }
1305
169k
        tok->parenstack[tok->level] = c;
1306
169k
        tok->parenlinenostack[tok->level] = tok->lineno;
1307
169k
        tok->parencolstack[tok->level] = (int)(tok->start - tok->line_start);
1308
169k
        tok->level++;
1309
169k
        if (INSIDE_FSTRING(tok)) {
1310
29.0k
            current_tok->curly_bracket_depth++;
1311
29.0k
        }
1312
169k
        break;
1313
61.0k
    case ')':
1314
72.5k
    case ']':
1315
98.3k
    case '}':
1316
98.3k
        if (INSIDE_FSTRING(tok) && !current_tok->curly_bracket_depth && c == '}') {
1317
46
            return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok,
1318
46
                "%c-string: single '}' is not allowed", TOK_GET_STRING_PREFIX(tok)));
1319
46
        }
1320
98.3k
        if (!tok->tok_extra_tokens && !tok->level) {
1321
212
            return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "unmatched '%c'", c));
1322
212
        }
1323
98.1k
        if (tok->level > 0) {
1324
98.1k
            tok->level--;
1325
98.1k
            int opening = tok->parenstack[tok->level];
1326
98.1k
            if (!tok->tok_extra_tokens && !((opening == '(' && c == ')') ||
1327
98.1k
                                            (opening == '[' && c == ']') ||
1328
98.1k
                                            (opening == '{' && c == '}'))) {
1329
                /* If the opening bracket belongs to an f-string's expression
1330
                part (e.g. f"{)}") and the closing bracket is an arbitrary
1331
                nested expression, then instead of matching a different
1332
                syntactical construct with it; we'll throw an unmatched
1333
                parentheses error. */
1334
40
                if (INSIDE_FSTRING(tok) && opening == '{') {
1335
6
                    assert(current_tok->curly_bracket_depth >= 0);
1336
6
                    int previous_bracket = current_tok->curly_bracket_depth - 1;
1337
6
                    if (previous_bracket == current_tok->curly_bracket_expr_start_depth) {
1338
4
                        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok,
1339
4
                            "%c-string: unmatched '%c'", TOK_GET_STRING_PREFIX(tok), c));
1340
4
                    }
1341
6
                }
1342
36
                if (tok->parenlinenostack[tok->level] != tok->lineno) {
1343
2
                    return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok,
1344
2
                            "closing parenthesis '%c' does not match "
1345
2
                            "opening parenthesis '%c' on line %d",
1346
2
                            c, opening, tok->parenlinenostack[tok->level]));
1347
2
                }
1348
34
                else {
1349
34
                    return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok,
1350
34
                            "closing parenthesis '%c' does not match "
1351
34
                            "opening parenthesis '%c'",
1352
34
                            c, opening));
1353
34
                }
1354
36
            }
1355
98.1k
        }
1356
1357
98.0k
        if (INSIDE_FSTRING(tok)) {
1358
21.8k
            current_tok->curly_bracket_depth--;
1359
21.8k
            if (current_tok->curly_bracket_depth < 0) {
1360
1
                return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "%c-string: unmatched '%c'",
1361
1
                    TOK_GET_STRING_PREFIX(tok), c));
1362
1
            }
1363
21.7k
            if (c == '}' && current_tok->curly_bracket_depth == current_tok->curly_bracket_expr_start_depth) {
1364
20.0k
                current_tok->curly_bracket_expr_start_depth--;
1365
20.0k
                current_tok->kind = TOK_FSTRING_MODE;
1366
20.0k
                current_tok->in_format_spec = 0;
1367
20.0k
                current_tok->in_debug = 0;
1368
20.0k
            }
1369
21.7k
        }
1370
98.0k
        break;
1371
534k
    default:
1372
534k
        break;
1373
803k
    }
1374
1375
802k
    if (!Py_UNICODE_ISPRINTABLE(c)) {
1376
494
        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid non-printable character U+%04X", c));
1377
494
    }
1378
1379
802k
    if( c == '=' && INSIDE_FSTRING_EXPR(current_tok)) {
1380
41.8k
        current_tok->in_debug = 1;
1381
41.8k
    }
1382
1383
    /* Punctuation character */
1384
802k
    p_start = tok->start;
1385
802k
    p_end = tok->cur;
1386
802k
    return MAKE_TOKEN(_PyToken_OneChar(c));
1387
802k
}
1388
1389
static int
1390
tok_get_fstring_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct token *token)
1391
51.3k
{
1392
51.3k
    const char *p_start = NULL;
1393
51.3k
    const char *p_end = NULL;
1394
51.3k
    int end_quote_size = 0;
1395
51.3k
    int unicode_escape = 0;
1396
1397
51.3k
    tok->start = tok->cur;
1398
51.3k
    tok->first_lineno = tok->lineno;
1399
51.3k
    tok->starting_col_offset = tok->col_offset;
1400
1401
    // If we start with a bracket, we defer to the normal mode as there is nothing for us to tokenize
1402
    // before it.
1403
51.3k
    int start_char = tok_nextc(tok);
1404
51.3k
    if (start_char == '{') {
1405
13.6k
        int peek1 = tok_nextc(tok);
1406
13.6k
        tok_backup(tok, peek1);
1407
13.6k
        tok_backup(tok, start_char);
1408
13.6k
        if (peek1 != '{') {
1409
10.8k
            current_tok->curly_bracket_expr_start_depth++;
1410
10.8k
            if (current_tok->curly_bracket_expr_start_depth >= MAX_EXPR_NESTING) {
1411
4
                return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok,
1412
4
                    "%c-string: expressions nested too deeply", TOK_GET_STRING_PREFIX(tok)));
1413
4
            }
1414
10.8k
            TOK_GET_MODE(tok)->kind = TOK_REGULAR_MODE;
1415
10.8k
            return tok_get_normal_mode(tok, current_tok, token);
1416
10.8k
        }
1417
13.6k
    }
1418
37.7k
    else {
1419
37.7k
        tok_backup(tok, start_char);
1420
37.7k
    }
1421
1422
    // Check if we are at the end of the string
1423
57.9k
    for (int i = 0; i < current_tok->quote_size; i++) {
1424
46.1k
        int quote = tok_nextc(tok);
1425
46.1k
        if (quote != current_tok->quote) {
1426
28.6k
            tok_backup(tok, quote);
1427
28.6k
            goto f_string_middle;
1428
28.6k
        }
1429
46.1k
    }
1430
1431
11.8k
    if (current_tok->last_expr_buffer != NULL) {
1432
6.88k
        PyMem_Free(current_tok->last_expr_buffer);
1433
6.88k
        current_tok->last_expr_buffer = NULL;
1434
6.88k
        current_tok->last_expr_size = 0;
1435
6.88k
        current_tok->last_expr_end = -1;
1436
6.88k
    }
1437
1438
11.8k
    p_start = tok->start;
1439
11.8k
    p_end = tok->cur;
1440
11.8k
    tok->tok_mode_stack_index--;
1441
11.8k
    return MAKE_TOKEN(FTSTRING_END(current_tok));
1442
1443
28.6k
f_string_middle:
1444
1445
    // TODO: This is a bit of a hack, but it works for now. We need to find a better way to handle
1446
    // this.
1447
28.6k
    tok->multi_line_start = tok->line_start;
1448
158k
    while (end_quote_size != current_tok->quote_size) {
1449
153k
        int c = tok_nextc(tok);
1450
153k
        if (tok->done == E_ERROR || tok->done == E_DECODE) {
1451
0
            return MAKE_TOKEN(ERRORTOKEN);
1452
0
        }
1453
153k
        int in_format_spec = (
1454
153k
                current_tok->in_format_spec
1455
153k
                &&
1456
153k
                INSIDE_FSTRING_EXPR(current_tok)
1457
153k
        );
1458
1459
153k
       if (c == EOF || (current_tok->quote_size == 1 && c == '\n')) {
1460
484
            if (tok->decoding_erred) {
1461
0
                return MAKE_TOKEN(ERRORTOKEN);
1462
0
            }
1463
1464
            // If we are in a format spec and we found a newline,
1465
            // it means that the format spec ends here and we should
1466
            // return to the regular mode.
1467
484
            if (in_format_spec && c == '\n') {
1468
82
                if (current_tok->quote_size == 1) {
1469
82
                    return MAKE_TOKEN(
1470
82
                        _PyTokenizer_syntaxerror(
1471
82
                            tok,
1472
82
                            "%c-string: newlines are not allowed in format specifiers for single quoted %c-strings",
1473
82
                            TOK_GET_STRING_PREFIX(tok), TOK_GET_STRING_PREFIX(tok)
1474
82
                        )
1475
82
                    );
1476
82
                }
1477
0
                tok_backup(tok, c);
1478
0
                TOK_GET_MODE(tok)->kind = TOK_REGULAR_MODE;
1479
0
                current_tok->in_format_spec = 0;
1480
0
                p_start = tok->start;
1481
0
                p_end = tok->cur;
1482
0
                return MAKE_TOKEN(FTSTRING_MIDDLE(current_tok));
1483
82
            }
1484
1485
402
            assert(tok->multi_line_start != NULL);
1486
            // shift the tok_state's location into
1487
            // the start of string, and report the error
1488
            // from the initial quote character
1489
402
            tok->cur = (char *)current_tok->start;
1490
402
            tok->cur++;
1491
402
            tok->line_start = current_tok->multi_line_start;
1492
402
            int start = tok->lineno;
1493
1494
402
            tokenizer_mode *the_current_tok = TOK_GET_MODE(tok);
1495
402
            tok->lineno = the_current_tok->first_line;
1496
1497
402
            if (current_tok->quote_size == 3) {
1498
33
                _PyTokenizer_syntaxerror(tok,
1499
33
                                    "unterminated triple-quoted %c-string literal"
1500
33
                                    " (detected at line %d)",
1501
33
                                    TOK_GET_STRING_PREFIX(tok), start);
1502
33
                if (c != '\n') {
1503
33
                    tok->done = E_EOFS;
1504
33
                }
1505
33
                return MAKE_TOKEN(ERRORTOKEN);
1506
33
            }
1507
369
            else {
1508
369
                return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok,
1509
369
                                    "unterminated %c-string literal (detected at"
1510
369
                                    " line %d)", TOK_GET_STRING_PREFIX(tok), start));
1511
369
            }
1512
402
        }
1513
1514
152k
        if (c == current_tok->quote) {
1515
8.38k
            end_quote_size += 1;
1516
8.38k
            continue;
1517
144k
        } else {
1518
144k
            end_quote_size = 0;
1519
144k
        }
1520
1521
144k
        if (c == '{') {
1522
17.8k
            if (!_PyLexer_update_ftstring_expr(tok, c)) {
1523
0
                return MAKE_TOKEN(ENDMARKER);
1524
0
            }
1525
17.8k
            int peek = tok_nextc(tok);
1526
17.8k
            if (peek != '{' || in_format_spec) {
1527
14.4k
                tok_backup(tok, peek);
1528
14.4k
                tok_backup(tok, c);
1529
14.4k
                current_tok->curly_bracket_expr_start_depth++;
1530
14.4k
                if (current_tok->curly_bracket_expr_start_depth >= MAX_EXPR_NESTING) {
1531
5
                    return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok,
1532
5
                        "%c-string: expressions nested too deeply", TOK_GET_STRING_PREFIX(tok)));
1533
5
                }
1534
14.4k
                TOK_GET_MODE(tok)->kind = TOK_REGULAR_MODE;
1535
14.4k
                current_tok->in_format_spec = 0;
1536
14.4k
                p_start = tok->start;
1537
14.4k
                p_end = tok->cur;
1538
14.4k
            } else {
1539
3.36k
                p_start = tok->start;
1540
3.36k
                p_end = tok->cur - 1;
1541
3.36k
            }
1542
17.8k
            return MAKE_TOKEN(FTSTRING_MIDDLE(current_tok));
1543
126k
        } else if (c == '}') {
1544
4.74k
            if (unicode_escape) {
1545
477
                p_start = tok->start;
1546
477
                p_end = tok->cur;
1547
477
                return MAKE_TOKEN(FTSTRING_MIDDLE(current_tok));
1548
477
            }
1549
4.26k
            int peek = tok_nextc(tok);
1550
1551
            // The tokenizer can only be in the format spec if we have already completed the expression
1552
            // scanning (indicated by the end of the expression being set) and we are not at the top level
1553
            // of the bracket stack (-1 is the top level). Since format specifiers can't legally use double
1554
            // brackets, we can bypass it here.
1555
4.26k
            int cursor = current_tok->curly_bracket_depth;
1556
4.26k
            if (peek == '}' && !in_format_spec && cursor == 0) {
1557
1.71k
                p_start = tok->start;
1558
1.71k
                p_end = tok->cur - 1;
1559
2.54k
            } else {
1560
2.54k
                tok_backup(tok, peek);
1561
2.54k
                tok_backup(tok, c);
1562
2.54k
                TOK_GET_MODE(tok)->kind = TOK_REGULAR_MODE;
1563
2.54k
                current_tok->in_format_spec = 0;
1564
2.54k
                p_start = tok->start;
1565
2.54k
                p_end = tok->cur;
1566
2.54k
            }
1567
4.26k
            return MAKE_TOKEN(FTSTRING_MIDDLE(current_tok));
1568
121k
        } else if (c == '\\') {
1569
6.27k
            int peek = tok_nextc(tok);
1570
6.27k
            if (peek == '\r') {
1571
69
                peek = tok_nextc(tok);
1572
69
            }
1573
            // Special case when the backslash is right before a curly
1574
            // brace. We have to restore and return the control back
1575
            // to the loop for the next iteration.
1576
6.27k
            if (peek == '{' || peek == '}') {
1577
1.40k
                if (!current_tok->raw) {
1578
1.20k
                    if (_PyTokenizer_warn_invalid_escape_sequence(tok, peek)) {
1579
1
                        return MAKE_TOKEN(ERRORTOKEN);
1580
1
                    }
1581
1.20k
                }
1582
1.40k
                tok_backup(tok, peek);
1583
1.40k
                continue;
1584
1.40k
            }
1585
1586
4.86k
            if (!current_tok->raw) {
1587
4.60k
                if (peek == 'N') {
1588
                    /* Handle named unicode escapes (\N{BULLET}) */
1589
735
                    peek = tok_nextc(tok);
1590
735
                    if (peek == '{') {
1591
497
                        unicode_escape = 1;
1592
497
                    } else {
1593
238
                        tok_backup(tok, peek);
1594
238
                    }
1595
735
                }
1596
4.60k
            } /* else {
1597
                skip the escaped character
1598
            }*/
1599
4.86k
        }
1600
144k
    }
1601
1602
    // Backup the f-string quotes to emit a final FSTRING_MIDDLE and
1603
    // add the quotes to the FSTRING_END in the next tokenizer iteration.
1604
12.0k
    for (int i = 0; i < current_tok->quote_size; i++) {
1605
6.42k
        tok_backup(tok, current_tok->quote);
1606
6.42k
    }
1607
5.61k
    p_start = tok->start;
1608
5.61k
    p_end = tok->cur;
1609
5.61k
    return MAKE_TOKEN(FTSTRING_MIDDLE(current_tok));
1610
28.6k
}
1611
1612
static int
1613
tok_get(struct tok_state *tok, struct token *token)
1614
1.76M
{
1615
1.76M
    tokenizer_mode *current_tok = TOK_GET_MODE(tok);
1616
1.76M
    if (current_tok->kind == TOK_REGULAR_MODE) {
1617
1.71M
        return tok_get_normal_mode(tok, current_tok, token);
1618
1.71M
    } else {
1619
51.3k
        return tok_get_fstring_mode(tok, current_tok, token);
1620
51.3k
    }
1621
1.76M
}
1622
1623
int
1624
_PyTokenizer_Get(struct tok_state *tok, struct token *token)
1625
1.76M
{
1626
1.76M
    int result = tok_get(tok, token);
1627
1.76M
    if (tok->decoding_erred) {
1628
0
        result = ERRORTOKEN;
1629
0
        tok->done = E_DECODE;
1630
0
    }
1631
1.76M
    return result;
1632
1.76M
}