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.
- package/CHANGELOG.md +28 -0
- package/README.md +80 -26
- package/bin/build.ts +117 -0
- package/bin/package.json +1 -0
- package/bin/rapydscript +65 -62
- package/build_wheels.py +133 -0
- package/editor-plugins/README.md +80 -0
- package/editor-plugins/nvim.lua +321 -0
- package/local-agent.md +28 -0
- package/package.json +4 -5
- package/publish.py +27 -17
- package/release/baselib-plain-pretty.js +448 -326
- package/release/baselib-plain-ugly.js +3 -3
- package/release/compiler.js +9027 -8139
- package/release/signatures.json +27 -27
- package/release/stdlib_modules.json +1 -0
- package/src/ast.pyj +428 -280
- package/src/baselib-builtins.pyj +49 -28
- package/src/baselib-containers.pyj +241 -226
- package/src/baselib-errors.pyj +8 -1
- package/src/baselib-internal.pyj +38 -23
- package/src/baselib-itertools.pyj +13 -7
- package/src/baselib-str.pyj +59 -84
- package/src/compiler.pyj +4 -4
- package/src/errors.pyj +3 -4
- package/src/lib/aes.pyj +46 -32
- package/src/lib/elementmaker.pyj +11 -9
- package/src/lib/encodings.pyj +15 -5
- package/src/lib/gettext.pyj +9 -4
- package/src/lib/math.pyj +106 -45
- package/src/lib/operator.pyj +10 -10
- package/src/lib/pythonize.pyj +2 -1
- package/src/lib/random.pyj +28 -21
- package/src/lib/re.pyj +492 -19
- package/src/lib/traceback.pyj +171 -29
- package/src/lib/uuid.pyj +2 -2
- package/src/output/classes.pyj +28 -27
- package/src/output/codegen.pyj +87 -83
- package/src/output/comments.pyj +8 -8
- package/src/output/exceptions.pyj +20 -21
- package/src/output/functions.pyj +66 -64
- package/src/output/literals.pyj +24 -23
- package/src/output/loops.pyj +84 -142
- package/src/output/modules.pyj +55 -68
- package/src/output/operators.pyj +40 -29
- package/src/output/statements.pyj +21 -15
- package/src/output/stream.pyj +82 -56
- package/src/output/utils.pyj +12 -8
- package/src/parse.pyj +971 -620
- package/src/string_interpolation.pyj +5 -3
- package/src/tokenizer.pyj +188 -148
- package/src/utils.pyj +32 -15
- package/test/_treeshake_mod.pyj +30 -0
- package/test/_treeshake_side_effect.pyj +9 -0
- package/test/ast_serialization.pyj +392 -0
- package/test/async.pyj +95 -0
- package/test/baselib.pyj +1 -2
- package/test/embedded_compiler.pyj +57 -0
- package/test/fmt.pyj +291 -0
- package/test/generic.pyj +16 -3
- package/test/imports.pyj +8 -6
- package/test/internationalization.pyj +38 -37
- package/test/lint.pyj +120 -117
- package/test/lsp.pyj +257 -0
- package/test/repl.pyj +70 -65
- package/test/sourcemaps.pyj +315 -0
- package/test/starargs.pyj +46 -39
- package/test/str.pyj +8 -0
- package/test/traceback.pyj +263 -0
- package/test/treeshaking.pyj +181 -0
- package/test/web_repl_export.pyj +88 -0
- package/tools/ast_serialize.mjs +557 -0
- package/tools/cli.mjs +531 -0
- package/tools/compile.mjs +205 -0
- package/tools/compiler.mjs +159 -0
- package/tools/{completer.js → completer.mjs} +6 -6
- package/tools/{embedded_compiler.js → embedded_compiler.mjs} +17 -13
- package/tools/export.js +109 -56
- package/tools/fmt.mjs +975 -0
- package/tools/{gettext.js → gettext.mjs} +46 -53
- package/tools/ini.mjs +175 -0
- package/tools/{lint.js → lint.mjs} +78 -55
- package/tools/lsp.mjs +964 -0
- package/tools/lsp_protocol.mjs +259 -0
- package/tools/lsp_symbols.mjs +418 -0
- package/tools/{msgfmt.js → msgfmt.mjs} +18 -18
- package/tools/{repl.js → repl.mjs} +56 -42
- package/tools/{self.js → self.mjs} +71 -46
- package/tools/sourcemap.mjs +123 -0
- package/tools/test.mjs +252 -0
- package/tools/treeshake.mjs +400 -0
- package/tools/{utils.js → utils.mjs} +26 -23
- package/tools/{web_repl.js → web_repl.mjs} +15 -13
- package/tools/web_repl_export.mjs +97 -0
- package/tree-sitter/README.md +101 -0
- package/tree-sitter/grammar.js +992 -0
- package/tree-sitter/package.json +43 -0
- package/tree-sitter/queries/highlights.scm +232 -0
- package/tree-sitter/queries/indents.scm +64 -0
- package/tree-sitter/queries/injections.scm +14 -0
- package/tree-sitter/queries/locals.scm +108 -0
- package/tree-sitter/src/grammar.json +4976 -0
- package/tree-sitter/src/node-types.json +2901 -0
- package/tree-sitter/src/parser.c +196465 -0
- package/tree-sitter/src/scanner.c +294 -0
- package/tree-sitter/src/tree_sitter/alloc.h +54 -0
- package/tree-sitter/src/tree_sitter/array.h +330 -0
- package/tree-sitter/src/tree_sitter/parser.h +286 -0
- package/tree-sitter/test/corpus/chaining.txt +99 -0
- package/tree-sitter/test/corpus/classes.txt +147 -0
- package/tree-sitter/test/corpus/comprehensions.txt +155 -0
- package/tree-sitter/test/corpus/containers.txt +242 -0
- package/tree-sitter/test/corpus/control_flow.txt +361 -0
- package/tree-sitter/test/corpus/decorators.txt +64 -0
- package/tree-sitter/test/corpus/existential.txt +102 -0
- package/tree-sitter/test/corpus/functions.txt +293 -0
- package/tree-sitter/test/corpus/imports.txt +117 -0
- package/tree-sitter/test/corpus/literals.txt +135 -0
- package/tree-sitter/test/corpus/operators.txt +296 -0
- package/tree-sitter/test/corpus/statements.txt +189 -0
- package/tree-sitter/test/corpus/strings.txt +90 -0
- package/tree-sitter/test/corpus/subscripts.txt +227 -0
- package/tree-sitter/tree-sitter.json +36 -0
- package/web-repl/env.js +35 -23
- package/web-repl/main.js +8 -8
- package/bin/export +0 -75
- package/bin/web-repl-export +0 -102
- package/tools/cli.js +0 -523
- package/tools/compile.js +0 -184
- package/tools/compiler.js +0 -84
- package/tools/ini.js +0 -65
- 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
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
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
|
|
1458
|
-
|
|
1459
|
-
``--js-version`` option
|
|
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)`.
|
|
1495
|
-
|
|
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
|
-
|
|
1507
|
-
var
|
|
1508
|
-
|
|
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.
|
|
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
|
-
*
|
|
1531
|
-
|
|
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
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
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
|
+
}
|
package/bin/package.json
ADDED
|
@@ -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
|
-
|
|
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
|
-
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
|
|
13
|
-
if (
|
|
14
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
}
|
package/build_wheels.py
ADDED
|
@@ -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``.
|