/src/cpython/Python/Python-tokenize.c
Line | Count | Source |
1 | | #include "Python.h" |
2 | | #include "errcode.h" |
3 | | #include "internal/pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION |
4 | | #include "../Parser/tokenizer/tokenizer.h" |
5 | | #include "../Parser/pegen.h" // _PyPegen_byte_offset_to_character_offset() |
6 | | |
7 | | static struct PyModuleDef _tokenizemodule; |
8 | | |
9 | | typedef struct { |
10 | | PyTypeObject *TokenizerIter; |
11 | | } tokenize_state; |
12 | | |
13 | | static tokenize_state * |
14 | 674 | get_tokenize_state(PyObject *module) { |
15 | 674 | return (tokenize_state *)PyModule_GetState(module); |
16 | 674 | } |
17 | | |
18 | | #define _tokenize_get_state_by_type(type) \ |
19 | | get_tokenize_state(PyType_GetModuleByDef(type, &_tokenizemodule)) |
20 | | |
21 | | #include "pycore_runtime.h" |
22 | | |
23 | | /*[clinic input] |
24 | | module _tokenizer |
25 | | class _tokenizer.tokenizeriter "tokenizeriterobject *" "_tokenize_get_state_by_type(type)->TokenizerIter" |
26 | | [clinic start generated code]*/ |
27 | | /*[clinic end generated code: output=da39a3ee5e6b4b0d input=96d98ee2fef7a8bc]*/ |
28 | | |
29 | | typedef struct |
30 | | { |
31 | | PyObject_HEAD struct tok_state *tok; |
32 | | int done; |
33 | | int extra_tokens; |
34 | | |
35 | | /* Needed to cache line for performance */ |
36 | | PyObject *last_line; |
37 | | Py_ssize_t last_lineno; |
38 | | Py_ssize_t byte_col_offset_diff; |
39 | | } tokenizeriterobject; |
40 | | |
41 | | #include "clinic/Python-tokenize.c.h" |
42 | | |
43 | | /*[clinic input] |
44 | | @classmethod |
45 | | _tokenizer.tokenizeriter.__new__ as tokenizeriter_new |
46 | | |
47 | | readline: object |
48 | | / |
49 | | * |
50 | | extra_tokens: bool |
51 | | encoding: str(c_default="NULL") = 'utf-8' |
52 | | [clinic start generated code]*/ |
53 | | |
54 | | static PyObject * |
55 | | tokenizeriter_new_impl(PyTypeObject *type, PyObject *readline, |
56 | | int extra_tokens, const char *encoding) |
57 | | /*[clinic end generated code: output=7501a1211683ce16 input=f7dddf8a613ae8bd]*/ |
58 | 16 | { |
59 | 16 | struct tok_state *tok = _PyTokenizer_FromReadline(readline, encoding); |
60 | 16 | if (tok == NULL) { |
61 | 0 | return NULL; |
62 | 0 | } |
63 | 16 | _Py_DECLARE_STR(anon_string, "<string>"); |
64 | 16 | _PyTokenizer_SetContext(tok, &_Py_STR(anon_string), NULL); |
65 | 16 | tokenizeriterobject *self = (tokenizeriterobject *)type->tp_alloc(type, 0); |
66 | 16 | if (self == NULL) { |
67 | 0 | _PyTokenizer_Free(tok); |
68 | 0 | return NULL; |
69 | 0 | } |
70 | 16 | self->tok = tok; |
71 | 16 | _PyTokenizer_SetOptions(self->tok, extra_tokens, 0); |
72 | 16 | self->extra_tokens = extra_tokens; |
73 | 16 | self->done = 0; |
74 | | |
75 | 16 | self->last_line = NULL; |
76 | 16 | self->byte_col_offset_diff = 0; |
77 | 16 | self->last_lineno = 0; |
78 | | |
79 | 16 | return (PyObject *)self; |
80 | 16 | } |
81 | | |
82 | | static void |
83 | | _tokenizer_error(tokenizeriterobject *it) |
84 | 0 | { |
85 | 0 | _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(it); |
86 | 0 | assert(!PyErr_Occurred()); |
87 | |
|
88 | 0 | const char *msg = NULL; |
89 | 0 | PyObject* errtype = PyExc_SyntaxError; |
90 | 0 | struct tok_state *tok = it->tok; |
91 | 0 | _PyTokenizer_Info info = _PyTokenizer_GetInfo(tok); |
92 | 0 | switch (info.status) { |
93 | 0 | case E_TOKEN: |
94 | 0 | msg = "invalid token"; |
95 | 0 | break; |
96 | 0 | case E_EOF: |
97 | 0 | PyErr_SetString(PyExc_SyntaxError, "unexpected EOF in multi-line statement"); |
98 | 0 | PyErr_SyntaxLocationObject( |
99 | 0 | info.filename, info.location.lineno, |
100 | 0 | (int)Py_MAX(0, info.input_span.end - info.input_span.start)); |
101 | 0 | return; |
102 | 0 | case E_DEDENT: |
103 | 0 | msg = "unindent does not match any outer indentation level"; |
104 | 0 | errtype = PyExc_IndentationError; |
105 | 0 | break; |
106 | 0 | case E_INTR: |
107 | 0 | PyErr_SetNone(PyExc_KeyboardInterrupt); |
108 | 0 | return; |
109 | 0 | case E_NOMEM: |
110 | 0 | PyErr_NoMemory(); |
111 | 0 | return; |
112 | 0 | case E_TABSPACE: |
113 | 0 | errtype = PyExc_TabError; |
114 | 0 | msg = "inconsistent use of tabs and spaces in indentation"; |
115 | 0 | break; |
116 | 0 | case E_TOODEEP: |
117 | 0 | errtype = PyExc_IndentationError; |
118 | 0 | msg = "too many levels of indentation"; |
119 | 0 | break; |
120 | 0 | case E_LINECONT: { |
121 | 0 | msg = "unexpected character after line continuation character"; |
122 | 0 | break; |
123 | 0 | } |
124 | 0 | default: |
125 | 0 | msg = "unknown tokenization error"; |
126 | 0 | } |
127 | | |
128 | 0 | PyObject* error_line = NULL; |
129 | 0 | PyObject* value = NULL; |
130 | |
|
131 | 0 | Py_ssize_t input_size; |
132 | 0 | const char *input = _PyTokenizer_SpanView( |
133 | 0 | tok, info.input_span, &input_size); |
134 | 0 | Py_ssize_t size = input_size; |
135 | 0 | assert(input[size-1] == '\n'); |
136 | 0 | size -= 1; // Remove the newline character from the end of the line |
137 | 0 | error_line = PyUnicode_DecodeUTF8(input, size, "replace"); |
138 | 0 | if (!error_line) { |
139 | 0 | goto exit; |
140 | 0 | } |
141 | | |
142 | 0 | Py_ssize_t offset = _PyPegen_byte_offset_to_character_offset(error_line, input_size); |
143 | 0 | if (offset == -1) { |
144 | 0 | goto exit; |
145 | 0 | } |
146 | 0 | value = Py_BuildValue("(s(OinOOO))", msg, info.filename, |
147 | 0 | info.location.lineno, offset, error_line, |
148 | 0 | Py_None, Py_None); |
149 | 0 | if (!value) { |
150 | 0 | goto exit; |
151 | 0 | } |
152 | | |
153 | 0 | PyErr_SetObject(errtype, value); |
154 | |
|
155 | 0 | exit: |
156 | 0 | Py_XDECREF(error_line); |
157 | 0 | Py_XDECREF(value); |
158 | 0 | } |
159 | | |
160 | | static PyObject * |
161 | | _get_current_line(tokenizeriterobject *it, int current_lineno, |
162 | | const _PyToken_View *view, int *line_changed) |
163 | 1.99k | { |
164 | 1.99k | _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(it); |
165 | 1.99k | if (current_lineno != it->last_lineno) { |
166 | 316 | Py_ssize_t size; |
167 | 316 | const char *line_start = _PyTokenizer_SpanView( |
168 | 316 | it->tok, view->line_span, &size); |
169 | 316 | if (size > 0 && view->implicit_newline) { |
170 | 0 | size--; |
171 | 0 | } |
172 | 316 | Py_XDECREF(it->last_line); |
173 | 316 | it->last_line = PyUnicode_DecodeUTF8(line_start, size, "replace"); |
174 | 316 | it->byte_col_offset_diff = 0; |
175 | 316 | } |
176 | 1.67k | else { |
177 | 1.67k | *line_changed = 0; |
178 | 1.67k | } |
179 | 1.99k | return it->last_line; |
180 | 1.99k | } |
181 | | |
182 | | static int |
183 | | _get_col_offsets(tokenizeriterobject *it, const struct token *token, |
184 | | const _PyToken_View *view, PyObject *line, int line_changed, |
185 | | Py_ssize_t *col_offset, Py_ssize_t *end_col_offset) |
186 | 2.00k | { |
187 | 2.00k | _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(it); |
188 | 2.00k | _PyTok_Off token_start = token->span.start; |
189 | 2.00k | _PyTok_Off token_end = token->span.end; |
190 | 2.00k | Py_ssize_t lineno = token->start_loc.lineno; |
191 | 2.00k | Py_ssize_t end_lineno = token->end_loc.lineno; |
192 | 2.00k | Py_ssize_t byte_offset = -1; |
193 | 2.00k | if (token_start >= view->line_span.start) { |
194 | 2.00k | byte_offset = token_start - view->line_span.start; |
195 | 2.00k | if (line_changed) { |
196 | 328 | *col_offset = _PyPegen_byte_offset_to_character_offset_line(line, 0, byte_offset); |
197 | 328 | if (*col_offset < 0) { |
198 | 0 | return -1; |
199 | 0 | } |
200 | 328 | it->byte_col_offset_diff = byte_offset - *col_offset; |
201 | 328 | } |
202 | 1.67k | else { |
203 | 1.67k | *col_offset = byte_offset - it->byte_col_offset_diff; |
204 | 1.67k | } |
205 | 2.00k | } |
206 | | |
207 | 2.00k | if (token_end >= view->end_line_start) { |
208 | 2.00k | Py_ssize_t end_byte_offset = token_end - view->end_line_start; |
209 | 2.00k | if (lineno == end_lineno) { |
210 | | // Avoid rescanning the prefix of a very long line. |
211 | 2.00k | Py_ssize_t token_col_offset = _PyPegen_byte_offset_to_character_offset_line(line, byte_offset, end_byte_offset); |
212 | 2.00k | if (token_col_offset < 0) { |
213 | 0 | return -1; |
214 | 0 | } |
215 | 2.00k | *end_col_offset = *col_offset + token_col_offset; |
216 | 2.00k | it->byte_col_offset_diff += token_end - token_start - token_col_offset; |
217 | 2.00k | } |
218 | 4 | else { |
219 | 4 | *end_col_offset = _PyPegen_byte_offset_to_character_offset_line( |
220 | 4 | line, view->end_line_start - view->line_span.start, |
221 | 4 | token_end - view->line_span.start); |
222 | 4 | if (*end_col_offset < 0) { |
223 | 0 | return -1; |
224 | 0 | } |
225 | 4 | it->byte_col_offset_diff += end_byte_offset - *end_col_offset; |
226 | 4 | } |
227 | 2.00k | } |
228 | 2.00k | it->last_lineno = lineno; |
229 | 2.00k | return 0; |
230 | 2.00k | } |
231 | | |
232 | | /*[clinic input] |
233 | | @critical_section |
234 | | _tokenizer.tokenizeriter.__next__ as tokenizeriter_next |
235 | | [clinic start generated code]*/ |
236 | | |
237 | | static PyObject * |
238 | | tokenizeriter_next_impl(tokenizeriterobject *self) |
239 | | /*[clinic end generated code: output=745c9c646781ff45 input=66f70c965ba8ccfe]*/ |
240 | 2.00k | { |
241 | 2.00k | tokenizeriterobject *it = self; |
242 | 2.00k | PyObject* result = NULL; |
243 | | |
244 | 2.00k | if (it->done) { |
245 | 0 | PyErr_SetString(PyExc_StopIteration, "EOF"); |
246 | 0 | return NULL; |
247 | 0 | } |
248 | | |
249 | 2.00k | struct token token; |
250 | 2.00k | _PyToken_Init(&token); |
251 | | |
252 | 2.00k | _PyTokenizer_Get(it->tok, &token); |
253 | 2.00k | int type = token.type; |
254 | 2.00k | if (type == ERRORTOKEN) { |
255 | 0 | if(!PyErr_Occurred()) { |
256 | 0 | _tokenizer_error(it); |
257 | 0 | assert(PyErr_Occurred()); |
258 | 0 | } |
259 | 0 | goto exit; |
260 | 0 | } |
261 | 2.00k | _PyToken_View view; |
262 | 2.00k | _PyToken_GetView(it->tok, &token, &view); |
263 | 2.00k | PyObject *str = PyUnicode_FromStringAndSize(view.text, view.length); |
264 | 2.00k | if (str == NULL) { |
265 | 0 | goto exit; |
266 | 0 | } |
267 | | |
268 | 2.00k | int is_trailing_token = type == ENDMARKER || (type == DEDENT && view.at_eof); |
269 | | |
270 | 2.00k | PyObject* line = NULL; |
271 | 2.00k | int line_changed = 1; |
272 | 2.00k | if (it->extra_tokens && is_trailing_token) { |
273 | 16 | line = Py_GetConstant(Py_CONSTANT_EMPTY_STR); |
274 | 1.99k | } else { |
275 | 1.99k | line = _get_current_line( |
276 | 1.99k | it, token.end_loc.lineno, &view, &line_changed); |
277 | 1.99k | } |
278 | 2.00k | if (line == NULL) { |
279 | 0 | Py_DECREF(str); |
280 | 0 | goto exit; |
281 | 0 | } |
282 | | |
283 | 2.00k | Py_ssize_t lineno = token.start_loc.lineno; |
284 | 2.00k | Py_ssize_t end_lineno = token.end_loc.lineno; |
285 | 2.00k | Py_ssize_t col_offset = -1; |
286 | 2.00k | Py_ssize_t end_col_offset = -1; |
287 | 2.00k | if (_get_col_offsets(it, &token, &view, line, |
288 | 2.00k | line_changed, &col_offset, &end_col_offset) < 0) { |
289 | 0 | Py_DECREF(str); |
290 | 0 | goto exit; |
291 | 0 | } |
292 | | |
293 | 2.00k | if (it->extra_tokens) { |
294 | 2.00k | if (is_trailing_token) { |
295 | 16 | lineno = end_lineno = lineno + 1; |
296 | 16 | col_offset = end_col_offset = 0; |
297 | 16 | } |
298 | | // Necessary adjustments to match the original Python tokenize |
299 | | // implementation |
300 | 2.00k | if (type > DEDENT && type < OP) { |
301 | 764 | type = OP; |
302 | 764 | } |
303 | 1.24k | else if (type == NEWLINE) { |
304 | 160 | if (!view.implicit_newline) { |
305 | 160 | Py_DECREF(str); |
306 | 160 | if (view.text[0] == '\r') { |
307 | 0 | str = PyUnicode_FromString("\r\n"); |
308 | 160 | } else { |
309 | 160 | str = PyUnicode_FromString("\n"); |
310 | 160 | } |
311 | 160 | } |
312 | 160 | end_col_offset++; |
313 | 160 | } |
314 | 1.08k | else if (type == NL) { |
315 | 140 | if (view.implicit_newline) { |
316 | 0 | Py_DECREF(str); |
317 | 0 | str = Py_GetConstant(Py_CONSTANT_EMPTY_STR); |
318 | 0 | } |
319 | 140 | } |
320 | | |
321 | 2.00k | if (str == NULL) { |
322 | 0 | goto exit; |
323 | 0 | } |
324 | 2.00k | } |
325 | | |
326 | 2.00k | result = Py_BuildValue("(iN(nn)(nn)O)", type, str, lineno, col_offset, end_lineno, end_col_offset, line); |
327 | 2.00k | exit: |
328 | 2.00k | _PyToken_Free(&token); |
329 | 2.00k | if (type == ENDMARKER) { |
330 | 4 | it->done = 1; |
331 | 4 | } |
332 | 2.00k | return result; |
333 | 2.00k | } |
334 | | |
335 | | static void |
336 | | tokenizeriter_dealloc(PyObject *op) |
337 | 16 | { |
338 | 16 | tokenizeriterobject *it = (tokenizeriterobject*)op; |
339 | 16 | PyTypeObject *tp = Py_TYPE(it); |
340 | 16 | PyObject_GC_UnTrack(it); |
341 | 16 | Py_XDECREF(it->last_line); |
342 | 16 | _PyTokenizer_Free(it->tok); |
343 | 16 | tp->tp_free(it); |
344 | 16 | Py_DECREF(tp); |
345 | 16 | } |
346 | | |
347 | | static int |
348 | | tokenizeriter_traverse(PyObject *op, visitproc visit, void *arg) |
349 | 0 | { |
350 | 0 | tokenizeriterobject *it = (tokenizeriterobject *)op; |
351 | 0 | Py_VISIT(Py_TYPE(it)); |
352 | 0 | Py_VISIT(it->last_line); |
353 | 0 | return _PyTokenizer_Traverse(it->tok, visit, arg); |
354 | 0 | } |
355 | | |
356 | | static PyType_Slot tokenizeriter_slots[] = { |
357 | | {Py_tp_new, tokenizeriter_new}, |
358 | | {Py_tp_dealloc, tokenizeriter_dealloc}, |
359 | | {Py_tp_traverse, tokenizeriter_traverse}, |
360 | | {Py_tp_getattro, PyObject_GenericGetAttr}, |
361 | | {Py_tp_iter, PyObject_SelfIter}, |
362 | | {Py_tp_iternext, tokenizeriter_next}, |
363 | | {0, NULL}, |
364 | | }; |
365 | | |
366 | | static PyType_Spec tokenizeriter_spec = { |
367 | | .name = "_tokenize.TokenizerIter", |
368 | | .basicsize = sizeof(tokenizeriterobject), |
369 | | .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), |
370 | | .slots = tokenizeriter_slots, |
371 | | }; |
372 | | |
373 | | static int |
374 | | tokenizemodule_exec(PyObject *m) |
375 | 6 | { |
376 | 6 | tokenize_state *state = get_tokenize_state(m); |
377 | 6 | if (state == NULL) { |
378 | 0 | return -1; |
379 | 0 | } |
380 | | |
381 | 6 | state->TokenizerIter = (PyTypeObject *)PyType_FromModuleAndSpec(m, &tokenizeriter_spec, NULL); |
382 | 6 | if (state->TokenizerIter == NULL) { |
383 | 0 | return -1; |
384 | 0 | } |
385 | 6 | if (PyModule_AddType(m, state->TokenizerIter) < 0) { |
386 | 0 | return -1; |
387 | 0 | } |
388 | | |
389 | 6 | return 0; |
390 | 6 | } |
391 | | |
392 | | static PyMethodDef tokenize_methods[] = { |
393 | | {NULL, NULL, 0, NULL} /* Sentinel */ |
394 | | }; |
395 | | |
396 | | static PyModuleDef_Slot tokenizemodule_slots[] = { |
397 | | _Py_ABI_SLOT, |
398 | | {Py_mod_exec, tokenizemodule_exec}, |
399 | | {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, |
400 | | {Py_mod_gil, Py_MOD_GIL_NOT_USED}, |
401 | | {0, NULL} |
402 | | }; |
403 | | |
404 | | static int |
405 | | tokenizemodule_traverse(PyObject *m, visitproc visit, void *arg) |
406 | 668 | { |
407 | 668 | tokenize_state *state = get_tokenize_state(m); |
408 | 668 | Py_VISIT(state->TokenizerIter); |
409 | 668 | return 0; |
410 | 668 | } |
411 | | |
412 | | static int |
413 | | tokenizemodule_clear(PyObject *m) |
414 | 0 | { |
415 | 0 | tokenize_state *state = get_tokenize_state(m); |
416 | 0 | Py_CLEAR(state->TokenizerIter); |
417 | 0 | return 0; |
418 | 0 | } |
419 | | |
420 | | static void |
421 | | tokenizemodule_free(void *m) |
422 | 0 | { |
423 | 0 | tokenizemodule_clear((PyObject *)m); |
424 | 0 | } |
425 | | |
426 | | static struct PyModuleDef _tokenizemodule = { |
427 | | PyModuleDef_HEAD_INIT, |
428 | | .m_name = "_tokenize", |
429 | | .m_size = sizeof(tokenize_state), |
430 | | .m_slots = tokenizemodule_slots, |
431 | | .m_methods = tokenize_methods, |
432 | | .m_traverse = tokenizemodule_traverse, |
433 | | .m_clear = tokenizemodule_clear, |
434 | | .m_free = tokenizemodule_free, |
435 | | }; |
436 | | |
437 | | PyMODINIT_FUNC |
438 | | PyInit__tokenize(void) |
439 | 6 | { |
440 | 6 | return PyModuleDef_Init(&_tokenizemodule); |
441 | 6 | } |