Coverage Report

Created: 2026-10-17 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Parser/pegen.c
Line
Count
Source
1
#include <Python.h>
2
#include "pycore_ast.h"           // _PyAST_Validate(),
3
#include "pycore_pystate.h"       // _PyThreadState_GET()
4
#include "pycore_parser.h"        // _PYPEGEN_NSTATISTICS
5
#include "pycore_pyerrors.h"      // PyExc_IncompleteInputError
6
#include "pycore_runtime.h"       // _PyRuntime
7
#include "pycore_unicodeobject.h" // _PyUnicode_InternImmortal
8
#include <errcode.h>
9
10
#include "lexer/state.h"
11
#include "tokenizer/tokenizer.h"
12
#include "pegen.h"
13
14
1.13M
#define IDENTIFIER_CACHE_SIZE 2048  // Must be a power of two.
15
539k
#define IDENTIFIER_CACHE_MAX_PROBES 8
16
3.92M
#define PEGEN_ARRAY_GROWTH_FACTOR 2
17
0
#define TYPE_IGNORE_INITIAL_CAPACITY 10
18
19
struct _identifier_cache_entry {
20
    const char *key;   // Borrowed from arena-owned token bytes.
21
    Py_ssize_t len;
22
    Py_hash_t hash;
23
    PyObject *value;   // Borrowed from an arena-owned identifier.
24
};
25
26
// Internal parser functions
27
28
asdl_stmt_seq*
29
_PyPegen_interactive_exit(Parser *p)
30
0
{
31
0
    if (p->errcode) {
32
0
        *(p->errcode) = E_EOF;
33
0
    }
34
0
    return NULL;
35
0
}
36
37
Py_ssize_t
38
_PyPegen_byte_offset_to_character_offset_line(PyObject *line, Py_ssize_t col_offset, Py_ssize_t end_col_offset)
39
2.38k
{
40
2.38k
    const unsigned char *data = (const unsigned char*)PyUnicode_AsUTF8(line);
41
2.38k
    if (data == NULL) {
42
0
        return -1;
43
0
    }
44
45
2.38k
    Py_ssize_t len = 0;
46
12.3k
    while (col_offset < end_col_offset) {
47
9.99k
        Py_UCS4 ch = data[col_offset];
48
9.99k
        if (ch < 0x80) {
49
9.99k
            col_offset += 1;
50
9.99k
        } else if ((ch & 0xe0) == 0xc0) {
51
0
            col_offset += 2;
52
0
        } else if ((ch & 0xf0) == 0xe0) {
53
0
            col_offset += 3;
54
0
        } else if ((ch & 0xf8) == 0xf0) {
55
0
            col_offset += 4;
56
0
        } else {
57
0
            PyErr_SetString(PyExc_ValueError, "Invalid UTF-8 sequence");
58
0
            return -1;
59
0
        }
60
9.99k
        len++;
61
9.99k
    }
62
2.38k
    return len;
63
2.38k
}
64
65
Py_ssize_t
66
_PyPegen_byte_offset_to_character_offset_raw(const char* str, Py_ssize_t col_offset)
67
102k
{
68
102k
    Py_ssize_t len = (Py_ssize_t)strlen(str);
69
102k
    if (col_offset > len + 1) {
70
3
        col_offset = len + 1;
71
3
    }
72
102k
    assert(col_offset >= 0);
73
102k
    PyObject *text = PyUnicode_DecodeUTF8(str, col_offset, "replace");
74
102k
    if (!text) {
75
0
        return -1;
76
0
    }
77
102k
    Py_ssize_t size = PyUnicode_GET_LENGTH(text);
78
102k
    Py_DECREF(text);
79
102k
    return size;
80
102k
}
81
82
Py_ssize_t
83
_PyPegen_byte_offset_to_character_offset(PyObject *line, Py_ssize_t col_offset)
84
102k
{
85
102k
    const char *str = PyUnicode_AsUTF8(line);
86
102k
    if (!str) {
87
0
        return -1;
88
0
    }
89
102k
    return _PyPegen_byte_offset_to_character_offset_raw(str, col_offset);
90
102k
}
91
92
// Here, mark is the start of the node, while p->mark is the end.
93
// If node==NULL, they should be the same.
94
int
95
_PyPegen_insert_memo(Parser *p, int mark, int type, void *node)
96
13.2M
{
97
    // Insert in front
98
13.2M
    Memo *m = _PyArena_Malloc(p->arena, sizeof(Memo));
99
13.2M
    if (m == NULL) {
100
0
        return -1;
101
0
    }
102
13.2M
    m->type = type;
103
13.2M
    m->node = node;
104
13.2M
    m->mark = p->mark;
105
13.2M
    m->next = p->tokens[mark]->memo;
106
13.2M
    p->tokens[mark]->memo = m;
107
13.2M
    p->tokens[mark]->memo_mask |= 1ULL << (type & 63);
108
13.2M
    return 0;
109
13.2M
}
110
111
// Like _PyPegen_insert_memo(), but returns the inserted Memo so callers
112
// can update it in place without re-walking the token's memo list.
113
Memo *
114
_PyPegen_insert_memo_direct(Parser *p, int mark, int type)
115
6.06M
{
116
6.06M
    if (_PyPegen_insert_memo(p, mark, type, NULL) < 0) {
117
0
        return NULL;
118
0
    }
119
6.06M
    return p->tokens[mark]->memo;
120
6.06M
}
121
122
// Like _PyPegen_insert_memo(), but updates an existing node if found.
123
int
124
_PyPegen_update_memo(Parser *p, int mark, int type, void *node)
125
0
{
126
0
    for (Memo *m = p->tokens[mark]->memo; m != NULL; m = m->next) {
127
0
        if (m->type == type) {
128
            // Update existing node.
129
0
            m->node = node;
130
0
            m->mark = p->mark;
131
0
            return 0;
132
0
        }
133
0
    }
134
    // Insert new node.
135
0
    return _PyPegen_insert_memo(p, mark, type, node);
136
0
}
137
138
static int
139
init_normalization(Parser *p)
140
3.64k
{
141
3.64k
    if (p->normalize) {
142
2.12k
        return 1;
143
2.12k
    }
144
1.51k
    p->normalize = PyImport_ImportModuleAttrString("unicodedata", "normalize");
145
1.51k
    if (!p->normalize)
146
0
    {
147
0
        return 0;
148
0
    }
149
1.51k
    return 1;
150
1.51k
}
151
152
int
153
_PyPegen_grow_loop_buffer(void ***buffer, Py_ssize_t *capacity)
154
2.99M
{
155
2.99M
    if ((size_t)*capacity > (size_t)PY_SSIZE_T_MAX /
156
2.99M
            (PEGEN_ARRAY_GROWTH_FACTOR * sizeof(**buffer))) {
157
0
        return -1;
158
0
    }
159
2.99M
    Py_ssize_t new_capacity = *capacity == 0
160
2.99M
        ? 1 : *capacity * PEGEN_ARRAY_GROWTH_FACTOR;
161
2.99M
    void **new_buffer = PyMem_Realloc(*buffer, new_capacity * sizeof(**buffer));
162
2.99M
    if (new_buffer == NULL) {
163
0
        return -1;
164
0
    }
165
2.99M
    *buffer = new_buffer;
166
2.99M
    *capacity = new_capacity;
167
2.99M
    return 0;
168
2.99M
}
169
170
static int
171
growable_comment_array_add(growable_comment_array *arr, int lineno, char *comment)
172
0
{
173
0
    if (arr->num_items >= arr->size) {
174
0
        if (arr->size > (size_t)PY_SSIZE_T_MAX /
175
0
                (PEGEN_ARRAY_GROWTH_FACTOR * sizeof(*arr->items))) {
176
0
            return 0;
177
0
        }
178
0
        size_t new_size = arr->size == 0
179
0
            ? TYPE_IGNORE_INITIAL_CAPACITY
180
0
            : arr->size * PEGEN_ARRAY_GROWTH_FACTOR;
181
0
        void *new_items_array = PyMem_Realloc(
182
0
            arr->items, new_size * sizeof(*arr->items));
183
0
        if (!new_items_array) {
184
0
            return 0;
185
0
        }
186
0
        arr->items = new_items_array;
187
0
        arr->size = new_size;
188
0
    }
189
190
0
    arr->items[arr->num_items].lineno = lineno;
191
0
    arr->items[arr->num_items].comment = comment;  // Take ownership
192
0
    arr->num_items++;
193
0
    return 1;
194
0
}
195
196
static void
197
growable_comment_array_deallocate(growable_comment_array *arr)
198
62.5k
{
199
62.5k
    for (size_t i = 0; i < arr->num_items; i++) {
200
0
        PyMem_Free(arr->items[i].comment);
201
0
    }
202
62.5k
    PyMem_Free(arr->items);
203
62.5k
}
204
205
static int
206
_get_keyword_or_name_type(Parser *p, const char *text, Py_ssize_t length)
207
520k
{
208
520k
    assert(length > 0);
209
210
520k
    if (length >= p->n_keyword_lists ||
211
495k
        p->keywords[length] == NULL ||
212
495k
        p->keywords[length]->type == -1) {
213
273k
        return NAME;
214
273k
    }
215
1.29M
    for (KeywordToken *k = p->keywords[length]; k != NULL && k->type != -1; k++) {
216
1.18M
        if (memcmp(k->str, text, (size_t)length) == 0) {
217
136k
            return k->type;
218
136k
        }
219
1.18M
    }
220
110k
    return NAME;
221
246k
}
222
223
// Keep text only where grammar actions or helpers read it. Keyword error
224
// actions use fixed spellings, so keywords do not need their text.
225
static inline int
226
token_needs_text(int type)
227
1.63M
{
228
1.63M
    switch (type) {
229
384k
        case NAME:
230
482k
        case NUMBER:
231
520k
        case STRING:
232
534k
        case FSTRING_START:
233
553k
        case FSTRING_MIDDLE:
234
563k
        case FSTRING_END:
235
568k
        case TSTRING_START:
236
575k
        case TSTRING_MIDDLE:
237
578k
        case TSTRING_END:
238
578k
        case TYPE_COMMENT:
239
579k
        case NOTEQUAL:  // _PyPegen_check_barry_as_flufl() distinguishes != and <>.
240
579k
            return 1;
241
1.05M
        default:
242
1.05M
            return 0;
243
1.63M
    }
244
1.63M
}
245
246
static int
247
1.63M
initialize_token(Parser *p, Token *parser_token, struct token *new_token, int token_type) {
248
1.63M
    assert(parser_token != NULL);
249
250
1.63M
    Py_ssize_t length;
251
1.63M
    const char *text = _PyToken_TextView(p->tok, new_token, &length);
252
1.63M
    parser_token->type = token_type == NAME
253
1.63M
        ? _get_keyword_or_name_type(p, text, length) : token_type;
254
1.63M
    if (token_needs_text(parser_token->type)) {
255
579k
        parser_token->bytes = PyBytes_FromStringAndSize(text, length);
256
579k
        if (parser_token->bytes == NULL) {
257
0
            return -1;
258
0
        }
259
579k
        if (_PyArena_AddPyObject(p->arena, parser_token->bytes) < 0) {
260
0
            Py_DECREF(parser_token->bytes);
261
0
            return -1;
262
0
        }
263
579k
    }
264
1.05M
    else {
265
1.05M
        parser_token->bytes = NULL;
266
1.05M
    }
267
268
1.63M
    parser_token->metadata = NULL;
269
1.63M
    if (new_token->metadata != NULL) {
270
11.0k
        if (_PyArena_AddPyObject(p->arena, new_token->metadata) < 0) {
271
0
            return -1;
272
0
        }
273
11.0k
        parser_token->metadata = new_token->metadata;
274
11.0k
        new_token->metadata = NULL;
275
11.0k
    }
276
277
1.63M
    parser_token->level = new_token->level;
278
1.63M
    parser_token->is_raw = new_token->is_raw;
279
1.63M
    parser_token->lineno = new_token->start_loc.lineno;
280
1.63M
    parser_token->col_offset = new_token->end_loc.lineno == p->starting_lineno
281
1.63M
        ? p->starting_col_offset + new_token->start_loc.byte_col
282
1.63M
        : new_token->start_loc.byte_col;
283
1.63M
    parser_token->end_lineno = new_token->end_loc.lineno;
284
1.63M
    parser_token->end_col_offset = new_token->end_loc.lineno == p->starting_lineno
285
1.63M
        ? p->starting_col_offset + new_token->end_loc.byte_col
286
1.63M
        : new_token->end_loc.byte_col;
287
288
1.63M
    p->fill += 1;
289
290
1.63M
    if (token_type == ERRORTOKEN && _PyTokenizer_GetInfo(p->tok).status == E_DECODE) {
291
0
        return _Pypegen_raise_decode_error(p);
292
0
    }
293
294
1.63M
    return (token_type == ERRORTOKEN ? _Pypegen_tokenizer_error(p) : 0);
295
1.63M
}
296
297
static int
298
164k
_resize_tokens_array(Parser *p) {
299
164k
    if (p->size > INT_MAX / PEGEN_ARRAY_GROWTH_FACTOR ||
300
164k
            (size_t)p->size > PY_SSIZE_T_MAX /
301
164k
                (PEGEN_ARRAY_GROWTH_FACTOR * sizeof(*p->tokens))) {
302
0
        PyErr_NoMemory();
303
0
        return -1;
304
0
    }
305
164k
    int newsize = p->size * PEGEN_ARRAY_GROWTH_FACTOR;
306
164k
    Token **new_tokens = PyMem_Realloc(p->tokens, (size_t)newsize * sizeof(Token *));
307
164k
    if (new_tokens == NULL) {
308
0
        PyErr_NoMemory();
309
0
        return -1;
310
0
    }
311
164k
    p->tokens = new_tokens;
312
313
    // Allocate new tokens together without moving existing ones: parser rules
314
    // may still hold pointers into earlier blocks. Parser_Free frees each block.
315
164k
    Token *chunk = PyMem_Calloc((size_t)(newsize - p->size), sizeof(Token));
316
164k
    if (chunk == NULL) {
317
0
        PyErr_NoMemory();
318
0
        return -1;
319
0
    }
320
2.46M
    for (int i = p->size; i < newsize; i++) {
321
2.29M
        p->tokens[i] = &chunk[i - p->size];
322
2.29M
    }
323
164k
    p->size = newsize;
324
164k
    return 0;
325
164k
}
326
327
int
328
_PyPegen_fill_token(Parser *p)
329
1.63M
{
330
1.63M
    struct token new_token;
331
1.63M
    _PyToken_Init(&new_token);
332
1.63M
    _PyTokenizer_Get(p->tok, &new_token);
333
334
    // Record and skip '# type: ignore' comments
335
1.63M
    while (new_token.type == TYPE_IGNORE) {
336
0
        Py_ssize_t len;
337
0
        const char *text = _PyToken_TextView(p->tok, &new_token, &len);
338
0
        char *tag = PyMem_Malloc((size_t)len + 1);
339
0
        if (tag == NULL) {
340
0
            PyErr_NoMemory();
341
0
            goto error;
342
0
        }
343
0
        memcpy(tag, text, (size_t)len);
344
0
        tag[len] = '\0';
345
        // Ownership of tag passes to the growable array
346
0
        if (!growable_comment_array_add(&p->type_ignore_comments, new_token.end_loc.lineno, tag)) {
347
0
            PyMem_Free(tag);
348
0
            PyErr_NoMemory();
349
0
            goto error;
350
0
        }
351
0
        _PyTokenizer_Get(p->tok, &new_token);
352
0
    }
353
354
1.63M
    int type = new_token.type;
355
356
    // If we have reached the end and we are in single input mode we need to insert a newline and reset the parsing
357
1.63M
    if (p->start_rule == Py_single_input && type == ENDMARKER && p->parsing_started) {
358
0
        type = NEWLINE; /* Add an extra newline */
359
0
        p->parsing_started = 0;
360
361
0
        if (!(p->flags & PyPARSE_DONT_IMPLY_DEDENT)) {
362
0
            _PyTokenizer_ImplyDedents(p->tok);
363
0
        }
364
0
    }
365
1.63M
    else {
366
1.63M
        p->parsing_started = 1;
367
1.63M
    }
368
369
    // Check if we are at the limit of the token array capacity and resize if needed
370
1.63M
    if ((p->fill == p->size) && (_resize_tokens_array(p) != 0)) {
371
0
        goto error;
372
0
    }
373
374
1.63M
    Token *t = p->tokens[p->fill];
375
1.63M
    int result = initialize_token(p, t, &new_token, type);
376
1.63M
    _PyToken_Free(&new_token);
377
1.63M
    return result;
378
0
error:
379
0
    _PyToken_Free(&new_token);
380
0
    return -1;
381
1.63M
}
382
383
#if defined(Py_DEBUG)
384
// Instrumentation to count the effectiveness of memoization.
385
// The array counts the number of tokens skipped by memoization,
386
// indexed by type.
387
388
#define NSTATISTICS _PYPEGEN_NSTATISTICS
389
#define memo_statistics _PyRuntime.parser.memo_statistics
390
391
void
392
_PyPegen_clear_memo_statistics(void)
393
{
394
    PyMutex_Lock(&_PyRuntime.parser.mutex);
395
    for (int i = 0; i < NSTATISTICS; i++) {
396
        memo_statistics[i] = 0;
397
    }
398
    PyMutex_Unlock(&_PyRuntime.parser.mutex);
399
}
400
401
PyObject *
402
_PyPegen_get_memo_statistics(void)
403
{
404
    PyObject *ret = PyList_New(NSTATISTICS);
405
    if (ret == NULL) {
406
        return NULL;
407
    }
408
409
    PyMutex_Lock(&_PyRuntime.parser.mutex);
410
    for (int i = 0; i < NSTATISTICS; i++) {
411
        PyObject *value = PyLong_FromLong(memo_statistics[i]);
412
        if (value == NULL) {
413
            PyMutex_Unlock(&_PyRuntime.parser.mutex);
414
            Py_DECREF(ret);
415
            return NULL;
416
        }
417
        // PyList_SetItem borrows a reference to value.
418
        if (PyList_SetItem(ret, i, value) < 0) {
419
            PyMutex_Unlock(&_PyRuntime.parser.mutex);
420
            Py_DECREF(ret);
421
            return NULL;
422
        }
423
    }
424
    PyMutex_Unlock(&_PyRuntime.parser.mutex);
425
    return ret;
426
}
427
#endif
428
429
int  // bool
430
_PyPegen_is_memoized(Parser *p, int type, void *pres)
431
43.9M
{
432
43.9M
    if (p->mark == p->fill) {
433
429k
        if (_PyPegen_fill_token(p) < 0) {
434
773
            p->error_indicator = 1;
435
773
            return -1;
436
773
        }
437
429k
    }
438
439
43.9M
    Token *t = p->tokens[p->mark];
440
441
43.9M
    if (!(t->memo_mask & (1ULL << (type & 63)))) {
442
13.2M
        return 0;
443
13.2M
    }
444
445
76.6M
    for (Memo *m = t->memo; m != NULL; m = m->next) {
446
76.3M
        if (m->type == type) {
447
#if defined(Py_DEBUG)
448
            if (0 <= type && type < NSTATISTICS) {
449
                long count = m->mark - p->mark;
450
                // A memoized negative result counts for one.
451
                if (count <= 0) {
452
                    count = 1;
453
                }
454
                PyMutex_Lock(&_PyRuntime.parser.mutex);
455
                memo_statistics[type] += count;
456
                PyMutex_Unlock(&_PyRuntime.parser.mutex);
457
            }
458
#endif
459
30.4M
            p->mark = m->mark;
460
30.4M
            *(void **)(pres) = m->node;
461
30.4M
            return 1;
462
30.4M
        }
463
76.3M
    }
464
242k
    return 0;
465
30.7M
}
466
467
#define LOOKAHEAD1(NAME, RES_TYPE)                                  \
468
    int                                                             \
