mirror of
https://github.com/python/cpython.git
synced 2026-10-28 10:25:05 +08:00
gh-159465: Improve incomplete input detection (#159466)
* gh-159465: Improve incomplete input detection * gh-159465: Track newline effects and preserve parser layout * gh-159465: Share incomplete-input error cleanup
This commit is contained in:
1 parent
c5cbe312ca
commit
6af655301a
25 files changed
+395
-115
No files matched your search
@@ -38,6 +38,7 @@ extern "C" {
|
||||
#define E_BADSINGLE 27 /* Ill-formed single statement input */
|
||||
#define E_INTERACT_STOP 28 /* Interactive mode stopped tokenization */
|
||||
#define E_COLUMNOVERFLOW 29 /* Column offset overflow */
|
||||
#define E_INCOMPLETE 30 /* An unfinished token in partial input */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -27,13 +27,17 @@ _Py_DECLARE_STR(empty, "")
|
||||
}, \
|
||||
}
|
||||
|
||||
/* The caller must initialize *incomplete to NULL if supplied. A returned AST
|
||||
can also return an owned exception there for a missing line terminator.
|
||||
Validate the AST before reporting that exception. */
|
||||
extern struct _mod* _PyParser_ASTFromString(
|
||||
const char *str,
|
||||
PyObject* filename,
|
||||
int mode,
|
||||
PyCompilerFlags *flags,
|
||||
PyArena *arena,
|
||||
PyObject *module);
|
||||
PyObject *module,
|
||||
PyObject **incomplete);
|
||||
|
||||
extern struct _mod* _PyParser_ASTFromFile(
|
||||
FILE *fp,
|
||||
|
||||
+3
-8
@@ -62,15 +62,10 @@ def _maybe_compile(compiler, source, filename, symbol, flags):
|
||||
warnings.simplefilter("ignore", (SyntaxWarning, DeprecationWarning))
|
||||
try:
|
||||
compiler(source, filename, symbol, flags=flags)
|
||||
except _IncompleteInputError:
|
||||
return None
|
||||
except SyntaxError: # Let other compile() errors propagate.
|
||||
try:
|
||||
compiler(source + "\n", filename, symbol, flags=flags)
|
||||
return None
|
||||
except _IncompleteInputError:
|
||||
return None
|
||||
except SyntaxError:
|
||||
pass
|
||||
# fallthrough
|
||||
pass
|
||||
|
||||
return compiler(source, filename, symbol, incomplete_input=False)
|
||||
|
||||
|
||||
@@ -124,6 +124,99 @@ class CodeopTests(unittest.TestCase):
|
||||
self.assertEqual(cm.exception.args, (
|
||||
'incomplete input', ('<input>', 2, 9, text, 2, -1)))
|
||||
|
||||
@subTests('compiler', COMPILERS)
|
||||
@subTests('newline', ('\n', '\r', '\r\n'))
|
||||
def test_unterminated_trailing_line(self, compiler, newline):
|
||||
for head, symbol in (('1', 'eval'), ('if 1: pass', 'single'),
|
||||
('if 1:\n pass', 'single')):
|
||||
for tail in (' ', '\t', '\f ', '\n '):
|
||||
source = head + newline + tail
|
||||
with self.subTest(source=source, symbol=symbol):
|
||||
if compiler in RAW_COMPILERS:
|
||||
with self.assertRaises(_IncompleteInputError):
|
||||
compiler(source, '<input>', symbol)
|
||||
else:
|
||||
self.assertIsNone(compiler(source, '<input>', symbol))
|
||||
self.assertValid(source + '\n', symbol, compiler=compiler)
|
||||
for tail in ('\f', ' \f', '\n\f'):
|
||||
with self.subTest(head=head, tail=tail, symbol=symbol):
|
||||
self.assertValid(head + newline + tail, symbol,
|
||||
compiler=compiler)
|
||||
|
||||
for tail in ('# comment', ' # comment'):
|
||||
self.assertValid('if 1: pass' + newline + tail, compiler=compiler)
|
||||
self.assertValid('1' + newline + tail, 'eval', compiler=compiler)
|
||||
|
||||
for head in ('1', 'if 1: pass', 'if 1:\n pass'):
|
||||
for tail in (' ', '\t', '\f', '# comment'):
|
||||
self.assertValid(head + newline + tail, 'exec',
|
||||
compiler=compiler)
|
||||
|
||||
@subTests('symbol', ('single', 'exec', 'eval'))
|
||||
def test_unfinished_token_in_invalid_position(self, symbol):
|
||||
for source in ('a b', '(a b', '1 """', '(1 """',
|
||||
'f"{lambda:\\', 't"{lambda:\\'):
|
||||
for compiler in COMPILERS:
|
||||
with self.subTest(source=source, compiler=compiler):
|
||||
with self.assertRaises(SyntaxError) as cm:
|
||||
compiler(source, '<input>', symbol)
|
||||
self.assertNotIsInstance(cm.exception, _IncompleteInputError)
|
||||
|
||||
@subTests('symbol', ('single', 'exec', 'eval'))
|
||||
def test_invalid_partial_string_diagnostic(self, symbol):
|
||||
with self.assertRaisesRegex(SyntaxError, 'invalid syntax'):
|
||||
Compile()('a"""', '<input>', symbol)
|
||||
|
||||
@subTests('symbol', ('single', 'exec', 'eval'))
|
||||
@subTests('prefix', ('', 'r', 'b', 'f', 't', 'rf', 'rt'))
|
||||
def test_string_continuation(self, symbol, prefix):
|
||||
source = prefix + '"first\\'
|
||||
compile(source + '\nsecond"\n', '<input>', symbol)
|
||||
self.assertIsNone(compile_command(source, symbol=symbol))
|
||||
with self.assertRaises(_IncompleteInputError):
|
||||
Compile()(source, '<input>', symbol)
|
||||
|
||||
@subTests('symbol', ('single', 'exec', 'eval'))
|
||||
@subTests('newline', ('\n', '\r', '\r\n'))
|
||||
def test_continued_final_line(self, symbol, newline):
|
||||
source = '1\\' + newline
|
||||
compile(source + '+ 2\n', '<input>', symbol)
|
||||
self.assertIsNone(compile_command(source, symbol=symbol))
|
||||
with self.assertRaises(_IncompleteInputError):
|
||||
Compile()(source, '<input>', symbol)
|
||||
with self.assertRaises(SyntaxError) as cm:
|
||||
Compile()('1 + \\' + newline, '<input>', symbol)
|
||||
# Appending LF to CR completes the CRLF line terminator.
|
||||
if newline != '\r':
|
||||
self.assertNotIsInstance(cm.exception, _IncompleteInputError)
|
||||
|
||||
@subTests('symbol', ('single', 'exec'))
|
||||
def test_eof_before_nested_body(self, symbol):
|
||||
for source in ('if True:\n if True:',
|
||||
'if True:\n def f():',
|
||||
'match 1:\n case 1:'):
|
||||
for tail in ('', '\n', '\n\n'):
|
||||
with self.subTest(source=source, tail=tail):
|
||||
compile(source + tail + '\n pass\n', '<input>', symbol)
|
||||
self.assertIsNone(compile_command(source + tail, symbol=symbol))
|
||||
with self.assertRaises(_IncompleteInputError):
|
||||
Compile()(source + tail, '<input>', symbol)
|
||||
|
||||
def test_incomplete_suite_semantic_errors(self):
|
||||
cases = (
|
||||
('if True: break', "'break' outside loop"),
|
||||
('if True: return 1', "'return' outside function"),
|
||||
('def f(x, x):\n pass', 'duplicate parameter'),
|
||||
)
|
||||
for source, message in cases:
|
||||
for compiler in COMPILERS:
|
||||
with self.subTest(source=source, compiler=compiler):
|
||||
with self.assertRaises(SyntaxError) as cm:
|
||||
compiler(source, '<input>', 'single')
|
||||
self.assertNotIsInstance(cm.exception, _IncompleteInputError)
|
||||
if compiler in RAW_COMPILERS:
|
||||
self.assertIn(message, str(cm.exception))
|
||||
|
||||
@subTests('compiler', COMPILERS)
|
||||
def test_incomplete(self, compiler):
|
||||
ai = functools.partial(self.assertIncomplete, compiler=compiler)
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
Improve incomplete input detection in :mod:`codeop` without an extra parser
|
||||
pass, and preserve syntax and compiler errors.
|
||||
@@ -95,6 +95,35 @@ test_tokenizer_source(PyObject *Py_UNUSED(module),
|
||||
goto error;
|
||||
}
|
||||
|
||||
_PyTok_SourceClear(&source);
|
||||
if (check(_PyTok_SourceAppendLineWithNewline(&source, "", 0) == 0,
|
||||
"cannot append empty terminated line") < 0 ||
|
||||
check(_PyTok_SourceAppendLineWithNewline(&source, tail, 4) == 1,
|
||||
"wrong terminated line offset") < 0 ||
|
||||
check(source.len == 6 && source.nlines == 2 &&
|
||||
memcmp(source.bytes, "\ntail\n", 7) == 0,
|
||||
"wrong terminated source contents") < 0 ||
|
||||
check_system_error(_PyTok_SourceAppendLineWithNewline(
|
||||
&source, terminated_line, 2) < 0,
|
||||
"accepted newline in line payload") < 0) {
|
||||
goto error;
|
||||
}
|
||||
source.base_offset = PY_SSIZE_T_MAX - source.len;
|
||||
if (check(_PyTok_SourceAppendLineWithNewline(&source, "", 0) < 0 &&
|
||||
PyErr_ExceptionMatches(PyExc_MemoryError),
|
||||
"accepted overflowing appended newline") < 0) {
|
||||
goto error;
|
||||
}
|
||||
PyErr_Clear();
|
||||
source.base_offset = 0;
|
||||
source.nlines = INT_MAX - 1;
|
||||
if (check(_PyTok_SourceAppendLineWithNewline(&source, "", 0) < 0 &&
|
||||
PyErr_ExceptionMatches(PyExc_OverflowError),
|
||||
"accepted overflowing newline count") < 0) {
|
||||
goto error;
|
||||
}
|
||||
PyErr_Clear();
|
||||
|
||||
_PyTok_SourceClear(&source);
|
||||
Py_RETURN_NONE;
|
||||
|
||||
|
||||
+10
-10
@@ -1497,11 +1497,11 @@ _PyPegen_joined_str(Parser *p, Token* a, asdl_expr_seq* raw_expressions, Token*b
|
||||
}
|
||||
|
||||
expr_ty _PyPegen_decoded_constant_from_token(Parser* p, Token* tok) {
|
||||
Py_ssize_t bsize;
|
||||
char* bstr;
|
||||
if (PyBytes_AsStringAndSize(tok->bytes, &bstr, &bsize) == -1) {
|
||||
return NULL;
|
||||
if (tok->bytes == NULL) {
|
||||
return _PyPegen_raise_incomplete_input(p);
|
||||
}
|
||||
Py_ssize_t bsize = PyBytes_GET_SIZE(tok->bytes);
|
||||
const char *bstr = PyBytes_AS_STRING(tok->bytes);
|
||||
|
||||
int is_raw = tok->is_raw;
|
||||
|
||||
@@ -1519,10 +1519,10 @@ expr_ty _PyPegen_decoded_constant_from_token(Parser* p, Token* tok) {
|
||||
}
|
||||
|
||||
expr_ty _PyPegen_constant_from_token(Parser* p, Token* tok) {
|
||||
const char* bstr = PyBytes_AsString(tok->bytes);
|
||||
if (bstr == NULL) {
|
||||
return NULL;
|
||||
if (tok->bytes == NULL) {
|
||||
return _PyPegen_raise_incomplete_input(p);
|
||||
}
|
||||
const char *bstr = PyBytes_AS_STRING(tok->bytes);
|
||||
PyObject* str = PyUnicode_FromString(bstr);
|
||||
if (str == NULL) {
|
||||
return NULL;
|
||||
@@ -1537,10 +1537,10 @@ expr_ty _PyPegen_constant_from_token(Parser* p, Token* tok) {
|
||||
}
|
||||
|
||||
expr_ty _PyPegen_constant_from_string(Parser* p, Token* tok) {
|
||||
const char* the_str = PyBytes_AsString(tok->bytes);
|
||||
if (the_str == NULL) {
|
||||
return NULL;
|
||||
if (tok->bytes == NULL) {
|
||||
return _PyPegen_raise_incomplete_input(p);
|
||||
}
|
||||
const char *the_str = PyBytes_AS_STRING(tok->bytes);
|
||||
PyObject *s = _PyPegen_parse_string(p, tok);
|
||||
if (s == NULL) {
|
||||
_Pypegen_raise_decode_error(p);
|
||||
|
||||
@@ -21,6 +21,9 @@ _PyLexer_ContinueLine(struct tok_state *tok)
|
||||
return -1;
|
||||
}
|
||||
c = tok_nextc(tok);
|
||||
if (tok->implicit_newline == _PyTokenizer_APPEND_NEWLINE) {
|
||||
tok->needs_newline |= _PyTokenizer_NEWLINE_CONTINUATION;
|
||||
}
|
||||
if (c == EOF) {
|
||||
tok->done = E_EOF;
|
||||
tok->cur = tok->inp;
|
||||
@@ -114,6 +117,11 @@ _PyLexer_BeginLine(struct tok_state *tok)
|
||||
}
|
||||
}
|
||||
tok_backup(tok, c);
|
||||
if (c == '\n' && tok->implicit_newline == _PyTokenizer_IMPLY_NEWLINE &&
|
||||
tok->level == 0 && (cont_line_col || col)) {
|
||||
// Without the newline, a whitespace-only line affects indentation.
|
||||
tok->needs_newline |= _PyTokenizer_NEWLINE_INDENT;
|
||||
}
|
||||
if (c == '#' || c == '\n' || c == '\r') {
|
||||
int interactive = _PyTok_ReaderIsInteractive(tok);
|
||||
/* Lines with only whitespace and/or comments
|
||||
@@ -171,6 +179,10 @@ _PyLexer_IndentationToken(struct tok_state *tok, struct token *token)
|
||||
int
|
||||
_PyLexer_Newline(struct tok_state *tok, struct token *token, int blankline)
|
||||
{
|
||||
if (!blankline && tok->implicit_newline == _PyTokenizer_IMPLY_NEWLINE) {
|
||||
// The statement's terminating newline was not in the input.
|
||||
tok->needs_newline |= _PyTokenizer_NEWLINE_STATEMENT;
|
||||
}
|
||||
tok->layout.at_bol = 1;
|
||||
if (blankline || tok->level > 0) {
|
||||
if (!tok->tok_extra_tokens) {
|
||||
|
||||
@@ -235,6 +235,11 @@ _PyLexer_get_normal(struct tok_state *tok, ftstring_state *current, struct token
|
||||
return type;
|
||||
}
|
||||
}
|
||||
if (tok->implicit_newline == _PyTokenizer_IMPLY_NEWLINE &&
|
||||
tok->layout.depth > 0) {
|
||||
// Ending this comment lets the next line dedent the suite.
|
||||
tok->needs_newline |= _PyTokenizer_NEWLINE_STATEMENT;
|
||||
}
|
||||
if (tok->tok_extra_tokens) {
|
||||
tok_backup(tok, c); /* don't eat the newline or EOF */
|
||||
p_start = comment;
|
||||
@@ -516,7 +521,7 @@ _PyTokenizer_Get(struct tok_state *tok, struct token *token)
|
||||
Py_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
if (tok_failed(tok)) {
|
||||
if (tok_failed(tok) && tok->done != E_INCOMPLETE) {
|
||||
result = ERRORTOKEN;
|
||||
}
|
||||
token->type = result;
|
||||
|
||||
@@ -105,6 +105,8 @@ struct tok_state {
|
||||
int debug;
|
||||
#endif
|
||||
int barry_as_bdfl;
|
||||
unsigned char allow_incomplete_input;
|
||||
unsigned char needs_newline;
|
||||
};
|
||||
|
||||
static inline ftstring_state *
|
||||
|
||||
@@ -21,6 +21,15 @@ string_error_token(struct tok_state *tok, struct token *token,
|
||||
return type;
|
||||
}
|
||||
|
||||
static int
|
||||
incomplete_string_token(struct tok_state *tok, struct token *token,
|
||||
int type, _PyTok_Off start, _PyTok_Loc location)
|
||||
{
|
||||
string_error_token(tok, token, start, location);
|
||||
tok->done = E_INCOMPLETE;
|
||||
return type;
|
||||
}
|
||||
|
||||
int
|
||||
_PyLexer_record_ftstring_comment(struct tok_state *tok, ftstring_state *state,
|
||||
_PyTok_Off start, _PyTok_Off end)
|
||||
@@ -337,6 +346,10 @@ _PyLexer_scan_string(struct tok_state *tok, struct token *token, int c)
|
||||
if (tok_failed(tok)) {
|
||||
return MAKE_TOKEN(ERRORTOKEN);
|
||||
}
|
||||
if (c == EOF && tok->allow_incomplete_input) {
|
||||
return incomplete_string_token(tok, token, STRING,
|
||||
tok->start, tok->start_loc);
|
||||
}
|
||||
int end_lineno = tok->lineno;
|
||||
_PyTok_Loc location = tok->start_loc;
|
||||
const char *line = _PyTok_SourcePointer(
|
||||
@@ -450,6 +463,10 @@ _PyLexer_get_ftstring(struct tok_state *tok, ftstring_state *current, struct tok
|
||||
if (tok_failed(tok)) {
|
||||
return MAKE_TOKEN(ERRORTOKEN);
|
||||
}
|
||||
if (c == EOF && tok->allow_incomplete_input) {
|
||||
return incomplete_string_token(tok, token,
|
||||
FTSTRING_MIDDLE(current), current->start, current->start_loc);
|
||||
}
|
||||
|
||||
if (in_format_spec && c == '\n') {
|
||||
return MAKE_TOKEN(_PyTokenizer_syntaxerror(
|
||||
|
||||
+3
-3
@@ -5,14 +5,14 @@
|
||||
mod_ty
|
||||
_PyParser_ASTFromString(const char *str, PyObject* filename, int mode,
|
||||
PyCompilerFlags *flags, PyArena *arena,
|
||||
PyObject *module)
|
||||
PyObject *module, PyObject **incomplete)
|
||||
{
|
||||
if (PySys_Audit("compile", "yO", str, filename) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mod_ty result = _PyPegen_run_parser_from_string(str, mode, filename, flags,
|
||||
arena, module);
|
||||
arena, module, incomplete);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -39,4 +39,4 @@ _PyParser_InteractiveASTFromFile(FILE *fp, PyObject *filename_ob, const char *en
|
||||
}
|
||||
return _PyPegen_run_parser_from_file_pointer(fp, mode, filename_ob, enc, ps1, ps2,
|
||||
flags, errcode, interactive_src, arena);
|
||||
}
|
||||
}
|
||||
+61
-30
@@ -247,23 +247,26 @@ static int
|
||||
initialize_token(Parser *p, Token *parser_token, struct token *new_token, int token_type) {
|
||||
assert(parser_token != NULL);
|
||||
|
||||
Py_ssize_t length;
|
||||
const char *text = _PyToken_TextView(p->tok, new_token, &length);
|
||||
parser_token->type = token_type == NAME
|
||||
? _get_keyword_or_name_type(p, text, length) : token_type;
|
||||
if (token_needs_text(parser_token->type)) {
|
||||
parser_token->bytes = PyBytes_FromStringAndSize(text, length);
|
||||
if (parser_token->bytes == NULL) {
|
||||
return -1;
|
||||
parser_token->type = token_type;
|
||||
parser_token->bytes = NULL;
|
||||
if (token_needs_text(token_type) && new_token->span.start >= 0) {
|
||||
Py_ssize_t length;
|
||||
const char *text = _PyToken_TextView(p->tok, new_token, &length);
|
||||
if (token_type == NAME) {
|
||||
parser_token->type = _get_keyword_or_name_type(p, text, length);
|
||||
}
|
||||
if (_PyArena_AddPyObject(p->arena, parser_token->bytes) < 0) {
|
||||
Py_DECREF(parser_token->bytes);
|
||||
return -1;
|
||||
// A name reclassified as a keyword does not need its text.
|
||||
if (parser_token->type == token_type) {
|
||||
parser_token->bytes = PyBytes_FromStringAndSize(text, length);
|
||||
if (parser_token->bytes == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (_PyArena_AddPyObject(p->arena, parser_token->bytes) < 0) {
|
||||
Py_DECREF(parser_token->bytes);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
parser_token->bytes = NULL;
|
||||
}
|
||||
|
||||
parser_token->metadata = NULL;
|
||||
if (new_token->metadata != NULL) {
|
||||
@@ -287,7 +290,7 @@ initialize_token(Parser *p, Token *parser_token, struct token *new_token, int to
|
||||
|
||||
p->fill += 1;
|
||||
|
||||
if (token_type == ERRORTOKEN && _PyTokenizer_GetInfo(p->tok).status == E_DECODE) {
|
||||
if (token_type == ERRORTOKEN && _PyTokenizer_GetStatus(p->tok) == E_DECODE) {
|
||||
return _Pypegen_raise_decode_error(p);
|
||||
}
|
||||
|
||||
@@ -352,6 +355,13 @@ _PyPegen_fill_token(Parser *p)
|
||||
}
|
||||
|
||||
int type = new_token.type;
|
||||
// EOF can finish an ordinary token. Only a boundary token means that
|
||||
// the grammar requested input beyond the last physical line.
|
||||
if (type == ENDMARKER ||
|
||||
(type == DEDENT && (p->flags & PyPARSE_ALLOW_INCOMPLETE_INPUT) &&
|
||||
_PyTokenizer_GetStatus(p->tok) == E_EOF)) {
|
||||
p->input_exhausted = 1;
|
||||
}
|
||||
|
||||
// If we have reached the end and we are in single input mode we need to insert a newline and reset the parsing
|
||||
if (p->start_rule == Py_single_input && type == ENDMARKER && p->parsing_started) {
|
||||
@@ -953,6 +963,7 @@ _PyPegen_Parser_New(struct tok_state *tok, int start_rule, int flags,
|
||||
}
|
||||
assert(tok != NULL);
|
||||
_PyTokenizer_SetOptions(tok, 0, (flags & PyPARSE_TYPE_COMMENTS) > 0);
|
||||
_PyTokenizer_AllowIncompleteInput(tok, flags & PyPARSE_ALLOW_INCOMPLETE_INPUT);
|
||||
p->tok = tok;
|
||||
p->keywords = NULL;
|
||||
p->n_keyword_lists = -1;
|
||||
@@ -998,6 +1009,7 @@ _PyPegen_Parser_New(struct tok_state *tok, int start_rule, int flags,
|
||||
#endif
|
||||
p->level = 0;
|
||||
p->call_invalid_rules = 0;
|
||||
p->input_exhausted = 0;
|
||||
p->last_stmt_location.lineno = 0;
|
||||
p->last_stmt_location.col_offset = 0;
|
||||
p->last_stmt_location.end_lineno = 0;
|
||||
@@ -1035,16 +1047,12 @@ reset_parser_state_for_error_pass(Parser *p)
|
||||
p->tokens[i]->memo_mask = 0;
|
||||
}
|
||||
p->mark = 0;
|
||||
p->flags &= ~PyPARSE_ALLOW_INCOMPLETE_INPUT;
|
||||
_PyTokenizer_AllowIncompleteInput(p->tok, 0);
|
||||
p->call_invalid_rules = 1;
|
||||
_PyTokenizer_StopInteractive(p->tok);
|
||||
}
|
||||
|
||||
static inline int
|
||||
_is_end_of_source(Parser *p) {
|
||||
int err = _PyTokenizer_GetInfo(p->tok).status;
|
||||
return err == E_EOF || err == E_EOFS || err == E_EOLS;
|
||||
}
|
||||
|
||||
static void
|
||||
_PyPegen_set_syntax_error_metadata(Parser *p) {
|
||||
PyObject *exc = PyErr_GetRaisedException();
|
||||
@@ -1095,13 +1103,15 @@ _PyPegen_run_parser(Parser *p)
|
||||
return NULL;
|
||||
}
|
||||
if (res == NULL) {
|
||||
if ((p->flags & PyPARSE_ALLOW_INCOMPLETE_INPUT) && _is_end_of_source(p)) {
|
||||
PyErr_Clear();
|
||||
return _PyPegen_raise_error(p, PyExc_IncompleteInputError, 0, "incomplete input");
|
||||
}
|
||||
if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_SyntaxError)) {
|
||||
if (PyErr_Occurred() &&
|
||||
(PyErr_ExceptionMatches(PyExc_IncompleteInputError) ||
|
||||
!PyErr_ExceptionMatches(PyExc_SyntaxError))) {
|
||||
return NULL;
|
||||
}
|
||||
if ((p->flags & PyPARSE_ALLOW_INCOMPLETE_INPUT) &&
|
||||
p->input_exhausted && !PyErr_Occurred()) {
|
||||
return _PyPegen_raise_incomplete_input(p);
|
||||
}
|
||||
// Make a second parser pass. In this pass we activate heavier and slower checks
|
||||
// to produce better error messages and more complete diagnostics. Extra "invalid_*"
|
||||
// rules will be active during parsing.
|
||||
@@ -1194,16 +1204,21 @@ error:
|
||||
|
||||
mod_ty
|
||||
_PyPegen_run_parser_from_string(const char *str, int start_rule, PyObject *filename_ob,
|
||||
PyCompilerFlags *flags, PyArena *arena, PyObject *module)
|
||||
PyCompilerFlags *flags, PyArena *arena, PyObject *module,
|
||||
PyObject **incomplete)
|
||||
{
|
||||
int exec_input = start_rule == Py_file_input;
|
||||
_PyTokenizer_Newline newline_mode = start_rule == Py_file_input
|
||||
? _PyTokenizer_IMPLY_NEWLINE : _PyTokenizer_KEEP_NEWLINE;
|
||||
int parser_flags = compute_parser_flags(flags);
|
||||
if (parser_flags & PyPARSE_ALLOW_INCOMPLETE_INPUT) {
|
||||
newline_mode = _PyTokenizer_APPEND_NEWLINE;
|
||||
}
|
||||
|
||||
struct tok_state *tok;
|
||||
if (flags != NULL && flags->cf_flags & PyCF_IGNORE_COOKIE) {
|
||||
tok = _PyTokenizer_FromUTF8(str, exec_input, 0);
|
||||
tok = _PyTokenizer_FromUTF8(str, newline_mode, 0);
|
||||
} else {
|
||||
tok = _PyTokenizer_FromString(str, exec_input, 0);
|
||||
tok = _PyTokenizer_FromString(str, newline_mode, 0);
|
||||
}
|
||||
if (tok == NULL) {
|
||||
if (PyErr_Occurred()) {
|
||||
@@ -1231,6 +1246,22 @@ _PyPegen_run_parser_from_string(const char *str, int start_rule, PyObject *filen
|
||||
}
|
||||
|
||||
result = _PyPegen_run_parser(p);
|
||||
if (result != NULL && (parser_flags & PyPARSE_ALLOW_INCOMPLETE_INPUT)) {
|
||||
int needs_newline = _PyTokenizer_GetNewlineRequirements(tok);
|
||||
if ((needs_newline & _PyTokenizer_NEWLINE_CONTINUATION) ||
|
||||
(start_rule != Py_file_input &&
|
||||
(needs_newline & _PyTokenizer_NEWLINE_INDENT)) ||
|
||||
(start_rule == Py_single_input && p->input_exhausted &&
|
||||
(needs_newline & _PyTokenizer_NEWLINE_STATEMENT))) {
|
||||
_PyPegen_raise_incomplete_input(p);
|
||||
if (incomplete != NULL && PyErr_ExceptionMatches(PyExc_IncompleteInputError)) {
|
||||
*incomplete = PyErr_GetRaisedException();
|
||||
}
|
||||
else {
|
||||
result = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
_PyPegen_Parser_Free(p);
|
||||
|
||||
error:
|
||||
|
||||
+6
-2
@@ -39,6 +39,8 @@ typedef struct _memo {
|
||||
typedef struct {
|
||||
int type;
|
||||
// Text required by grammar actions or helpers; otherwise NULL.
|
||||
// An unfinished string token also has NULL text. Its grammar action
|
||||
// requests more input only if the token is valid at that position.
|
||||
PyObject *bytes;
|
||||
int level;
|
||||
int is_raw;
|
||||
@@ -86,7 +88,8 @@ typedef struct {
|
||||
int n_keyword_lists;
|
||||
int start_rule;
|
||||
int *errcode;
|
||||
int parsing_started;
|
||||
unsigned char parsing_started;
|
||||
unsigned char input_exhausted;
|
||||
PyObject* normalize;
|
||||
int starting_lineno;
|
||||
int starting_col_offset;
|
||||
@@ -210,6 +213,7 @@ typedef enum {
|
||||
// Error handling functions and APIs
|
||||
int _Pypegen_raise_decode_error(Parser *p);
|
||||
int _Pypegen_tokenizer_error(Parser *p);
|
||||
void *_PyPegen_raise_incomplete_input(Parser *p);
|
||||
void *_PyPegen_raise_error(Parser *p, PyObject *errtype, int use_mark, const char *errmsg, ...);
|
||||
void *_PyPegen_raise_error_known_location(Parser *p, PyObject *errtype,
|
||||
Py_ssize_t lineno, Py_ssize_t col_offset,
|
||||
@@ -422,7 +426,7 @@ mod_ty _PyPegen_run_parser_from_file_pointer(FILE *, int, PyObject *, const char
|
||||
const char *, const char *, PyCompilerFlags *, int *, PyObject **,
|
||||
PyArena *);
|
||||
void *_PyPegen_run_parser(Parser *);
|
||||
mod_ty _PyPegen_run_parser_from_string(const char *, int, PyObject *, PyCompilerFlags *, PyArena *, PyObject *);
|
||||
mod_ty _PyPegen_run_parser_from_string(const char *, int, PyObject *, PyCompilerFlags *, PyArena *, PyObject *, PyObject **);
|
||||
asdl_stmt_seq *_PyPegen_interactive_exit(Parser *);
|
||||
|
||||
// Generated function in parse.c - function definition in python.gram
|
||||
|
||||
@@ -9,6 +9,16 @@
|
||||
|
||||
// TOKENIZER ERRORS
|
||||
|
||||
void *
|
||||
_PyPegen_raise_incomplete_input(Parser *p)
|
||||
{
|
||||
if ((p->flags & PyPARSE_ALLOW_INCOMPLETE_INPUT) && !PyErr_Occurred()) {
|
||||
return _PyPegen_raise_error(p, PyExc_IncompleteInputError, 0,
|
||||
"incomplete input");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
raise_unclosed_parentheses_error(Parser *p) {
|
||||
_PyTokenizer_Info info = _PyTokenizer_GetInfo(p->tok);
|
||||
@@ -32,7 +42,17 @@ _Pypegen_tokenizer_error(Parser *p)
|
||||
Py_ssize_t col_offset = -1;
|
||||
p->error_indicator = 1;
|
||||
_PyTokenizer_Info info = _PyTokenizer_GetInfo(p->tok);
|
||||
if ((p->flags & PyPARSE_ALLOW_INCOMPLETE_INPUT) &&
|
||||
(info.status == E_EOF || info.status == E_INCOMPLETE))
|
||||
{
|
||||
_PyPegen_raise_incomplete_input(p);
|
||||
return -1;
|
||||
}
|
||||
switch (info.status) {
|
||||
case E_INCOMPLETE:
|
||||
// The diagnostic pass rejected this unfinished token. Let the
|
||||
// parser report the syntax error at its original failure point.
|
||||
return -1;
|
||||
case E_TOKEN:
|
||||
msg = "invalid token";
|
||||
break;
|
||||
@@ -298,6 +318,12 @@ _PyPegen_raise_error_known_location(Parser *p, PyObject *errtype,
|
||||
Py_ssize_t size;
|
||||
const char *line = _PyTokenizer_SpanView(
|
||||
p->tok, text_span, &size);
|
||||
if (errtype == PyExc_IncompleteInputError && size > 0 &&
|
||||
(info.implicit_newline == _PyTokenizer_APPEND_NEWLINE ||
|
||||
(info.implicit_newline == _PyTokenizer_IMPLY_NEWLINE &&
|
||||
p->start_rule != Py_file_input))) {
|
||||
size--;
|
||||
}
|
||||
error_line = PyUnicode_DecodeUTF8(line, size, "replace");
|
||||
}
|
||||
else if (!info.is_file) {
|
||||
|
||||
@@ -7,11 +7,24 @@
|
||||
#include "reader_internal.h"
|
||||
#include "../lexer/state.h"
|
||||
|
||||
int
|
||||
_PyTokenizer_GetStatus(const struct tok_state *tok)
|
||||
{
|
||||
return tok->done;
|
||||
}
|
||||
|
||||
int
|
||||
_PyTokenizer_GetNewlineRequirements(const struct tok_state *tok)
|
||||
{
|
||||
return tok->needs_newline;
|
||||
}
|
||||
|
||||
_PyTokenizer_Info
|
||||
_PyTokenizer_GetInfo(const struct tok_state *tok)
|
||||
{
|
||||
_PyTokenizer_Info info = {
|
||||
.status = tok->done,
|
||||
.implicit_newline = tok->implicit_newline,
|
||||
.diagnostic = tok->diagnostic,
|
||||
.location = {tok->lineno, tok->line_start < 0
|
||||
? -1 : (int)(tok->cur - tok->line_start)},
|
||||
@@ -103,6 +116,12 @@ _PyTokenizer_ImplyDedents(struct tok_state *tok)
|
||||
_PyLexer_ImplyDedents(tok);
|
||||
}
|
||||
|
||||
void
|
||||
_PyTokenizer_AllowIncompleteInput(struct tok_state *tok, int allow)
|
||||
{
|
||||
tok->allow_incomplete_input = allow != 0;
|
||||
}
|
||||
|
||||
int
|
||||
_PyTokenizer_HasTrailingStatement(const struct tok_state *tok)
|
||||
{
|
||||
|
||||
+21
-32
@@ -14,7 +14,6 @@
|
||||
#define UTF16_BOM_LENGTH ((Py_ssize_t)(sizeof(UTF16_LE_BOM) - 1))
|
||||
#define UTF8_BOM "\xef\xbb\xbf"
|
||||
#define UTF8_BOM_LENGTH ((Py_ssize_t)(sizeof(UTF8_BOM) - 1))
|
||||
#define NORMALIZED_LINE_INITIAL_CAPACITY 256
|
||||
|
||||
char *
|
||||
_PyTok_CopyBytes(const char *data, Py_ssize_t len)
|
||||
@@ -284,9 +283,6 @@ store_prepared_source(struct tok_state *tok, const char *data, Py_ssize_t len,
|
||||
int preserve_crlf, int add_final_newline)
|
||||
{
|
||||
Py_ssize_t pos = 0;
|
||||
// SourceAppendLine copies the bytes, so reuse this buffer for each line.
|
||||
char *normalized = NULL;
|
||||
Py_ssize_t capacity = 0;
|
||||
while (pos < len) {
|
||||
Py_ssize_t raw_line_len;
|
||||
if (preserve_crlf) {
|
||||
@@ -304,52 +300,45 @@ store_prepared_source(struct tok_state *tok, const char *data, Py_ssize_t len,
|
||||
int add_newline = add_final_newline &&
|
||||
pos + raw_line_len == len && !terminated;
|
||||
const char *line = data + pos;
|
||||
Py_ssize_t line_len = raw_line_len;
|
||||
// raw_line_length stops at the first CR or LF, so any CR is in
|
||||
// the line terminator.
|
||||
Py_ssize_t terminator_len = 0;
|
||||
if (!preserve_crlf) {
|
||||
if (line[line_len - 1] == '\r') {
|
||||
if (line[raw_line_len - 1] == '\r') {
|
||||
terminator_len = 1;
|
||||
}
|
||||
else if (line_len > 1 && line[line_len - 2] == '\r') {
|
||||
else if (raw_line_len > 1 && line[raw_line_len - 2] == '\r') {
|
||||
terminator_len = 2;
|
||||
}
|
||||
}
|
||||
_PyTok_Off appended;
|
||||
if (terminator_len || add_newline) {
|
||||
Py_ssize_t payload_len = line_len - terminator_len;
|
||||
if (payload_len > PY_SSIZE_T_MAX - 2) {
|
||||
PyErr_NoMemory();
|
||||
tok->done = E_NOMEM;
|
||||
goto error;
|
||||
}
|
||||
Py_ssize_t needed = payload_len + 2;
|
||||
if (_PyTok_ReserveBuffer(&normalized, &capacity, needed,
|
||||
NORMALIZED_LINE_INITIAL_CAPACITY) < 0) {
|
||||
tok->done = E_NOMEM;
|
||||
goto error;
|
||||
}
|
||||
memcpy(normalized, line, payload_len);
|
||||
normalized[payload_len] = '\n';
|
||||
line_len = payload_len + 1;
|
||||
normalized[line_len] = '\0';
|
||||
line = normalized;
|
||||
appended = _PyTok_SourceAppendLineWithNewline(
|
||||
&tok->source, line, raw_line_len - terminator_len);
|
||||
}
|
||||
else {
|
||||
appended = _PyTok_SourceAppendLine(&tok->source, line, raw_line_len);
|
||||
}
|
||||
_PyTok_Off appended = _PyTok_SourceAppendLine(
|
||||
&tok->source, line, line_len);
|
||||
if (appended < 0) {
|
||||
tok->done = PyErr_ExceptionMatches(PyExc_MemoryError)
|
||||
? E_NOMEM : E_ERROR;
|
||||
goto error;
|
||||
return -1;
|
||||
}
|
||||
tok->reader->prepared_final_newline_is_implicit = add_newline;
|
||||
tok->reader->prepared_final_newline = add_newline;
|
||||
pos += raw_line_len;
|
||||
}
|
||||
PyMem_Free(normalized);
|
||||
// Partial input includes the newline submitted by the next Enter key.
|
||||
// A final CR pairs with that newline instead of starting a blank line.
|
||||
if (add_final_newline == _PyTokenizer_APPEND_NEWLINE &&
|
||||
!tok->reader->prepared_final_newline &&
|
||||
(len == 0 || data[len - 1] != '\r'))
|
||||
{
|
||||
if (_PyTok_SourceAppendLine(&tok->source, "\n", 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
tok->reader->prepared_final_newline = _PyTokenizer_APPEND_NEWLINE;
|
||||
}
|
||||
return 0;
|
||||
error:
|
||||
PyMem_Free(normalized);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
@@ -180,8 +180,8 @@ next_prepared(struct tok_state *tok, _PyTok_Chunk *chunk)
|
||||
chunk->data = (char *)_PyTok_SourceSpanView(
|
||||
&tok->source, line, &chunk->len);
|
||||
chunk->ownership = _PYTOK_CHUNK_BORROWED;
|
||||
chunk->implicit_newline = line.end == tail.end &&
|
||||
tok->reader->prepared_final_newline_is_implicit;
|
||||
chunk->implicit_newline = line.end == tail.end
|
||||
? tok->reader->prepared_final_newline : 0;
|
||||
return _PYTOK_READ_LINE;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ typedef struct _PyTok_Reader {
|
||||
Py_ssize_t decoded_len;
|
||||
Py_ssize_t decoded_cap;
|
||||
_PyTok_ReaderKind kind;
|
||||
unsigned char prepared_final_newline_is_implicit;
|
||||
unsigned char prepared_final_newline; // _PyTokenizer_Newline
|
||||
unsigned char decoded_tail_is_implicit;
|
||||
unsigned char file_initialized;
|
||||
unsigned char file_eof;
|
||||
|
||||
+28
-11
@@ -71,51 +71,68 @@ reserve_bytes(_PyTok_SourceText *source, Py_ssize_t needed)
|
||||
|
||||
static int
|
||||
validate_line(const _PyTok_SourceText *source, const char *bytes,
|
||||
Py_ssize_t len)
|
||||
Py_ssize_t len, int append_newline)
|
||||
{
|
||||
if (len <= 0 || bytes == NULL ||
|
||||
if (len < 0 || (len == 0 && !append_newline) || bytes == NULL ||
|
||||
(source->nlines > 0 &&
|
||||
(source->len == 0 || source->bytes[source->len - 1] != '\n'))) {
|
||||
PyErr_SetString(PyExc_SystemError, "invalid tokenizer source line");
|
||||
return -1;
|
||||
}
|
||||
const char *newline = memchr(bytes, '\n', len);
|
||||
if (newline != NULL && newline != bytes + len - 1) {
|
||||
if (newline != NULL && (append_newline || newline != bytes + len - 1)) {
|
||||
PyErr_SetString(PyExc_SystemError, "invalid tokenizer source line");
|
||||
return -1;
|
||||
}
|
||||
if (source->nlines == INT_MAX ||
|
||||
(source->nlines == INT_MAX - 1 && newline != NULL)) {
|
||||
(source->nlines == INT_MAX - 1 && (newline != NULL || append_newline))) {
|
||||
PyErr_SetString(PyExc_OverflowError, "too many tokenizer source lines");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
_PyTok_Off
|
||||
_PyTok_SourceAppendLine(_PyTok_SourceText *source, const char *bytes,
|
||||
Py_ssize_t len)
|
||||
static _PyTok_Off
|
||||
append_line(_PyTok_SourceText *source, const char *bytes,
|
||||
Py_ssize_t len, int append_newline)
|
||||
{
|
||||
if (validate_line(source, bytes, len) < 0) {
|
||||
if (validate_line(source, bytes, len, append_newline) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (source->len > PY_SSIZE_T_MAX - len - 1 ||
|
||||
source->base_offset > PY_SSIZE_T_MAX - source->len - len) {
|
||||
if (source->len > PY_SSIZE_T_MAX - len - append_newline - 1 ||
|
||||
source->base_offset > PY_SSIZE_T_MAX - source->len - len - append_newline) {
|
||||
PyErr_NoMemory();
|
||||
return -1;
|
||||
}
|
||||
if (reserve_bytes(source, source->len + len + 1) < 0) {
|
||||
if (reserve_bytes(source, source->len + len + append_newline + 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
_PyTok_Off start = source->len;
|
||||
memcpy(source->bytes + start, bytes, len);
|
||||
source->len += len;
|
||||
if (append_newline) {
|
||||
source->bytes[source->len++] = '\n';
|
||||
}
|
||||
source->bytes[source->len] = '\0';
|
||||
source->nlines++;
|
||||
return source->base_offset + start;
|
||||
}
|
||||
|
||||
_PyTok_Off
|
||||
_PyTok_SourceAppendLine(_PyTok_SourceText *source, const char *bytes,
|
||||
Py_ssize_t len)
|
||||
{
|
||||
return append_line(source, bytes, len, 0);
|
||||
}
|
||||
|
||||
_PyTok_Off
|
||||
_PyTok_SourceAppendLineWithNewline(_PyTok_SourceText *source, const char *bytes,
|
||||
Py_ssize_t len)
|
||||
{
|
||||
return append_line(source, bytes, len, 1);
|
||||
}
|
||||
|
||||
const char *
|
||||
_PyTok_SourceLineView(const _PyTok_SourceText *source, Py_ssize_t lineno,
|
||||
Py_ssize_t *len)
|
||||
|
||||
@@ -70,6 +70,10 @@ PyAPI_FUNC(void) _PyTok_SourceDiscard(_PyTok_SourceText *);
|
||||
final line. The input must not point into source storage. */
|
||||
PyAPI_FUNC(_PyTok_Off) _PyTok_SourceAppendLine(
|
||||
_PyTok_SourceText *source, const char *bytes, Py_ssize_t len);
|
||||
/* Append a line payload followed by '\n'. The payload may be empty and must
|
||||
contain no newline. Other SourceAppendLine constraints still apply. */
|
||||
PyAPI_FUNC(_PyTok_Off) _PyTok_SourceAppendLineWithNewline(
|
||||
_PyTok_SourceText *source, const char *bytes, Py_ssize_t len);
|
||||
/* Return borrowed bytes excluding '\n', writing the byte length to *len.
|
||||
Line numbers are 1-based and clamp to the first or final line; a trailing
|
||||
'\n' adds an empty final line. The view need not be NUL-terminated.
|
||||
|
||||
@@ -6,6 +6,19 @@
|
||||
|
||||
struct tok_state;
|
||||
|
||||
typedef enum {
|
||||
_PyTokenizer_KEEP_NEWLINE, // Preserve the original line terminator.
|
||||
_PyTokenizer_IMPLY_NEWLINE, // Add a missing line terminator.
|
||||
_PyTokenizer_APPEND_NEWLINE, // Also append a blank line to terminated input.
|
||||
} _PyTokenizer_Newline;
|
||||
|
||||
/* Reasons that the appended newline can change the token stream. */
|
||||
enum {
|
||||
_PyTokenizer_NEWLINE_STATEMENT = 1,
|
||||
_PyTokenizer_NEWLINE_INDENT = 2,
|
||||
_PyTokenizer_NEWLINE_CONTINUATION = 4,
|
||||
};
|
||||
|
||||
/* Initialize before use. metadata owns a reference released by _PyToken_Free;
|
||||
a consumer taking that reference must set metadata to NULL. */
|
||||
struct token {
|
||||
@@ -37,6 +50,7 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
int status;
|
||||
unsigned char implicit_newline; // _PyTokenizer_Newline
|
||||
_PyTokenizer_Diagnostic diagnostic;
|
||||
_PyTok_Loc location;
|
||||
_PyTok_Off cursor;
|
||||
@@ -54,7 +68,9 @@ typedef struct {
|
||||
} _PyTokenizer_Info;
|
||||
|
||||
/* Get replaces the initialized token, releasing its previous metadata.
|
||||
Errors are returned as ERRORTOKEN, with or without a Python exception. */
|
||||
Errors are returned as ERRORTOKEN, with or without a Python exception.
|
||||
In incomplete-input mode, an unfinished string retains its token type
|
||||
with an absent span and E_INCOMPLETE status. No Python exception is set. */
|
||||
void _PyTokenizer_Get(struct tok_state *, struct token *);
|
||||
void _PyTokenizer_Free(struct tok_state *);
|
||||
int _PyTokenizer_Traverse(struct tok_state *, visitproc, void *);
|
||||
@@ -69,6 +85,8 @@ _PyToken_Free(struct token *token)
|
||||
/* Views and borrowed snapshot references remain valid until the tokenizer is
|
||||
mutated or freed. Source spans may be discarded when reading more input. */
|
||||
_PyTokenizer_Info _PyTokenizer_GetInfo(const struct tok_state *);
|
||||
int _PyTokenizer_GetStatus(const struct tok_state *);
|
||||
int _PyTokenizer_GetNewlineRequirements(const struct tok_state *);
|
||||
/* An absent token span has a nonnull empty text view. */
|
||||
const char *_PyToken_TextView(
|
||||
const struct tok_state *, const struct token *, Py_ssize_t *);
|
||||
@@ -91,6 +109,7 @@ void _PyTokenizer_SetContext(
|
||||
struct tok_state *tok, PyObject *filename, PyObject *module);
|
||||
void _PyTokenizer_SetOptions(
|
||||
struct tok_state *tok, int extra_tokens, int type_comments);
|
||||
void _PyTokenizer_AllowIncompleteInput(struct tok_state *, int);
|
||||
void _PyTokenizer_ImplyDedents(struct tok_state *);
|
||||
int _PyTokenizer_HasTrailingStatement(const struct tok_state *);
|
||||
int _PyTokenizer_IsInteractive(const struct tok_state *);
|
||||
|
||||
+22
-11
@@ -1285,7 +1285,7 @@ _PyRun_String(const char *str, PyObject* name, int start,
|
||||
goto done;
|
||||
}
|
||||
|
||||
mod = _PyParser_ASTFromString(str, name, start, flags, arena, module);
|
||||
mod = _PyParser_ASTFromString(str, name, start, flags, arena, module, NULL);
|
||||
Py_XDECREF(module);
|
||||
|
||||
if (mod != NULL) {
|
||||
@@ -1569,32 +1569,43 @@ _Py_CompileString(const char *str, PyObject *filename, int start,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyCodeObject *co;
|
||||
mod_ty mod;
|
||||
PyArena *arena = _PyArena_New();
|
||||
if (arena == NULL)
|
||||
return NULL;
|
||||
|
||||
mod = _PyParser_ASTFromString(str, filename, start, flags, arena, module);
|
||||
PyObject *result = NULL;
|
||||
PyObject *incomplete = NULL;
|
||||
mod_ty mod = _PyParser_ASTFromString(str, filename, start, flags, arena,
|
||||
module, &incomplete);
|
||||
if (mod == NULL) {
|
||||
_PyArena_Free(arena);
|
||||
return NULL;
|
||||
goto done;
|
||||
}
|
||||
if (flags && (flags->cf_flags & PyCF_ONLY_AST)) {
|
||||
int syntax_check_only = ((flags->cf_flags & PyCF_OPTIMIZED_AST) == PyCF_ONLY_AST); /* unoptiomized AST */
|
||||
if (_PyCompile_AstPreprocess(mod, filename, flags, optimize, arena,
|
||||
syntax_check_only, module) < 0)
|
||||
{
|
||||
_PyArena_Free(arena);
|
||||
return NULL;
|
||||
goto done;
|
||||
}
|
||||
PyObject *result = PyAST_mod2obj(mod);
|
||||
if (incomplete != NULL) {
|
||||
goto done;
|
||||
}
|
||||
result = PyAST_mod2obj(mod);
|
||||
_PyArena_Free(arena);
|
||||
return result;
|
||||
}
|
||||
co = _PyAST_Compile(mod, filename, flags, optimize, arena, module);
|
||||
result = (PyObject *)_PyAST_Compile(mod, filename, flags, optimize, arena, module);
|
||||
|
||||
done:
|
||||
_PyArena_Free(arena);
|
||||
return (PyObject *)co;
|
||||
if (incomplete != NULL && !PyErr_Occurred()) {
|
||||
Py_CLEAR(result);
|
||||
PyErr_SetRaisedException(incomplete);
|
||||
}
|
||||
else {
|
||||
Py_XDECREF(incomplete);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
|
||||
+1
-1
@@ -3271,7 +3271,7 @@ _Py_SymtableStringObjectFlags(const char *str, PyObject *filename,
|
||||
if (arena == NULL)
|
||||
return NULL;
|
||||
|
||||
mod = _PyParser_ASTFromString(str, filename, start, flags, arena, module);
|
||||
mod = _PyParser_ASTFromString(str, filename, start, flags, arena, module, NULL);
|
||||
if (mod == NULL) {
|
||||
_PyArena_Free(arena);
|
||||
return NULL;
|
||||
|
||||
@@ -93,7 +93,7 @@ parse_string(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
|
||||
PyCompilerFlags flags = _PyCompilerFlags_INIT;
|
||||
mod_ty res = _PyPegen_run_parser_from_string(the_string, Py_file_input, filename_ob,
|
||||
&flags, arena, NULL);
|
||||
&flags, arena, NULL, NULL);
|
||||
if (res == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user