GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-153569: return tokenizer tokens as source spans · python/cpython@f134e81

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • Parser
    • lexer
      • lexer.c
      • lexer.h
      • state.c
      • state.h
    • pegen.c
    • tokenizer
      • source.h
  • Python
    • Python-tokenize.c
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎Parser/lexer/lexer.c‎

Copy file name to clipboardExpand all lines: Parser/lexer/lexer.c
+8-6Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212

1313

1414
#define MAKE_TOKEN(token_type) _PyLexer_token_setup(tok, token, token_type, p_start, p_end)
15-
#define MAKE_TYPE_COMMENT_TOKEN(token_type, col_offset, end_col_offset) (\
16-
_PyLexer_type_comment_token_setup(tok, token, token_type, col_offset, end_col_offset, p_start, p_end))
1715

1816
/* Spaces in this constant are treated as "zero or more spaces or tabs" when
1917
tokenizing. */
@@ -360,21 +358,25 @@ _PyLexer_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, str
360358
&& !(tok->cur > ignore_end
361359
&& ((unsigned char)ignore_end[0] >= 128 || Py_ISALNUM(ignore_end[0]))));
362360

361+
int type = is_type_ignore ? TYPE_IGNORE : TYPE_COMMENT;
362+
int start_col_offset = is_type_ignore
363+
? ignore_end_col_offset : current_starting_col_offset;
364+
p_end = tok->cur;
363365
if (is_type_ignore) {
364366
p_start = ignore_end;
365-
p_end = tok->cur;
366367

367368
/* If this type ignore is the only thing on the line, consume the newline also. */
368369
if (blankline) {
369370
tok_nextc(tok);
370371
tok->atbol = 1;
371372
}
372-
return MAKE_TYPE_COMMENT_TOKEN(TYPE_IGNORE, ignore_end_col_offset, tok->col_offset);
373373
} else {
374374
p_start = type_start;
375-
p_end = tok->cur;
376-
return MAKE_TYPE_COMMENT_TOKEN(TYPE_COMMENT, current_starting_col_offset, tok->col_offset);
377375
}
376+
_PyLexer_token_setup(tok, token, type, p_start, p_end);
377+
token->start_loc = (_PyTok_Loc){tok->lineno, start_col_offset};
378+
token->end_loc = (_PyTok_Loc){tok->lineno, tok->col_offset};
379+
return type;
378380
}
379381
}
380382
if (tok->tok_extra_tokens) {
Collapse file

‎Parser/lexer/lexer.h‎

Copy file name to clipboardExpand all lines: Parser/lexer/lexer.h
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,25 @@ int _PyLexer_update_ftstring_expr(struct tok_state *tok, char cur);
77

88
int _PyTokenizer_Get(struct tok_state *, struct token *);
99

10+
/* The view points into the current input window. The next
11+
_PyTokenizer_Get() call may discard it. */
12+
static inline const char *
13+
_PyToken_TextView(const struct tok_state *tok, const struct token *token,
14+
Py_ssize_t *length)
15+
{
16+
assert(length != NULL);
17+
if (token->span.start < 0) {
18+
assert(token->span.start == -1 && token->span.end == -1);
19+
*length = 0;
20+
return "";
21+
}
22+
assert(_PyTok_SpanIsValid(token->span));
23+
assert(tok->buf != NULL);
24+
assert(tok->inp >= tok->buf);
25+
assert(token->span.start >= tok->buf_offset);
26+
assert(token->span.end - tok->buf_offset <= tok->inp - tok->buf);
27+
*length = token->span.end - token->span.start;
28+
return tok->buf + (token->span.start - tok->buf_offset);
29+
}
30+
1031
#endif
Collapse file

‎Parser/lexer/state.c‎

Copy file name to clipboardExpand all lines: Parser/lexer/state.c
+28-23Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -100,41 +100,46 @@ _PyToken_Free(struct token *token) {
100100

101101
void
102102
_PyToken_Init(struct token *token) {
103+
#ifdef Py_DEBUG
104+
token->span = (_PyTok_Span){-1, -1};
105+
token->start_loc = (_PyTok_Loc){-1, -1};
106+
token->end_loc = (_PyTok_Loc){-1, -1};
107+
#endif
103108
token->metadata = NULL;
104109
}
105110

106-
int
107-
_PyLexer_type_comment_token_setup(struct tok_state *tok, struct token *token, int type, int col_offset,
108-
int end_col_offset, const char *start, const char *end)
111+
static inline _PyTok_Span
112+
buffer_span(const struct tok_state *tok, const char *start, const char *end)
109113
{
110-
token->level = tok->level;
111-
token->lineno = token->end_lineno = tok->lineno;
112-
token->col_offset = col_offset;
113-
token->end_col_offset = end_col_offset;
114-
token->start = start;
115-
token->end = end;
116-
return type;
114+
if (start == NULL) {
115+
assert(end == NULL);
116+
return (_PyTok_Span){-1, -1};
117+
}
118+
assert(end != NULL);
119+
const char *base = tok->buf;
120+
assert(base != NULL);
121+
assert(tok->inp >= base);
122+
Py_ssize_t start_offset = start - base;
123+
Py_ssize_t end_offset = end - base;
124+
assert(start_offset >= 0 && start_offset <= end_offset);
125+
assert(end_offset <= tok->inp - base);
126+
assert(tok->buf_offset <= PY_SSIZE_T_MAX - end_offset);
127+
return _PyTok_SpanFromBounds(
128+
tok->buf_offset + start_offset, tok->buf_offset + end_offset);
117129
}
118130

119131
int
120132
_PyLexer_token_setup(struct tok_state *tok, struct token *token, int type, const char *start, const char *end)
121133
{
122-
assert((start == NULL && end == NULL) || (start != NULL && end != NULL));
123134
token->level = tok->level;
124-
if (ISSTRINGLIT(type)) {
125-
token->lineno = tok->first_lineno;
126-
}
127-
else {
128-
token->lineno = tok->lineno;
129-
}
130-
token->end_lineno = tok->lineno;
131-
token->col_offset = token->end_col_offset = -1;
132-
token->start = start;
133-
token->end = end;
135+
token->span = buffer_span(tok, start, end);
136+
int lineno = ISSTRINGLIT(type) ? tok->first_lineno : tok->lineno;
137+
token->start_loc = (_PyTok_Loc){lineno, -1};
138+
token->end_loc = (_PyTok_Loc){tok->lineno, -1};
134139

135140
if (start != NULL && end != NULL) {
136-
token->col_offset = tok->starting_col_offset;
137-
token->end_col_offset = tok->col_offset;
141+
token->start_loc.byte_col = tok->starting_col_offset;
142+
token->end_loc.byte_col = tok->col_offset;
138143
}
139144
return type;
140145
}
Collapse file

‎Parser/lexer/state.h‎

Copy file name to clipboardExpand all lines: Parser/lexer/state.h
+4-5Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ enum interactive_underflow_t {
2323

2424
struct token {
2525
int level;
26-
int lineno, col_offset, end_lineno, end_col_offset;
27-
const char *start, *end;
26+
_PyTok_Span span;
27+
_PyTok_Loc start_loc;
28+
_PyTok_Loc end_loc;
2829
PyObject *metadata;
2930
};
3031

@@ -69,7 +70,7 @@ typedef struct _tokenizer_mode {
6970
struct tok_state {
7071
/* Input state; buf <= cur <= inp */
7172
/* NB an entire line is held in the buffer */
72-
char *buf; /* Input buffer, or NULL; malloc'ed if fp != NULL or readline != NULL */
73+
char *buf; /* Owned for file/readline input; source-backed otherwise. */
7374
char *cur; /* Next character in buffer */
7475
char *inp; /* End of data in buffer */
7576
_PyTok_Off buf_offset; /* Logical offset of buf[0]. */
@@ -128,8 +129,6 @@ struct tok_state {
128129
#endif
129130
};
130131

131-
int _PyLexer_type_comment_token_setup(struct tok_state *tok, struct token *token, int type, int col_offset,
132-
int end_col_offset, const char *start, const char *end);
133132
int _PyLexer_token_setup(struct tok_state *tok, struct token *token, int type, const char *start, const char *end);
134133

135134
struct tok_state *_PyTokenizer_tok_new(void);
Collapse file

‎Parser/pegen.c‎

Copy file name to clipboardExpand all lines: Parser/pegen.c
+23-18Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -171,18 +171,17 @@ growable_comment_array_deallocate(growable_comment_array *arr) {
171171
}
172172

173173
static int
174-
_get_keyword_or_name_type(Parser *p, struct token *new_token)
174+
_get_keyword_or_name_type(Parser *p, const char *text, Py_ssize_t length)
175175
{
176-
Py_ssize_t name_len = new_token->end_col_offset - new_token->col_offset;
177-
assert(name_len > 0);
176+
assert(length > 0);
178177

179-
if (name_len >= p->n_keyword_lists ||
180-
p->keywords[name_len] == NULL ||
181-
p->keywords[name_len]->type == -1) {
178+
if (length >= p->n_keyword_lists ||
179+
p->keywords[length] == NULL ||
180+
p->keywords[length]->type == -1) {
182181
return NAME;
183182
}
184-
for (KeywordToken *k = p->keywords[name_len]; k != NULL && k->type != -1; k++) {
185-
if (strncmp(k->str, new_token->start, (size_t)name_len) == 0) {
183+
for (KeywordToken *k = p->keywords[length]; k != NULL && k->type != -1; k++) {
184+
if (memcmp(k->str, text, (size_t)length) == 0) {
186185
return k->type;
187186
}
188187
}
@@ -193,8 +192,11 @@ static int
193192
initialize_token(Parser *p, Token *parser_token, struct token *new_token, int token_type) {
194193
assert(parser_token != NULL);
195194

196-
parser_token->type = (token_type == NAME) ? _get_keyword_or_name_type(p, new_token) : token_type;
197-
parser_token->bytes = PyBytes_FromStringAndSize(new_token->start, new_token->end - new_token->start);
195+
Py_ssize_t length;
196+
const char *text = _PyToken_TextView(p->tok, new_token, &length);
197+
parser_token->type = token_type == NAME
198+
? _get_keyword_or_name_type(p, text, length) : token_type;
199+
parser_token->bytes = PyBytes_FromStringAndSize(text, length);
198200
if (parser_token->bytes == NULL) {
199201
return -1;
200202
}
@@ -214,12 +216,14 @@ initialize_token(Parser *p, Token *parser_token, struct token *new_token, int to
214216
}
215217

216218
parser_token->level = new_token->level;
217-
parser_token->lineno = new_token->lineno;
218-
parser_token->col_offset = p->tok->lineno == p->starting_lineno ? p->starting_col_offset + new_token->col_offset
219-
: new_token->col_offset;
220-
parser_token->end_lineno = new_token->end_lineno;
221-
parser_token->end_col_offset = p->tok->lineno == p->starting_lineno ? p->starting_col_offset + new_token->end_col_offset
222-
: new_token->end_col_offset;
219+
parser_token->lineno = new_token->start_loc.lineno;
220+
parser_token->col_offset = p->tok->lineno == p->starting_lineno
221+
? p->starting_col_offset + new_token->start_loc.byte_col
222+
: new_token->start_loc.byte_col;
223+
parser_token->end_lineno = new_token->end_loc.lineno;
224+
parser_token->end_col_offset = p->tok->lineno == p->starting_lineno
225+
? p->starting_col_offset + new_token->end_loc.byte_col
226+
: new_token->end_loc.byte_col;
223227

224228
p->fill += 1;
225229

@@ -261,13 +265,14 @@ _PyPegen_fill_token(Parser *p)
261265

262266
// Record and skip '# type: ignore' comments
263267
while (type == TYPE_IGNORE) {
264-
Py_ssize_t len = new_token.end_col_offset - new_token.col_offset;
268+
Py_ssize_t len;
269+
const char *text = _PyToken_TextView(p->tok, &new_token, &len);
265270
char *tag = PyMem_Malloc((size_t)len + 1);
266271
if (tag == NULL) {
267272
PyErr_NoMemory();
268273
goto error;
269274
}
270-
strncpy(tag, new_token.start, (size_t)len);
275+
memcpy(tag, text, (size_t)len);
271276
tag[len] = '\0';
272277
// Ownership of tag passes to the growable array
273278
if (!growable_comment_array_add(&p->type_ignore_comments, p->tok->lineno, tag)) {
Collapse file

‎Parser/tokenizer/source.h‎

Copy file name to clipboardExpand all lines: Parser/tokenizer/source.h
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
typedef Py_ssize_t _PyTok_Off;
77

8-
/* Half-open byte offsets into a _PyTok_SourceText. */
8+
/* Spans use half-open logical byte offsets into decoded input. Their backing
9+
storage may retain only the current input window. */
910
typedef struct {
1011
_PyTok_Off start;
1112
_PyTok_Off end;
Collapse file

‎Python/Python-tokenize.c‎

Copy file name to clipboardExpand all lines: Python/Python-tokenize.c
+27-18Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -203,14 +203,19 @@ _get_current_line(tokenizeriterobject *it, const char *line_start, Py_ssize_t si
203203
}
204204

205205
static void
206-
_get_col_offsets(tokenizeriterobject *it, struct token token, const char *line_start,
207-
PyObject *line, int line_changed, Py_ssize_t lineno, Py_ssize_t end_lineno,
206+
_get_col_offsets(tokenizeriterobject *it, const struct token *token,
207+
const char *token_start, const char *line_start,
208+
PyObject *line, int line_changed,
208209
Py_ssize_t *col_offset, Py_ssize_t *end_col_offset)
209210
{
210211
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(it);
212+
const char *token_end = token_start == NULL
213+
? NULL : token_start + token->span.end - token->span.start;
214+
Py_ssize_t lineno = token->start_loc.lineno;
215+
Py_ssize_t end_lineno = token->end_loc.lineno;
211216
Py_ssize_t byte_offset = -1;
212-
if (token.start != NULL && token.start >= line_start) {
213-
byte_offset = token.start - line_start;
217+
if (token_start != NULL && token_start >= line_start) {
218+
byte_offset = token_start - line_start;
214219
if (line_changed) {
215220
*col_offset = _PyPegen_byte_offset_to_character_offset_line(line, 0, byte_offset);
216221
it->byte_col_offset_diff = byte_offset - *col_offset;
@@ -220,15 +225,13 @@ _get_col_offsets(tokenizeriterobject *it, struct token token, const char *line_s
220225
}
221226
}
222227

223-
if (token.end != NULL && token.end >= it->tok->line_start) {
224-
Py_ssize_t end_byte_offset = token.end - it->tok->line_start;
228+
if (token_end != NULL && token_end >= it->tok->line_start) {
229+
Py_ssize_t end_byte_offset = token_end - it->tok->line_start;
225230
if (lineno == end_lineno) {
226-
// If the whole token is at the same line, we can just use the token.start
227-
// buffer for figuring out the new column offset, since using line is not
228-
// performant for very long lines.
231+
// Avoid rescanning the prefix of a very long line.
229232
Py_ssize_t token_col_offset = _PyPegen_byte_offset_to_character_offset_line(line, byte_offset, end_byte_offset);
230233
*end_col_offset = *col_offset + token_col_offset;
231-
it->byte_col_offset_diff += token.end - token.start - token_col_offset;
234+
it->byte_col_offset_diff += token_end - token_start - token_col_offset;
232235
}
233236
else {
234237
*end_col_offset = _PyPegen_byte_offset_to_character_offset_raw(it->tok->line_start, end_byte_offset);
@@ -263,12 +266,17 @@ tokenizeriter_next(PyObject *op)
263266
it->done = 1;
264267
goto exit;
265268
}
266-
PyObject *str = NULL;
267-
if (token.start == NULL || token.end == NULL) {
269+
const char *token_start = NULL;
270+
PyObject *str;
271+
if (token.span.start < 0) {
272+
assert(token.span.start == -1 && token.span.end == -1);
268273
str = Py_GetConstant(Py_CONSTANT_EMPTY_STR);
269274
}
270275
else {
271-
str = PyUnicode_FromStringAndSize(token.start, token.end - token.start);
276+
Py_ssize_t token_length;
277+
token_start = _PyToken_TextView(
278+
it->tok, &token, &token_length);
279+
str = PyUnicode_FromStringAndSize(token_start, token_length);
272280
}
273281
if (str == NULL) {
274282
goto exit;
@@ -297,12 +305,12 @@ tokenizeriter_next(PyObject *op)
297305
goto exit;
298306
}
299307

300-
Py_ssize_t lineno = ISSTRINGLIT(type) ? it->tok->first_lineno : it->tok->lineno;
301-
Py_ssize_t end_lineno = it->tok->lineno;
308+
Py_ssize_t lineno = token.start_loc.lineno;
309+
Py_ssize_t end_lineno = token.end_loc.lineno;
302310
Py_ssize_t col_offset = -1;
303311
Py_ssize_t end_col_offset = -1;
304-
_get_col_offsets(it, token, line_start, line, line_changed,
305-
lineno, end_lineno, &col_offset, &end_col_offset);
312+
_get_col_offsets(it, &token, token_start, line_start, line, line_changed,
313+
&col_offset, &end_col_offset);
306314

307315
if (it->tok->tok_extra_tokens) {
308316
if (is_trailing_token) {
@@ -317,7 +325,8 @@ tokenizeriter_next(PyObject *op)
317325
else if (type == NEWLINE) {
318326
Py_DECREF(str);
319327
if (!it->tok->implicit_newline) {
320-
if (it->tok->start[0] == '\r') {
328+
assert(token_start != NULL);
329+
if (token_start[0] == '\r') {
321330
str = PyUnicode_FromString("\r\n");
322331
} else {
323332
str = PyUnicode_FromString("\n");

0 commit comments

Comments
0 (0)
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.