469
    NAME (int positive, RES_TYPE (func)(Parser *), Parser *p)       \
470
2.80M
    {                                                               \
471
2.80M
        int mark = p->mark;                                         \
472
2.80M
        void *res = func(p);                                        \
473
2.80M
        p->mark = mark;                                             \
474
2.80M
        return (res != NULL) == positive;                           \
475
2.80M
    }
476
477
2.80M
LOOKAHEAD1(_PyPegen_lookahead, void *)
478
952
LOOKAHEAD1(_PyPegen_lookahead_for_expr, expr_ty)
479
0
LOOKAHEAD1(_PyPegen_lookahead_for_stmt, stmt_ty)
480
#undef LOOKAHEAD1
481
482
#define LOOKAHEAD2(NAME, RES_TYPE, T)                                   \
483
    int                                                                 \
484
    NAME (int positive, RES_TYPE (func)(Parser *, T), Parser *p, T arg) \
485
7.01M
    {                                                                   \
486
7.01M
        int mark = p->mark;                                             \
487
7.01M
        void *res = func(p, arg);                                       \
488
7.01M
        p->mark = mark;                                                 \
489
7.01M
        return (res != NULL) == positive;                               \
490
7.01M
    }
491
492
6.72M
LOOKAHEAD2(_PyPegen_lookahead_with_int, Token *, int)
493
287k
LOOKAHEAD2(_PyPegen_lookahead_with_string, expr_ty, const char *)
494
#undef LOOKAHEAD2
495
496
Token *
497
_PyPegen_expect_token(Parser *p, int type)
498
76.9M
{
499
76.9M
    if (p->mark == p->fill) {
500
852k
        if (_PyPegen_fill_token(p) < 0) {
501
1.99k
            p->error_indicator = 1;
502
1.99k
            return NULL;
503
1.99k
        }
504
852k
    }
505
76.9M
    Token *t = p->tokens[p->mark];
506
76.9M
    if (t->type != type) {
507
63.5M
       return NULL;
508
63.5M
    }
509
13.3M
    p->mark += 1;
510
13.3M
    return t;
511
76.9M
}
512
513
void*
514
0
_PyPegen_expect_forced_result(Parser *p, void* result, const char* expected) {
515
516
0
    if (p->error_indicator == 1) {
517
0
        return NULL;
518
0
    }
519
0
    if (result == NULL) {
520
0
        RAISE_SYNTAX_ERROR("expected (%s)", expected);
521
0
        return NULL;
522
0
    }
523
0
    return result;
524
0
}
525
526
Token *
527
16.5k
_PyPegen_expect_forced_token(Parser *p, int type, const char* expected) {
528
529
16.5k
    if (p->error_indicator == 1) {
530
0
        return NULL;
531
0
    }
532
533
16.5k
    if (p->mark == p->fill) {
534
4.05k
        if (_PyPegen_fill_token(p) < 0) {
535
2
            p->error_indicator = 1;
536
2
            return NULL;
537
2
        }
538
4.05k
    }
539
16.5k
    Token *t = p->tokens[p->mark];
540
16.5k
    if (t->type != type) {
541
132
        RAISE_SYNTAX_ERROR_KNOWN_LOCATION(t, "expected '%s'", expected);
542
132
        return NULL;
543
132
    }
544
16.4k
    p->mark += 1;
545
16.4k
    return t;
546
16.5k
}
547
548
expr_ty
549
_PyPegen_expect_soft_keyword(Parser *p, const char *keyword)
550
971k
{
551
971k
    if (p->mark == p->fill) {
552
5.05k
        if (_PyPegen_fill_token(p) < 0) {
553
7
            p->error_indicator = 1;
554
7
            return NULL;
555
7
        }
556
5.05k
    }
557
971k
    Token *t = p->tokens[p->mark];
558
971k
    if (t->type != NAME) {
559
702k
        return NULL;
560
702k
    }
561
268k
    const char *s = PyBytes_AsString(t->bytes);
562
268k
    if (!s) {
563
0
        p->error_indicator = 1;
564
0
        return NULL;
565
0
    }
566
268k
    if (strcmp(s, keyword) != 0) {
567
234k
        return NULL;
568
234k
    }
569
33.9k
    return _PyPegen_name_token(p);
570
268k
}
571
572
Token *
573
_PyPegen_get_last_nonnwhitespace_token(Parser *p)
574
6.58M
{
575
6.58M
    assert(p->mark >= 0);
576
6.58M
    Token *token = NULL;
577
6.62M
    for (int m = p->mark - 1; m >= 0; m--) {
578
6.62M
        token = p->tokens[m];
579
6.62M
        if (token->type != ENDMARKER && (token->type < NEWLINE || token->type > DEDENT)) {
580
6.58M
            break;
581
6.58M
        }
582
6.62M
    }
583
6.58M
    return token;
584
6.58M
}
585
586
PyObject *
587
_PyPegen_new_identifier(Parser *p, const char *n)
588
104k
{
589
104k
    PyObject *id = PyUnicode_DecodeUTF8(n, (Py_ssize_t)strlen(n), NULL);
590
104k
    if (!id) {
591
0
        goto error;
592
0
    }
593
    /* Check whether there are non-ASCII characters in the
594
       identifier; if so, normalize to NFKC. */
595
104k
    if (!PyUnicode_IS_ASCII(id))
596
3.64k
    {
597
3.64k
        if (!init_normalization(p))
598
0
        {
599
0
            Py_DECREF(id);
600
0
            goto error;
601
0
        }
602
3.64k
        PyObject *form = PyUnicode_InternFromString("NFKC");
603
3.64k
        if (form == NULL)
604
0
        {
605
0
            Py_DECREF(id);
606
0
            goto error;
607
0
        }
608
3.64k
        PyObject *args[2] = {form, id};
609
3.64k
        PyObject *id2 = PyObject_Vectorcall(p->normalize, args, 2, NULL);
610
3.64k
        Py_DECREF(id);
611
3.64k
        Py_DECREF(form);
612
3.64k
        if (!id2) {
613
0
            goto error;
614
0
        }
615
616
3.64k
        if (!PyUnicode_Check(id2))
617
0
        {
618
0
            PyErr_Format(PyExc_TypeError,
619
0
                         "unicodedata.normalize() must return a string, not "
620
0
                         "%.200s",
621
0
                         _PyType_Name(Py_TYPE(id2)));
622
0
            Py_DECREF(id2);
623
0
            goto error;
624
0
        }
625
3.64k
        id = id2;
626
3.64k
    }
627
104k
    static const char * const forbidden[] = {
628
104k
        "None",
629
104k
        "True",
630
104k
        "False",
631
104k
        NULL
632
104k
    };
633
419k
    for (int i = 0; forbidden[i] != NULL; i++) {
634
314k
        if (_PyUnicode_EqualToASCIIString(id, forbidden[i])) {
635
2
            PyErr_Format(PyExc_ValueError,
636
2
                         "identifier field can't represent '%s' constant",
637
2
                         forbidden[i]);
638
2
            Py_DECREF(id);
639
2
            goto error;
640
2
        }
641
314k
    }
642
104k
    PyInterpreterState *interp = _PyInterpreterState_GET();
643
104k
    _PyUnicode_InternImmortal(interp, &id);
644
104k
    if (_PyArena_AddPyObject(p->arena, id) < 0)
645
0
    {
646
0
        Py_DECREF(id);
647
0
        goto error;
648
0
    }
649
104k
    return id;
650
651
2
error:
652
2
    p->error_indicator = 1;
653
2
    return NULL;
654
104k
}
655
656
// Return an arena-owned identifier; callers borrow the reference.
657
static PyObject *
658
get_cached_identifier(Parser *p, PyObject *bytes)
659
538k
{
660
538k
    const char *s = PyBytes_AsString(bytes);
661
538k
    if (!s) {
662
0
        p->error_indicator = 1;
663
0
        return NULL;
664
0
    }
665
    // Identifiers repeat constantly; a small span-keyed cache skips the
666
    // UTF-8 decode + intern for repeated occurrences. Keys point into
667
    // arena-owned token bytes and values are arena-owned interned strings,
668
    // so borrowed references are valid for the lifetime of the parse
669
    // (including the second error pass, which reuses parser and arena).
670
538k
    Py_ssize_t len = PyBytes_GET_SIZE(bytes);
671
538k
    Py_hash_t hash = PyObject_Hash(bytes);
672
538k
    if (hash == -1) {
673
0
        p->error_indicator = 1;
674
0
        return NULL;
675
0
    }
676
    // Parses without identifiers do not need this cache.
677
538k
    if (p->identifier_cache == NULL) {
678
54.3k
        p->identifier_cache = PyMem_Calloc(
679
54.3k
            IDENTIFIER_CACHE_SIZE, sizeof(*p->identifier_cache));
680
54.3k
        if (p->identifier_cache == NULL) {
681
0
            p->error_indicator = 1;
682
0
            return PyErr_NoMemory();
683
0
        }
684
54.3k
    }
685
538k
    IdentifierCacheEntry *free_slot = NULL;
686
538k
    size_t idx = (size_t)hash & (IDENTIFIER_CACHE_SIZE - 1);
687
539k
    for (int probe = 0; probe < IDENTIFIER_CACHE_MAX_PROBES; probe++) {
688
539k
        IdentifierCacheEntry *entry = &p->identifier_cache[
689
539k
            (idx + probe) & (IDENTIFIER_CACHE_SIZE - 1)];
690
539k
        if (entry->key == NULL) {
691
102k
            free_slot = entry;
692
102k
            break;
693
102k
        }
694
437k
        if (entry->hash == hash && entry->len == len &&
695
435k
            memcmp(entry->key, s, len) == 0)
696
435k
        {
697
435k
            return entry->value;
698
435k
        }
699
437k
    }
700
102k
    PyObject *id = _PyPegen_new_identifier(p, s);
701
102k
    if (id == NULL) {
702
2
        p->error_indicator = 1;
703
2
        return NULL;
704
2
    }
705
102k
    if (free_slot != NULL) {
706
102k
        free_slot->key = s;
707
102k
        free_slot->len = len;
708
102k
        free_slot->hash = hash;
709
102k
        free_slot->value = id;
710
102k
    }
711
102k
    return id;
712
102k
}
713
714
static expr_ty
715
_PyPegen_name_from_token(Parser *p, Token* t)
716
18.6M
{
717
18.6M
    if (t == NULL) {
718
11.6M
        return NULL;
719
11.6M
    }
720
    // Reuse the AST node when backtracking revisits this token. Memo lookup
721
    // starts before the token and restores the position after it on a hit.
722
    // Token kinds can be memo keys: generated grammar rule IDs start at 1000.
723
7.07M
    int mark = p->mark - 1;
724
7.07M
    p->mark = mark;
725
7.07M
    expr_ty cached = NULL;
726
7.07M
    if (_PyPegen_is_memoized(p, NAME, &cached)) {
727
6.53M
        return cached;
728
6.53M
    }
729
538k
    p->mark = mark + 1;
730
538k
    PyObject *id = get_cached_identifier(p, t->bytes);
731
538k
    if (id == NULL) {
732
2
        return NULL;
733
2
    }
734
538k
    expr_ty result = _PyAST_Name(id, Load, t->lineno, t->col_offset,
735
538k
                                t->end_lineno, t->end_col_offset, p->arena);
736
538k
    if (result != NULL && _PyPegen_insert_memo(p, mark, NAME, result) < 0) {
737
0
        p->error_indicator = 1;
738
0
        return NULL;
739
0
    }
740
538k
    return result;
741
538k
}
742
743
expr_ty
744
_PyPegen_name_token(Parser *p)
745
18.6M
{
746
18.6M
    Token *t = _PyPegen_expect_token(p, NAME);
747
18.6M
    return _PyPegen_name_from_token(p, t);
748
18.6M
}
749
750
void *
751
_PyPegen_string_token(Parser *p)
752
1.36M
{
753
1.36M
    return _PyPegen_expect_token(p, STRING);
754
1.36M
}
755
756
306k
expr_ty _PyPegen_soft_keyword_token(Parser *p) {
757
306k
    Token *t = _PyPegen_expect_token(p, NAME);
758
306k
    if (t == NULL) {
759
233k
        return NULL;
760
233k
    }
761
73.2k
    char *the_token;
762
73.2k
    Py_ssize_t size;
763
73.2k
    PyBytes_AsStringAndSize(t->bytes, &the_token, &size);
764
414k
    for (char **keyword = p->soft_keywords; *keyword != NULL; keyword++) {
765
347k
        if (strlen(*keyword) == (size_t)size &&
766
65.2k
            strncmp(*keyword, the_token, (size_t)size) == 0) {
767
6.26k
            return _PyPegen_name_from_token(p, t);
768
6.26k
        }
769
347k
    }
770
66.9k
    return NULL;
771
73.2k
}
772
773
static PyObject *
774
parsenumber_raw(const char *s)
775
126k
{
776
126k
    const char *end;
777
126k
    long x;
778
126k
    double dx;
779
126k
    Py_complex compl;
780
126k
    int imflag;
781
782
126k
    assert(s != NULL);
783
126k
    errno = 0;
784
126k
    end = s + strlen(s) - 1;
785
126k
    imflag = *end == 'j' || *end == 'J';
786
126k
    if (s[0] == '0') {
787
33.9k
        x = (long)PyOS_strtoul(s, (char **)&end, 0);
788
33.9k
        if (x < 0 && errno == 0) {
789
559
            return PyLong_FromString(s, (char **)0, 0);
790
559
        }
791
33.9k
    }
792
92.7k
    else {
793
92.7k
        x = PyOS_strtol(s, (char **)&end, 0);
794
92.7k
    }
795
126k
    if (*end == '\0') {
796
88.6k
        if (errno != 0) {
797
2.35k
            return PyLong_FromString(s, (char **)0, 0);
798
2.35k
        }
799
86.3k
        return PyLong_FromLong(x);
800
88.6k
    }
801
    /* XXX Huge floats may silently fail */
802
37.4k
    if (imflag) {
803
7.69k
        compl.real = 0.;
804
7.69k
        compl.imag = PyOS_string_to_double(s, (char **)&end, NULL);
805
7.69k
        if (compl.imag == -1.0 && PyErr_Occurred()) {
806
0
            return NULL;
807
0
        }
808
7.69k
        return PyComplex_FromCComplex(compl);
809
7.69k
    }
810
29.7k
    dx = PyOS_string_to_double(s, NULL, NULL);
811
29.7k
    if (dx == -1.0 && PyErr_Occurred()) {
812
0
        return NULL;
813
0
    }
814
29.7k
    return PyFloat_FromDouble(dx);
815
29.7k
}
816
817
static PyObject *
818
parsenumber(const char *s)
819
126k
{
820
126k
    char *dup;
821
126k
    char *end;
822
126k
    PyObject *res = NULL;
823
824
126k
    assert(s != NULL);
825
826
126k
    if (strchr(s, '_') == NULL) {
827
125k
        return parsenumber_raw(s);
828
125k
    }
829
    /* Create a duplicate without underscores. */
830
1.43k
    dup = PyMem_Malloc(strlen(s) + 1);
831
1.43k
    if (dup == NULL) {
832
0
        return PyErr_NoMemory();
833
0
    }
834
1.43k
    end = dup;
835
33.2k
    for (; *s; s++) {
836
31.8k
        if (*s != '_') {
837
27.0k
            *end++ = *s;
838
27.0k
        }
839
31.8k
    }
840
1.43k
    *end = '\0';
841
1.43k
    res = parsenumber_raw(dup);
842
1.43k
    PyMem_Free(dup);
843
1.43k
    return res;
844
1.43k
}
845
846
expr_ty
847
_PyPegen_number_token(Parser *p)
848
935k
{
849
935k
    int mark = p->mark;
850
935k
    Token *t = _PyPegen_expect_token(p, NUMBER);
851
935k
    if (t == NULL) {
852
684k
        return NULL;
853
684k
    }
854
855
    // Look up the node at the token's start, as for NAME tokens. A memo hit
856
    // advances the mark; on a miss, restore the position after the token.
857
250k
    p->mark = mark;
858
250k
    expr_ty cached = NULL;
859
250k
    if (_PyPegen_is_memoized(p, NUMBER, &cached)) {
860
123k
        return cached;
861
123k
    }
862
126k
    p->mark = mark + 1;
863
864
126k
    const char *num_raw = PyBytes_AsString(t->bytes);
865
126k
    if (num_raw == NULL) {
866
0
        p->error_indicator = 1;
867
0
        return NULL;
868
0
    }
869
870
126k
    if (p->feature_version < 6 && strchr(num_raw, '_') != NULL) {
871
0
        p->error_indicator = 1;
872
0
        return RAISE_SYNTAX_ERROR("Underscores in numeric literals are only supported "
873
0
                                  "in Python 3.6 and greater");
874
0
    }
875
876
126k
    PyObject *c = parsenumber(num_raw);
877
878
126k
    if (c == NULL) {
879
0
        p->error_indicator = 1;
880
0
        PyThreadState *tstate = _PyThreadState_GET();
881
        // The only way a ValueError should happen in _this_ code is via
882
        // PyLong_FromString hitting a length limit.
883
0
        if (tstate->current_exception != NULL &&
884
0
            Py_TYPE(tstate->current_exception) == (PyTypeObject *)PyExc_ValueError
885
0
        ) {
886
0
            PyObject *exc = PyErr_GetRaisedException();
887
            /* Intentionally omitting columns to avoid a wall of 1000s of '^'s
888
             * on the error message. Nobody is going to overlook their huge
889
             * numeric literal once given the line. */
890
0
            RAISE_ERROR_KNOWN_LOCATION(
891
0
                p, PyExc_SyntaxError,
892
0
                t->lineno, -1 /* col_offset */,
893
0
                t->end_lineno, -1 /* end_col_offset */,
894
0
                "%S - Consider hexadecimal for huge integer literals "
895
0
                "to avoid decimal conversion limits.",
896
0
                exc);
897
0
            Py_DECREF(exc);
898
0
        }
899
0
        return NULL;
900
0
    }
901
902
126k
    if (_PyArena_AddPyObject(p->arena, c) < 0) {
903
0
        Py_DECREF(c);
904
0
        p->error_indicator = 1;
905
0
        return NULL;
906
0
    }
907
908
126k
    expr_ty result = _PyAST_Constant(c, NULL, t->lineno, t->col_offset,
909
126k
                                    t->end_lineno, t->end_col_offset, p->arena);
910
126k
    if (result != NULL && _PyPegen_insert_memo(p, mark, NUMBER, result) < 0) {
911
0
        p->error_indicator = 1;
912
0
        return NULL;
913
0
    }
914
126k
    return result;
915
126k
}
916
917
918
919
static int
920
compute_parser_flags(PyCompilerFlags *flags)
921
65.0k
{
922
65.0k
    int parser_flags = 0;
923
65.0k
    if (!flags) {
924
111
        return 0;
925
111
    }
926
64.9k
    if (flags->cf_flags & PyCF_DONT_IMPLY_DEDENT) {
927
0
        parser_flags |= PyPARSE_DONT_IMPLY_DEDENT;
928
0
    }
929
64.9k
    if (flags->cf_flags & PyCF_IGNORE_COOKIE) {
930
42.7k
        parser_flags |= PyPARSE_IGNORE_COOKIE;
931
42.7k
    }
932
64.9k
    if (flags->cf_flags & CO_FUTURE_BARRY_AS_BDFL) {
933
0
        parser_flags |= PyPARSE_BARRY_AS_BDFL;
934
0
    }
935
64.9k
    if (flags->cf_flags & PyCF_TYPE_COMMENTS) {
936
0
        parser_flags |= PyPARSE_TYPE_COMMENTS;
937
0
    }
938
64.9k
    if (flags->cf_flags & PyCF_ALLOW_INCOMPLETE_INPUT) {
939
0
        parser_flags |= PyPARSE_ALLOW_INCOMPLETE_INPUT;
940
0
    }
941
64.9k
    return parser_flags;
942
65.0k
}
943
944
// Parser API
945
946
Parser *
947
_PyPegen_Parser_New(struct tok_state *tok, int start_rule, int flags,
948
                    int feature_version, int *errcode, const char* source, PyArena *arena)
