rapydscript-ng 0.7.24 → 0.8.1

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 (132) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +80 -26
  3. package/bin/build.ts +117 -0
  4. package/bin/package.json +1 -0
  5. package/bin/rapydscript +65 -62
  6. package/build_wheels.py +133 -0
  7. package/editor-plugins/README.md +80 -0
  8. package/editor-plugins/nvim.lua +321 -0
  9. package/local-agent.md +28 -0
  10. package/package.json +4 -5
  11. package/publish.py +27 -17
  12. package/release/baselib-plain-pretty.js +448 -326
  13. package/release/baselib-plain-ugly.js +3 -3
  14. package/release/compiler.js +9027 -8139
  15. package/release/signatures.json +27 -27
  16. package/release/stdlib_modules.json +1 -0
  17. package/src/ast.pyj +428 -280
  18. package/src/baselib-builtins.pyj +49 -28
  19. package/src/baselib-containers.pyj +241 -226
  20. package/src/baselib-errors.pyj +8 -1
  21. package/src/baselib-internal.pyj +38 -23
  22. package/src/baselib-itertools.pyj +13 -7
  23. package/src/baselib-str.pyj +59 -84
  24. package/src/compiler.pyj +4 -4
  25. package/src/errors.pyj +3 -4
  26. package/src/lib/aes.pyj +46 -32
  27. package/src/lib/elementmaker.pyj +11 -9
  28. package/src/lib/encodings.pyj +15 -5
  29. package/src/lib/gettext.pyj +9 -4
  30. package/src/lib/math.pyj +106 -45
  31. package/src/lib/operator.pyj +10 -10
  32. package/src/lib/pythonize.pyj +2 -1
  33. package/src/lib/random.pyj +28 -21
  34. package/src/lib/re.pyj +492 -19
  35. package/src/lib/traceback.pyj +171 -29
  36. package/src/lib/uuid.pyj +2 -2
  37. package/src/output/classes.pyj +28 -27
  38. package/src/output/codegen.pyj +87 -83
  39. package/src/output/comments.pyj +8 -8
  40. package/src/output/exceptions.pyj +20 -21
  41. package/src/output/functions.pyj +66 -64
  42. package/src/output/literals.pyj +24 -23
  43. package/src/output/loops.pyj +84 -142
  44. package/src/output/modules.pyj +55 -68
  45. package/src/output/operators.pyj +40 -29
  46. package/src/output/statements.pyj +21 -15
  47. package/src/output/stream.pyj +82 -56
  48. package/src/output/utils.pyj +12 -8
  49. package/src/parse.pyj +971 -620
  50. package/src/string_interpolation.pyj +5 -3
  51. package/src/tokenizer.pyj +188 -148
  52. package/src/utils.pyj +32 -15
  53. package/test/_treeshake_mod.pyj +30 -0
  54. package/test/_treeshake_side_effect.pyj +9 -0
  55. package/test/ast_serialization.pyj +392 -0
  56. package/test/async.pyj +95 -0
  57. package/test/baselib.pyj +1 -2
  58. package/test/embedded_compiler.pyj +57 -0
  59. package/test/fmt.pyj +291 -0
  60. package/test/generic.pyj +16 -3
  61. package/test/imports.pyj +8 -6
  62. package/test/internationalization.pyj +38 -37
  63. package/test/lint.pyj +120 -117
  64. package/test/lsp.pyj +257 -0
  65. package/test/repl.pyj +70 -65
  66. package/test/sourcemaps.pyj +315 -0
  67. package/test/starargs.pyj +46 -39
  68. package/test/str.pyj +8 -0
  69. package/test/traceback.pyj +263 -0
  70. package/test/treeshaking.pyj +181 -0
  71. package/test/web_repl_export.pyj +88 -0
  72. package/tools/ast_serialize.mjs +557 -0
  73. package/tools/cli.mjs +531 -0
  74. package/tools/compile.mjs +205 -0
  75. package/tools/compiler.mjs +159 -0
  76. package/tools/{completer.js → completer.mjs} +6 -6
  77. package/tools/{embedded_compiler.js → embedded_compiler.mjs} +17 -13
  78. package/tools/export.js +109 -56
  79. package/tools/fmt.mjs +975 -0
  80. package/tools/{gettext.js → gettext.mjs} +46 -53
  81. package/tools/ini.mjs +175 -0
  82. package/tools/{lint.js → lint.mjs} +78 -55
  83. package/tools/lsp.mjs +964 -0
  84. package/tools/lsp_protocol.mjs +259 -0
  85. package/tools/lsp_symbols.mjs +418 -0
  86. package/tools/{msgfmt.js → msgfmt.mjs} +18 -18
  87. package/tools/{repl.js → repl.mjs} +56 -42
  88. package/tools/{self.js → self.mjs} +71 -46
  89. package/tools/sourcemap.mjs +123 -0
  90. package/tools/test.mjs +252 -0
  91. package/tools/treeshake.mjs +400 -0
  92. package/tools/{utils.js → utils.mjs} +26 -23
  93. package/tools/{web_repl.js → web_repl.mjs} +15 -13
  94. package/tools/web_repl_export.mjs +97 -0
  95. package/tree-sitter/README.md +101 -0
  96. package/tree-sitter/grammar.js +992 -0
  97. package/tree-sitter/package.json +43 -0
  98. package/tree-sitter/queries/highlights.scm +232 -0
  99. package/tree-sitter/queries/indents.scm +64 -0
  100. package/tree-sitter/queries/injections.scm +14 -0
  101. package/tree-sitter/queries/locals.scm +108 -0
  102. package/tree-sitter/src/grammar.json +4976 -0
  103. package/tree-sitter/src/node-types.json +2901 -0
  104. package/tree-sitter/src/parser.c +196465 -0
  105. package/tree-sitter/src/scanner.c +294 -0
  106. package/tree-sitter/src/tree_sitter/alloc.h +54 -0
  107. package/tree-sitter/src/tree_sitter/array.h +330 -0
  108. package/tree-sitter/src/tree_sitter/parser.h +286 -0
  109. package/tree-sitter/test/corpus/chaining.txt +99 -0
  110. package/tree-sitter/test/corpus/classes.txt +147 -0
  111. package/tree-sitter/test/corpus/comprehensions.txt +155 -0
  112. package/tree-sitter/test/corpus/containers.txt +242 -0
  113. package/tree-sitter/test/corpus/control_flow.txt +361 -0
  114. package/tree-sitter/test/corpus/decorators.txt +64 -0
  115. package/tree-sitter/test/corpus/existential.txt +102 -0
  116. package/tree-sitter/test/corpus/functions.txt +293 -0
  117. package/tree-sitter/test/corpus/imports.txt +117 -0
  118. package/tree-sitter/test/corpus/literals.txt +135 -0
  119. package/tree-sitter/test/corpus/operators.txt +296 -0
  120. package/tree-sitter/test/corpus/statements.txt +189 -0
  121. package/tree-sitter/test/corpus/strings.txt +90 -0
  122. package/tree-sitter/test/corpus/subscripts.txt +227 -0
  123. package/tree-sitter/tree-sitter.json +36 -0
  124. package/web-repl/env.js +35 -23
  125. package/web-repl/main.js +8 -8
  126. package/bin/export +0 -75
  127. package/bin/web-repl-export +0 -102
  128. package/tools/cli.js +0 -523
  129. package/tools/compile.js +0 -184
  130. package/tools/compiler.js +0 -84
  131. package/tools/ini.js +0 -65
  132. package/tools/test.js +0 -110
