rapydscript-ng 0.8.2 → 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/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
- async function get_ini(toplevel_dir) {
667
- if (has_prop(ini_cache, toplevel_dir)) return ini_cache[toplevel_dir];
668
- var rl = (await read_config(toplevel_dir)).rapydscript || {};
669
- ini_cache[toplevel_dir] = rl;
670
- return rl;
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 (const filename of (files.length ? files : [null])) {
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
- // Lint!
751
- if ((await lint_code(code, {filename:path_for_filename(filename || '-'), builtins:final_builtins, noqa:final_noqa, errorformat:argv.errorformat || false})).length) all_ok = false;
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
  // }}}
@@ -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/f/b modifiers (the `v` modifier denotes a
937
- // verbatim JavaScript literal and is handled separately). Interpolation
938
- // and escapes are not sub-tokenised; the whole literal is a single node.
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(/[rRuUfFbB]+/),
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": "SYMBOL",
4497
- "name": "string"
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": "SYMBOL",
4503
- "name": "string"
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": "[rRuUfFbB]+"
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