949
62.5k
{
950
62.5k
    Parser *p = PyMem_Malloc(sizeof(Parser));
951
62.5k
    if (p == NULL) {
952
0
        return (Parser *) PyErr_NoMemory();
953
0
    }
954
62.5k
    assert(tok != NULL);
955
62.5k
    _PyTokenizer_SetOptions(tok, 0, (flags & PyPARSE_TYPE_COMMENTS) > 0);
956
62.5k
    p->tok = tok;
957
62.5k
    p->keywords = NULL;
958
62.5k
    p->n_keyword_lists = -1;
959
62.5k
    p->soft_keywords = NULL;
960
62.5k
    p->tokens = PyMem_Malloc(sizeof(Token *));
961
62.5k
    if (!p->tokens) {
962
0
        PyMem_Free(p);
963
0
        return (Parser *) PyErr_NoMemory();
964
0
    }
965
62.5k
    p->tokens[0] = PyMem_Calloc(1, sizeof(Token));
966
62.5k
    if (!p->tokens[0]) {
967
0
        PyMem_Free(p->tokens);
968
0
        PyMem_Free(p);
969
0
        return (Parser *) PyErr_NoMemory();
970
0
    }
971
62.5k
    p->type_ignore_comments = (growable_comment_array){0};
972
973
62.5k
    p->mark = 0;
974
62.5k
    p->fill = 0;
975
62.5k
    p->size = 1;
976
977
62.5k
    p->errcode = errcode;
978
62.5k
    p->arena = arena;
979
62.5k
    p->start_rule = start_rule;
980
62.5k
    p->parsing_started = 0;
981
62.5k
    p->normalize = NULL;
982
62.5k
    p->error_indicator = 0;
983
984
62.5k
    p->starting_lineno = 0;
985
62.5k
    p->starting_col_offset = 0;
986
62.5k
    p->flags = flags;
987
62.5k
    p->feature_version = feature_version;
988
62.5k
    p->known_err_token = NULL;
989
62.5k
    p->identifier_cache = NULL;
990
62.5k
    p->tstate = PyThreadState_Get();
991
    // Stack limits are initialized when the thread state is attached.
992
62.5k
    assert(((_PyThreadStateImpl *)p->tstate)->c_stack_hard_limit != 0);
993
62.5k
    p->stack_soft_limit = ((_PyThreadStateImpl *)p->tstate)->c_stack_soft_limit;
994
62.5k
#if _Py_STACK_GROWS_DOWN
995
62.5k
    p->stack_soft_limit += _PyOS_STACK_MARGIN_BYTES;
996
#else
997
    p->stack_soft_limit -= _PyOS_STACK_MARGIN_BYTES;
998
#endif
999
62.5k
    p->level = 0;
1000
62.5k
    p->call_invalid_rules = 0;
1001
62.5k
    p->last_stmt_location.lineno = 0;
1002
62.5k
    p->last_stmt_location.col_offset = 0;
1003
62.5k
    p->last_stmt_location.end_lineno = 0;
1004
62.5k
    p->last_stmt_location.end_col_offset = 0;
1005
#ifdef Py_DEBUG
1006
    p->debug = _Py_GetConfig()->parser_debug;
1007
#endif
1008
62.5k
    return p;
1009
62.5k
}
1010
1011
void
1012
_PyPegen_Parser_Free(Parser *p)
1013
62.5k
{
1014
62.5k
    PyMem_Free(p->identifier_cache);
1015
62.5k
    Py_XDECREF(p->normalize);
1016
    // Resizes allocate blocks starting at indices 1, 2, 4, and so on.
1017
62.5k
    PyMem_Free(p->tokens[0]);
1018
227k
    for (int i = 1; i < p->size; i *= PEGEN_ARRAY_GROWTH_FACTOR) {
1019
164k
        PyMem_Free(p->tokens[i]);
1020
164k
    }
1021
62.5k
    PyMem_Free(p->tokens);
1022
62.5k
    growable_comment_array_deallocate(&p->type_ignore_comments);
1023
62.5k
    PyMem_Free(p);
1024
62.5k
}
1025
1026
static void
1027
reset_parser_state_for_error_pass(Parser *p)
1028
54.2k
{
1029
54.2k
    p->last_stmt_location.lineno = 0;
1030
54.2k
    p->last_stmt_location.col_offset = 0;
1031
54.2k
    p->last_stmt_location.end_lineno = 0;
1032
54.2k
    p->last_stmt_location.end_col_offset = 0;
1033
739k
    for (int i = 0; i < p->fill; i++) {
1034
685k
        p->tokens[i]->memo = NULL;
1035
685k
        p->tokens[i]->memo_mask = 0;
1036
685k
    }
1037
54.2k
    p->mark = 0;
1038
54.2k
    p->call_invalid_rules = 1;
1039
54.2k
    _PyTokenizer_StopInteractive(p->tok);
1040
54.2k
}
1041
1042
static inline int
1043
0
_is_end_of_source(Parser *p) {
1044
0
    int err = _PyTokenizer_GetInfo(p->tok).status;
1045
0
    return err == E_EOF || err == E_EOFS || err == E_EOLS;
1046
0
}
1047
1048
static void
1049
54.2k
_PyPegen_set_syntax_error_metadata(Parser *p) {
1050
54.2k
    PyObject *exc = PyErr_GetRaisedException();
1051
54.2k
    if (!exc || !PyObject_TypeCheck(exc, (PyTypeObject *)PyExc_SyntaxError)) {
1052
0
        PyErr_SetRaisedException(exc);
1053
0
        return;
1054
0
    }
1055
54.2k
    const char *source = _PyTokenizer_RetainedSource(p->tok);
1056
54.2k
    const char *encoding = _PyTokenizer_GetInfo(p->tok).encoding;
1057
54.2k
    PyObject* the_source = NULL;
1058
54.2k
    if (source) {
1059
54.2k
        if (encoding == NULL) {
1060
10.6k
            the_source = PyUnicode_FromString(source);
1061
43.5k
        } else {
1062
43.5k
            the_source = PyUnicode_Decode(source, strlen(source), encoding, NULL);
1063
43.5k
        }
1064
54.2k
    }
1065
54.2k
    if (!the_source) {
1066
601
        PyErr_Clear();
1067
601
        the_source = Py_None;
1068
601
        Py_INCREF(the_source);
1069
601
    }
1070
54.2k
    PyObject* metadata = Py_BuildValue(
1071
54.2k
        "(iiN)",
1072
54.2k
        p->last_stmt_location.lineno,
1073
54.2k
        p->last_stmt_location.col_offset,
1074
54.2k
        the_source // N gives ownership to metadata
1075
54.2k
    );
1076
54.2k
    if (!metadata) {
1077
0
        PyErr_Clear();
1078
0
        return;
1079
0
    }
1080
54.2k
    PySyntaxErrorObject *syntax_error = (PySyntaxErrorObject *)exc;
1081
1082
54.2k
    Py_XDECREF(syntax_error->metadata);
1083
54.2k
    syntax_error->metadata = metadata;
1084
54.2k
    PyErr_SetRaisedException(exc);
1085
54.2k
}
1086
1087
void *
1088
_PyPegen_run_parser(Parser *p)
1089
62.5k
{
1090
62.5k
    void *res = _PyPegen_parse(p);
1091
62.5k
    assert(p->level == 0);
1092
62.5k
    if (res != NULL && PyErr_Occurred()) {
1093
        // Discard a result returned with an exception still pending
1094
        // (e.g. a MemoryError from a recovered-from allocation failure).
1095
0
        return NULL;
1096
0
    }
1097
62.5k
    if (res == NULL) {
1098
54.2k
        if ((p->flags & PyPARSE_ALLOW_INCOMPLETE_INPUT) &&  _is_end_of_source(p)) {
1099
0
            PyErr_Clear();
1100
0
            return _PyPegen_raise_error(p, PyExc_IncompleteInputError, 0, "incomplete input");
1101
0
        }
1102
54.2k
        if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_SyntaxError)) {
1103
8
            return NULL;
1104
8
        }
