rapydscript-ng 0.8.2 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/bin/build.ts +29 -0
- package/local-agent.md +4 -0
- package/package.json +1 -1
- package/release/baselib-plain-pretty.js +85 -61
- package/release/baselib-plain-ugly.js +3 -3
- package/release/compiler.js +9661 -9540
- package/release/signatures.json +26 -26
- package/src/ast.pyj +169 -64
- package/src/baselib-builtins.pyj +35 -14
- package/src/baselib-containers.pyj +70 -40
- package/src/baselib-internal.pyj +20 -8
- package/src/baselib-itertools.pyj +6 -2
- package/src/baselib-str.pyj +74 -28
- package/src/errors.pyj +4 -1
- package/src/lib/aes.pyj +664 -35
- package/src/lib/elementmaker.pyj +105 -13
- package/src/lib/encodings.pyj +31 -7
- package/src/lib/gettext.pyj +6 -3
- package/src/lib/math.pyj +38 -22
- package/src/lib/pythonize.pyj +5 -3
- package/src/lib/random.pyj +13 -5
- package/src/lib/re.pyj +81 -18
- package/src/lib/traceback.pyj +42 -10
- package/src/lib/uuid.pyj +2 -1
- package/src/output/classes.pyj +70 -26
- package/src/output/codegen.pyj +92 -20
- package/src/output/comments.pyj +11 -4
- package/src/output/exceptions.pyj +17 -4
- package/src/output/functions.pyj +145 -26
- package/src/output/literals.pyj +7 -2
- package/src/output/loops.pyj +79 -23
- package/src/output/modules.pyj +41 -12
- package/src/output/operators.pyj +154 -31
- package/src/output/statements.pyj +38 -10
- package/src/output/stream.pyj +43 -12
- package/src/output/utils.pyj +10 -3
- package/src/parse.pyj +740 -259
- package/src/string_interpolation.pyj +4 -1
- package/src/tokenizer.pyj +314 -57
- package/src/unicode_aliases.pyj +4 -2
- package/src/utils.pyj +19 -5
- package/test/functions.pyj +8 -0
- package/test/generic.pyj +9 -0
- package/test/lsp.pyj +16 -0
- package/test/str.pyj +10 -0
- package/tools/cli.mjs +4 -3
- package/tools/compile.mjs +9 -3
- package/tools/compiler.mjs +0 -6
- package/tools/fmt.mjs +41 -2
- package/tools/lint-worker.mjs +107 -0
- package/tools/lint.mjs +134 -8
- package/tools/lsp.mjs +11 -1
- package/tree-sitter/grammar.js +27 -6
- package/tree-sitter/queries/highlights.scm +10 -0
- package/tree-sitter/src/grammar.json +130 -5
- package/tree-sitter/src/node-types.json +73 -0
- package/tree-sitter/src/parser.c +119589 -114993
- package/tree-sitter/src/scanner.c +275 -17
- package/tree-sitter/test/corpus/strings.txt +41 -5
|
@@ -9,8 +9,17 @@ enum TokenType {
|
|
|
9
9
|
INDENT,
|
|
10
10
|
DEDENT,
|
|
11
11
|
REGEX,
|
|
12
|
+
FSTRING_START,
|
|
13
|
+
FSTRING_CONTENT,
|
|
14
|
+
FSTRING_END,
|
|
12
15
|
};
|
|
13
16
|
|
|
17
|
+
// Per-nesting-level metadata for f-strings.
|
|
18
|
+
typedef struct {
|
|
19
|
+
char quote_char; // '"' or '\''
|
|
20
|
+
uint8_t triple; // 1 if triple-quoted, 0 otherwise
|
|
21
|
+
} FStringFrame;
|
|
22
|
+
|
|
14
23
|
// A simple growable stack of indentation widths. The bottom of the stack is
|
|
15
24
|
// always 0 (the module level indentation).
|
|
16
25
|
typedef struct {
|
|
@@ -24,6 +33,10 @@ typedef struct {
|
|
|
24
33
|
// (already consumed) newline.
|
|
25
34
|
uint16_t pending_indent;
|
|
26
35
|
uint8_t has_pending;
|
|
36
|
+
// Stack of open f-string frames.
|
|
37
|
+
uint32_t fstring_size;
|
|
38
|
+
uint32_t fstring_capacity;
|
|
39
|
+
FStringFrame *fstring_data;
|
|
27
40
|
} Scanner;
|
|
28
41
|
|
|
29
42
|
static void stack_push(Scanner *s, uint16_t value) {
|
|
@@ -46,6 +59,21 @@ static void stack_pop(Scanner *s) {
|
|
|
46
59
|
}
|
|
47
60
|
}
|
|
48
61
|
|
|
62
|
+
static void fstack_push(Scanner *s, FStringFrame frame) {
|
|
63
|
+
if (s->fstring_size >= s->fstring_capacity) {
|
|
64
|
+
uint32_t new_cap = s->fstring_capacity ? s->fstring_capacity * 2 : 4;
|
|
65
|
+
s->fstring_data = realloc(s->fstring_data, new_cap * sizeof(FStringFrame));
|
|
66
|
+
s->fstring_capacity = new_cap;
|
|
67
|
+
}
|
|
68
|
+
s->fstring_data[s->fstring_size++] = frame;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static void fstack_pop(Scanner *s) {
|
|
72
|
+
if (s->fstring_size > 0) {
|
|
73
|
+
s->fstring_size--;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
49
77
|
void *tree_sitter_rapydscript_external_scanner_create(void) {
|
|
50
78
|
Scanner *s = calloc(1, sizeof(Scanner));
|
|
51
79
|
stack_push(s, 0);
|
|
@@ -57,23 +85,43 @@ void tree_sitter_rapydscript_external_scanner_destroy(void *payload) {
|
|
|
57
85
|
if (s->data) {
|
|
58
86
|
free(s->data);
|
|
59
87
|
}
|
|
88
|
+
if (s->fstring_data) {
|
|
89
|
+
free(s->fstring_data);
|
|
90
|
+
}
|
|
60
91
|
free(s);
|
|
61
92
|
}
|
|
62
93
|
|
|
63
94
|
unsigned tree_sitter_rapydscript_external_scanner_serialize(void *payload, char *buffer) {
|
|
64
95
|
Scanner *s = (Scanner *)payload;
|
|
65
96
|
unsigned i = 0;
|
|
97
|
+
|
|
98
|
+
// has_pending flag and pending_indent value
|
|
66
99
|
buffer[i++] = (char)(s->has_pending ? 1 : 0);
|
|
67
100
|
buffer[i++] = (char)(s->pending_indent & 0xFF);
|
|
68
101
|
buffer[i++] = (char)((s->pending_indent >> 8) & 0xFF);
|
|
102
|
+
|
|
103
|
+
// Indent stack size (so we can distinguish it from fstring data)
|
|
104
|
+
uint16_t indent_count = (uint16_t)(s->size < 0xFFFF ? s->size : 0xFFFF);
|
|
105
|
+
buffer[i++] = (char)(indent_count & 0xFF);
|
|
106
|
+
buffer[i++] = (char)((indent_count >> 8) & 0xFF);
|
|
107
|
+
|
|
69
108
|
for (uint32_t k = 0; k < s->size; k++) {
|
|
70
|
-
if (i + 2 > TREE_SITTER_SERIALIZATION_BUFFER_SIZE)
|
|
71
|
-
break;
|
|
72
|
-
}
|
|
109
|
+
if (i + 2 > TREE_SITTER_SERIALIZATION_BUFFER_SIZE) break;
|
|
73
110
|
uint16_t v = s->data[k];
|
|
74
111
|
buffer[i++] = (char)(v & 0xFF);
|
|
75
112
|
buffer[i++] = (char)((v >> 8) & 0xFF);
|
|
76
113
|
}
|
|
114
|
+
|
|
115
|
+
// F-string stack
|
|
116
|
+
if (i + 1 <= TREE_SITTER_SERIALIZATION_BUFFER_SIZE) {
|
|
117
|
+
buffer[i++] = (char)(s->fstring_size & 0xFF);
|
|
118
|
+
for (uint32_t k = 0; k < s->fstring_size; k++) {
|
|
119
|
+
if (i + 2 > TREE_SITTER_SERIALIZATION_BUFFER_SIZE) break;
|
|
120
|
+
buffer[i++] = s->fstring_data[k].quote_char;
|
|
121
|
+
buffer[i++] = s->fstring_data[k].triple;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
77
125
|
return i;
|
|
78
126
|
}
|
|
79
127
|
|
|
@@ -82,13 +130,22 @@ void tree_sitter_rapydscript_external_scanner_deserialize(void *payload, const c
|
|
|
82
130
|
s->size = 0;
|
|
83
131
|
s->has_pending = 0;
|
|
84
132
|
s->pending_indent = 0;
|
|
133
|
+
s->fstring_size = 0;
|
|
85
134
|
unsigned i = 0;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
s
|
|
89
|
-
|
|
135
|
+
|
|
136
|
+
if (length < 5) {
|
|
137
|
+
stack_push(s, 0);
|
|
138
|
+
return;
|
|
90
139
|
}
|
|
91
|
-
|
|
140
|
+
|
|
141
|
+
s->has_pending = (uint8_t)buffer[i++];
|
|
142
|
+
s->pending_indent = (uint8_t)buffer[i] | ((uint16_t)(uint8_t)buffer[i + 1] << 8);
|
|
143
|
+
i += 2;
|
|
144
|
+
|
|
145
|
+
uint16_t indent_count = (uint8_t)buffer[i] | ((uint16_t)(uint8_t)buffer[i + 1] << 8);
|
|
146
|
+
i += 2;
|
|
147
|
+
|
|
148
|
+
for (uint16_t k = 0; k < indent_count && i + 2 <= length; k++) {
|
|
92
149
|
uint16_t v = (uint8_t)buffer[i] | ((uint16_t)(uint8_t)buffer[i + 1] << 8);
|
|
93
150
|
i += 2;
|
|
94
151
|
stack_push(s, v);
|
|
@@ -96,6 +153,17 @@ void tree_sitter_rapydscript_external_scanner_deserialize(void *payload, const c
|
|
|
96
153
|
if (s->size == 0) {
|
|
97
154
|
stack_push(s, 0);
|
|
98
155
|
}
|
|
156
|
+
|
|
157
|
+
// F-string stack
|
|
158
|
+
if (i < length) {
|
|
159
|
+
uint8_t fcount = (uint8_t)buffer[i++];
|
|
160
|
+
for (uint8_t k = 0; k < fcount && i + 2 <= length; k++) {
|
|
161
|
+
FStringFrame frame;
|
|
162
|
+
frame.quote_char = buffer[i++];
|
|
163
|
+
frame.triple = (uint8_t)buffer[i++];
|
|
164
|
+
fstack_push(s, frame);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
99
167
|
}
|
|
100
168
|
|
|
101
169
|
static void advance(TSLexer *lexer) { lexer->advance(lexer, false); }
|
|
@@ -178,10 +246,196 @@ static bool scan_regex(TSLexer *lexer) {
|
|
|
178
246
|
return true;
|
|
179
247
|
}
|
|
180
248
|
|
|
249
|
+
// Scan the opening of an f-string: optional modifier chars containing at least
|
|
250
|
+
// one f/F (but no v/V), followed by 1 or 3 quote characters.
|
|
251
|
+
static bool scan_fstring_start(TSLexer *lexer, Scanner *s) {
|
|
252
|
+
// Skip leading whitespace (the extras rule handles it for most tokens, but
|
|
253
|
+
// we do it here explicitly to match the REGEX scanner's behaviour).
|
|
254
|
+
while (lexer->lookahead == ' ' || lexer->lookahead == '\t' ||
|
|
255
|
+
lexer->lookahead == '\n' || lexer->lookahead == '\r' ||
|
|
256
|
+
lexer->lookahead == '\f') {
|
|
257
|
+
skip(lexer);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
bool has_f = false;
|
|
261
|
+
int mod_count = 0;
|
|
262
|
+
|
|
263
|
+
while (mod_count < 6) {
|
|
264
|
+
int32_t c = lexer->lookahead;
|
|
265
|
+
if (c == 'f' || c == 'F') {
|
|
266
|
+
has_f = true;
|
|
267
|
+
advance(lexer);
|
|
268
|
+
mod_count++;
|
|
269
|
+
} else if (c == 'r' || c == 'R' || c == 'b' || c == 'B' ||
|
|
270
|
+
c == 'u' || c == 'U') {
|
|
271
|
+
advance(lexer);
|
|
272
|
+
mod_count++;
|
|
273
|
+
} else if (c == 'v' || c == 'V') {
|
|
274
|
+
// Verbatim string — not an f-string.
|
|
275
|
+
return false;
|
|
276
|
+
} else {
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (!has_f) return false;
|
|
282
|
+
|
|
283
|
+
int32_t q = lexer->lookahead;
|
|
284
|
+
if (q != '"' && q != '\'') return false;
|
|
285
|
+
|
|
286
|
+
advance(lexer); // consume first quote
|
|
287
|
+
lexer->mark_end(lexer); // tentative token end after one quote
|
|
288
|
+
|
|
289
|
+
bool triple = false;
|
|
290
|
+
if (lexer->lookahead == q) {
|
|
291
|
+
advance(lexer); // peek at second quote
|
|
292
|
+
if (lexer->lookahead == q) {
|
|
293
|
+
advance(lexer); // consume third quote
|
|
294
|
+
triple = true;
|
|
295
|
+
lexer->mark_end(lexer); // extend token to include triple quote
|
|
296
|
+
}
|
|
297
|
+
// If only two quotes (f''), mark_end is still after the first quote;
|
|
298
|
+
// the second quote will be scanned as FSTRING_END immediately.
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
FStringFrame frame = {(char)q, triple ? 1 : 0};
|
|
302
|
+
fstack_push(s, frame);
|
|
303
|
+
|
|
304
|
+
lexer->result_symbol = FSTRING_START;
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Scan literal content inside an f-string up to (but not including) the next
|
|
309
|
+
// interpolation opener '{' or the closing quote sequence. Returns false if
|
|
310
|
+
// there is no content (e.g. we are already at '{' or the closing quote).
|
|
311
|
+
static bool scan_fstring_content(TSLexer *lexer, Scanner *s) {
|
|
312
|
+
if (s->fstring_size == 0) return false;
|
|
313
|
+
FStringFrame frame = s->fstring_data[s->fstring_size - 1];
|
|
314
|
+
int32_t q = (int32_t)(unsigned char)frame.quote_char;
|
|
315
|
+
bool triple = frame.triple != 0;
|
|
316
|
+
|
|
317
|
+
bool has_content = false;
|
|
318
|
+
|
|
319
|
+
while (true) {
|
|
320
|
+
int32_t c = lexer->lookahead;
|
|
321
|
+
|
|
322
|
+
if (c == 0) break; // EOF
|
|
323
|
+
|
|
324
|
+
// Unterminated single-line f-string.
|
|
325
|
+
if (c == '\n' && !triple) break;
|
|
326
|
+
|
|
327
|
+
if (c == q) {
|
|
328
|
+
if (triple) {
|
|
329
|
+
// Check for closing triple quote.
|
|
330
|
+
advance(lexer);
|
|
331
|
+
if (lexer->lookahead == q) {
|
|
332
|
+
advance(lexer);
|
|
333
|
+
if (lexer->lookahead == q) {
|
|
334
|
+
// Found closing """/'''. Stop before it.
|
|
335
|
+
// mark_end was last set before we advanced into the quotes.
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
// Two matching quotes but not three: include both in content.
|
|
339
|
+
lexer->mark_end(lexer);
|
|
340
|
+
has_content = true;
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
// Single matching quote in a triple-quoted string: part of content.
|
|
344
|
+
lexer->mark_end(lexer);
|
|
345
|
+
has_content = true;
|
|
346
|
+
continue;
|
|
347
|
+
} else {
|
|
348
|
+
// Single-quoted: closing quote. Stop before it.
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (c == '{') {
|
|
354
|
+
advance(lexer); // tentatively consume
|
|
355
|
+
if (lexer->lookahead == '{') {
|
|
356
|
+
// '{{' is an escaped brace — include both in content.
|
|
357
|
+
advance(lexer);
|
|
358
|
+
lexer->mark_end(lexer);
|
|
359
|
+
has_content = true;
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
// Lone '{' starts an interpolation. The last mark_end is before
|
|
363
|
+
// this '{', so returning false here leaves the '{' unconsumed.
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (c == '}') {
|
|
368
|
+
advance(lexer); // tentatively consume
|
|
369
|
+
if (lexer->lookahead == '}') {
|
|
370
|
+
// '}}' is an escaped brace — include both in content.
|
|
371
|
+
advance(lexer);
|
|
372
|
+
lexer->mark_end(lexer);
|
|
373
|
+
has_content = true;
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
// Lone '}' — either a format-spec terminator or a syntax error.
|
|
377
|
+
// Stop before it.
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (c == '\\') {
|
|
382
|
+
advance(lexer); // consume backslash
|
|
383
|
+
if (lexer->lookahead != 0) {
|
|
384
|
+
advance(lexer); // consume escaped character
|
|
385
|
+
}
|
|
386
|
+
lexer->mark_end(lexer);
|
|
387
|
+
has_content = true;
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
advance(lexer);
|
|
392
|
+
lexer->mark_end(lexer);
|
|
393
|
+
has_content = true;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (!has_content) return false;
|
|
397
|
+
|
|
398
|
+
lexer->result_symbol = FSTRING_CONTENT;
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Scan the closing quote sequence of an f-string and pop the frame.
|
|
403
|
+
static bool scan_fstring_end(TSLexer *lexer, Scanner *s) {
|
|
404
|
+
if (s->fstring_size == 0) return false;
|
|
405
|
+
FStringFrame frame = s->fstring_data[s->fstring_size - 1];
|
|
406
|
+
int32_t q = (int32_t)(unsigned char)frame.quote_char;
|
|
407
|
+
|
|
408
|
+
if (lexer->lookahead != q) return false;
|
|
409
|
+
|
|
410
|
+
if (frame.triple) {
|
|
411
|
+
advance(lexer);
|
|
412
|
+
if (lexer->lookahead != q) return false;
|
|
413
|
+
advance(lexer);
|
|
414
|
+
if (lexer->lookahead != q) return false;
|
|
415
|
+
advance(lexer);
|
|
416
|
+
} else {
|
|
417
|
+
advance(lexer);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
fstack_pop(s);
|
|
421
|
+
lexer->mark_end(lexer);
|
|
422
|
+
lexer->result_symbol = FSTRING_END;
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
|
|
181
426
|
bool tree_sitter_rapydscript_external_scanner_scan(void *payload, TSLexer *lexer,
|
|
182
427
|
const bool *valid_symbols) {
|
|
183
428
|
Scanner *scanner = (Scanner *)payload;
|
|
184
429
|
|
|
430
|
+
// F-string content must be handled before any whitespace skipping because
|
|
431
|
+
// the whitespace is part of the string's content.
|
|
432
|
+
if (valid_symbols[FSTRING_CONTENT]) {
|
|
433
|
+
if (scan_fstring_content(lexer, scanner)) return true;
|
|
434
|
+
}
|
|
435
|
+
if (valid_symbols[FSTRING_END]) {
|
|
436
|
+
if (scan_fstring_end(lexer, scanner)) return true;
|
|
437
|
+
}
|
|
438
|
+
|
|
185
439
|
// 1. Reconcile any pending indentation left over from a previously emitted
|
|
186
440
|
// NEWLINE/DEDENT. This is how we emit several DEDENT tokens for a single
|
|
187
441
|
// line without re-reading the (already consumed) newline. Only DEDENT is
|
|
@@ -200,25 +454,29 @@ bool tree_sitter_rapydscript_external_scanner_scan(void *payload, TSLexer *lexer
|
|
|
200
454
|
scanner->has_pending = 0;
|
|
201
455
|
}
|
|
202
456
|
|
|
203
|
-
// 2. Regular expression literals
|
|
204
|
-
// begin, so '/'
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
//
|
|
209
|
-
//
|
|
457
|
+
// 2. Regular expression literals and f-string starts are both only valid
|
|
458
|
+
// where a primary expression may begin, so neither '/' (division) nor an
|
|
459
|
+
// f-modifier identifier causes ambiguity in other positions.
|
|
460
|
+
if ((valid_symbols[REGEX] || valid_symbols[FSTRING_START]) &&
|
|
461
|
+
!valid_symbols[NEWLINE] && !valid_symbols[INDENT] && !valid_symbols[DEDENT]) {
|
|
462
|
+
// Skip surrounding whitespace, including newlines: a literal may appear
|
|
463
|
+
// on its own line, e.g. as an argument following a preceding multi-line
|
|
464
|
+
// one (`foo(\n def(): ...\n ,\n /re/\n)`).
|
|
210
465
|
while (lexer->lookahead == ' ' || lexer->lookahead == '\t' ||
|
|
211
466
|
lexer->lookahead == '\n' || lexer->lookahead == '\r' ||
|
|
212
467
|
lexer->lookahead == '\f') {
|
|
213
468
|
skip(lexer);
|
|
214
469
|
}
|
|
215
|
-
if (lexer->lookahead == '/') {
|
|
470
|
+
if (valid_symbols[REGEX] && lexer->lookahead == '/') {
|
|
216
471
|
return scan_regex(lexer);
|
|
217
472
|
}
|
|
473
|
+
if (valid_symbols[FSTRING_START]) {
|
|
474
|
+
return scan_fstring_start(lexer, scanner);
|
|
475
|
+
}
|
|
218
476
|
return false;
|
|
219
477
|
}
|
|
220
478
|
|
|
221
|
-
//
|
|
479
|
+
// 4. Indentation / newline handling.
|
|
222
480
|
bool found_end_of_line = false;
|
|
223
481
|
uint32_t indent_length = 0;
|
|
224
482
|
|
|
@@ -33,13 +33,11 @@ line"""
|
|
|
33
33
|
right: (string)))
|
|
34
34
|
|
|
35
35
|
==================
|
|
36
|
-
string modifiers r u
|
|
36
|
+
string modifiers r u
|
|
37
37
|
==================
|
|
38
38
|
|
|
39
39
|
a = r'x'
|
|
40
40
|
b = u'y'
|
|
41
|
-
c = f'val {x}'
|
|
42
|
-
|
|
43
41
|
|
|
44
42
|
---
|
|
45
43
|
|
|
@@ -51,11 +49,49 @@ c = f'val {x}'
|
|
|
51
49
|
(assignment
|
|
52
50
|
left: (pattern
|
|
53
51
|
(identifier))
|
|
54
|
-
right: (string))
|
|
52
|
+
right: (string)))
|
|
53
|
+
|
|
54
|
+
==================
|
|
55
|
+
f-string basic interpolation
|
|
56
|
+
==================
|
|
57
|
+
|
|
58
|
+
c = f'val {x}'
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
(module
|
|
55
63
|
(assignment
|
|
56
64
|
left: (pattern
|
|
57
65
|
(identifier))
|
|
58
|
-
right: (
|
|
66
|
+
right: (f_string
|
|
67
|
+
(fstring_start)
|
|
68
|
+
(fstring_content)
|
|
69
|
+
(interpolation
|
|
70
|
+
expression: (identifier))
|
|
71
|
+
(fstring_end))))
|
|
72
|
+
|
|
73
|
+
==================
|
|
74
|
+
f-string expression
|
|
75
|
+
==================
|
|
76
|
+
|
|
77
|
+
d = f'{obj.method(arg)}'
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
(module
|
|
82
|
+
(assignment
|
|
83
|
+
left: (pattern
|
|
84
|
+
(identifier))
|
|
85
|
+
right: (f_string
|
|
86
|
+
(fstring_start)
|
|
87
|
+
(interpolation
|
|
88
|
+
expression: (call
|
|
89
|
+
function: (attribute
|
|
90
|
+
object: (identifier)
|
|
91
|
+
attribute: (identifier))
|
|
92
|
+
arguments: (argument_list
|
|
93
|
+
(identifier))))
|
|
94
|
+
(fstring_end))))
|
|
59
95
|
|
|
60
96
|
==================
|
|
61
97
|
verbatim javascript literal
|