rapydscript-ng 0.8.3 → 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.
Files changed (49) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/bin/build.ts +29 -0
  3. package/package.json +1 -1
  4. package/release/baselib-plain-pretty.js +85 -61
  5. package/release/baselib-plain-ugly.js +3 -3
  6. package/release/compiler.js +2543 -2432
  7. package/release/signatures.json +26 -26
  8. package/src/ast.pyj +170 -58
  9. package/src/baselib-builtins.pyj +35 -14
  10. package/src/baselib-containers.pyj +70 -40
  11. package/src/baselib-internal.pyj +20 -8
  12. package/src/baselib-itertools.pyj +6 -2
  13. package/src/baselib-str.pyj +74 -28
  14. package/src/errors.pyj +4 -1
  15. package/src/lib/aes.pyj +664 -35
  16. package/src/lib/elementmaker.pyj +105 -13
  17. package/src/lib/encodings.pyj +31 -7
  18. package/src/lib/gettext.pyj +6 -3
  19. package/src/lib/math.pyj +38 -22
  20. package/src/lib/pythonize.pyj +5 -3
  21. package/src/lib/random.pyj +13 -5
  22. package/src/lib/re.pyj +81 -18
  23. package/src/lib/traceback.pyj +42 -10
  24. package/src/lib/uuid.pyj +2 -1
  25. package/src/output/classes.pyj +70 -26
  26. package/src/output/codegen.pyj +92 -20
  27. package/src/output/comments.pyj +11 -4
  28. package/src/output/exceptions.pyj +17 -4
  29. package/src/output/functions.pyj +34 -13
  30. package/src/output/literals.pyj +7 -2
  31. package/src/output/loops.pyj +50 -16
  32. package/src/output/modules.pyj +41 -12
  33. package/src/output/operators.pyj +154 -31
  34. package/src/output/statements.pyj +38 -10
  35. package/src/output/stream.pyj +43 -12
  36. package/src/output/utils.pyj +10 -3
  37. package/src/parse.pyj +740 -259
  38. package/src/string_interpolation.pyj +4 -1
  39. package/src/tokenizer.pyj +300 -55
  40. package/src/unicode_aliases.pyj +4 -2
  41. package/src/utils.pyj +19 -5
  42. package/test/functions.pyj +8 -0
  43. package/test/generic.pyj +9 -0
  44. package/test/lsp.pyj +16 -0
  45. package/tools/compiler.mjs +0 -6
  46. package/tools/fmt.mjs +41 -2
  47. package/tools/lint-worker.mjs +7 -1
  48. package/tools/lint.mjs +12 -5
  49. package/tools/lsp.mjs +11 -1
package/src/utils.pyj CHANGED
@@ -2,7 +2,8 @@
2
2
  # License: BSD Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
3
3
  from __python__ import hash_literals
4
4
 
5
- has_prop = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty)
5
+ has_prop = Object.prototype.hasOwnProperty.call.bind(
6
+ Object.prototype.hasOwnProperty)
6
7
 
7
8
 
8
9
  def array_to_hash(a):
@@ -41,7 +42,11 @@ def repeat_string(str_, i):
41
42
 
42
43
  class DefaultsError(ValueError):
43
44
  def __init__(self, name, defs):
44
- ValueError.__init__(self, name + ' is not a supported option. Supported options are: ' + str(Object.keys(defs)))
45
+ ValueError.__init__(
46
+ self,
47
+ name + ' is not a supported option. Supported options are: '
48
+ + str(Object.keys(defs))
49
+ )
45
50
 
46
51
 
47
52
  def defaults(args, defs, croak):
@@ -80,12 +85,18 @@ MAP = def ():
80
85
  if isinstance(val, AtTop):
81
86
  val = val.v
82
87
  if isinstance(val, Splice):
83
- top.push.apply(top, (val.v.slice().reverse() if backwards else val.v))
88
+ top.push.apply(
89
+ top,
90
+ (val.v.slice().reverse() if backwards else val.v)
91
+ )
84
92
  else:
85
93
  top.push(val)
86
94
  elif val is not skip:
87
95
  if isinstance(val, Splice):