1105
       // Make a second parser pass. In this pass we activate heavier and slower checks
1106
        // to produce better error messages and more complete diagnostics. Extra "invalid_*"
1107
        // rules will be active during parsing.
1108
54.2k
        Token *last_token = p->tokens[p->fill - 1];
1109
54.2k
        reset_parser_state_for_error_pass(p);
1110
54.2k
        _PyPegen_parse(p);
1111
1112
        // Set SyntaxErrors accordingly depending on the parser/tokenizer status at the failure
1113
        // point.
1114
54.2k
        _Pypegen_set_syntax_error(p, last_token);
1115
1116
        // Set the metadata in the exception from p->last_stmt_location
1117
54.2k
        if (PyErr_ExceptionMatches(PyExc_SyntaxError)) {
1118
54.2k
            _PyPegen_set_syntax_error_metadata(p);
1119
54.2k
        }
1120
54.2k
       return NULL;
1121
54.2k
    }
1122
1123
8.23k
    if (p->start_rule == Py_single_input && _PyTokenizer_HasTrailingStatement(p->tok)) {
1124
0
        return RAISE_SYNTAX_ERROR("multiple statements found while compiling a single statement");
1125
0
    }
1126
1127
    // test_peg_generator defines _Py_TEST_PEGEN to not call PyAST_Validate()
