Skip to content

Commit f3d0a9b

Browse files
committed
gh-153569: make the reader own interactive state
Interactive prompting and parser stop requests describe how the reader obtains more input. Store that state on the reader and expose small queries for the lexer and parser. The accumulated interactive statement is already the tokenizer source. Return it directly after a successful parse and remove duplicate source pointers from tok_state.
1 parent da3629e commit f3d0a9b

7 files changed

Lines changed: 34 additions & 36 deletions

File tree

Parser/lexer/lexer.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,15 +204,16 @@ _PyLexer_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, str
204204
}
205205
tok_backup(tok, c);
206206
if (c == '#' || c == '\n' || c == '\r') {
207+
int interactive = _PyTok_ReaderIsInteractive(tok);
207208
/* Lines with only whitespace and/or comments
208209
shouldn't affect the indentation and are
209210
not passed to the parser as NEWLINE tokens,
210211
except *totally* empty lines in interactive
211212
mode, which signal the end of a command group. */
212-
if (col == 0 && c == '\n' && tok->prompt != NULL) {
213+
if (col == 0 && c == '\n' && interactive) {
213214
blankline = 0; /* Let it through */
214215
}
215-
else if (tok->prompt != NULL && tok->lineno == 1) {
216+
else if (interactive && tok->lineno == 1) {
216217
/* In interactive mode, if the first line contains
217218
only spaces and/or a comment, let it through. */
218219
blankline = 0;

Parser/lexer/state.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,6 @@
1313
#define INSIDE_FSTRING_EXPR_AT_TOP(tok) \
1414
(tok->curly_bracket_depth - tok->curly_bracket_expr_start_depth == 1)
1515

16-
enum interactive_underflow_t {
17-
/* Normal mode of operation: return a new token when asked in interactive mode */
18-
IUNDERFLOW_NORMAL,
19-
/* Forcefully return ENDMARKER when asked for a new token in interactive mode. This
20-
* can be used to prevent the tokenizer to prompt the user for new tokens */
21-
IUNDERFLOW_STOP,
22-
};
23-
2416
struct token {
2517
int level;
2618
_PyTok_Span span;
@@ -76,9 +68,6 @@ struct tok_state {
7668
char *cur; /* Next character in buffer */
7769
char *inp; /* End of data in buffer */
7870
_PyTok_Off buf_offset; /* Logical offset of buf[0]. */
79-
int fp_interactive; /* If the file descriptor is interactive */
80-
char *interactive_src_start; /* The start of the source parsed so far in interactive mode */
81-
char *interactive_src_end; /* The end of the source parsed so far in interactive mode */
8271
const char *start; /* Start of current token if not NULL */
8372
int done; /* E_OK normally, E_EOF at EOF, otherwise error code */
8473
/* NB If done != E_OK, cur must be == inp!!! */
@@ -87,7 +76,6 @@ struct tok_state {
8776
int indstack[MAXINDENT]; /* Stack of indents */
8877
int atbol; /* Nonzero if at begin of new line */
8978
int pendin; /* Pending indents (if > 0) or dedents (if < 0) */
90-
const char *prompt; /* For interactive prompting */
9179
int lineno; /* Current line number */
9280
_PyTok_Loc start_loc;
9381
int level; /* () [] {} Parentheses nesting level */
@@ -108,8 +96,6 @@ struct tok_state {
10896

10997
int type_comments; /* Whether to look for type comments */
11098

111-
/* How to proceed when asked for a new token in interactive mode */
112-
enum interactive_underflow_t interactive_underflow;
11399
int report_warnings;
114100
// TODO: Factor this into its own thing
115101
tokenizer_mode tok_mode_stack[MAXFSTRINGLEVEL];

Parser/pegen.c

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
#include <errcode.h>
99

1010
#include "lexer/lexer.h"
11-
#include "tokenizer/tokenizer.h"
1211
#include "tokenizer/helpers.h"
12+
#include "tokenizer/reader.h"
13+
#include "tokenizer/tokenizer.h"
1314
#include "pegen.h"
1415

1516
#define IDENTIFIER_CACHE_SIZE 2048 // Must be a power of two.
@@ -943,9 +944,7 @@ reset_parser_state_for_error_pass(Parser *p)
943944
}
944945
p->mark = 0;
945946
p->call_invalid_rules = 1;
946-
// Don't try to get extra tokens in interactive mode when trying to
947-
// raise specialized errors in the second pass.
948-
p->tok->interactive_underflow = IUNDERFLOW_STOP;
947+
_PyTok_ReaderStopInteractive(p->tok);
949948
}
950949

951950
static inline int
@@ -1066,10 +1065,6 @@ _PyPegen_run_parser_from_file_pointer(FILE *fp, int start_rule, PyObject *filena
10661065
}
10671066
return NULL;
10681067
}
1069-
if (!tok->fp || ps1 != NULL || ps2 != NULL ||
1070-
PyUnicode_CompareWithASCIIString(filename_ob, "<stdin>") == 0) {
1071-
tok->fp_interactive = 1;
1072-
}
10731068
// This transfers the ownership to the tokenizer
10741069
tok->filename = Py_NewRef(filename_ob);
10751070

@@ -1091,8 +1086,8 @@ _PyPegen_run_parser_from_file_pointer(FILE *fp, int start_rule, PyObject *filena
10911086
result = _PyPegen_run_parser(p);
10921087
_PyPegen_Parser_Free(p);
10931088

1094-
if (tok->fp_interactive && tok->interactive_src_start && result && interactive_src != NULL) {
1095-
*interactive_src = PyUnicode_FromString(tok->interactive_src_start);
1089+
if (tok->source.bytes != NULL && result && interactive_src != NULL) {
1090+
*interactive_src = PyUnicode_FromString(tok->source.bytes);
10961091
if (!*interactive_src || _PyArena_AddPyObject(arena, *interactive_src) < 0) {
10971092
Py_XDECREF(*interactive_src);
10981093
result = NULL;

Parser/pegen_errors.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "lexer/state.h"
88
#include "lexer/lexer.h"
99
#include "pegen.h"
10+
#include "tokenizer/reader.h"
1011

1112
// TOKENIZER ERRORS
1213

@@ -122,7 +123,7 @@ _PyPegen_tokenize_full_source_to_check_for_errors(Parser *p) {
122123
// before the one that we had for the generic error.
123124

124125
// We don't want to tokenize to the end for interactive input
125-
if (p->tok->prompt != NULL) {
126+
if (_PyTok_ReaderIsInteractive(p->tok)) {
126127
return 0;
127128
}
128129

@@ -273,7 +274,8 @@ _PyPegen_raise_error_known_location(Parser *p, PyObject *errtype,
273274
goto error;
274275
}
275276

276-
if (p->tok->fp_interactive && p->tok->interactive_src_start != NULL) {
277+
if (_PyTok_ReaderIsInteractive(p->tok) &&
278+
p->tok->source.bytes != NULL) {
277279
error_line = get_error_line_from_source(p, lineno);
278280
}
279281
else if (p->start_rule == Py_file_input) {

Parser/tokenizer/reader.c

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -529,13 +529,13 @@ static _PyTok_ReadResult
529529
next_interactive(struct tok_state *tok, _PyTok_Chunk *chunk)
530530
{
531531
_PyTok_Reader *reader = tok->reader;
532-
if (tok->interactive_underflow == IUNDERFLOW_STOP) {
532+
if (reader->stop_interactive) {
533533
return _PYTOK_READ_STOPPED;
534534
}
535535
char *input = PyOS_Readline(
536-
tok->fp != NULL ? tok->fp : stdin, stdout, tok->prompt);
536+
tok->fp != NULL ? tok->fp : stdin, stdout, reader->prompt);
537537
if (reader->nextprompt != NULL) {
538-
tok->prompt = reader->nextprompt;
538+
reader->prompt = reader->nextprompt;
539539
}
540540
if (input == NULL) {
541541
return _PYTOK_READ_INTERRUPT;
@@ -569,6 +569,20 @@ next_interactive(struct tok_state *tok, _PyTok_Chunk *chunk)
569569
return _PYTOK_READ_LINE;
570570
}
571571

572+
int
573+
_PyTok_ReaderIsInteractive(const struct tok_state *tok)
574+
{
575+
return tok->reader->kind == _PYTOK_READER_INTERACTIVE;
576+
}
577+
578+
void
579+
_PyTok_ReaderStopInteractive(struct tok_state *tok)
580+
{
581+
if (_PyTok_ReaderIsInteractive(tok)) {
582+
tok->reader->stop_interactive = 1;
583+
}
584+
}
585+
572586
static _PyTok_ReadResult
573587
reader_next(struct tok_state *tok, _PyTok_Chunk *chunk)
574588
{
@@ -686,10 +700,6 @@ _PyTok_ReaderUnderflow(struct tok_state *tok)
686700
}
687701
tok->inp = tok->source.bytes + source_start + scan_len;
688702
}
689-
if (tok->fp_interactive) {
690-
tok->interactive_src_start = tok->source.bytes;
691-
tok->interactive_src_end = tok->source.bytes + tok->source.len;
692-
}
693703
if (prepared) {
694704
if (tok->start == NULL && !INSIDE_FSTRING(tok)) {
695705
tok->buf = tok->cur;
@@ -801,7 +811,7 @@ _PyTokenizer_FromFile(FILE *fp, const char *encoding,
801811
return NULL;
802812
}
803813
tok->fp = fp;
804-
tok->prompt = ps1;
814+
tok->reader->prompt = ps1;
805815
tok->reader->nextprompt = ps2;
806816
return tok;
807817
}

Parser/tokenizer/reader.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@ struct tok_state;
55

66
void _PyTok_ReaderFree(struct tok_state *);
77
int _PyTok_ReaderUnderflow(struct tok_state *);
8+
int _PyTok_ReaderIsInteractive(const struct tok_state *);
9+
void _PyTok_ReaderStopInteractive(struct tok_state *);
810

911
#endif

Parser/tokenizer/reader_internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ typedef struct _PyTok_Reader {
4242
_PyTok_ReaderKind kind;
4343
PyObject *readline;
4444
PyObject *decoder;
45+
const char *prompt;
4546
const char *nextprompt;
4647

4748
Py_ssize_t input_buffer_cap;
@@ -61,6 +62,7 @@ typedef struct _PyTok_Reader {
6162
int file_initialized;
6263
int file_eof;
6364
int decoder_finalized;
65+
unsigned char stop_interactive;
6466
} _PyTok_Reader;
6567

6668
struct tok_state;

0 commit comments

Comments
 (0)