Coverage Report

Created: 2026-01-10 06:41

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