rapydscript-ng 0.8.1 → 0.8.3
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 +16 -0
- package/local-agent.md +4 -0
- package/package.json +1 -1
- package/release/compiler.js +5125 -5115
- package/release/signatures.json +6 -6
- package/src/ast.pyj +18 -25
- package/src/output/functions.pyj +111 -13
- package/src/output/loops.pyj +31 -9
- package/src/tokenizer.pyj +17 -5
- package/test/str.pyj +10 -0
- package/tools/cli.mjs +4 -3
- package/tools/compile.mjs +9 -3
- package/tools/fmt.mjs +7 -0
- package/tools/lint-worker.mjs +101 -0
- package/tools/lint.mjs +127 -8
- 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
package/tools/lint.mjs
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import fs from 'fs';
|
|
9
9
|
import path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
10
11
|
import { read_config } from './ini.mjs';
|
|
11
12
|
import * as utils from './utils.mjs';
|
|
12
13
|
|
|
@@ -663,11 +664,12 @@ function cli_vim_report(r) {
|
|
|
663
664
|
|
|
664
665
|
var ini_cache = {};
|
|
665
666
|
|
|
666
|
-
|
|
667
|
-
if (has_prop(ini_cache, toplevel_dir))
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
667
|
+
function get_ini(toplevel_dir) {
|
|
668
|
+
if (!has_prop(ini_cache, toplevel_dir)) {
|
|
669
|
+
// Store the promise so concurrent callers for the same dir share one read.
|
|
670
|
+
ini_cache[toplevel_dir] = read_config(toplevel_dir).then(function(r) { return r.rapydscript || {}; });
|
|
671
|
+
}
|
|
672
|
+
return ini_cache[toplevel_dir];
|
|
671
673
|
}
|
|
672
674
|
|
|
673
675
|
export async function cli(argv, base_path, src_path, lib_path) {
|
|
@@ -716,7 +718,52 @@ export async function cli(argv, base_path, src_path, lib_path) {
|
|
|
716
718
|
return x === '-' ? argv.stdin_filename : x;
|
|
717
719
|
}
|
|
718
720
|
|
|
719
|
-
for
|
|
721
|
+
// reportcb used for final output (same logic as inside lint_code)
|
|
722
|
+
var reportcb = {'json': cli_json_report, 'vim': cli_vim_report, 'undef': cli_undef_report}[argv.errorformat] || cli_report;
|
|
723
|
+
|
|
724
|
+
// Expand directories to .pyj files recursively, stat-ing entries in parallel.
|
|
725
|
+
async function expand_dirs(inputs, toplevel) {
|
|
726
|
+
// Stat all entries concurrently, then process results in sorted order.
|
|
727
|
+
var entries = await Promise.all(inputs.map(async function(f) {
|
|
728
|
+
if (f === '-') return { f, type: 'stdin' };
|
|
729
|
+
var st;
|
|
730
|
+
try { st = await fs.promises.lstat(f); }
|
|
731
|
+
catch (e) {
|
|
732
|
+
if (toplevel) { console.error("ERROR: can't access: " + f); process.exit(1); }
|
|
733
|
+
return null;
|
|
734
|
+
}
|
|
735
|
+
if (st.isDirectory()) return { f, type: 'dir' };
|
|
736
|
+
if (st.isFile()) return { f, type: 'file' };
|
|
737
|
+
return null;
|
|
738
|
+
}));
|
|
739
|
+
|
|
740
|
+
var result = [];
|
|
741
|
+
for (var i = 0; i < entries.length; i++) {
|
|
742
|
+
var entry = entries[i];
|
|
743
|
+
if (!entry) continue;
|
|
744
|
+
if (entry.type === 'stdin') { result.push(entry.f); continue; }
|
|
745
|
+
if (entry.type === 'file') { result.push(entry.f); continue; }
|
|
746
|
+
// Directory: read children and recurse
|
|
747
|
+
var children;
|
|
748
|
+
try {
|
|
749
|
+
children = (await fs.promises.readdir(entry.f)).sort().map(function(x) { return path.join(entry.f, x); });
|
|
750
|
+
} catch(e) {
|
|
751
|
+
if (toplevel) { console.error("ERROR: can't read directory: " + entry.f); process.exit(1); }
|
|
752
|
+
continue;
|
|
753
|
+
}
|
|
754
|
+
var sub = await expand_dirs(children, false);
|
|
755
|
+
sub.forEach(function(x) { if (x.endsWith('.pyj')) result.push(x); });
|
|
756
|
+
}
|
|
757
|
+
return result;
|
|
758
|
+
}
|
|
759
|
+
if (files.length) files = await expand_dirs(files, true);
|
|
760
|
+
|
|
761
|
+
var all_files = files.length ? files : [null];
|
|
762
|
+
var has_stdin = all_files.indexOf('-') !== -1 || all_files.indexOf(null) !== -1;
|
|
763
|
+
|
|
764
|
+
// Process a single file and return its messages without printing them.
|
|
765
|
+
// Used when the worker pool is not active (stdin, or too few files).
|
|
766
|
+
async function process_file(filename) {
|
|
720
767
|
var code;
|
|
721
768
|
try {
|
|
722
769
|
code = await read_whole_file(filename === '-' ? null : filename);
|
|
@@ -747,10 +794,82 @@ export async function cli(argv, base_path, src_path, lib_path) {
|
|
|
747
794
|
}
|
|
748
795
|
});
|
|
749
796
|
|
|
750
|
-
|
|
751
|
-
|
|
797
|
+
var lines = code.split('\n');
|
|
798
|
+
var msgs = await lint_code(code, {filename:path_for_filename(filename || '-'), builtins:final_builtins, noqa:final_noqa, errorformat:argv.errorformat || false, report:function() {}});
|
|
799
|
+
msgs.forEach(function(msg) { msg.code_lines = lines; });
|
|
800
|
+
return msgs;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
var all_results;
|
|
804
|
+
// Use a worker pool when there are enough files to justify the overhead.
|
|
805
|
+
// Workers own their own compiler instance so parsing runs in parallel across CPU cores.
|
|
806
|
+
var WORKER_THRESHOLD = 4;
|
|
807
|
+
|
|
808
|
+
if (!has_stdin && all_files.length >= WORKER_THRESHOLD) {
|
|
809
|
+
var { Worker } = await import('worker_threads');
|
|
810
|
+
var os_mod = await import('os');
|
|
811
|
+
|
|
812
|
+
var worker_path = path.join(path.dirname(fileURLToPath(import.meta.url)), 'lint-worker.mjs');
|
|
813
|
+
var num_workers = Math.min(os_mod.cpus().length, all_files.length, 8);
|
|
814
|
+
|
|
815
|
+
// Dispatch files to workers dynamically so fast files don't leave workers idle.
|
|
816
|
+
var ordered_results = new Array(all_files.length);
|
|
817
|
+
var next_idx = 0;
|
|
818
|
+
var pending = all_files.length;
|
|
819
|
+
var done_resolve;
|
|
820
|
+
var done_promise = new Promise(function(res) { done_resolve = res; });
|
|
821
|
+
if (pending === 0) done_resolve();
|
|
822
|
+
|
|
823
|
+
function dispatch(worker) {
|
|
824
|
+
if (next_idx >= all_files.length) return;
|
|
825
|
+
var idx = next_idx++;
|
|
826
|
+
worker.postMessage({
|
|
827
|
+
type: 'lint',
|
|
828
|
+
id: idx,
|
|
829
|
+
filename: all_files[idx],
|
|
830
|
+
base_builtins: builtins,
|
|
831
|
+
base_noqa: noqa,
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// Create workers and dispatch eagerly as each becomes ready rather than
|
|
836
|
+
// waiting for all workers before starting any work.
|
|
837
|
+
var worker_promises = Array.from({length: num_workers}, function() {
|
|
838
|
+
return new Promise(function(resolve) {
|
|
839
|
+
var w = new Worker(worker_path);
|
|
840
|
+
w.once('message', function(m) {
|
|
841
|
+
if (m.type !== 'ready') return;
|
|
842
|
+
w.on('message', function(msg) {
|
|
843
|
+
if (msg.type === 'result') {
|
|
844
|
+
ordered_results[msg.id] = msg.messages;
|
|
845
|
+
} else if (msg.type === 'error') {
|
|
846
|
+
console.error("ERROR: lint failed for " + all_files[msg.id] + ": " + msg.error);
|
|
847
|
+
ordered_results[msg.id] = [];
|
|
848
|
+
}
|
|
849
|
+
pending--;
|
|
850
|
+
if (pending === 0) { done_resolve(); return; }
|
|
851
|
+
dispatch(w);
|
|
852
|
+
});
|
|
853
|
+
dispatch(w);
|
|
854
|
+
resolve(w);
|
|
855
|
+
});
|
|
856
|
+
});
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
await done_promise;
|
|
860
|
+
var workers = await Promise.all(worker_promises);
|
|
861
|
+
workers.forEach(function(w) { w.terminate(); });
|
|
862
|
+
all_results = ordered_results;
|
|
863
|
+
} else {
|
|
864
|
+
// Single-threaded path: process concurrently on the main thread.
|
|
865
|
+
all_results = await Promise.all(all_files.map(process_file));
|
|
752
866
|
}
|
|
753
867
|
|
|
868
|
+
var all_messages = [];
|
|
869
|
+
all_results.forEach(function(msgs) { Array.prototype.push.apply(all_messages, msgs); });
|
|
870
|
+
if (all_messages.length) all_ok = false;
|
|
871
|
+
all_messages.forEach(function(msg, i) { reportcb(msg, i, all_messages); });
|
|
872
|
+
|
|
754
873
|
process.exit((all_ok) ? 0 : 1);
|
|
755
874
|
}
|
|
756
875
|
// }}}
|
package/tree-sitter/grammar.js
CHANGED
|
@@ -57,6 +57,9 @@ module.exports = grammar({
|
|
|
57
57
|
$._indent,
|
|
58
58
|
$._dedent,
|
|
59
59
|
$.regex,
|
|
60
|
+
$.fstring_start,
|
|
61
|
+
$.fstring_content,
|
|
62
|
+
$.fstring_end,
|
|
60
63
|
],
|
|
61
64
|
|
|
62
65
|
extras: $ => [
|
|
@@ -589,6 +592,7 @@ module.exports = grammar({
|
|
|
589
592
|
$.none,
|
|
590
593
|
$.number,
|
|
591
594
|
$.string,
|
|
595
|
+
$.f_string,
|
|
592
596
|
$.concatenated_string,
|
|
593
597
|
$.regex,
|
|
594
598
|
$.verbatim,
|
|
@@ -924,20 +928,37 @@ module.exports = grammar({
|
|
|
924
928
|
// ---- atoms / literals ------------------------------------------------
|
|
925
929
|
|
|
926
930
|
concatenated_string: $ => prec.left(seq(
|
|
927
|
-
$.string,
|
|
928
|
-
repeat1($.string),
|
|
931
|
+
choice($.string, $.f_string),
|
|
932
|
+
repeat1(choice($.string, $.f_string)),
|
|
929
933
|
)),
|
|
930
934
|
|
|
935
|
+
f_string: $ => seq(
|
|
936
|
+
$.fstring_start,
|
|
937
|
+
repeat(choice(
|
|
938
|
+
$.fstring_content,
|
|
939
|
+
$.interpolation,
|
|
940
|
+
)),
|
|
941
|
+
$.fstring_end,
|
|
942
|
+
),
|
|
943
|
+
|
|
944
|
+
interpolation: $ => seq(
|
|
945
|
+
'{',
|
|
946
|
+
field('expression', $._expression),
|
|
947
|
+
optional(field('type_conversion', /![rsa]/)),
|
|
948
|
+
optional(seq(':', field('format_spec', $.fstring_content))),
|
|
949
|
+
'}',
|
|
950
|
+
),
|
|
951
|
+
|
|
931
952
|
this: _ => 'this',
|
|
932
953
|
true: _ => 'True',
|
|
933
954
|
false: _ => 'False',
|
|
934
955
|
none: _ => 'None',
|
|
935
956
|
|
|
936
|
-
// string with optional r/u/
|
|
937
|
-
// verbatim JavaScript literal
|
|
938
|
-
//
|
|
957
|
+
// string with optional r/u/b modifiers (the `v` modifier denotes a
|
|
958
|
+
// verbatim JavaScript literal, handled separately; the `f` modifier
|
|
959
|
+
// produces an f_string with structured interpolation nodes).
|
|
939
960
|
string: _ => token(seq(
|
|
940
|
-
optional(/[
|
|
961
|
+
optional(/[rRuUbB]+/),
|
|
941
962
|
choice(
|
|
942
963
|
seq('"""', repeat(choice(/[^"\\]/, /\\(.|\n)/, /"[^"]/, /""[^"]/)), '"""'),
|
|
943
964
|
seq("'''", repeat(choice(/[^'\\]/, /\\(.|\n)/, /'[^']/, /''[^']/)), "'''"),
|
|
@@ -21,6 +21,16 @@
|
|
|
21
21
|
(regex) @string.regexp
|
|
22
22
|
(number) @number
|
|
23
23
|
|
|
24
|
+
; f-strings: delimiters and literal content get @string, interpolation braces
|
|
25
|
+
; get @punctuation.special so they stand out, and the expression inside is
|
|
26
|
+
; transparent — its child nodes (identifiers, calls, etc.) are highlighted by
|
|
27
|
+
; the normal rules below.
|
|
28
|
+
(fstring_start) @string
|
|
29
|
+
(fstring_content) @string
|
|
30
|
+
(fstring_end) @string
|
|
31
|
+
(interpolation "{" @punctuation.special)
|
|
32
|
+
(interpolation "}" @punctuation.special)
|
|
33
|
+
|
|
24
34
|
(true) @boolean
|
|
25
35
|
(false) @boolean
|
|
26
36
|
(none) @constant.builtin
|
|
@@ -2474,6 +2474,10 @@
|
|
|
2474
2474
|
"type": "SYMBOL",
|
|
2475
2475
|
"name": "string"
|
|
2476
2476
|
},
|
|
2477
|
+
{
|
|
2478
|
+
"type": "SYMBOL",
|
|
2479
|
+
"name": "f_string"
|
|
2480
|
+
},
|
|
2477
2481
|
{
|
|
2478
2482
|
"type": "SYMBOL",
|
|
2479
2483
|
"name": "concatenated_string"
|
|
@@ -4493,19 +4497,128 @@
|
|
|
4493
4497
|
"type": "SEQ",
|
|
4494
4498
|
"members": [
|
|
4495
4499
|
{
|
|
4496
|
-
"type": "
|
|
4497
|
-
"
|
|
4500
|
+
"type": "CHOICE",
|
|
4501
|
+
"members": [
|
|
4502
|
+
{
|
|
4503
|
+
"type": "SYMBOL",
|
|
4504
|
+
"name": "string"
|
|
4505
|
+
},
|
|
4506
|
+
{
|
|
4507
|
+
"type": "SYMBOL",
|
|
4508
|
+
"name": "f_string"
|
|
4509
|
+
}
|
|
4510
|
+
]
|
|
4498
4511
|
},
|
|
4499
4512
|
{
|
|
4500
4513
|
"type": "REPEAT1",
|
|
4501
4514
|
"content": {
|
|
4502
|
-
"type": "
|
|
4503
|
-
"
|
|
4515
|
+
"type": "CHOICE",
|
|
4516
|
+
"members": [
|
|
4517
|
+
{
|
|
4518
|
+
"type": "SYMBOL",
|
|
4519
|
+
"name": "string"
|
|
4520
|
+
},
|
|
4521
|
+
{
|
|
4522
|
+
"type": "SYMBOL",
|
|
4523
|
+
"name": "f_string"
|
|
4524
|
+
}
|
|
4525
|
+
]
|
|
4504
4526
|
}
|
|
4505
4527
|
}
|
|
4506
4528
|
]
|
|
4507
4529
|
}
|
|
4508
4530
|
},
|
|
4531
|
+
"f_string": {
|
|
4532
|
+
"type": "SEQ",
|
|
4533
|
+
"members": [
|
|
4534
|
+
{
|
|
4535
|
+
"type": "SYMBOL",
|
|
4536
|
+
"name": "fstring_start"
|
|
4537
|
+
},
|
|
4538
|
+
{
|
|
4539
|
+
"type": "REPEAT",
|
|
4540
|
+
"content": {
|
|
4541
|
+
"type": "CHOICE",
|
|
4542
|
+
"members": [
|
|
4543
|
+
{
|
|
4544
|
+
"type": "SYMBOL",
|
|
4545
|
+
"name": "fstring_content"
|
|
4546
|
+
},
|
|
4547
|
+
{
|
|
4548
|
+
"type": "SYMBOL",
|
|
4549
|
+
"name": "interpolation"
|
|
4550
|
+
}
|
|
4551
|
+
]
|
|
4552
|
+
}
|
|
4553
|
+
},
|
|
4554
|
+
{
|
|
4555
|
+
"type": "SYMBOL",
|
|
4556
|
+
"name": "fstring_end"
|
|
4557
|
+
}
|
|
4558
|
+
]
|
|
4559
|
+
},
|
|
4560
|
+
"interpolation": {
|
|
4561
|
+
"type": "SEQ",
|
|
4562
|
+
"members": [
|
|
4563
|
+
{
|
|
4564
|
+
"type": "STRING",
|
|
4565
|
+
"value": "{"
|
|
4566
|
+
},
|
|
4567
|
+
{
|
|
4568
|
+
"type": "FIELD",
|
|
4569
|
+
"name": "expression",
|
|
4570
|
+
"content": {
|
|
4571
|
+
"type": "SYMBOL",
|
|
4572
|
+
"name": "_expression"
|
|
4573
|
+
}
|
|
4574
|
+
},
|
|
4575
|
+
{
|
|
4576
|
+
"type": "CHOICE",
|
|
4577
|
+
"members": [
|
|
4578
|
+
{
|
|
4579
|
+
"type": "FIELD",
|
|
4580
|
+
"name": "type_conversion",
|
|
4581
|
+
"content": {
|
|
4582
|
+
"type": "PATTERN",
|
|
4583
|
+
"value": "![rsa]"
|
|
4584
|
+
}
|
|
4585
|
+
},
|
|
4586
|
+
{
|
|
4587
|
+
"type": "BLANK"
|
|
4588
|
+
}
|
|
4589
|
+
]
|
|
4590
|
+
},
|
|
4591
|
+
{
|
|
4592
|
+
"type": "CHOICE",
|
|
4593
|
+
"members": [
|
|
4594
|
+
{
|
|
4595
|
+
"type": "SEQ",
|
|
4596
|
+
"members": [
|
|
4597
|
+
{
|
|
4598
|
+
"type": "STRING",
|
|
4599
|
+
"value": ":"
|
|
4600
|
+
},
|
|
4601
|
+
{
|
|
4602
|
+
"type": "FIELD",
|
|
4603
|
+
"name": "format_spec",
|
|
4604
|
+
"content": {
|
|
4605
|
+
"type": "SYMBOL",
|
|
4606
|
+
"name": "fstring_content"
|
|
4607
|
+
}
|
|
4608
|
+
}
|
|
4609
|
+
]
|
|
4610
|
+
},
|
|
4611
|
+
{
|
|
4612
|
+
"type": "BLANK"
|
|
4613
|
+
}
|
|
4614
|
+
]
|
|
4615
|
+
},
|
|
4616
|
+
{
|
|
4617
|
+
"type": "STRING",
|
|
4618
|
+
"value": "}"
|
|
4619
|
+
}
|
|
4620
|
+
]
|
|
4621
|
+
},
|
|
4509
4622
|
"this": {
|
|
4510
4623
|
"type": "STRING",
|
|
4511
4624
|
"value": "this"
|
|
@@ -4532,7 +4645,7 @@
|
|
|
4532
4645
|
"members": [
|
|
4533
4646
|
{
|
|
4534
4647
|
"type": "PATTERN",
|
|
4535
|
-
"value": "[
|
|
4648
|
+
"value": "[rRuUbB]+"
|
|
4536
4649
|
},
|
|
4537
4650
|
{
|
|
4538
4651
|
"type": "BLANK"
|
|
@@ -4963,6 +5076,18 @@
|
|
|
4963
5076
|
{
|
|
4964
5077
|
"type": "SYMBOL",
|
|
4965
5078
|
"name": "regex"
|
|
5079
|
+
},
|
|
5080
|
+
{
|
|
5081
|
+
"type": "SYMBOL",
|
|
5082
|
+
"name": "fstring_start"
|
|
5083
|
+
},
|
|
5084
|
+
{
|
|
5085
|
+
"type": "SYMBOL",
|
|
5086
|
+
"name": "fstring_content"
|
|
5087
|
+
},
|
|
5088
|
+
{
|
|
5089
|
+
"type": "SYMBOL",
|
|
5090
|
+
"name": "fstring_end"
|
|
4966
5091
|
}
|
|
4967
5092
|
],
|
|
4968
5093
|
"inline": [],
|
|
@@ -131,6 +131,10 @@
|
|
|
131
131
|
"type": "existential",
|
|
132
132
|
"named": true
|
|
133
133
|
},
|
|
134
|
+
{
|
|
135
|
+
"type": "f_string",
|
|
136
|
+
"named": true
|
|
137
|
+
},
|
|
134
138
|
{
|
|
135
139
|
"type": "false",
|
|
136
140
|
"named": true
|
|
@@ -976,6 +980,10 @@
|
|
|
976
980
|
"multiple": true,
|
|
977
981
|
"required": true,
|
|
978
982
|
"types": [
|
|
983
|
+
{
|
|
984
|
+
"type": "f_string",
|
|
985
|
+
"named": true
|
|
986
|
+
},
|
|
979
987
|
{
|
|
980
988
|
"type": "string",
|
|
981
989
|
"named": true
|
|
@@ -1376,6 +1384,33 @@
|
|
|
1376
1384
|
]
|
|
1377
1385
|
}
|
|
1378
1386
|
},
|
|
1387
|
+
{
|
|
1388
|
+
"type": "f_string",
|
|
1389
|
+
"named": true,
|
|
1390
|
+
"fields": {},
|
|
1391
|
+
"children": {
|
|
1392
|
+
"multiple": true,
|
|
1393
|
+
"required": true,
|
|
1394
|
+
"types": [
|
|
1395
|
+
{
|
|
1396
|
+
"type": "fstring_content",
|
|
1397
|
+
"named": true
|
|
1398
|
+
},
|
|
1399
|
+
{
|
|
1400
|
+
"type": "fstring_end",
|
|
1401
|
+
"named": true
|
|
1402
|
+
},
|
|
1403
|
+
{
|
|
1404
|
+
"type": "fstring_start",
|
|
1405
|
+
"named": true
|
|
1406
|
+
},
|
|
1407
|
+
{
|
|
1408
|
+
"type": "interpolation",
|
|
1409
|
+
"named": true
|
|
1410
|
+
}
|
|
1411
|
+
]
|
|
1412
|
+
}
|
|
1413
|
+
},
|
|
1379
1414
|
{
|
|
1380
1415
|
"type": "finally_clause",
|
|
1381
1416
|
"named": true,
|
|
@@ -1702,6 +1737,32 @@
|
|
|
1702
1737
|
}
|
|
1703
1738
|
}
|
|
1704
1739
|
},
|
|
1740
|
+
{
|
|
1741
|
+
"type": "interpolation",
|
|
1742
|
+
"named": true,
|
|
1743
|
+
"fields": {
|
|
1744
|
+
"expression": {
|
|
1745
|
+
"multiple": false,
|
|
1746
|
+
"required": true,
|
|
1747
|
+
"types": [
|
|
1748
|
+
{
|
|
1749
|
+
"type": "_expression",
|
|
1750
|
+
"named": true
|
|
1751
|
+
}
|
|
1752
|
+
]
|
|
1753
|
+
},
|
|
1754
|
+
"format_spec": {
|
|
1755
|
+
"multiple": false,
|
|
1756
|
+
"required": false,
|
|
1757
|
+
"types": [
|
|
1758
|
+
{
|
|
1759
|
+
"type": "fstring_content",
|
|
1760
|
+
"named": true
|
|
1761
|
+
}
|
|
1762
|
+
]
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
},
|
|
1705
1766
|
{
|
|
1706
1767
|
"type": "keyword_argument",
|
|
1707
1768
|
"named": true,
|
|
@@ -2770,6 +2831,18 @@
|
|
|
2770
2831
|
"type": "from",
|
|
2771
2832
|
"named": false
|
|
2772
2833
|
},
|
|
2834
|
+
{
|
|
2835
|
+
"type": "fstring_content",
|
|
2836
|
+
"named": true
|
|
2837
|
+
},
|
|
2838
|
+
{
|
|
2839
|
+
"type": "fstring_end",
|
|
2840
|
+
"named": true
|
|
2841
|
+
},
|
|
2842
|
+
{
|
|
2843
|
+
"type": "fstring_start",
|
|
2844
|
+
"named": true
|
|
2845
|
+
},
|
|
2773
2846
|
{
|
|
2774
2847
|
"type": "global",
|
|
2775
2848
|
"named": false
|