1128
#if defined(Py_DEBUG) && !defined(_Py_TEST_PEGEN)
1129
    if (p->start_rule == Py_single_input ||
1130
        p->start_rule == Py_file_input ||
1131
        p->start_rule == Py_eval_input)
1132
    {
1133
        if (!_PyAST_Validate(res)) {
1134
            return NULL;
1135
        }
1136
    }
1137
#endif
1138
8.23k
    return res;
1139
8.23k
}
1140
1141
mod_ty
1142
_PyPegen_run_parser_from_file_pointer(FILE *fp, int start_rule, PyObject *filename_ob,
1143
                             const char *enc, const char *ps1, const char *ps2,
1144
                             PyCompilerFlags *flags, int *errcode,
1145
                             PyObject **interactive_src, PyArena *arena)
1146
0
{
1147
0
    int parser_flags = compute_parser_flags(flags);
1148
0
    struct tok_state *tok = _PyTokenizer_FromFile(fp, enc, ps1, ps2);
1149
0
    if (tok == NULL) {
1150
0
        if (PyErr_Occurred()) {
1151
0
            _PyTokenizer_raise_init_error(filename_ob);
1152
0
        }
1153
0
        else {
1154
            // The only silent tokenizer init failure is a failed allocation.
1155
0
            PyErr_NoMemory();
1156
0
        }
1157
0
        return NULL;
1158
0
    }
1159
0
    tok->barry_as_bdfl = parser_flags & PyPARSE_BARRY_AS_BDFL;
1160
1161
    // From here on we need to clean up even if there's an error
1162
0
    mod_ty result = NULL;
1163
1164
0
    PyObject *module = PyUnicode_FromString("__main__");
1165
0
    if (module == NULL) {
1166
0
        goto error;
1167
0
    }
1168
0
    _PyTokenizer_SetContext(tok, filename_ob, module);
1169
0
    Py_DECREF(module);
1170
1171
0
    Parser *p = _PyPegen_Parser_New(tok, start_rule, parser_flags, PY_MINOR_VERSION,
1172
0
                                    errcode, NULL, arena);
1173
0
    if (p == NULL) {
1174
0
        goto error;
1175
0
    }
1176
1177
0
    result = _PyPegen_run_parser(p);
1178
0
    _PyPegen_Parser_Free(p);
1179
1180
0
    const char *source = _PyTokenizer_RetainedSource(tok);
1181
0
    if (source != NULL && result && interactive_src != NULL) {
1182
0
        *interactive_src = PyUnicode_FromString(source);
1183
0
        if (!*interactive_src || _PyArena_AddPyObject(arena, *interactive_src) < 0) {
1184
0
            Py_XDECREF(*interactive_src);
1185
0
            result = NULL;
1186
0
            goto error;
1187
0
        }
1188
0
    }
1189
1190
0
error:
1191
0
    _PyTokenizer_Free(tok);
1192
0
    return result;
1193
0
}
1194
1195
mod_ty
1196
_PyPegen_run_parser_from_string(const char *str, int start_rule, PyObject *filename_ob,
1197
                       PyCompilerFlags *flags, PyArena *arena, PyObject *module)