88
- ret.push.apply(ret, (val.v.slice().reverse() if backwards else val.v))
96
+ ret.push.apply(
97
+ ret,
98
+ (val.v.slice().reverse() if backwards else val.v)
99
+ )
89
100
  else:
90
101
  ret.push(val)
91
102
  return is_last
@@ -205,5 +216,8 @@ def make_predicate(words):
205
216
  def cache_file_name(src, cache_dir):
206
217
  if cache_dir:
207
218
  src = str.replace(src, '\\', '/')
208
- return cache_dir + '/' + str.lstrip(str.replace(src, '/', '-') + '.bin', '-')
219
+ return cache_dir + '/' + str.lstrip(
220
+ str.replace(src, '/', '-') + '.bin',
221
+ '-'
222
+ )
209
223
  return src + '-cached'
@@ -22,6 +22,14 @@ mul = None
22
22
  assrt.equal(add(1,2), 3)
23
23
  assrt.equal(sub(1,2), -1)
24
24
  assrt.equal(mul(2,2), 4)
25
+
26
+ # newline as implicit comma separator in function calls
27
+ assrt.equal(add(1
28
+ 2), 3)
29
+ assrt.equal(sub(1
30
+ 2), -1)
31
+ assrt.equal(add(list().length
32
+ list().length), 0)
25
33
  # for some reason input to throws must be of type block, hence the 'def' wrapper
