Skip to content

Commit c9086df

Browse files
committed
gh-153569: store scanner positions as logical source offsets
1 parent b0c4518 commit c9086df

11 files changed

Lines changed: 139 additions & 213 deletions

File tree

Parser/lexer/layout.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ int
147147
_PyLexer_IndentationToken(struct tok_state *tok, struct token *token)
148148
{
149149
assert(tok->layout.pending != 0);
150-
const char *p_start = NULL;
151-
const char *p_end = NULL;
150+
_PyTok_Off p_start = -1;
151+
_PyTok_Off p_end = -1;
152152
if (tok->layout.pending < 0) {
153153
if (tok->tok_extra_tokens) {
154154
p_start = tok->cur;
@@ -159,7 +159,7 @@ _PyLexer_IndentationToken(struct tok_state *tok, struct token *token)
159159
}
160160
else {
161161
if (tok->tok_extra_tokens) {
162-
p_start = tok->buf;
162+
p_start = tok->buf_offset;
163163
p_end = tok->cur;
164164
}
165165
tok->layout.pending--;

Parser/lexer/lexer.c

Lines changed: 44 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -19,54 +19,44 @@ contains_null_bytes(const char* str, size_t size)
1919
return memchr(str, 0, size) != NULL;
2020
}
2121

22-
/* Get next char, updating state; error code goes into tok->done */
2322
int
24-
_PyLexer_nextc(struct tok_state *tok)
23+
_PyLexer_refill(struct tok_state *tok)
2524
{
26-
int rc;
27-
for (;;) {
28-
if (tok->cur != tok->inp) {
29-
if (tok->cur - tok->line_start >= INT_MAX) {
30-
tok->done = E_COLUMNOVERFLOW;
31-
return EOF;
32-
}
33-
return Py_CHARMASK(*tok->cur++); /* Fast path */
34-
}
35-
if (tok->done != E_OK) {
36-
return EOF;
37-
}
38-
rc = _PyTok_ReaderUnderflow(tok);
25+
if (tok->done != E_OK) {
26+
return 0;
27+
}
28+
int rc = _PyTok_ReaderUnderflow(tok);
3929
#if defined(Py_DEBUG)
40-
if (tok->debug) {
41-
fprintf(stderr, "line[%d] = ", tok->lineno);
42-
_PyTokenizer_print_escape(stderr, tok->cur, tok->inp - tok->cur);
43-
fprintf(stderr, " tok->done = %d\n", tok->done);
44-
}
30+
if (tok->debug) {
31+
fprintf(stderr, "line[%d] = ", tok->lineno);
32+
_PyTokenizer_print_escape(stderr, _PyLexer_BufferPointer(tok, tok->cur),
33+
tok->inp - tok->cur);
34+
fprintf(stderr, " tok->done = %d\n", tok->done);
35+
}
4536
#endif
46-
if (!rc) {
47-
tok->cur = tok->inp;
48-
return EOF;
49-
}
50-
tok->line_start = tok->cur;
51-
52-
if (contains_null_bytes(tok->line_start, tok->inp - tok->line_start)) {
53-
_PyTokenizer_syntaxerror(tok, "source code cannot contain null bytes");
54-
tok->cur = tok->inp;
55-
return EOF;
56-
}
37+
if (!rc) {
38+
tok->cur = tok->inp;
39+
return 0;
5740
}
58-
Py_UNREACHABLE();
41+
tok->line_start = tok->cur;
42+
if (contains_null_bytes(_PyLexer_BufferPointer(tok, tok->line_start),
43+
tok->inp - tok->line_start)) {
44+
_PyTokenizer_syntaxerror(tok, "source code cannot contain null bytes");
45+
tok->cur = tok->inp;
46+
return 0;
47+
}
48+
return 1;
5949
}
6050

6151
/* Back-up one character */
6252
void
6353
_PyLexer_backup(struct tok_state *tok, int c)
6454
{
6555
if (c != EOF) {
66-
if (--tok->cur < tok->buf) {
56+
if (--tok->cur < tok->buf_offset) {
6757
Py_FatalError("tokenizer beginning of buffer");
6858
}
69-
if ((int)(unsigned char)*tok->cur != Py_CHARMASK(c)) {
59+
if ((int)(unsigned char)*_PyLexer_BufferPointer(tok, tok->cur) != Py_CHARMASK(c)) {
7060
Py_FatalError("tok_backup: wrong character");
7161
}
7262
}
@@ -84,7 +74,7 @@ verify_identifier(struct tok_state *tok)
8474
PyObject *s;
8575
if (tok_failed(tok))
8676
return 0;
87-
s = PyUnicode_DecodeUTF8(tok->start, tok->cur - tok->start, NULL);
77+
s = PyUnicode_DecodeUTF8(_PyLexer_BufferPointer(tok, tok->start), tok->cur - tok->start, NULL);
8878
if (s == NULL) {
8979
if (PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) {
9080
tok->done = E_DECODE;
@@ -99,7 +89,7 @@ verify_identifier(struct tok_state *tok)
9989
assert(PyUnicode_GET_LENGTH(s) > 0);
10090
if (invalid < PyUnicode_GET_LENGTH(s)) {
10191
Py_UCS4 ch = PyUnicode_READ_CHAR(s, invalid);
102-
const char *error_cursor = tok->cur;
92+
_PyTok_Off error_cursor = tok->cur;
10393
if (invalid + 1 < PyUnicode_GET_LENGTH(s)) {
10494
/* Determine the offset in UTF-8 encoded input */
10595
Py_SETREF(s, PyUnicode_Substring(s, 0, invalid + 1));
@@ -115,13 +105,13 @@ verify_identifier(struct tok_state *tok)
115105
Py_DECREF(s);
116106
if (Py_UNICODE_ISPRINTABLE(ch)) {
117107
_PyTokenizer_syntaxerror_at(
118-
tok, tok->line_start,
108+
tok, _PyLexer_BufferPointer(tok, tok->line_start),
119109
error_cursor - tok->line_start, tok->lineno, -1, -1,
120110
"invalid character '%c' (U+%04X)", ch, ch);
121111
}
122112
else {
123113
_PyTokenizer_syntaxerror_at(
124-
tok, tok->line_start,
114+
tok, _PyLexer_BufferPointer(tok, tok->line_start),
125115
error_cursor - tok->line_start, tok->lineno, -1, -1,
126116
"invalid non-printable character U+%04X", ch);
127117
}
@@ -140,10 +130,10 @@ _PyLexer_get_normal(struct tok_state *tok, ftstring_state *current, struct token
140130
int c;
141131
int blankline, nonascii;
142132

143-
const char *p_start = NULL;
144-
const char *p_end = NULL;
133+
_PyTok_Off p_start = -1;
134+
_PyTok_Off p_end = -1;
145135
nextline:
146-
tok->start = NULL;
136+
tok->start = -1;
147137
tok->start_loc = (_PyTok_Loc){tok->lineno, -1};
148138
blankline = 0;
149139

@@ -157,7 +147,7 @@ _PyLexer_get_normal(struct tok_state *tok, ftstring_state *current, struct token
157147

158148
tok->start = tok->cur;
159149
tok->start_loc = (_PyTok_Loc){
160-
tok->lineno, tok->cur != NULL ? _PyLexer_ByteColumn(tok) : -1};
150+
tok->lineno, _PyLexer_ByteColumn(tok)};
161151

162152
if (tok->layout.pending != 0) {
163153
return _PyLexer_IndentationToken(tok, token);
@@ -168,16 +158,16 @@ _PyLexer_get_normal(struct tok_state *tok, ftstring_state *current, struct token
168158
tok_backup(tok, c);
169159

170160
again:
171-
tok->start = NULL;
161+
tok->start = -1;
172162
/* Skip spaces */
173163
do {
174164
c = tok_nextc(tok);
175165
} while (c == ' ' || c == '\t' || c == '\014');
176166

177167
/* Set start of current token */
178-
tok->start = tok->cur == NULL ? NULL : tok->cur - 1;
168+
tok->start = tok->cur - 1;
179169
tok->start_loc = (_PyTok_Loc){
180-
tok->lineno, tok->cur != NULL ? _PyLexer_ByteColumn(tok) - 1 : -1};
170+
tok->lineno, _PyLexer_ByteColumn(tok) - 1};
181171

182172
/* Skip comment, unless it's a type comment */
183173
if (c == '#') {
@@ -191,7 +181,7 @@ _PyLexer_get_normal(struct tok_state *tok, ftstring_state *current, struct token
191181
}
192182

193183
if (current != NULL) {
194-
const char *comment_end = tok->cur;
184+
_PyTok_Off comment_end = tok->cur;
195185
if (c == '\n' || c == '\r') {
196186
comment_end--;
197187
}
@@ -203,14 +193,14 @@ _PyLexer_get_normal(struct tok_state *tok, ftstring_state *current, struct token
203193
}
204194

205195
if (tok->tok_extra_tokens) {
206-
p = tok->start;
196+
p = _PyLexer_BufferPointer(tok, tok->start);
207197
}
208198

209199
if (tok->type_comments) {
210-
p = tok->start;
200+
p = _PyLexer_BufferPointer(tok, tok->start);
211201
current_starting_col_offset = tok->start_loc.byte_col;
212202
prefix = type_comment_prefix;
213-
while (*prefix && p < tok->cur) {
203+
while (*prefix && p < _PyLexer_BufferPointer(tok, tok->cur)) {
214204
if (*prefix == ' ') {
215205
while (*p == ' ' || *p == '\t') {
216206
p++;
@@ -239,24 +229,24 @@ _PyLexer_get_normal(struct tok_state *tok, ftstring_state *current, struct token
239229
/* A TYPE_IGNORE is "type: ignore" followed by the end of the token
240230
* or anything ASCII and non-alphanumeric. */
241231
is_type_ignore = (
242-
tok->cur >= ignore_end && memcmp(p, "ignore", 6) == 0
243-
&& !(tok->cur > ignore_end
232+
_PyLexer_BufferPointer(tok, tok->cur) >= ignore_end && memcmp(p, "ignore", 6) == 0
233+
&& !(_PyLexer_BufferPointer(tok, tok->cur) > ignore_end
244234
&& ((unsigned char)ignore_end[0] >= 128 || Py_ISALNUM(ignore_end[0]))));
245235

246236
int type = is_type_ignore ? TYPE_IGNORE : TYPE_COMMENT;
247237
int start_col_offset = is_type_ignore
248238
? ignore_end_col_offset : current_starting_col_offset;
249239
p_end = tok->cur;
250240
if (is_type_ignore) {
251-
p_start = ignore_end;
241+
p_start = _PyLexer_BufferOffset(tok, ignore_end);
252242

253243
/* If this type ignore is the only thing on the line, consume the newline also. */
254244
if (blankline) {
255245
tok_nextc(tok);
256246
tok->layout.at_bol = 1;
257247
}
258248
} else {
259-
p_start = type_start;
249+
p_start = _PyLexer_BufferOffset(tok, type_start);
260250
}
261251
_PyLexer_token_setup(tok, token, type, p_start, p_end);
262252
token->start_loc = (_PyTok_Loc){tok->lineno, start_col_offset};
@@ -267,7 +257,7 @@ _PyLexer_get_normal(struct tok_state *tok, ftstring_state *current, struct token
267257
}
268258
if (tok->tok_extra_tokens) {
269259
tok_backup(tok, c); /* don't eat the newline or EOF */
270-
p_start = p;
260+
p_start = _PyLexer_BufferOffset(tok, p);
271261
p_end = tok->cur;
272262
tok->layout.comment_newline = blankline;
273263
return MAKE_TOKEN(COMMENT);

Parser/lexer/lexer_internal.h

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
(_PyLexer_IsTString((state)->kind) ? TSTRING_MIDDLE : FSTRING_MIDDLE)
2222
#define FTSTRING_END(state) \
2323
(_PyLexer_IsTString((state)->kind) ? TSTRING_END : FSTRING_END)
24-
#define tok_nextc _PyLexer_nextc
2524
#define tok_backup _PyLexer_backup
2625

2726
static inline int
@@ -37,10 +36,30 @@ int _PyLexer_ContinueLine(struct tok_state *);
3736
int _PyLexer_IndentationToken(struct tok_state *, struct token *);
3837
/* Return zero when the newline is suppressed, otherwise its token type. */
3938
int _PyLexer_Newline(struct tok_state *, struct token *, int);
40-
int _PyLexer_nextc(struct tok_state *);
39+
int _PyLexer_refill(struct tok_state *);
40+
41+
static inline int
42+
tok_nextc(struct tok_state *tok)
43+
{
44+
while (tok->cur == tok->inp) {
45+
if (!_PyLexer_refill(tok)) {
46+
return EOF;
47+
}
48+
}
49+
assert(tok->cur >= tok->line_start);
50+
assert(tok->cur >= tok->source.base_offset);
51+
assert(tok->cur - tok->source.base_offset < tok->source.len);
52+
if (tok->cur - tok->line_start >= INT_MAX) {
53+
tok->done = E_COLUMNOVERFLOW;
54+
return EOF;
55+
}
56+
return Py_CHARMASK(
57+
tok->source.bytes[tok->cur++ - tok->source.base_offset]);
58+
}
59+
4160
void _PyLexer_backup(struct tok_state *, int);
4261
int _PyLexer_record_ftstring_comment(
43-
struct tok_state *, ftstring_state *, const char *, const char *);
62+
struct tok_state *, ftstring_state *, _PyTok_Off, _PyTok_Off);
4463
int _PyLexer_ftstring_punctuation(
4564
struct tok_state *, ftstring_state *, struct token *, int);
4665
int _PyLexer_close_ftstring_expr(

Parser/lexer/number.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ int
113113
_PyLexer_scan_number(struct tok_state *tok, struct token *token, int c,
114114
int leading_dot)
115115
{
116-
const char *p_start = NULL;
117-
const char *p_end = NULL;
116+
_PyTok_Off p_start = -1;
117+
_PyTok_Off p_end = -1;
118118

119119
if (leading_dot) {
120120
goto fraction;
@@ -214,7 +214,7 @@ _PyLexer_scan_number(struct tok_state *tok, struct token *token, int c,
214214
}
215215
c = tok_nextc(tok);
216216
}
217-
char* zeros_end = tok->cur;
217+
_PyTok_Off zeros_end = tok->cur;
218218
if (Py_ISDIGIT(c)) {
219219
nonzero = 1;
220220
c = tok_decimal_tail(tok);

Parser/lexer/state.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,14 @@ _PyToken_Init(struct token *token) {
9090
}
9191

9292
int
93-
_PyLexer_token_setup(struct tok_state *tok, struct token *token, int type, const char *start, const char *end)
93+
_PyLexer_token_setup(struct tok_state *tok, struct token *token, int type,
94+
_PyTok_Off start, _PyTok_Off end)
9495
{
9596
token->level = tok->level;
9697
token->is_raw = 0;
97-
token->span = _PyLexer_BufferSpan(tok, start, end);
98-
if (start != NULL && end != NULL) {
98+
assert((start == -1 && end == -1) || (start >= 0 && end >= start));
99+
token->span = (_PyTok_Span){start, end};
100+
if (start >= 0) {
99101
token->start_loc = tok->start_loc;
100102
token->end_loc = (_PyTok_Loc){tok->lineno, _PyLexer_ByteColumn(tok)};
101103
}

0 commit comments

Comments
 (0)