@ -5006,10 +5006,16 @@ fstring_parse(const char **str, const char *end, int raw, int recurse_lvl,
closing brace doesn ' t match an opening paren , for example . It
doesn ' t need to error on all invalid expressions , just correctly
find the end of all valid ones . Any errors inside the expression
will be caught when we parse it later . */
will be caught when we parse it later .
* expression is set to the expression . For an ' = ' " debug " expression ,
* expr_text is set to the debug text ( the original text of the expression ,
* including the ' = ' and any whitespace around it , as a string object ) . If
* not a debug expression , * expr_text set to NULL . */
static int
fstring_find_expr ( const char * * str , const char * end , int raw , int recurse_lvl ,
expr_ty * expression , struct compiling * c , const node * n )
PyObject * * expr_text , expr_ty * expression ,
struct compiling * c , const node * n )
{
/* Return -1 on error, else 0. */
@ -5020,9 +5026,6 @@ fstring_find_expr(const char **str, const char *end, int raw, int recurse_lvl,
int conversion = - 1 ; /* The conversion char. Use default if not
specified , or ! r if using = and no format
spec . */
int equal_flag = 0 ; /* Are we using the = feature? */
PyObject * expr_text = NULL ; /* The text of the expression, used for =. */
const char * expr_text_end ;
/* 0 if we're not in a string, else the quote char we're trying to
match ( single or double quote ) . */
@ -5198,7 +5201,6 @@ fstring_find_expr(const char **str, const char *end, int raw, int recurse_lvl,
expr_text . */
if ( * * str = = ' = ' ) {
* str + = 1 ;
equal_flag = 1 ;
/* Skip over ASCII whitespace. No need to test for end of string
here , since we know there ' s at least a trailing quote somewhere
@ -5206,7 +5208,14 @@ fstring_find_expr(const char **str, const char *end, int raw, int recurse_lvl,
while ( Py_ISSPACE ( * * str ) ) {
* str + = 1 ;
}
expr_text_end = * str ;
/* Set *expr_text to the text of the expression. */
* expr_text = PyUnicode_FromStringAndSize ( expr_start , * str - expr_start ) ;
if ( ! * expr_text ) {
goto error ;
}
} else {
* expr_text = NULL ;
}
/* Check for a conversion char, if present. */
@ -5227,17 +5236,6 @@ fstring_find_expr(const char **str, const char *end, int raw, int recurse_lvl,
}
}
if ( equal_flag ) {
Py_ssize_t len = expr_text_end - expr_start ;
expr_text = PyUnicode_FromStringAndSize ( expr_start , len ) ;
if ( ! expr_text ) {
goto error ;
}
if ( PyArena_AddPyObject ( c - > c_arena , expr_text ) < 0 ) {
Py_DECREF ( expr_text ) ;
goto error ;
}
}
/* Check for the format spec, if present. */
if ( * str > = end )
@ -5261,16 +5259,16 @@ fstring_find_expr(const char **str, const char *end, int raw, int recurse_lvl,
assert ( * * str = = ' } ' ) ;
* str + = 1 ;
/* If we're in = mode, and have no format spec and no explict conversion,
set the conversion to ' r ' . */
if ( equal_flag & & format_spec = = NULL & & conversion = = - 1 ) {
/* If we're in = mode (detected by non-NULL expr_text) , and have no format
spec and no explict conversion , s et the conversion to ' r ' . */
if ( * expr_text & & format_spec = = NULL & & conversion = = - 1 ) {
conversion = ' r ' ;
}
/* And now create the FormattedValue node that represents this
entire expression with the conversion and format spec . */
* expression = FormattedValue ( simple_expression , conversion ,
format_spec , expr_text , LINENO ( n ) ,
format_spec , LINENO ( n ) ,
n - > n_col_offset , n - > n_end_lineno ,
n - > n_end_col_offset , c - > c_arena ) ;
if ( ! * expression )
@ -5313,7 +5311,7 @@ error:
static int
fstring_find_literal_and_expr ( const char * * str , const char * end , int raw ,
int recurse_lvl , PyObject * * literal ,
expr_ty * expression ,
PyObject * * expr_text , expr_ty * expression ,
struct compiling * c , const node * n )
{
int result ;
@ -5341,7 +5339,8 @@ fstring_find_literal_and_expr(const char **str, const char *end, int raw,
/* We must now be the start of an expression, on a '{'. */
assert ( * * str = = ' { ' ) ;
if ( fstring_find_expr ( str , end , raw , recurse_lvl , expression , c , n ) < 0 )
if ( fstring_find_expr ( str , end , raw , recurse_lvl , expr_text ,
expression , c , n ) < 0 )
goto error ;
return 0 ;
@ -5604,7 +5603,7 @@ FstringParser_ConcatFstring(FstringParser *state, const char **str,
/* Parse the f-string. */
while ( 1 ) {
PyObject * literal = NULL ;
PyObject * literal [ 2 ] = { NULL , NULL } ;
expr_ty expression = NULL ;
/* If there's a zero length literal in front of the
@ -5612,31 +5611,34 @@ FstringParser_ConcatFstring(FstringParser *state, const char **str,
the f - string , expression will be NULL ( unless result = = 1 ,
see below ) . */
int result = fstring_find_literal_and_expr ( str , end , raw , recurse_lvl ,
& literal , & expression ,
c , n ) ;
& literal [ 0 ] , & literal [ 1 ] ,
& expression , c , n ) ;
if ( result < 0 )
return - 1 ;
/* Add the literal, if any. */
if ( ! literal ) {
/* Do nothing. Just leave last_str alone (and possibly
NULL ) . */
} else if ( ! state - > last_str ) {
/* Note that the literal can be zero length, if the
input string is " \\ \n " or " \\ \r " , among others . */
state - > last_str = literal ;
literal = NULL ;
} else {
/* We have a literal, concatenate it. */
assert ( PyUnicode_GET_LENGTH ( literal ) ! = 0 ) ;
if ( FstringParser_ConcatAndDel ( state , literal ) < 0 )
return - 1 ;
literal = NULL ;
/* Add the literals, if any. */
for ( int i = 0 ; i < 2 ; i + + ) {
if ( ! literal [ i ] ) {
/* Do nothing. Just leave last_str alone (and possibly
NULL ) . */
} else if ( ! state - > last_str ) {
/* Note that the literal can be zero length, if the
input string is " \\ \n " or " \\ \r " , among others . */
state - > last_str = literal [ i ] ;
literal [ i ] = NULL ;
} else {
/* We have a literal, concatenate it. */
assert ( PyUnicode_GET_LENGTH ( literal [ i ] ) ! = 0 ) ;
if ( FstringParser_ConcatAndDel ( state , literal [ i ] ) < 0 )
return - 1 ;
literal [ i ] = NULL ;
}
}
/* We've dealt with the literal now. It can't be leaked on further
/* We've dealt with the literals now. They can't be leaked on further
errors . */
assert ( literal = = NULL ) ;
assert ( literal [ 0 ] = = NULL ) ;
assert ( literal [ 1 ] = = NULL ) ;
/* See if we should just loop around to get the next literal
and expression , while ignoring the expression this