26
34
  assrt.throws(
27
35
  def():
package/test/generic.pyj CHANGED
@@ -48,6 +48,15 @@ assrt.deepEqual((1,
48
48
  assrt.deepEqual((1,
49
49
  2), [1, 2])
50
50
 
51
+ # Tuples with newlines as implicit separators (no comma)
52
+ assrt.deepEqual((1
53
+ 2), [1, 2])
54
+ assrt.deepEqual((list()
55
+ list()), [[], []])
56
+ assrt.deepEqual((1
57
+ 2
58
+ 3), [1, 2, 3])
59
+
51
60
  # Conditional operators
52
61
  assrt.equal(1 if True else 2, 1)
53
62
  assrt.equal(1
package/test/lsp.pyj CHANGED
@@ -50,6 +50,22 @@ async def test_diagnostics():
50
50
  assrt.ok(c['import-unresolved'], 'expected import-unresolved for nonexistent_module')
51
51
  fs.rmSync(tmp, {'recursive': True})
52
52
 
53
+ async def test_globals_directive_suppresses_undef():
54
+ tmp = write_workspace()
55
+ ctx = make_ctx(tmp)
56
+ uri = uri_of(path.join(tmp, 'globals_test.pyj'))
57
+ # ext_func is declared via # globals: so it must not produce an undef diagnostic.
58
+ text = '# globals: ext_func\n\nresult = ext_func(42)\n'
59
+ diags = await lsp.compute_diagnostics(ctx, uri, text)
60
+ c = codes(diags)
61
+ assrt.ok(not c['undef'], 'ext_func declared in # globals: must not be flagged as undef')
62
+ # A name NOT in # globals: must still be flagged.
63
+ text2 = '# globals: ext_func\n\nresult = ext_func(42)\nbad = undeclared_thing()\n'
64
+ diags2 = await lsp.compute_diagnostics(ctx, uri, text2)
65
+ c2 = codes(diags2)
66
+ assrt.ok(c2['undef'], 'undeclared_thing not in # globals: must still be flagged as undef')
67
+ fs.rmSync(tmp, {'recursive': True})
68
+
53
69
  async def test_hover_and_definition():
54
70
  tmp = write_workspace()
55
71
  ctx = make_ctx(tmp)
@@ -13,7 +13,6 @@ import path from 'path';
13
13
  import fs from 'fs';
14
14
  import crypto from 'crypto';
15
15
  import vm from 'vm';
16
- import * as terser from 'terser';
17
16
  import { fileURLToPath } from 'url';
18
17
  import { createRequire } from 'module';
19
18
  import { generate_source_map } from './sourcemap.mjs';
@@ -39,11 +38,6 @@ async function path_exists(p) {
39
38
  }
40
39
  }
41
40
 
42
- function uglify(code) {
43
- var ans = terser.minify_sync(code);
44
- if (ans.error) throw ans.error;
45
- return ans.code;
46
- }
47
41
 
48
42
 
49
43
  async function find_compiler_dir() {
package/tools/fmt.mjs CHANGED
@@ -848,13 +848,51 @@ function normalize_opts(options) {
848
848
  // check-only reporting
849
849
  // ---------------------------------------------------------------------------
850
850
 
851
+ // Returns a Set of 0-based line indices that fall inside vr/v verbatim
852
+ // triple-quoted blocks. The formatter cannot reflow that content (it is
853
+ // raw JS/text), so line-length violations inside those blocks are skipped.
854
+ function verbatim_line_set(lines) {
855
+ var skip = new Set();
856
+ var in_block = false;
857
+ var close_q = null;
858
+ for (var i = 0; i < lines.length; i++) {
859
+ var line = lines[i];
860
+ if (!in_block) {
861
+ // Detect the opening of a verbatim triple-quoted block.
862
+ // Patterns: vr''', vr""", v''', v""" (with any combination of
863
+ // v/r/u/f prefix letters, case-insensitive).
864
+ var m = /\b[vrufVRUF]*(?:'{3}|"{3})/.exec(line);
865
+ if (m) {
866
+ var q = m[0].slice(-3); // ''' or """
867
+ var after = line.slice(m.index + m[0].length);
868
+ if (after.indexOf(q) < 0) {
869
+ // Block does not close on the same line.
870
+ in_block = true;
871
+ close_q = q;
872
+ skip.add(i); // opening line itself may be long
873
+ }
874
+ // If it closes on the same line we leave skip alone —
875
+ // the single-line v'...' case is handled per-line below.
876
+ }
877
+ } else {
878
+ skip.add(i);
879
+ if (line.indexOf(close_q) >= 0) {
880
+ in_block = false;
881
+ close_q = null;
882
+ }
883
+ }
884
+ }
885
+ return skip;
886
+ }
887
+
851
888
  export function check_report(file, src, formatted, options) {
852
889
  var opts = normalize_opts(options);
853
890
  var errs = [];
854
891
  if (formatted !== src.replace(/\r\n?/g, '\n')) errs.push(file + ': would be reformatted');
855
892
  var lines = formatted.split('\n');
893
+ var skip = verbatim_line_set(lines);
856
894
  for (var i = 0; i < lines.length; i++) {
857
- if (lines[i].length > opts.line_length) {
895
+ if (!skip.has(i) && lines[i].length > opts.line_length) {
858
896
  errs.push(file + ':' + (i + 1) + ': line exceeds ' + opts.line_length + ' characters (' + lines[i].length + ')');
859
897
  }
860
898
  }
@@ -970,8 +1008,9 @@ export async function cli(argv, base_path, src_path, lib_path) {
970
1008
  console.log('reformatted ' + f);
971
1009
  }
972
1010
  var flines = formatted.split('\n');
1011
+ var fskip = verbatim_line_set(flines);
973
1012
  for (var li = 0; li < flines.length; li++) {
974
- if (flines[li].length > opts.line_length) {
1013
+ if (!fskip.has(li) && flines[li].length > opts.line_length) {
975
1014
  console.error(f + ':' + (li + 1) + ': line exceeds ' + opts.line_length + ' characters (' + flines[li].length + ')');
976
1015
  had_errors = true;
977
1016
  }
@@ -8,7 +8,7 @@
8
8
  * instance so parsing can run on multiple CPU cores simultaneously.
9
9
  */
10
10
 
11
- import { parentPort } from 'worker_threads';
11
+ import { parentPort, workerData } from 'worker_threads';
12
12
  import { create_compiler } from './compiler.mjs';
13
13
  import { read_config } from './ini.mjs';
14
14
  import * as utils from './utils.mjs';
@@ -18,6 +18,12 @@ import path from 'path';
18
18
  const merge = utils.merge;
19
19
  const has_prop = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty);
20
20
 
21
+ // Restore embedded assets forwarded by the main thread (needed in compiled binaries
22
+ // where workers don't inherit the main thread's globalThis).
23
+ if (workerData && workerData.embedded) {
24
+ globalThis.__rapydscript_embedded__ = workerData.embedded;
25
+ }
26
+
21
27
  // Initialize the compiler first — lint.mjs reads globalThis.create_rapydscript_compiler
22
28
  // at module evaluation time, so the global must be set before the dynamic import.
23
29
  const compiler = await create_compiler();
package/tools/lint.mjs CHANGED
@@ -7,7 +7,6 @@
7
7
 
8
8
  import fs from 'fs';
9
9
  import path from 'path';
10
- import { fileURLToPath } from 'url';
11
10
  import { read_config } from './ini.mjs';
12
11
  import * as utils from './utils.mjs';
13
12
 
@@ -805,11 +804,19 @@ export async function cli(argv, base_path, src_path, lib_path) {
805
804
  // Workers own their own compiler instance so parsing runs in parallel across CPU cores.
806
805
  var WORKER_THRESHOLD = 4;
807
806
 
808
- if (!has_stdin && all_files.length >= WORKER_THRESHOLD) {
807
+ // In compiled binaries lint.mjs is dynamically imported so Bun's static
808
+ // analysis never sees the new Worker() call. build.ts pre-bundles
809
+ // lint-worker.mjs and embeds it via `with { type: 'file' }`, storing the
810
+ // virtual-FS path in __rapydscript_embedded__['lint-worker-url'].
811
+ // Fall back gracefully (single-threaded) if the URL isn't available.
812
+ var embedded = globalThis.__rapydscript_embedded__;
813
+ var worker_url = embedded
814
+ ? embedded['lint-worker-url']
815
+ : new URL('./lint-worker.mjs', import.meta.url);
816
+
817
+ if (!has_stdin && all_files.length >= WORKER_THRESHOLD && worker_url) {
809
818
  var { Worker } = await import('worker_threads');
810
819
  var os_mod = await import('os');
811
-
812
- var worker_path = path.join(path.dirname(fileURLToPath(import.meta.url)), 'lint-worker.mjs');
813
820
  var num_workers = Math.min(os_mod.cpus().length, all_files.length, 8);
814
821
 
815
822
  // Dispatch files to workers dynamically so fast files don't leave workers idle.
@@ -836,7 +843,7 @@ export async function cli(argv, base_path, src_path, lib_path) {
836
843
  // waiting for all workers before starting any work.
837
844
  var worker_promises = Array.from({length: num_workers}, function() {
838
845
  return new Promise(function(resolve) {
839
- var w = new Worker(worker_path);
846
+ var w = new Worker(worker_url, { workerData: { embedded: embedded || null } });
840
847
  w.once('message', function(m) {
841
848
  if (m.type !== 'ready') return;
842
849
  w.on('message', function(msg) {
package/tools/lsp.mjs CHANGED
@@ -230,7 +230,7 @@ export async function compute_diagnostics(ctx, uri, raw_text) {
230
230
  // 2. Linter diagnostics -- only when the file parsed cleanly, so we do not
231
231
  // emit misleading undefined/unused warnings from a partial AST.
232
232
  if (a.toplevel && (!a.recovered_errors || a.recovered_errors.length === 0)) {
233
- var builtins = utils.merge(BUILTINS);
233
+ var builtins = utils.merge(BUILTINS, file_globals(a.text));
234
234
  var messages = lint_parsed(a.toplevel, a.text, { filename: a.file_path, builtins: builtins, noqa: file_noqa(a.text) });
235
235
  messages.forEach(function (m) {
236
236
  out.push(lint_message_to_diagnostic(doc, m));
@@ -265,6 +265,16 @@ function file_noqa(code) {
265
265
  return noqa;
266
266
  }
267
267
 
268
+ // Parse `# globals: a,b` file-level directives from the first lines (mirrors lint cli).
269
+ function file_globals(code) {
270
+ var globals = Object.create(null);
271
+ code.split('\n', 20).forEach(function (line) {
272
+ var lq = line.replace(/\s+/g, '');
273
+ if (lq.slice(0, 9).toLowerCase() === '#globals:') (lq.split(':', 2)[1] || '').split(',').forEach(function (x) { if (x) globals[x] = true; });
274
+ });
275
+ return globals;
276
+ }
277
+
268
278
  function lint_message_to_diagnostic(doc, m) {
269
279
  var start_line = (m.start_line || 1) - 1;
270
280
  var start_col = (m.start_col === undefined || m.start_col === null) ? 0 : m.start_col;