package/CHANGELOG.md CHANGED
@@ -1,3 +1,31 @@
1
+ version 0.8.1
2
+ =======================
3
+
4
+ * Fix concatenation of mixed string and f-string literal
5
+ * fmt and lsp now by default read format related settings from pyproject.toml, overridden via command line
6
+ * fmt command reports unfixable long lines in normal mode as well
7
+ * Fix newlines inside tuples not being allowed
8
+ * Miscellaneous improvements for the code auto formatter
9
+
10
+ version 0.8.0
11
+ =======================
12
+
13
+ * Implement generation of source maps via the --source-map option
14
+ * Implement dead code removal via the --tree-shaking option
15
+ * Add support for async and await
16
+ * Implement a ``fmt`` command for code formatting and an ``lsp`` command for a Language Server
17
+ * Implement a tree sitter grammar so that robust syntax highlighting is potentially available in all editors
18
+ * Implement a neovim plugin that enables treesitter based syntax highlighting and LSP server for ``.pyj`` files
19
+ * Implement support for importing user modules in the embedded compiler
20
+ * Double the compilation speed, especially useful for large projects
21
+ * Drop support for ES5
22
+ * List objects are now unmodified JS Arrays for greater performance. Python
23
+ list methods continue to work.
24
+ * Change the cache format to cache only the AST and not final output so that
25
+ the cache can be re-used regardless of output and tree shaking options
26
+ * Make the compiler internally use async IO. This means the embedded compiler
27
+ functions are also async.
28
+
1
29
  version 0.7.24
2
30
  =======================
3
31
 
