summaryrefslogtreecommitdiff
path: root/py/lexer.h
diff options
context:
space:
mode:
authorJosh Klar <josh@klar.sh>2019-08-10 21:27:20 -0700
committerJeff Epler <jepler@gmail.com>2020-03-09 08:16:07 -0500
commit3a7a5ba6860c787cc691b5c828eff9a130f39526 (patch)
treef494349bc5876d7353de432b3b1566c88dbaca11 /py/lexer.h
parent83d5da95b7d2555466dfe24c292ce9b2b7e5b597 (diff)
py: Implement partial PEP-498 (f-string) support
This implements (most of) the PEP-498 spec for f-strings, with two exceptions: - raw f-strings (`fr` or `rf` prefixes) raise `NotImplementedError` - one special corner case does not function as specified in the PEP (more on that in a moment) This is implemented in the core as a syntax translation, brute-forcing all f-strings to run through `String.format`. For example, the statement `x='world'; print(f'hello {x}')` gets translated *at a syntax level* (injected into the lexer) to `x='world'; print('hello {}'.format(x))`. While this may lead to weird column results in tracebacks, it seemed like the fastest, most efficient, and *likely* most RAM-friendly option, despite being implemented under the hood with a completely separate `vstr_t`. Since [string concatenation of adjacent literals is implemented in the lexer](https://github.com/micropython/micropython/commit/534b7c368dc2af7720f3aaed0c936ef46d773957), two side effects emerge: - All strings with at least one f-string portion are concatenated into a single literal which *must* be run through `String.format()` wholesale, and: - Concatenation of a raw string with interpolation characters with an f-string will cause `IndexError`/`KeyError`, which is both different from CPython *and* different from the corner case mentioned in the PEP (which gave an example of the following:) ```python x = 10 y = 'hi' assert ('a' 'b' f'{x}' '{c}' f'str<{y:^4}>' 'd' 'e') == 'ab10{c}str< hi >de' ``` The above-linked commit detailed a pretty solid case for leaving string concatenation in the lexer rather than putting it in the parser, and undoing that decision would likely be disproportionately costly on resources for the sake of a probably-low-impact corner case. An alternative to become complaint with this corner case of the PEP would be to revert to string concatenation in the parser *only when an f-string is part of concatenation*, though I've done no investigation on the difficulty or costs of doing this. A decent set of tests is included. I've manually tested this on the `unix` port on Linux and on a Feather M4 Express (`atmel-samd`) and things seem sane.
Diffstat (limited to 'py/lexer.h')
-rw-r--r--py/lexer.h10
1 files changed, 10 insertions, 0 deletions
diff --git a/py/lexer.h b/py/lexer.h
index a29709107..7fe271e84 100644
--- a/py/lexer.h
+++ b/py/lexer.h
@@ -44,6 +44,12 @@ typedef enum _mp_token_kind_t {
MP_TOKEN_INVALID,
MP_TOKEN_DEDENT_MISMATCH,
MP_TOKEN_LONELY_STRING_OPEN,
+ 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,
MP_TOKEN_NEWLINE,
MP_TOKEN_INDENT,
@@ -150,6 +156,7 @@ typedef struct _mp_lexer_t {
mp_reader_t reader; // stream source
unichar chr0, chr1, chr2; // current cached characters from source
+ unichar chr3, chr4, chr5; // current cached characters from alt source
size_t line; // current source line
size_t column; // current source column
@@ -165,6 +172,9 @@ 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
+ vstr_t vstr_postfix; // postfix to apply to string
+ bool vstr_postfix_processing;
+ uint16_t vstr_postfix_idx;
} mp_lexer_t;
mp_lexer_t *mp_lexer_new(qstr src_name, mp_reader_t reader);