1198
65.0k
{
1199
65.0k
    int exec_input = start_rule == Py_file_input;
1200
65.0k
    int parser_flags = compute_parser_flags(flags);
1201
1202
65.0k
    struct tok_state *tok;
1203
65.0k
    if (flags != NULL && flags->cf_flags & PyCF_IGNORE_COOKIE) {
1204
42.7k
        tok = _PyTokenizer_FromUTF8(str, exec_input, 0);
1205
42.7k
    } else {
1206
22.2k
        tok = _PyTokenizer_FromString(str, exec_input, 0);
1207
22.2k
    }
1208
65.0k
    if (tok == NULL) {
1209
2.52k
        if (PyErr_Occurred()) {
1210
2.52k
            _PyTokenizer_raise_init_error(filename_ob);
1211
2.52k
        }
1212
0
        else {
1213
            // The only silent tokenizer init failure is a failed allocation.
1214
0
            PyErr_NoMemory();
1215
0
        }
1216
2.52k
        return NULL;
1217
2.52k
    }
1218
62.5k
    tok->barry_as_bdfl = parser_flags & PyPARSE_BARRY_AS_BDFL;
1219
1220
62.5k
    _PyTokenizer_SetContext(tok, filename_ob, module);
1221
1222
    // We need to clear up from here on
1223
62.5k
    mod_ty result = NULL;
1224
1225
62.5k
    int feature_version = flags && (flags->cf_flags & PyCF_ONLY_AST) ?
1226
61.7k
        flags->cf_feature_version : PY_MINOR_VERSION;
1227
62.5k
    Parser *p = _PyPegen_Parser_New(tok, start_rule, parser_flags, feature_version,
1228
62.5k
                                    NULL, str, arena);
1229
62.5k
    if (p == NULL) {
1230
0
        goto error;
1231
0
    }
1232
1233
62.5k
    result = _PyPegen_run_parser(p);
1234
62.5k
    _PyPegen_Parser_Free(p);
1235
1236
62.5k
error:
1237
62.5k
    _PyTokenizer_Free(tok);
1238
62.5k
    return result;
1239
62.5k
}