Skip to content

Commit 55e93d7

Browse files
committed
cool fstrings
1 parent a1ec746 commit 55e93d7

15 files changed

Lines changed: 584 additions & 306 deletions

File tree

Grammar/python.gram

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -966,9 +966,15 @@ fstring_middle[expr_ty]:
966966
| fstring_replacement_field
967967
| t=FSTRING_MIDDLE { _PyPegen_constant_from_token(p, t) }
968968
fstring_replacement_field[expr_ty]:
969+
| '{' a=annotated_rhs debug_expr='='? conv=fstring_pretty_conversion ':' pretty_func=annotated_rhs rbrace='}' {
970+
_PyPegen_pretty_formatted_value(p, a, debug_expr, conv, pretty_func, rbrace, EXTRA) }
971+
| '{' a=annotated_rhs debug_expr='='? conv=fstring_pretty_conversion rbrace='}' {
972+
_PyPegen_pretty_formatted_value(p, a, debug_expr, conv, NULL, rbrace, EXTRA) }
969973
| '{' a=annotated_rhs debug_expr='='? conversion=[fstring_conversion] format=[fstring_full_format_spec] rbrace='}' {
970974
_PyPegen_formatted_value(p, a, debug_expr, conversion, format, rbrace, EXTRA) }
971975
| invalid_fstring_replacement_field
976+
fstring_pretty_conversion[Token*]:
977+
| conv_token="!" conv=NAME { _PyPegen_check_pretty_conversion(p, conv_token, conv) }
972978
fstring_conversion[ResultTokenWithMetadata*]:
973979
| conv_token="!" conv=NAME { _PyPegen_check_fstring_conversion(p, conv_token, conv) }
974980
fstring_full_format_spec[ResultTokenWithMetadata*]:

Include/ceval.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,14 @@ PyAPI_FUNC(void) PyEval_ReleaseThread(PyThreadState *tstate);
125125
}
126126

127127
/* Masks and values used by FORMAT_VALUE opcode. */
128-
#define FVC_MASK 0x3
128+
#define FVC_MASK 0x7
129129
#define FVC_NONE 0x0
130130
#define FVC_STR 0x1
131131
#define FVC_REPR 0x2
132132
#define FVC_ASCII 0x3
133-
#define FVS_MASK 0x4
134-
#define FVS_HAVE_SPEC 0x4
133+
#define FVC_PRETTY 0x4
134+
#define FVS_MASK 0x8
135+
#define FVS_HAVE_SPEC 0x8
135136

136137
#ifndef Py_LIMITED_API
137138
# define Py_CPYTHON_CEVAL_H

Modules/_testinternalcapi/test_cases.c.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Objects/interpolationobject.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,12 @@ _PyInterpolation_Build(PyObject *value, PyObject *str, int conversion, PyObject
212212
case FVC_STR:
213213
interpolation->conversion = _Py_LATIN1_CHR('s');
214214
break;
215+
case FVC_PRETTY:
216+
interpolation->conversion = _Py_LATIN1_CHR('p');
217+
break;
215218
default:
216219
PyErr_SetString(PyExc_SystemError,
217-
"Interpolation() argument 'conversion' must be one of 's', 'a' or 'r'");
220+
"Interpolation() argument 'conversion' must be one of 's', 'a', 'r', or 'p'");
218221
Py_DECREF(interpolation);
219222
return NULL;
220223
}

Parser/action_helpers.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,22 @@ _PyPegen_check_fstring_conversion(Parser *p, Token* conv_token, expr_ty conv)
10121012
return result_token_with_metadata(p, conv, conv_token->metadata);
10131013
}
10141014

1015+
Token *
1016+
_PyPegen_check_pretty_conversion(Parser *p, Token *conv_token, expr_ty conv)
1017+
{
1018+
if (conv_token->lineno != conv->lineno || conv_token->end_col_offset != conv->col_offset) {
1019+
return NULL;
1020+
}
1021+
if (PyUnicode_GET_LENGTH(conv->v.Name.id) != 1) {
1022+
return NULL;
1023+
}
1024+
Py_UCS4 first = PyUnicode_READ_CHAR(conv->v.Name.id, 0);
1025+
if (first != 'p') {
1026+
return NULL;
1027+
}
1028+
return conv_token;
1029+
}
1030+
10151031
ResultTokenWithMetadata *
10161032
_PyPegen_setup_full_format_spec(Parser *p, Token *colon, asdl_expr_seq *spec, int lineno, int col_offset,
10171033
int end_lineno, int end_col_offset, PyArena *arena)
@@ -1633,6 +1649,38 @@ expr_ty _PyPegen_formatted_value(Parser *p, expr_ty expression, Token *debug, Re
16331649
return _PyAST_JoinedStr(values, lineno, col_offset, debug_end_line, debug_end_offset, p->arena);
16341650
}
16351651

