Merge pull request #2690 from jepler/topic-pep-498-fstrings

Implement partial PEP-498 (f-string) support
crypto-aes
Scott Shawcroft 3 years ago committed by GitHub
commit d988af02d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 30
      locale/ID.po
  2. 30
      locale/circuitpython.pot
  3. 30
      locale/de_DE.po
  4. 30
      locale/en_US.po
  5. 30
      locale/en_x_pirate.po
  6. 30
      locale/es.po
  7. 30
      locale/fil.po
  8. 30
      locale/fr.po
  9. 30
      locale/it_IT.po
  10. 30
      locale/ko.po
  11. 30
      locale/pl.po
  12. 30
      locale/pt_BR.po
  13. 30
      locale/zh_Latn_pinyin.po
  14. 3
      py/circuitpy_mpconfig.h
  15. 166
      py/lexer.c
  16. 16
      py/lexer.h
  17. 5
      py/mpconfig.h
  18. 61
      py/parse.c
  19. 113
      tests/basics/string_pep498_fstring.py
  20. 0
      tests/basics/string_pep498_fstring.py.exp
  21. 4
      tests/cmdline/cmd_parsetree.py.exp

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-03 20:13-0600\n"
"POT-Creation-Date: 2020-03-09 08:19-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -2080,6 +2080,26 @@ msgstr "argumen keyword ekstra telah diberikan"
msgid "extra positional arguments given"
msgstr "argumen posisi ekstra telah diberikan"
#: py/parse.c
msgid "f-string expression part cannot include a '#'"
msgstr ""
#: py/parse.c
msgid "f-string expression part cannot include a backslash"
msgstr ""
#: py/parse.c
msgid "f-string: empty expression not allowed"
msgstr ""
#: py/parse.c
msgid "f-string: expecting '}'"
msgstr ""
#: py/parse.c
msgid "f-string: single '}' is not allowed"
msgstr ""
#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3Decoder.c
#: shared-bindings/displayio/OnDiskBitmap.c
msgid "file must be a file opened in byte mode"
@ -2393,6 +2413,10 @@ msgstr ""
msgid "long int not supported in this build"
msgstr ""
#: py/parse.c
msgid "malformed f-string"
msgstr ""
#: shared-bindings/_stage/Layer.c
msgid "map buffer too small"
msgstr ""
@ -2721,6 +2745,10 @@ msgstr ""
msgid "queue overflow"
msgstr "antrian meluap (overflow)"
#: py/parse.c
msgid "raw f-strings are not implemented"
msgstr ""
#: extmod/ulab/code/fft.c
msgid "real and imaginary parts must be of equal length"
msgstr ""

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-03 20:13-0600\n"
"POT-Creation-Date: 2020-03-09 08:19-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -2056,6 +2056,26 @@ msgstr ""
msgid "extra positional arguments given"
msgstr ""
#: py/parse.c
msgid "f-string expression part cannot include a '#'"
msgstr ""
#: py/parse.c
msgid "f-string expression part cannot include a backslash"
msgstr ""
#: py/parse.c
msgid "f-string: empty expression not allowed"
msgstr ""
#: py/parse.c
msgid "f-string: expecting '}'"
msgstr ""
#: py/parse.c
msgid "f-string: single '}' is not allowed"
msgstr ""
#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3Decoder.c
#: shared-bindings/displayio/OnDiskBitmap.c
msgid "file must be a file opened in byte mode"
@ -2369,6 +2389,10 @@ msgstr ""
msgid "long int not supported in this build"
msgstr ""
#: py/parse.c
msgid "malformed f-string"
msgstr ""
#: shared-bindings/_stage/Layer.c
msgid "map buffer too small"
msgstr ""
@ -2696,6 +2720,10 @@ msgstr ""
msgid "queue overflow"
msgstr ""
#: py/parse.c
msgid "raw f-strings are not implemented"
msgstr ""
#: extmod/ulab/code/fft.c
msgid "real and imaginary parts must be of equal length"
msgstr ""

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-03 20:13-0600\n"
"POT-Creation-Date: 2020-03-09 08:19-0500\n"
"PO-Revision-Date: 2018-07-27 11:55-0700\n"
"Last-Translator: Pascal Deneaux\n"
"Language-Team: Sebastian Plamauer, Pascal Deneaux\n"
@ -2085,6 +2085,26 @@ msgstr "Es wurden zusätzliche Keyword-Argumente angegeben"
msgid "extra positional arguments given"
msgstr "Es wurden zusätzliche Argumente ohne Keyword angegeben"
#: py/parse.c
msgid "f-string expression part cannot include a '#'"
msgstr ""
#: py/parse.c
msgid "f-string expression part cannot include a backslash"
msgstr ""
#: py/parse.c
msgid "f-string: empty expression not allowed"
msgstr ""
#: py/parse.c
msgid "f-string: expecting '}'"
msgstr ""
#: py/parse.c
msgid "f-string: single '}' is not allowed"
msgstr ""
#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3Decoder.c
#: shared-bindings/displayio/OnDiskBitmap.c
msgid "file must be a file opened in byte mode"
@ -2405,6 +2425,10 @@ msgstr ""
msgid "long int not supported in this build"
msgstr "long int wird in diesem Build nicht unterstützt"
#: py/parse.c
msgid "malformed f-string"
msgstr ""
#: shared-bindings/_stage/Layer.c
msgid "map buffer too small"
msgstr "map buffer zu klein"
@ -2734,6 +2758,10 @@ msgstr ""
msgid "queue overflow"
msgstr "Warteschlangenüberlauf"
#: py/parse.c
msgid "raw f-strings are not implemented"
msgstr ""
#: extmod/ulab/code/fft.c
msgid "real and imaginary parts must be of equal length"
msgstr ""

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-03 20:13-0600\n"
"POT-Creation-Date: 2020-03-09 08:19-0500\n"
"PO-Revision-Date: 2018-07-27 11:55-0700\n"
"Last-Translator: \n"
"Language-Team: \n"
@ -2056,6 +2056,26 @@ msgstr ""
msgid "extra positional arguments given"
msgstr ""
#: py/parse.c
msgid "f-string expression part cannot include a '#'"
msgstr ""
#: py/parse.c
msgid "f-string expression part cannot include a backslash"
msgstr ""
#: py/parse.c
msgid "f-string: empty expression not allowed"
msgstr ""
#: py/parse.c
msgid "f-string: expecting '}'"
msgstr ""
#: py/parse.c
msgid "f-string: single '}' is not allowed"
msgstr ""
#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3Decoder.c
#: shared-bindings/displayio/OnDiskBitmap.c
msgid "file must be a file opened in byte mode"
@ -2369,6 +2389,10 @@ msgstr ""
msgid "long int not supported in this build"
msgstr ""
#: py/parse.c
msgid "malformed f-string"
msgstr ""
#: shared-bindings/_stage/Layer.c
msgid "map buffer too small"
msgstr ""
@ -2696,6 +2720,10 @@ msgstr ""
msgid "queue overflow"
msgstr ""
#: py/parse.c
msgid "raw f-strings are not implemented"
msgstr ""
#: extmod/ulab/code/fft.c
msgid "real and imaginary parts must be of equal length"
msgstr ""

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-03 20:13-0600\n"
"POT-Creation-Date: 2020-03-09 08:19-0500\n"
"PO-Revision-Date: 2018-07-27 11:55-0700\n"
"Last-Translator: \n"
"Language-Team: @sommersoft, @MrCertainly\n"
@ -2060,6 +2060,26 @@ msgstr ""
msgid "extra positional arguments given"
msgstr ""
#: py/parse.c
msgid "f-string expression part cannot include a '#'"
msgstr ""
#: py/parse.c
msgid "f-string expression part cannot include a backslash"
msgstr ""
#: py/parse.c
msgid "f-string: empty expression not allowed"
msgstr ""
#: py/parse.c
msgid "f-string: expecting '}'"
msgstr ""
#: py/parse.c
msgid "f-string: single '}' is not allowed"
msgstr ""
#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3Decoder.c
#: shared-bindings/displayio/OnDiskBitmap.c
msgid "file must be a file opened in byte mode"
@ -2373,6 +2393,10 @@ msgstr ""
msgid "long int not supported in this build"
msgstr ""
#: py/parse.c
msgid "malformed f-string"
msgstr ""
#: shared-bindings/_stage/Layer.c
msgid "map buffer too small"
msgstr ""
@ -2700,6 +2724,10 @@ msgstr ""
msgid "queue overflow"
msgstr ""
#: py/parse.c
msgid "raw f-strings are not implemented"
msgstr ""
#: extmod/ulab/code/fft.c
msgid "real and imaginary parts must be of equal length"
msgstr ""

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-03 20:13-0600\n"
"POT-Creation-Date: 2020-03-09 08:19-0500\n"
"PO-Revision-Date: 2018-08-24 22:56-0500\n"
"Last-Translator: \n"
"Language-Team: \n"
@ -2087,6 +2087,26 @@ msgstr "argumento(s) por palabra clave adicionales fueron dados"
msgid "extra positional arguments given"
msgstr "argumento posicional adicional dado"
#: py/parse.c
msgid "f-string expression part cannot include a '#'"
msgstr ""
#: py/parse.c
msgid "f-string expression part cannot include a backslash"
msgstr ""
#: py/parse.c
msgid "f-string: empty expression not allowed"
msgstr ""
#: py/parse.c
msgid "f-string: expecting '}'"
msgstr ""
#: py/parse.c
msgid "f-string: single '}' is not allowed"
msgstr ""
#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3Decoder.c
#: shared-bindings/displayio/OnDiskBitmap.c
msgid "file must be a file opened in byte mode"
@ -2403,6 +2423,10 @@ msgstr "variable local referenciada antes de la asignación"
msgid "long int not supported in this build"
msgstr "long int no soportado en esta compilación"
#: py/parse.c
msgid "malformed f-string"
msgstr ""
#: shared-bindings/_stage/Layer.c
msgid "map buffer too small"
msgstr "map buffer muy pequeño"
@ -2734,6 +2758,10 @@ msgstr "pow() con 3 argumentos requiere enteros"
msgid "queue overflow"
msgstr "desbordamiento de cola(queue)"
#: py/parse.c
msgid "raw f-strings are not implemented"
msgstr ""
#: extmod/ulab/code/fft.c
msgid "real and imaginary parts must be of equal length"
msgstr ""

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-03 20:13-0600\n"
"POT-Creation-Date: 2020-03-09 08:19-0500\n"
"PO-Revision-Date: 2018-12-20 22:15-0800\n"
"Last-Translator: Timothy <me@timothygarcia.ca>\n"
"Language-Team: fil\n"
@ -2101,6 +2101,26 @@ msgstr "dagdag na keyword argument na ibinigay"
msgid "extra positional arguments given"
msgstr "dagdag na positional argument na ibinigay"
#: py/parse.c
msgid "f-string expression part cannot include a '#'"
msgstr ""
#: py/parse.c
msgid "f-string expression part cannot include a backslash"
msgstr ""
#: py/parse.c
msgid "f-string: empty expression not allowed"
msgstr ""
#: py/parse.c
msgid "f-string: expecting '}'"
msgstr ""
#: py/parse.c
msgid "f-string: single '}' is not allowed"
msgstr ""
#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3Decoder.c
#: shared-bindings/displayio/OnDiskBitmap.c
msgid "file must be a file opened in byte mode"
@ -2419,6 +2439,10 @@ msgstr "local variable na reference bago na i-assign"
msgid "long int not supported in this build"
msgstr "long int hindi sinusuportahan sa build na ito"
#: py/parse.c
msgid "malformed f-string"
msgstr ""
#: shared-bindings/_stage/Layer.c
msgid "map buffer too small"
msgstr "masyadong maliit ang buffer map"
@ -2748,6 +2772,10 @@ msgstr "pow() na may 3 argumento kailangan ng integers"
msgid "queue overflow"
msgstr "puno na ang pila (overflow)"
#: py/parse.c
msgid "raw f-strings are not implemented"
msgstr ""
#: extmod/ulab/code/fft.c
msgid "real and imaginary parts must be of equal length"
msgstr ""

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-03 20:13-0600\n"
"POT-Creation-Date: 2020-03-09 08:19-0500\n"
"PO-Revision-Date: 2019-04-14 20:05+0100\n"
"Last-Translator: Pierrick Couturier <arofarn@arofarn.info>\n"
"Language-Team: fr\n"
@ -2126,6 +2126,26 @@ msgstr "argument(s) nommé(s) supplémentaire(s) donné(s)"
msgid "extra positional arguments given"
msgstr "argument(s) positionnel(s) supplémentaire(s) donné(s)"
#: py/parse.c
msgid "f-string expression part cannot include a '#'"
msgstr ""
#: py/parse.c
msgid "f-string expression part cannot include a backslash"
msgstr ""
#: py/parse.c
msgid "f-string: empty expression not allowed"
msgstr ""
#: py/parse.c
msgid "f-string: expecting '}'"
msgstr ""
#: py/parse.c
msgid "f-string: single '}' is not allowed"
msgstr ""
#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3Decoder.c
#: shared-bindings/displayio/OnDiskBitmap.c
msgid "file must be a file opened in byte mode"
@ -2443,6 +2463,10 @@ msgstr "variable locale référencée avant d'être assignée"
msgid "long int not supported in this build"
msgstr "entiers longs non supportés dans cette build"
#: py/parse.c
msgid "malformed f-string"
msgstr ""
#: shared-bindings/_stage/Layer.c
msgid "map buffer too small"
msgstr "tampon trop petit"
@ -2780,6 +2804,10 @@ msgstr "pow() avec 3 arguments nécessite des entiers"
msgid "queue overflow"
msgstr "dépassement de file"
#: py/parse.c
msgid "raw f-strings are not implemented"
msgstr ""
#: extmod/ulab/code/fft.c
msgid "real and imaginary parts must be of equal length"
msgstr ""

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-03 20:13-0600\n"
"POT-Creation-Date: 2020-03-09 08:19-0500\n"
"PO-Revision-Date: 2018-10-02 16:27+0200\n"
"Last-Translator: Enrico Paganin <enrico.paganin@mail.com>\n"
"Language-Team: \n"
@ -2102,6 +2102,26 @@ msgstr "argomento nominato aggiuntivo fornito"
msgid "extra positional arguments given"
msgstr "argomenti posizonali extra dati"
#: py/parse.c
msgid "f-string expression part cannot include a '#'"
msgstr ""
#: py/parse.c
msgid "f-string expression part cannot include a backslash"
msgstr ""
#: py/parse.c
msgid "f-string: empty expression not allowed"
msgstr ""
#: py/parse.c
msgid "f-string: expecting '}'"
msgstr ""
#: py/parse.c
msgid "f-string: single '}' is not allowed"
msgstr ""
#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3Decoder.c
#: shared-bindings/displayio/OnDiskBitmap.c
msgid "file must be a file opened in byte mode"
@ -2421,6 +2441,10 @@ msgstr "variabile locale richiamata prima di un assegnamento"
msgid "long int not supported in this build"
msgstr "long int non supportata in questa build"
#: py/parse.c
msgid "malformed f-string"
msgstr ""
#: shared-bindings/_stage/Layer.c
msgid "map buffer too small"
msgstr "map buffer troppo piccolo"
@ -2755,6 +2779,10 @@ msgstr "pow() con 3 argomenti richiede interi"
msgid "queue overflow"
msgstr "overflow della coda"
#: py/parse.c
msgid "raw f-strings are not implemented"
msgstr ""
#: extmod/ulab/code/fft.c
msgid "real and imaginary parts must be of equal length"
msgstr ""

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-03 20:13-0600\n"
"POT-Creation-Date: 2020-03-09 08:19-0500\n"
"PO-Revision-Date: 2019-05-06 14:22-0700\n"
"Last-Translator: \n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -2061,6 +2061,26 @@ msgstr ""
msgid "extra positional arguments given"
msgstr ""
#: py/parse.c
msgid "f-string expression part cannot include a '#'"
msgstr ""
#: py/parse.c
msgid "f-string expression part cannot include a backslash"
msgstr ""
#: py/parse.c
msgid "f-string: empty expression not allowed"
msgstr ""
#: py/parse.c
msgid "f-string: expecting '}'"
msgstr ""
#: py/parse.c
msgid "f-string: single '}' is not allowed"
msgstr ""
#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3Decoder.c
#: shared-bindings/displayio/OnDiskBitmap.c
msgid "file must be a file opened in byte mode"
@ -2374,6 +2394,10 @@ msgstr ""
msgid "long int not supported in this build"
msgstr ""
#: py/parse.c
msgid "malformed f-string"
msgstr ""
#: shared-bindings/_stage/Layer.c
msgid "map buffer too small"
msgstr ""
@ -2701,6 +2725,10 @@ msgstr ""
msgid "queue overflow"
msgstr ""
#: py/parse.c
msgid "raw f-strings are not implemented"
msgstr ""
#: extmod/ulab/code/fft.c
msgid "real and imaginary parts must be of equal length"
msgstr ""

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-03 20:13-0600\n"
"POT-Creation-Date: 2020-03-09 08:19-0500\n"
"PO-Revision-Date: 2019-03-19 18:37-0700\n"
"Last-Translator: Radomir Dopieralski <circuitpython@sheep.art.pl>\n"
"Language-Team: pl\n"
@ -2065,6 +2065,26 @@ msgstr "nadmiarowe argumenty nazwane"
msgid "extra positional arguments given"
msgstr "nadmiarowe argumenty pozycyjne"
#: py/parse.c
msgid "f-string expression part cannot include a '#'"
msgstr ""
#: py/parse.c
msgid "f-string expression part cannot include a backslash"
msgstr ""
#: py/parse.c
msgid "f-string: empty expression not allowed"
msgstr ""
#: py/parse.c
msgid "f-string: expecting '}'"
msgstr ""
#: py/parse.c
msgid "f-string: single '}' is not allowed"
msgstr ""
#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3Decoder.c
#: shared-bindings/displayio/OnDiskBitmap.c
msgid "file must be a file opened in byte mode"
@ -2378,6 +2398,10 @@ msgstr "zmienna lokalna użyta przed przypisaniem"
msgid "long int not supported in this build"
msgstr "long int jest nieobsługiwany"
#: py/parse.c
msgid "malformed f-string"
msgstr ""
#: shared-bindings/_stage/Layer.c
msgid "map buffer too small"
msgstr "bufor mapy zbyt mały"
@ -2706,6 +2730,10 @@ msgstr "trzyargumentowe pow() wymaga liczb całkowitych"
msgid "queue overflow"
msgstr "przepełnienie kolejki"
#: py/parse.c
msgid "raw f-strings are not implemented"
msgstr ""
#: extmod/ulab/code/fft.c
msgid "real and imaginary parts must be of equal length"
msgstr ""

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-03 20:13-0600\n"
"POT-Creation-Date: 2020-03-09 08:19-0500\n"
"PO-Revision-Date: 2018-10-02 21:14-0000\n"
"Last-Translator: \n"
"Language-Team: \n"
@ -2078,6 +2078,26 @@ msgstr "argumentos extras de palavras-chave passados"
msgid "extra positional arguments given"
msgstr "argumentos extra posicionais passados"
#: py/parse.c
msgid "f-string expression part cannot include a '#'"
msgstr ""
#: py/parse.c
msgid "f-string expression part cannot include a backslash"
msgstr ""
#: py/parse.c
msgid "f-string: empty expression not allowed"
msgstr ""
#: py/parse.c
msgid "f-string: expecting '}'"
msgstr ""
#: py/parse.c
msgid "f-string: single '}' is not allowed"
msgstr ""
#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3Decoder.c
#: shared-bindings/displayio/OnDiskBitmap.c
msgid "file must be a file opened in byte mode"
@ -2391,6 +2411,10 @@ msgstr ""
msgid "long int not supported in this build"
msgstr ""
#: py/parse.c
msgid "malformed f-string"
msgstr ""
#: shared-bindings/_stage/Layer.c
msgid "map buffer too small"
msgstr ""
@ -2718,6 +2742,10 @@ msgstr ""
msgid "queue overflow"
msgstr "estouro de fila"
#: py/parse.c
msgid "raw f-strings are not implemented"
msgstr ""
#: extmod/ulab/code/fft.c
msgid "real and imaginary parts must be of equal length"
msgstr ""

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: circuitpython-cn\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-03 20:13-0600\n"
"POT-Creation-Date: 2020-03-09 08:19-0500\n"
"PO-Revision-Date: 2019-04-13 10:10-0700\n"
"Last-Translator: hexthat\n"
"Language-Team: Chinese Hanyu Pinyin\n"
@ -2093,6 +2093,26 @@ msgstr "éwài de guānjiàn cí cānshù"
msgid "extra positional arguments given"
msgstr "gěi chūle éwài de wèizhì cānshù"
#: py/parse.c
msgid "f-string expression part cannot include a '#'"
msgstr ""
#: py/parse.c
msgid "f-string expression part cannot include a backslash"
msgstr ""
#: py/parse.c
msgid "f-string: empty expression not allowed"
msgstr ""
#: py/parse.c
msgid "f-string: expecting '}'"
msgstr ""
#: py/parse.c
msgid "f-string: single '}' is not allowed"
msgstr ""
#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3Decoder.c
#: shared-bindings/displayio/OnDiskBitmap.c
msgid "file must be a file opened in byte mode"
@ -2407,6 +2427,10 @@ msgstr "fùzhí qián yǐnyòng de júbù biànliàng"
msgid "long int not supported in this build"
msgstr "cǐ bǎnběn bù zhīchí zhǎng zhěngshù"
#: py/parse.c
msgid "malformed f-string"
msgstr ""
#: shared-bindings/_stage/Layer.c
msgid "map buffer too small"
msgstr "dìtú huǎnchōng qū tài xiǎo"
@ -2735,6 +2759,10 @@ msgstr "pow() yǒu 3 cānshù xūyào zhěngshù"
msgid "queue overflow"
msgstr "duìliè yìchū"
#: py/parse.c
msgid "raw f-strings are not implemented"
msgstr ""
#: extmod/ulab/code/fft.c
msgid "real and imaginary parts must be of equal length"
msgstr ""

