From fb8792c095802a9fcc8b38c1d8cbc3f641918433 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Wed, 19 Oct 2022 11:30:28 +1100 Subject: [PATCH] py/lexer: Wrap in parenthesis all f-string arguments passed to format. This is important for literal tuples, e.g. f"{a,b,}, {c}" --> "{}".format((a,b), (c),) which would otherwise result in either a syntax error or the wrong result. Fixes issue #9635. This work was funded through GitHub Sponsors. Signed-off-by: Jim Mussared --- py/lexer.c | 7 ++++++- tests/basics/string_fstring.py | 5 +++++ tests/basics/string_fstring_debug.py | 4 ++++ tests/basics/string_fstring_debug.py.exp | 3 +++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/py/lexer.c b/py/lexer.c index 39e9662f63..e7d0e81440 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -356,6 +356,9 @@ STATIC void parse_string_literal(mp_lexer_t *lex, bool is_raw, bool is_fstring) vstr_add_byte(&lex->vstr, '{'); next_char(lex); } else { + // wrap each argument in (), e.g. + // f"{a,b,}, {c}" --> "{}".format((a,b), (c),) + vstr_add_byte(&lex->fstring_args, '('); // remember the start of this argument (if we need it for f'{a=}'). size_t i = lex->fstring_args.len; // extract characters inside the { until we reach the @@ -382,7 +385,9 @@ STATIC void parse_string_literal(mp_lexer_t *lex, bool is_raw, bool is_fstring) // remove the trailing '=' lex->fstring_args.len--; } - // comma-separate args + // close the paren-wrapped arg to .format(). + vstr_add_byte(&lex->fstring_args, ')'); + // comma-separate args to .format(). vstr_add_byte(&lex->fstring_args, ','); } vstr_add_byte(&lex->vstr, '{'); diff --git a/tests/basics/string_fstring.py b/tests/basics/string_fstring.py index 7e8a97fd30..8907a5c478 100644 --- a/tests/basics/string_fstring.py +++ b/tests/basics/string_fstring.py @@ -56,3 +56,8 @@ try: except (ValueError, SyntaxError): # MicroPython incorrectly raises ValueError here. print('SyntaxError') + +# Allow literal tuples +print(f"a {1,} b") +print(f"a {x,y,} b") +print(f"a {x,1} b") diff --git a/tests/basics/string_fstring_debug.py b/tests/basics/string_fstring_debug.py index 76a448ca06..95abd4d6d0 100644 --- a/tests/basics/string_fstring_debug.py +++ b/tests/basics/string_fstring_debug.py @@ -21,3 +21,7 @@ print(f"a {x=:08x} b {y} c") print(f'a {f() + g("foo") + h()=} b') print(f'a {f() + g("foo") + h()=:08x} b') + +print(f"a {1,=} b") +print(f"a {x,y,=} b") +print(f"a {x,1=} b") diff --git a/tests/basics/string_fstring_debug.py.exp b/tests/basics/string_fstring_debug.py.exp index 563030f400..f0309e1c98 100644 --- a/tests/basics/string_fstring_debug.py.exp +++ b/tests/basics/string_fstring_debug.py.exp @@ -4,3 +4,6 @@ a x=1 b 2 c a x=00000001 b 2 c a f() + g("foo") + h()=15 b a f() + g("foo") + h()=0000000f b +a 1,=(1,) b +a x,y,=(1, 2) b +a x,1=(1, 1) b