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