@ -187,6 +187,9 @@ typedef long mp_off_t;
#if !defined(MICROPY_CPYTHON_COMPAT)
#define MICROPY_CPYTHON_COMPAT (CIRCUITPY_FULL_BUILD)
#endif
#if !defined(MICROPY_COMP_FSTRING_LITERAL)
#define MICROPY_COMP_FSTRING_LITERAL (MICROPY_CPYTHON_COMPAT)
#endif
#define MICROPY_MODULE_WEAK_LINKS (CIRCUITPY_FULL_BUILD)
#define MICROPY_PY_ALL_SPECIAL_METHODS (CIRCUITPY_FULL_BUILD)
#define MICROPY_PY_BUILTINS_COMPLEX (CIRCUITPY_FULL_BUILD)

@ -64,6 +64,12 @@ STATIC bool is_char_or3(mp_lexer_t *lex, byte c1, byte c2, byte c3) {
return lex->chr0 == c1 || lex->chr0 == c2 || lex->chr0 == c3;
}
#if MICROPY_COMP_FSTRING_LITERAL
STATIC bool is_char_or4(mp_lexer_t *lex, byte c1, byte c2, byte c3, byte c4) {
return lex->chr0 == c1 || lex->chr0 == c2 || lex->chr0 == c3 || lex->chr0 == c4;
}
#endif
STATIC bool is_char_following(mp_lexer_t *lex, byte c) {
return lex->chr1 == c;
}
@ -107,7 +113,13 @@ STATIC bool is_following_odigit(mp_lexer_t *lex) {
STATIC bool is_string_or_bytes(mp_lexer_t *lex) {
return is_char_or(lex, '\'', '\"')
#if MICROPY_COMP_FSTRING_LITERAL
|| (is_char_or4(lex, 'r', 'u', 'b', 'f') && is_char_following_or(lex, '\'', '\"'))
|| ((is_char_and(lex, 'r', 'f') || is_char_and(lex, 'f', 'r'))
&& is_char_following_following_or(lex, '\'', '\"'))
#else
|| (is_char_or3(lex, 'r', 'u', 'b') && is_char_following_or(lex, '\'', '\"'))
#endif
|| ((is_char_and(lex, 'r', 'b') || is_char_and(lex, 'b', 'r'))
&& is_char_following_following_or(lex, '\'', '\"'));
}
@ -121,6 +133,31 @@ STATIC bool is_tail_of_identifier(mp_lexer_t *lex) {
return is_head_of_identifier(lex) || is_digit(lex);
}
#if MICROPY_COMP_FSTRING_LITERAL
STATIC void swap_char_banks(mp_lexer_t *lex) {
if (lex->vstr_postfix_processing) {
lex->chr3 = lex->chr0;
lex->chr4 = lex->chr1;
lex->chr5 = lex->chr2;
lex->chr0 = lex->vstr_postfix.buf[0];
lex->chr1 = lex->vstr_postfix.buf[1];
lex->chr2 = lex->vstr_postfix.buf[2];
lex->vstr_postfix_idx = 3;
} else {
// blindly reset to the "backup" bank when done postfix processing
// this restores control to the mp_reader
lex->chr0 = lex->chr3;
lex->chr1 = lex->chr4;
lex->chr2 = lex->chr5;
// willfully ignoring setting chr3-5 here - WARNING consider those garbage data now
vstr_reset(&lex->vstr_postfix);
lex->vstr_postfix_idx = 0;
}
}
#endif
STATIC void next_char(mp_lexer_t *lex) {
if (lex->chr0 == '\n') {
// a new line
@ -136,7 +173,19 @@ STATIC void next_char(mp_lexer_t *lex) {
lex->chr0 = lex->chr1;
lex->chr1 = lex->chr2;
lex->chr2 = lex->reader.readbyte(lex->reader.data);
#if MICROPY_COMP_FSTRING_LITERAL
if (lex->vstr_postfix_processing) {
if (lex->vstr_postfix_idx == lex->vstr_postfix.len) {
lex->chr2 = '\0';
} else {
lex->chr2 = lex->vstr_postfix.buf[lex->vstr_postfix_idx++];
}
} else
#endif
{
lex->chr2 = lex->reader.readbyte(lex->reader.data);
}
if (lex->chr1 == '\r') {
// CR is a new line, converted to LF
@ -151,6 +200,13 @@ STATIC void next_char(mp_lexer_t *lex) {
if (lex->chr2 == MP_LEXER_EOF && lex->chr1 != MP_LEXER_EOF && lex->chr1 != '\n') {
lex->chr2 = '\n';
}
#if MICROPY_COMP_FSTRING_LITERAL
if (lex->vstr_postfix_processing && lex->chr0 == '\0') {
lex->vstr_postfix_processing = false;
swap_char_banks(lex);
}
#endif
}
STATIC void indent_push(mp_lexer_t *lex, size_t indent) {
@ -270,7 +326,7 @@ STATIC bool get_hex(mp_lexer_t *lex, size_t num_digits, mp_uint_t *result) {
return true;
}
STATIC void parse_string_literal(mp_lexer_t *lex, bool is_raw) {
STATIC void parse_string_literal(mp_lexer_t *lex, bool is_raw, bool is_fstring) {
// get first quoting character
char quote_char = '\'';
if (is_char(lex, '\"')) {
@ -291,15 +347,71 @@ STATIC void parse_string_literal(mp_lexer_t *lex, bool is_raw) {
}
size_t n_closing = 0;
#if MICROPY_COMP_FSTRING_LITERAL
bool in_expression = false;
bool expression_eat = true;
#endif
while (!is_end(lex) && (num_quotes > 1 || !is_char(lex, '\n')) && n_closing < num_quotes) {
if (is_char(lex, quote_char)) {
n_closing += 1;
vstr_add_char(&lex->vstr, CUR_CHAR(lex));
} else {
n_closing = 0;
#if MICROPY_COMP_FSTRING_LITERAL
if (is_fstring && is_char(lex, '{')) {
vstr_add_char(&lex->vstr, CUR_CHAR(lex));
in_expression = !in_expression;
expression_eat = in_expression;
if (lex->vstr_postfix.len == 0) {
vstr_add_str(&lex->vstr_postfix, ".format(");
}
next_char(lex);
continue;
}
if (is_fstring && is_char(lex, '}')) {
vstr_add_char(&lex->vstr, CUR_CHAR(lex));
if (in_expression) {
in_expression = false;
vstr_add_char(&lex->vstr_postfix, ',');
}
next_char(lex);
continue;
}
if (in_expression) {
// throw errors for illegal chars inside f-string expressions
if (is_char(lex, '#')) {
lex->tok_kind = MP_TOKEN_FSTRING_COMMENT;
return;
} else if (is_char(lex, '\\')) {
lex->tok_kind = MP_TOKEN_FSTRING_BACKSLASH;
return;
} else if (is_char(lex, ':')) {
expression_eat = false;
}
unichar c = CUR_CHAR(lex);
if (expression_eat) {
vstr_add_char(&lex->vstr_postfix, c);
} else {
vstr_add_char(&lex->vstr, c);
}
next_char(lex);
continue;
}
#endif
if (is_char(lex, '\\')) {
next_char(lex);
unichar c = CUR_CHAR(lex);
if (is_raw) {
// raw strings allow escaping of quotes, but the backslash is also emitted
vstr_add_char(&lex->vstr, '\\');
@ -430,6 +542,15 @@ STATIC bool skip_whitespace(mp_lexer_t *lex, bool stop_at_newline) {
}
void mp_lexer_to_next(mp_lexer_t *lex) {
#if MICROPY_COMP_FSTRING_LITERAL
if (lex->vstr_postfix.len && !lex->vstr_postfix_processing) {
// end format call injection
vstr_add_char(&lex->vstr_postfix, ')');
lex->vstr_postfix_processing = true;
swap_char_banks(lex);
}
#endif
// start new token text
vstr_reset(&lex->vstr);
@ -481,10 +602,19 @@ void mp_lexer_to_next(mp_lexer_t *lex) {
// MP_TOKEN_END is used to indicate that this is the first string token
lex->tok_kind = MP_TOKEN_END;
#if MICROPY_COMP_FSTRING_LITERAL
bool saw_normal = false, saw_fstring = false;
#endif
// Loop to accumulate string/bytes literals
do {
// parse type codes
bool is_raw = false;
#if MICROPY_COMP_FSTRING_LITERAL
bool is_fstring = false;
#else
const bool is_fstring = false;
#endif
mp_token_kind_t kind = MP_TOKEN_STRING;
int n_char = 0;
if (is_char(lex, 'u')) {
@ -503,7 +633,33 @@ void mp_lexer_to_next(mp_lexer_t *lex) {
kind = MP_TOKEN_BYTES;
n_char = 2;
}
#if MICROPY_COMP_FSTRING_LITERAL
if (is_char_following(lex, 'f')) {
lex->tok_kind = MP_TOKEN_FSTRING_RAW;
break;
}
} else if (is_char(lex, 'f')) {
if (is_char_following(lex, 'r')) {
lex->tok_kind = MP_TOKEN_FSTRING_RAW;
break;
}
n_char = 1;
is_fstring = true;
#endif
}
#if MICROPY_COMP_FSTRING_LITERAL
if (is_fstring) {
saw_fstring = true;
} else {
saw_normal = true;
}
if (saw_fstring && saw_normal) {
// Can't concatenate f-string with normal string
break;
}
#endif
// Set or check token kind
if (lex->tok_kind == MP_TOKEN_END) {
@ -522,13 +678,12 @@ void mp_lexer_to_next(mp_lexer_t *lex) {
}
// Parse the literal
parse_string_literal(lex, is_raw);
parse_string_literal(lex, is_raw, is_fstring);
// Skip whitespace so we can check if there's another string following
skip_whitespace(lex, true);
} while (is_string_or_bytes(lex));
} else if (is_head_of_identifier(lex)) {
lex->tok_kind = MP_TOKEN_NAME;
@ -682,6 +837,9 @@ mp_lexer_t *mp_lexer_new(qstr src_name, mp_reader_t reader) {
lex->num_indent_level = 1;
lex->indent_level = m_new(uint16_t, lex->alloc_indent_level);
vstr_init(&lex->vstr, 32);
#if MICROPY_COMP_FSTRING_LITERAL
vstr_init(&lex->vstr_postfix, 0);
#endif
// store sentinel for first indentation level
lex->indent_level[0] = 0;

@ -44,6 +44,14 @@ typedef enum _mp_token_kind_t {
MP_TOKEN_INVALID,
MP_TOKEN_DEDENT_MISMATCH,
MP_TOKEN_LONELY_STRING_OPEN,
#if MICROPY_COMP_FSTRING_LITERAL
MP_TOKEN_FSTRING_BACKSLASH,
MP_TOKEN_FSTRING_COMMENT,
MP_TOKEN_FSTRING_UNCLOSED,
MP_TOKEN_FSTRING_UNOPENED,
MP_TOKEN_FSTRING_EMPTY_EXP,
MP_TOKEN_FSTRING_RAW,
#endif
MP_TOKEN_NEWLINE,
MP_TOKEN_INDENT,
@ -150,6 +158,9 @@ typedef struct _mp_lexer_t {
mp_reader_t reader; // stream source
unichar chr0, chr1, chr2; // current cached characters from source
#if MICROPY_COMP_FSTRING_LITERAL
unichar chr3, chr4, chr5; // current cached characters from alt source
#endif
size_t line; // current source line
size_t column; // current source column
@ -165,6 +176,11 @@ typedef struct _mp_lexer_t {
size_t tok_column; // token source column
mp_token_kind_t tok_kind; // token kind
vstr_t vstr; // token data
#if MICROPY_COMP_FSTRING_LITERAL
vstr_t vstr_postfix; // postfix to apply to string
bool vstr_postfix_processing;
uint16_t vstr_postfix_idx;
#endif
} mp_lexer_t;
mp_lexer_t *mp_lexer_new(qstr src_name, mp_reader_t reader);

@ -377,6 +377,11 @@
#define MICROPY_COMP_RETURN_IF_EXPR (0)
#endif
// Whether to include parsing of f-string literals
#ifndef MICROPY_COMP_FSTRING_LITERAL
#define MICROPY_COMP_FSTRING_LITERAL (1)
#endif
/*****************************************************************************/
/* Internal debugging stuff */

@ -924,6 +924,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
backtrack = false;
}
for (; i < n; ++i) {
//printf("--> inside for @L924\n");
uint16_t kind = rule_arg[i] & RULE_ARG_KIND_MASK;
if (kind == RULE_ARG_TOK) {
if (lex->tok_kind == (rule_arg[i] & RULE_ARG_ARG_MASK)) {
@ -1168,15 +1169,57 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
) {
syntax_error:;
mp_obj_t exc;
if (lex->tok_kind == MP_TOKEN_INDENT) {
exc = mp_obj_new_exception_msg(&mp_type_IndentationError,
translate("unexpected indent"));
} else if (lex->tok_kind == MP_TOKEN_DEDENT_MISMATCH) {
exc = mp_obj_new_exception_msg(&mp_type_IndentationError,
translate("unindent does not match any outer indentation level"));
} else {
exc = mp_obj_new_exception_msg(&mp_type_SyntaxError,
translate("invalid syntax"));
switch(lex->tok_kind) {
case MP_TOKEN_INDENT:
exc = mp_obj_new_exception_msg(&mp_type_IndentationError,
translate("unexpected indent"));
break;
case MP_TOKEN_DEDENT_MISMATCH:
exc = mp_obj_new_exception_msg(&mp_type_IndentationError,
translate("unindent does not match any outer indentation level"));
break;
#if MICROPY_COMP_FSTRING_LITERAL
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED
case MP_TOKEN_FSTRING_BACKSLASH:
exc = mp_obj_new_exception_msg(&mp_type_SyntaxError,
translate("f-string expression part cannot include a backslash"));
break;
case MP_TOKEN_FSTRING_COMMENT:
exc = mp_obj_new_exception_msg(&mp_type_SyntaxError,
translate("f-string expression part cannot include a '#'"));
break;
case MP_TOKEN_FSTRING_UNCLOSED:
exc = mp_obj_new_exception_msg(&mp_type_SyntaxError,
translate("f-string: expecting '}'"));
break;
case MP_TOKEN_FSTRING_UNOPENED:
exc = mp_obj_new_exception_msg(&mp_type_SyntaxError,
translate("f-string: single '}' is not allowed"));
break;
case MP_TOKEN_FSTRING_EMPTY_EXP:
exc = mp_obj_new_exception_msg(&mp_type_SyntaxError,
translate("f-string: empty expression not allowed"));
break;
case MP_TOKEN_FSTRING_RAW:
exc = mp_obj_new_exception_msg(&mp_type_NotImplementedError,
translate("raw f-strings are not implemented"));
break;
#else
case MP_TOKEN_FSTRING_BACKSLASH:
case MP_TOKEN_FSTRING_COMMENT:
case MP_TOKEN_FSTRING_UNCLOSED:
case MP_TOKEN_FSTRING_UNOPENED:
case MP_TOKEN_FSTRING_EMPTY_EXP:
case MP_TOKEN_FSTRING_RAW:
exc = mp_obj_new_exception_msg(&mp_type_SyntaxError,
translate("malformed f-string"));
break;
#endif
#endif
default:
exc = mp_obj_new_exception_msg(&mp_type_SyntaxError,
translate("invalid syntax"));
break;
}
// add traceback to give info about file name and location
// we don't have a 'block' name, so just pass the NULL qstr to indicate this

@ -0,0 +1,113 @@
# Tests against https://www.python.org/dev/peps/pep-0498/
assert f'no interpolation' == 'no interpolation'
assert f"no interpolation" == 'no interpolation'
# Quoth the PEP:
# Backslashes may not appear anywhere within expressions. Comments, using the
# '#' character, are not allowed inside an expression
#
# CPython (3.7.4 on Linux) raises a SyntaxError here:
# >>> f'{#}'
# File "<stdin>", line 1
# SyntaxError: f-string expression part cannot include '#'
# >>> f'{\}'
# File "<stdin>", line 1
# SyntaxError: f-string expression part cannot include a backslash
# >>> f'{\\}'
# File "<stdin>", line 1
# SyntaxError: f-string expression part cannot include a backslash
# >>> f'{\#}'
# File "<stdin>", line 1
# SyntaxError: f-string expression part cannot include a backslash
# Backslashes and comments allowed outside expression
assert f"\\" == "\\"
assert f'#' == '#'
## But not inside
try:
eval("f'{\}'")
except SyntaxError:
pass
else:
raise AssertionError('f-string with backslash in expression did not raise SyntaxError')
try:
eval("f'{#}'")
except SyntaxError:
pass
else:
raise AssertionError('f-string with \'#\' in expression did not raise SyntaxError')
# Quoth the PEP:
# While scanning the string for expressions, any doubled braces '{{' or '}}'
# inside literal portions of an f-string are replaced by the corresponding
# single brace. Doubled literal opening braces do not signify the start of an
# expression. A single closing curly brace '}' in the literal portion of a
# string is an error: literal closing curly braces must be doubled '}}' in
# order to represent a single closing brace.
#
# CPython (3.7.4 on Linux) raises a SyntaxError for the last case:
# >>> f'{{}'
# File "<stdin>", line 1
# SyntaxError: f-string: single '}' is not allowed
assert f'{{}}' == '{}'
try:
eval("f'{{}'")
except ValueError:
pass
else:
raise RuntimeError('Expected ValueError for invalid f-string literal bracing')
x = 1
assert f'{x}' == '1'
# Quoth the PEP:
# The expressions that are extracted from the string are evaluated in the
# context where the f-string appeared. This means the expression has full
# access to local and global variables. Any valid Python expression can be
# used, including function and method calls. Because the f-strings are
# evaluated where the string appears in the source code, there is no additional
# expressiveness available with f-strings. There are also no additional
# security concerns: you could have also just written the same expression, not
# inside of an f-string:
def foo():
return 20
assert f'result={foo()}' == 'result=20'
assert f'result={foo()}' == 'result={}'.format(foo())
assert f'result={foo()}' == 'result={result}'.format(result=foo())
# Quoth the PEP:
# Adjacent f-strings and regular strings are concatenated. Regular strings are
# concatenated at compile time, and f-strings are concatenated at run time. For
# example, the expression:
#
# >>> x = 10
# >>> y = 'hi'
# >>> 'a' 'b' f'{x}' '{c}' f'str<{y:^4}>' 'd' 'e'
#
# yields the value: 'ab10{c}str< hi >de'
#
# Because strings are concatenated at lexer time rather than parser time in
# MicroPython for mostly RAM efficiency reasons (see
# https://github.com/micropython/micropython/commit/534b7c368dc2af7720f3aaed0c936ef46d773957),
# and because f-strings here are implemented as a syntax translation
# (f'{something}' => '{}'.format(something)), this particular functionality is unimplemented,
# and in the above example, the '{c}' portion will trigger a KeyError on String.format()
x = 10
y = 'hi'
assert (f'h' f'i') == 'hi'
#assert (f'h' 'i') == 'hi'
#assert ('h' f'i') == 'hi'
assert f'{x:^4}' == ' 10 '
#assert ('a' 'b' f'{x}' f'str<{y:^4}>' 'd' 'e') == 'ab10str< hi >de'
# Other tests
assert f'{{{4*10}}}' == '{40}'

@ -1,6 +1,6 @@
----------------
[ 4] rule(1) (n=9)
tok(4)
tok(10)
[ 4] rule(22) (n=4)
id(i)
[ 4] rule(44) (n=1)
@ -9,7 +9,7 @@
NULL
[ 6] rule(5) (n=2)
id(a)
tok(14)
tok(20)
[ 7] rule(5) (n=2)
id(b)
str(str)