1652+
expr_ty _PyPegen_pretty_formatted_value(Parser *p, expr_ty expression, Token *debug,
1653+
Token *conv_token, expr_ty pretty_func,
1654+
Token *closing_brace, int lineno, int col_offset,
1655+
int end_lineno, int end_col_offset, PyArena *arena) {
1656+
int conversion_val = (int)'p';
1657+
1658+
expr_ty formatted_value = _PyAST_FormattedValue(
1659+
expression, conversion_val, pretty_func,
1660+
lineno, col_offset, end_lineno,
1661+
end_col_offset, arena
1662+
);
1663+
1664+
if (!debug) {
1665+
return formatted_value;
1666+
}
1667+
1668+
int debug_end_line = conv_token->lineno;
1669+
int debug_end_offset = conv_token->col_offset;
1670+
PyObject *debug_metadata = conv_token->metadata;
1671+
1672+
expr_ty debug_text = _PyAST_Constant(debug_metadata, NULL, lineno, col_offset + 1, debug_end_line,
1673+
debug_end_offset - 1, p->arena);
1674+
if (!debug_text) {
1675+
return NULL;
1676+
}
1677+
1678+
asdl_expr_seq *values = _Py_asdl_expr_seq_new(2, arena);
1679+
asdl_seq_SET(values, 0, debug_text);
1680+
asdl_seq_SET(values, 1, formatted_value);
1681+
return _PyAST_JoinedStr(values, lineno, col_offset, debug_end_line, debug_end_offset, p->arena);
1682+
}
1683+
16361684
static expr_ty
16371685
_build_concatenated_bytes(Parser *p, asdl_expr_seq *strings, int lineno,
16381686
int col_offset, int end_lineno, int end_col_offset,

Parser/lexer/lexer.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1099,6 +1099,7 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t
10991099
the_current_tok->last_expr_end = -1;
11001100
the_current_tok->in_format_spec = 0;
11011101
the_current_tok->in_debug = 0;
1102+
the_current_tok->in_pretty_conversion = 0;
11021103

11031104
enum string_kind_t string_kind = FSTRING;
11041105
switch (*tok->start) {
@@ -1268,7 +1269,20 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t
12681269
return MAKE_TOKEN(ERRORTOKEN);
12691270
}
12701271

1271-
if (c == ':' && cursor == current_tok->curly_bracket_expr_start_depth) {
1272+
if (c == '!' && cursor == current_tok->curly_bracket_expr_start_depth) {
1273+
int peek1 = tok_nextc(tok);
1274+
if (peek1 == 'p') {
1275+
int peek2 = tok_nextc(tok);
1276+
if (peek2 == ':') {
1277+
current_tok->in_pretty_conversion = 1;
1278+
}
1279+
tok_backup(tok, peek2);
1280+
}
1281+
tok_backup(tok, peek1);
1282+
}
1283+
1284+
if (c == ':' && cursor == current_tok->curly_bracket_expr_start_depth
1285+
&& !current_tok->in_pretty_conversion) {
12721286
current_tok->kind = TOK_FSTRING_MODE;
12731287
current_tok->in_format_spec = 1;
12741288
p_start = tok->start;
@@ -1368,6 +1382,7 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t
13681382
current_tok->kind = TOK_FSTRING_MODE;
13691383
current_tok->in_format_spec = 0;
13701384
current_tok->in_debug = 0;
1385+
current_tok->in_pretty_conversion = 0;
13711386
}
13721387
}
13731388
break;

Parser/lexer/state.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ typedef struct _tokenizer_mode {
6666
char* last_expr_buffer;
6767
int in_debug;
6868
int in_format_spec;
69+
int in_pretty_conversion;
6970

7071
enum string_kind_t string_kind;
7172
} tokenizer_mode;

0 commit comments

Comments
 (0)