package/README.md CHANGED
@@ -42,11 +42,13 @@ backwards compatible) features. For more on the forking, [see the bottom of this
42
42
  - [Method Binding](#method-binding)
43
43
  - [Iterators](#iterators)
44
44
  - [Generators](#generators)
45
+ - [Asnyc and Await](#async-and-await)
45
46
  - [Modules](#modules)
46
47
  - [Exception Handling](#exception-handling)
47
48
  - [Scope Control](#scope-control)
48
49
  - [Available Libraries](#available-libraries)
49
50
  - [Linter](#linter)
51
+ - [Language Server (LSP)](#language-server-lsp)
50
52
  - [Making RapydScript even more pythonic](#making-rapydscript-even-more-pythonic)
51
53
  - [Advanced Usage Topics](#advanced-usage-topics)
52
54
  - [Browser Compatibility](#browser-compatibility)
@@ -102,6 +104,14 @@ Installation
102
104
 
103
105
  [Try RapydScript-ng live via an in-browser REPL!](https://sw.kovidgoyal.net/rapydscript/repl/)
104
106
 
107
+ If you just want the standalone rapydscript binary, you can install it from
108
+ PyPI, with::
109
+
110
+ pip install rapydscript-ng
111
+
112
+ If you want the full package, needed to use the ``web-repl-export`` command, or
113
+ integrate with a node project, you can instead install using npm.
114
+
105
115
  First make sure you have installed the latest version of [node.js](https://nodejs.org/) (You may need to restart your computer after this step).
106
116
 
107
117
  From NPM for use as a command line app:
@@ -1254,9 +1264,12 @@ syntax.
1254
1264
  Generators create JavaScript iterator objects. For differences between python
1255
1265
  and JavaScript iterators, see the section on iterators above.
1256
1266
 
1257
- Currently, generators are down-converted to ES 5 switch statements. In the
1258
- future, when ES 6 support is widespread, they will be converted to native
1259
- JavaScript ES 6 generators.
1267
+ Async and Await
1268
+ ------------------
1269
+
1270
+ Python's ``async`` and ``await`` keywords are supported, but note that async
1271
+ functions return JavaScript Promise objects not co-routines. This is so that
1272
+ they interoperate with the JS ecosystem and browser APIs in particular.
1260
1273
 
1261
1274
  Modules
1262
1275
  -------
@@ -1427,6 +1440,41 @@ that the linter will not raise undefined errors for. You can turn off
1427
1440
  individual checks that you do not find useful. See ``rapydscript lint -h`` for
1428
1441
  details.
1429
1442
 
1443
+ Language Server (LSP)
1444
+ -----------------------
1445
+
1446
+ RapydScript ships with a Language Server Protocol server so that editors can
1447
+ offer rich language support for ``.pyj`` files. Start it with:
1448
+
1449
+ rapydscript lsp
1450
+
1451
+ The server talks the standard LSP JSON-RPC protocol over stdin/stdout, so it is
1452
+ meant to be launched by an editor/LSP client rather than run interactively. It
1453
+ provides:
1454
+
1455
+ - **Code completion** — in-scope symbols, builtins and keywords, plus member
1456
+ completion for imported modules (``mod.<complete>``).
1457
+ - **Diagnostics** — the same checks as the ``lint`` sub-command, plus warnings
1458
+ for imports that cannot be resolved in the import path. The parser recovers
1459
+ from syntax errors, so a file that is being edited is still analysed (and
1460
+ multiple errors can be reported at once).
1461
+ - **Hover** — the kind, origin and docstring of the symbol under the cursor.
1462
+ - **Code actions** — quick fixes such as removing an unused import/local,
1463
+ silencing a check with a ``# noqa`` comment, and formatting the document.
1464
+ - **Document formatting** — the same formatter as the ``fmt`` sub-command,
1465
+ controlled by ``--line-length`` and ``--preferred-quote``.
1466
+ - **Go to definition** — including jumping into imported modules.
1467
+ - **Find all references** — resolved across files through the import graph.
1468
+ - **Rename** — renames a symbol everywhere it is used across the workspace.
1469
+
1470
+ Imports are resolved with the same ``--import-path`` (``-p``) option as the
1471
+ ``compile`` sub-command, so point it at your project's import directories. For
1472
+ example:
1473
+
1474
+ rapydscript lsp --import-path src:vendor --line-length 100
1475
+
1476
+ See ``rapydscript lsp -h`` for the full list of options.
1477
+
1430
1478
  Making RapydScript even more pythonic
1431
1479
  ---------------------------------------
1432
1480
 
@@ -1454,11 +1502,9 @@ Advanced Usage Topics
1454
1502
 
1455
1503
  #### Browser Compatibility
1456
1504
 
1457
- RapydScript compiles your code such that it will work on browsers that are
1458
- compatible with the ES 5 JavaScript standard. The compiler has a
1459
- ``--js-version`` option that can also be used to output ES 6 only code. This
1460
- code is smaller and faster than the ES 5 version, but is not as widely
1461
- compatible.
1505
+ RapydScript compiles your code to ES 6 compatible JavaScript. Any modern
1506
+ browser or Node.js version that supports ES 6 will run the output. The
1507
+ ``--js-version`` option only accepts ``6``.
1462
1508
 
1463
1509
  #### Tabs vs Spaces
1464
1510
 
@@ -1491,9 +1537,9 @@ To do so, simply include the [embeddable rapydscript compiler](https://sw.kovidg
1491
1537
  in your page, and use it to compile arbitrary RapydScript code.
1492
1538
 
1493
1539
  You create the compiler by calling: `RapydScript.create_embedded_compiler()` and compile
1494
- code with `compiler.compile(code)`. You can execute the resulting JavaScript
1495
- using the standard `eval()` function. See the sample
1496
- HTML below for an example.
1540
+ code with `await compiler.compile(code)`. Both calls return Promises, so you must use
1541
+ `async`/`await` (or `.then()`) to get the results. You can execute the resulting JavaScript
1542
+ using the standard `eval()` function. See the sample HTML below for an example.
1497
1543
 
1498
1544
  ```html
1499
1545
  <!DOCTYPE html>
@@ -1503,9 +1549,9 @@ HTML below for an example.
1503
1549
  <title>Test embedded RapydScript</title>
1504
1550
  <script charset="UTF-8" src="https://sw.kovidgoyal.net/rapydscript/repl/rapydscript.js"></script>
1505
1551
  <script>
1506
- var compiler = RapydScript.create_embedded_compiler();
1507
- var js = compiler.compile("def hello_world():\n a='RapydScript is cool!'\n print(a)\n alert(a)");
1508
- window.onload = function() {
1552
+ window.onload = async function() {
1553
+ var compiler = await RapydScript.create_embedded_compiler();
1554
+ var js = await compiler.compile("def hello_world():\n a='RapydScript is cool!'\n print(a)\n alert(a)");
1509
1555
  document.body.textContent = js;
1510
1556
  eval(js);
1511
1557
  eval('hello_world()');
@@ -1519,20 +1565,28 @@ window.onload = function() {
1519
1565
  There are a couple of caveats when using the embedded compiler:
1520
1566
 
1521
1567
  * It only works when run in a modern browser (one that supports ES6) so no
1522
- Internet Explorer. You can have it work in an ES 5 runtime by passing
1523
- an option to the compile() method, like this:
1524
- ```
1525
- compiler.compile(code, {js_version:5})
1526
- ```
1527
- Note that doing this means that you cannot use generators and the
1528
- yield keyword in your RapydScript code.
1568
+ Internet Explorer.
1529
1569
 
1530
- * Importing of modules only works with the standard library modules. There is
1531
- currently no way to make your own modules importable.
1570
+ * To implement importing of modules you need to create a virtual file system
1571
+ like this, before calling ``create_embedded_compiler()``:
1532
1572
 
1533
- * To generate the embedded compiler yourself (rapydscript.js) from a source
1534
- checkout of rapydscript, follow the instructions above for installing from
1535
- source, then run `bin/web-repl-export /path/to/export/directory`
1573
+ ```
1574
+ RapydScript.virtual_file_system = {
1575
+ read_file: my_read_file,
1576
+ write_file: my_write_file,
1577
+ stat_file: my_stat_file
1578
+ }
1579
+ ```
1580
+ These functions should have the same signature and semantics as the
1581
+ ``fs.promises.readFile, fs.promises.writeFile and fs.promises.statFile``
1582
+ functions from Node. Currently only the ``mtimeMs`` field from the stat
1583
+ result is needed. They will be passed file paths starting with ``__vfs__/``
1584
+ and should read/write/stat them as needed.
1585
+
1586
+ * To generate the embedded compiler yourself (rapydscript.js)
1587
+ run `rapydscript web-repl-export /path/to/export/directory`.
1588
+ Then you can simply open the ``index.html`` file in that directory in a browser
1589
+ to see the web REPL in action.
1536
1590
 
1537
1591
  Internationalization
1538
1592
  -------------------------
package/bin/build.ts ADDED
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env bun
2
+ // vim:ft=typescript:ts=4:et
3
+ /*
4
+ * Generates bin/rapydscript.mjs — the bun compilation entry point — by scanning
5
+ * the filesystem for embeddable assets and prepending their import declarations
6
+ * and the __rapydscript_embedded__ global setup to the contents of bin/rapydscript.
7
+ *
8
+ * Usage:
9
+ * bun bin/build.ts # generate bin/rapydscript.mjs only
10
+ * bun bin/build.ts --compile --outfile foo # generate + run: bun build bin/rapydscript.mjs --compile --outfile foo
11
+ */
12
+
13
+ import path from 'path';
14
+ import fs from 'fs';
15
+
16
+ const repo_root = path.dirname(path.dirname(Bun.main));
17
+ const dev_dir = path.join(repo_root, 'dev');
18
+ const src_lib_dir = path.join(repo_root, 'src', 'lib');
19
+ const bin_dir = path.join(repo_root, 'bin');
20
+ const out_path = path.join(bin_dir, 'rapydscript.mjs');
21
+
22
+ async function exists(p: string): Promise<boolean> {
23
+ try { await fs.promises.access(p); return true; } catch { return false; }
24
+ }
25
+
26
+ // --- Scan assets ---
27
+
28
+ const DEV_TEXT_ASSETS = ['compiler.js', 'baselib-plain-pretty.js', 'baselib-plain-ugly.js'];
29
+ const DEV_JSON_ASSETS = ['stdlib_modules.json'];
30
+
31
+ const found_dev_text: string[] = [];
32
+ for (const a of DEV_TEXT_ASSETS) {
33
+ if (await exists(path.join(dev_dir, a))) found_dev_text.push(a);
34
+ }
35
+
36
+ const found_dev_json: string[] = [];
37
+ for (const a of DEV_JSON_ASSETS) {
38
+ if (await exists(path.join(dev_dir, a))) found_dev_json.push(a);
39
+ }
40
+
41
+ const stdlib_files: string[] = (await fs.promises.readdir(src_lib_dir))
42
+ .filter(f => f.endsWith('.pyj'))
43
+ .sort();
44
+
45
+ // --- Build the generated asset section ---
46
+
47
+ function var_name(prefix: string, filename: string): string {
48
+ return prefix + filename.replace(/[^a-zA-Z0-9]/g, '_');
49
+ }
50
+
51
+ const lines: string[] = [];
52
+
53
+ lines.push('// Generated by bin/build.ts — do not edit manually');
54
+ lines.push('// vim:ft=javascript:ts=4:et');
55
+ lines.push('');
56
+ lines.push('// === Embedded assets (generated by bin/build.ts) ===');
57
+
58
+ for (const a of found_dev_text) {
59
+ lines.push(`import ${var_name('_dev_', a)} from '../dev/${a}' with { type: 'text' };`);
60
+ }
61
+ for (const a of found_dev_json) {
62
+ lines.push(`import ${var_name('_dev_', a)} from '../dev/${a}' with { type: 'json' };`);
63
+ }
64
+ for (const f of stdlib_files) {
65
+ lines.push(`import ${var_name('_stdlib_', f)} from '../src/lib/${f}' with { type: 'text' };`);
66
+ }
67
+
68
+ lines.push('');
69
+ lines.push('globalThis.__rapydscript_embedded__ = {');
70
+
71
+ for (const a of found_dev_text) {
72
+ lines.push(` '${a}': ${var_name('_dev_', a)},`);
73
+ }
74
+ for (const a of found_dev_json) {
75
+ // strip extension for the key so consumers use e.g. embedded.stdlib_modules
76
+ const key = a.replace(/\.json$/, '').replace(/-/g, '_');
77
+ lines.push(` ${key}: ${var_name('_dev_', a)},`);
78
+ }
79
+ lines.push(' stdlib: {');
80
+ for (const f of stdlib_files) {
81
+ lines.push(` '${f}': ${var_name('_stdlib_', f)},`);
82
+ }
83
+ lines.push(' },');
84
+ lines.push('};');
85
+ lines.push('');
86
+ lines.push('// === Entry point (from bin/rapydscript) ===');
87
+ lines.push('');
88
+
89
+ // --- Append bin/rapydscript content (strip the shebang line) ---
90
+
91
+ const entry_src = await fs.promises.readFile(path.join(bin_dir, 'rapydscript'), 'utf-8');
92
+ const entry_lines = entry_src.split('\n');
93
+ // Drop the first line if it's the shebang, and the vim modeline comment right after
94
+ let start = 0;
95
+ if (entry_lines[0].startsWith('#!')) start = 1;
96
+ if (start < entry_lines.length && entry_lines[start].trim().startsWith('// vim:')) start++;
97
+
98
+ lines.push(...entry_lines.slice(start));
99
+
100
+ // Ensure the file ends with a newline
101
+ const content = lines.join('\n');
102
+ const final_content = content.endsWith('\n') ? content : content + '\n';
103
+
104
+ await fs.promises.writeFile(out_path, final_content, 'utf-8');
105
+ console.log(`Written ${path.relative(repo_root, out_path)}`);
106
+ console.log(` ${found_dev_text.length} dev text assets, ${found_dev_json.length} dev json assets, ${stdlib_files.length} stdlib files`);
107
+
108
+ // --- Optionally run bun build with passed-through args ---
109
+
110
+ const extra_args = process.argv.slice(2);
111
+ if (extra_args.length > 0) {
112
+ const cmd_args = ['build', out_path, ...extra_args];
113
+ console.log(`Running: bun ${cmd_args.join(' ')}`);
114
+ const proc = Bun.spawn(['bun', ...cmd_args], { stdout: 'inherit', stderr: 'inherit', stdin: 'inherit' });
115
+ const code = await proc.exited;
116
+ process.exit(code);
117
+ }
@@ -0,0 +1 @@
1
+ {"type": "module"}
package/bin/rapydscript CHANGED
@@ -1,70 +1,73 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  // vim:ft=javascript:ts=4:et
3
3
 
4
- "use strict";
4
+ import { fileURLToPath } from 'url';
5
+ import path from 'path';
6
+ import { spawn } from 'child_process';
7
+ import * as utils from '../tools/utils.mjs';
8
+ import { create_compiler } from '../tools/compiler.mjs';
5
9
 
6
- function load(mod) {
7
- return require('../tools/' + mod);
8
- }
10
+ const __filename = fileURLToPath(import.meta.url);
11
+
12
+ var start_time = new Date().getTime();
13
+
14
+ var { argv } = await import('../tools/cli.mjs');
15
+
16
+ var base_path = path.normalize(path.join(path.dirname(__filename), ".."));
17
+ var src_path = path.join(base_path, 'src');
18
+ var lib_path = path.join(base_path, 'dev');
19
+ if (!(await utils.path_exists(path.join(lib_path, 'compiler.js')))) lib_path = path.join(base_path, 'release');
9
20
 
10
- var utils = load('utils');
21
+ // Create compiler instance once, then expose it synchronously via the global
22
+ // so bundled modules (repl, gettext, etc.) can call create_rapydscript_compiler() synchronously.
23
+ const _compiler_instance = await create_compiler();
24
+ globalThis.create_rapydscript_compiler = () => _compiler_instance;
11
25
 
12
- // We need ES 6 generators so relaunch with the --harmony flag
13
- if (!utils.generators_available()) {
14
- if (process.execArgv.indexOf('--harmony') != -1) {
15
- console.error('RapydScript needs ES 6 generators, update your version of nodejs');
16
- process.exit(1);
26
+ if (argv.mode === 'self') {
27
+ if (argv.files.length > 0) {
28
+ console.error("WARN: Ignoring input files since --self was passed");
17
29
  }
18
- var args = ['--harmony', module.filename].concat(process.argv.slice(2));
19
- require('child_process').spawn(process.execPath, args, {stdio:'inherit'}).on('exit', function(code, signal) {
20
- process.exit(code);
21
- });
22
- } else {
23
-
24
- var start_time = new Date().getTime();
25
- var path = require('path');
26
-
27
- var argv = load('cli').argv;
28
-
29
- var base_path = path.normalize(path.join(path.dirname(module.filename), ".."));
30
- var src_path = path.join(base_path, 'src');
31
- var lib_path = path.join(base_path, 'dev');
32
- if (!utils.path_exists(path.join(lib_path, 'compiler.js'))) lib_path = path.join(base_path, 'release');
33
-
34
- if (argv.mode === 'self') {
35
- if (argv.files.length > 0) {
36
- console.error("WARN: Ignoring input files since --self was passed");
37
- }
38
- load('self')(base_path, src_path, lib_path, argv.complete, argv.profile);
39
- if (argv.test) {
40
- console.log('\nRunning test suite...\n');
41
- argv.files = []; // Ensure all tests are run
42
- load('test')(argv, base_path, src_path, path.join(base_path, 'dev'));
43
- }
44
- process.exit(0);
45
- } else
46
-
47
- if (argv.mode === 'test') {
48
- load('test')(argv, base_path, src_path, lib_path);
49
- } else
50
-
51
- if (argv.mode === 'lint') {
52
- load('lint').cli(argv, base_path, src_path, lib_path);
53
- } else
54
-
55
- if (argv.mode === 'repl') {
56
- load('repl')({'lib_path':lib_path, 'imp_path':path.join(src_path, 'lib'), show_js:!argv.no_js});
57
- } else
58
-
59
- if (argv.mode === 'gettext') {
60
- load('gettext').cli(argv, base_path, src_path, lib_path);
61
- } else
62
-
63
- if (argv.mode === 'msgfmt') {
64
- load('msgfmt').cli(argv, base_path, src_path, lib_path);
65
- } else
66
-
67
- {
68
- load('compile')(start_time, argv, base_path, src_path, lib_path);
30
+ await (await import('../tools/self.mjs')).default(base_path, src_path, lib_path, argv.complete, argv.profile);
31
+ if (argv.test) {
32
+ console.log('\nRunning test suite...\n');
33
+ argv.files = []; // Ensure all tests are run
34
+ await (await import('../tools/test.mjs')).default(argv, base_path, src_path, path.join(base_path, 'dev'));
69
35
  }
36
+ process.exit(0);
37
+ } else
38
+
39
+ if (argv.mode === 'test') {
40
+ await (await import('../tools/test.mjs')).default(argv, base_path, src_path, lib_path);
41
+ } else
42
+
43
+ if (argv.mode === 'lint') {
44
+ await (await import('../tools/lint.mjs')).cli(argv, base_path, src_path, lib_path);
45
+ } else
46
+
47
+ if (argv.mode === 'fmt') {
48
+ await (await import('../tools/fmt.mjs')).cli(argv, base_path, src_path, lib_path);
49
+ } else
50
+
51
+ if (argv.mode === 'lsp') {
52
+ await (await import('../tools/lsp.mjs')).cli(argv, base_path, src_path, lib_path);
53
+ } else
54
+
55
+ if (argv.mode === 'repl') {
56
+ await (await import('../tools/repl.mjs')).default({'lib_path':lib_path, 'imp_path':path.join(src_path, 'lib'), show_js:!argv.no_js});
57
+ } else
58
+
59
+ if (argv.mode === 'gettext') {
60
+ await (await import('../tools/gettext.mjs')).cli(argv, base_path, src_path, lib_path);
61
+ } else
62
+
63
+ if (argv.mode === 'msgfmt') {
64
+ (await import('../tools/msgfmt.mjs')).cli(argv, base_path, src_path, lib_path);
65
+ } else
66
+
67
+ if (argv.mode === 'web-repl-export') {
68
+ await (await import('../tools/web_repl_export.mjs')).default(base_path, lib_path, argv);
69
+ } else
70
+
71
+ {
72
+ await (await import('../tools/compile.mjs')).default(start_time, argv, base_path, src_path, lib_path);
70
73
  }
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env python3
2
+ # vim:fileencoding=utf-8
3
+
4
+ import os
5
+ import sys
6
+ import json
7
+ import shutil
8
+ import tempfile
9
+ import subprocess
10
+ import re
11
+
12
+ BASE = os.path.dirname(os.path.abspath(__file__))
13
+
14
+ # Maps (bun_target, wheel_platform_tag, binary_name)
15
+ PLATFORMS = [
16
+ ("bun-linux-x64", "manylinux_2_17_x86_64", "rapydscript"),
17
+ ("bun-linux-arm64", "manylinux_2_17_aarch64", "rapydscript"),
18
+ ("bun-linux-x64-musl", "musllinux_1_2_x86_64", "rapydscript"),
19
+ ("bun-linux-arm64-musl", "musllinux_1_2_aarch64", "rapydscript"),
20
+ ("bun-darwin-x64", "macosx_10_9_x86_64", "rapydscript"),
21
+ ("bun-darwin-arm64", "macosx_11_0_arm64", "rapydscript"),
22
+ ("bun-windows-x64", "win_amd64", "rapydscript.exe"),
23
+ ]
24
+
25
+
26
+ def normalize_name(name):
27
+ return re.sub(r"[-_.]+", "_", name).lower()
28
+
29
+
30
+ def load_metadata():
31
+ with open(os.path.join(BASE, "package.json"), "rb") as f:
32
+ return json.load(f)
33
+
34
+
35
+ def build_binary(bun_target, outfile):
36
+ cmd = [
37
+ "bun",
38
+ "bin/build.ts",
39
+ "--compile",
40
+ f"--outfile={outfile}",
41
+ f"--target={bun_target}",
42
+ ]
43
+ print(f" Building {bun_target} ...")
44
+ subprocess.check_call(cmd, cwd=BASE)
45
+
46
+
47
+ def write_wheel_metadata(dist_info_dir, meta, wheel_platform):
48
+ maintainers = meta.get("maintainers", [])
49
+ author = maintainers[0].get("name", "") if maintainers else ""
50
+ author_email = maintainers[0].get("email", "") if maintainers else ""
51
+ repo_url = meta.get("repository", {}).get("url", "")
52
+
53
+ with open(os.path.join(dist_info_dir, "WHEEL"), "w") as f:
54
+ f.write("Wheel-Version: 1.0\n")
55
+ f.write("Generator: wheel.py\n")
56
+ f.write("Root-Is-Purelib: false\n")
57
+ f.write(f"Tag: py3-none-{wheel_platform}\n")
58
+
59
+ with open(os.path.join(dist_info_dir, "METADATA"), "w") as f:
60
+ f.write("Metadata-Version: 2.1\n")
61
+ f.write(f"Name: {meta['name']}\n")
62
+ f.write(f"Version: {meta['version']}\n")
63
+ f.write(f"Summary: {meta.get('description', '')}\n")
64
+ if meta.get("homepage"):
65
+ f.write(f"Home-page: {meta['homepage']}\n")
66
+ if author:
67
+ f.write(f"Author: {author}\n")
68
+ if author_email:
69
+ f.write(f"Author-email: {author_email}\n")
70
+ if meta.get("license"):
71
+ f.write(f"License: {meta['license']}\n")
72
+ if meta.get("keywords"):
73
+ f.write(f"Keywords: {', '.join(meta['keywords'])}\n")
74
+ if repo_url:
75
+ f.write(f"Project-URL: Source Code, {repo_url}\n")
76
+ f.write("Description-Content-Type: text/markdown")
77
+ print(file=f)
78
+ with open("README.md") as s:
79
+ f.write(s.read())
80
+
81
+
82
+ def build_wheel(meta, bun_target, wheel_platform, binary_name, dest_dir, tmpdir):
83
+ norm_name = normalize_name(meta["name"])
84
+ version = meta["version"]
85
+
86
+ suffix = ".exe" if binary_name.endswith(".exe") else ""
87
+ binary_outfile = os.path.join(tmpdir, f"rapydscript-{bun_target}{suffix}")
88
+ build_binary(bun_target, binary_outfile)
89
+
90
+ # Assemble unpacked wheel layout
91
+ wheel_dir = os.path.join(tmpdir, f"{norm_name}-{version}-{wheel_platform}")
92
+ dist_info_dir = os.path.join(wheel_dir, f"{norm_name}-{version}.dist-info")
93
+ scripts_dir = os.path.join(wheel_dir, f"{norm_name}-{version}.data", "scripts")
94
+ os.makedirs(dist_info_dir)
95
+ os.makedirs(scripts_dir)
96
+
97
+ dest_binary = os.path.join(scripts_dir, binary_name)
98
+ shutil.copy2(binary_outfile, dest_binary)
99
+ os.chmod(dest_binary, 0o755)
100
+
101
+ write_wheel_metadata(dist_info_dir, meta, wheel_platform)
102
+
103
+ print(" Packing wheel ...")
104
+ subprocess.check_call(
105
+ [sys.executable, "-m", "wheel", "pack", wheel_dir, "--dest-dir", dest_dir],
106
+ cwd=tmpdir,
107
+ )
108
+
109
+ return f"{norm_name}-{version}-py3-none-{wheel_platform}.whl"
110
+
111
+
112
+ def main():
113
+ meta = load_metadata()
114
+ dest_dir = os.path.join(BASE, "dist")
115
+ os.makedirs(dest_dir, exist_ok=True)
116
+ for x in os.listdir(dest_dir):
117
+ os.remove(os.path.join(dest_dir, x))
118
+
119
+ wheels = []
120
+ with tempfile.TemporaryDirectory(prefix="rapydscript-wheel-") as tmpdir:
121
+ for bun_target, wheel_platform, binary_name in PLATFORMS:
122
+ print(f"\n[{bun_target}]")
123
+ whl = build_wheel(
124
+ meta, bun_target, wheel_platform, binary_name, dest_dir, tmpdir
125
+ )
126
+ wheels.append(whl)
127
+ print(f" -> dist/{whl}")
128
+
129
+ print(f"\nBuilt {len(wheels)} wheels in dist/")
130
+
131
+
132
+ if __name__ == "__main__":
133
+ main()
@@ -0,0 +1,80 @@
1
+ # Plugins to integrate with editors
2
+
3
+ Wires up the RapydScript LSP server (`rapydscript lsp`) as a
4
+ LSP client for various editors (currently only Neovim).
5
+ It also enables treesitter based syntax highlighting.
6
+
7
+ ## Features
8
+
9
+ Provided by the LSP server — no extra plugins required:
10
+
11
+ - **Completions** — in-scope symbols, builtins, keywords, and member completion (`mod.<TAB>`)
12
+ - **Diagnostics** — lint checks and unresolved import warnings, updated as you type
13
+ - **Hover** (`K`) — kind, origin, and docstring of the symbol under the cursor
14
+ - **Go to definition** (`gd`) — including jumping into imported modules
15
+ - **Find references** (`gr`) — resolved across files through the import graph
16
+ - **Rename** (`<leader>rn`) — renames everywhere across the workspace
17
+ - **Code actions** (`<leader>ca`) — remove unused import/local, add `# noqa`, format document
18
+ - **Document formatting** (`<leader>f`) — same as `rapydscript fmt`
19
+ - **Document symbols** — outline view via any symbols picker
20
+
21
+ ## Requirements
22
+
23
+ - `node` (from nodejs) on your `$PATH`
24
+
25
+ - **Syntax highlighting only**: a C compiler on your `$PATH` is required to compile the
26
+ tree-sitter parser the first time the plugin loads. On Linux/macOS any `cc`-compatible
27
+ compiler works (gcc, clang, etc.). On Windows, `cl` (MSVC) or `clang-cl` must be
28
+ available. LSP features work regardless of whether compilation
29
+ succeeds.
30
+
31
+ ## Neovim Installation
32
+
33
+ Needs Neovim >= 0.12. Just add the following to your ``~/.config/nvim/init.lua``, changing the
34
+ options to suit yourself:
35
+
36
+ ```lua
37
+ vim.pack.add({ { src = "https://github.com/kovidgoyal/rapydscript-ng", name = "rapydscript" } }, {
38
+ load = function(plug_data)
39
+ vim.cmd.packadd(plug_data.spec.name)
40
+ for _, pack in ipairs(vim.pack.get({ plug_data.spec.name })) do
41
+ vim.opt.rtp:append(pack.path .. "/editor-plugins/nvim/rapydscript")
42
+ dofile(pack.path .. '/editor-plugins/nvim.lua').setup({
43
+ -- options you should customize
44
+ line_length = 80,
45
+ preferred_quote = "single",
46
+ })
47
+ end
48
+ end
49
+ })
50
+ ```
51
+
52
+ Or if you want to load it manually for older versions of Neovim:
53
+
54
+ ```lua
55
+ dofile("/path/to/rapydscript/editor-plugins/nvim.lua").setup()
56
+ ```
57
+
58
+ ## Configuration reference
59
+
60
+ | Key | Type | Default | Description |
61
+ |-----|------|---------|-------------|
62
+ | `cmd` | `string[]` | `{"rapydscript","lsp"}` | Command used to start the server |
63
+ | `import_path_patterns` | `string[]` | `{".", "src", "src/pyj"}` | Glob patterns relative to the project root; matching directories that contain `.pyj` files are passed automatically as `--import-path` |
64
+ | `line_length` | `number\|nil` | `nil` | Max line length for the formatter, passed as `--line-length` |
65
+ | `preferred_quote` | `string\|nil` | `nil` | `"single"` or `"double"`, passed as `--preferred-quote` |
66
+ | `join_lines` | `boolean\|nil` | `nil` | When `true`, multi-line statements that fit within `line_length` are joined onto one line, passed as `--join-lines`. Disabled by default. |
67
+ | `filetypes` | `string[]` | `{"rapydscript"}` | Filetypes that trigger server attachment |
68
+ | `root_markers` | `string[]` | `{".git","package.json","rapydscript.json"}` | Files/dirs used to detect the project root |
69
+
70
+ ### Automatic import path detection
71
+
72
+ When a project root is found (via `root_markers`), the plugin expands each pattern in
73
+ `import_path_patterns` as a glob relative to that root. Any matching directory that
74
+ contains at least one `.pyj` file is added to the server's import search path
75
+ automatically — no manual configuration needed for standard project layouts.
76
+
77
+ If no project root is detected the setting has no effect.
78
+
79
+ To add custom search locations alongside the defaults, use
80
+ ``import_path_patterns``.