rapydscript-ng 0.8.0 → 0.8.2

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 (72) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +8 -0
  3. package/bin/build.ts +117 -0
  4. package/build_wheels.py +133 -0
  5. package/editor-plugins/README.md +80 -0
  6. package/editor-plugins/nvim.lua +321 -0
  7. package/package.json +1 -1
  8. package/publish.py +27 -17
  9. package/release/compiler.js +8125 -8093
  10. package/release/signatures.json +27 -27
  11. package/release/stdlib_modules.json +1 -0
  12. package/src/ast.pyj +379 -279
  13. package/src/baselib-builtins.pyj +47 -26
  14. package/src/baselib-containers.pyj +105 -92
  15. package/src/baselib-errors.pyj +8 -1
  16. package/src/baselib-internal.pyj +35 -20
  17. package/src/baselib-itertools.pyj +13 -7
  18. package/src/baselib-str.pyj +55 -80
  19. package/src/compiler.pyj +4 -4
  20. package/src/errors.pyj +3 -4
  21. package/src/lib/aes.pyj +46 -32
  22. package/src/lib/elementmaker.pyj +11 -9
  23. package/src/lib/encodings.pyj +15 -5
  24. package/src/lib/gettext.pyj +9 -4
  25. package/src/lib/math.pyj +106 -45
  26. package/src/lib/operator.pyj +10 -10
  27. package/src/lib/pythonize.pyj +2 -1
  28. package/src/lib/random.pyj +28 -21
  29. package/src/lib/re.pyj +490 -17
  30. package/src/lib/traceback.pyj +7 -2
  31. package/src/lib/uuid.pyj +2 -2
  32. package/src/output/classes.pyj +28 -27
  33. package/src/output/codegen.pyj +80 -83
  34. package/src/output/comments.pyj +8 -8
  35. package/src/output/exceptions.pyj +20 -21
  36. package/src/output/functions.pyj +37 -26
  37. package/src/output/literals.pyj +14 -10
  38. package/src/output/loops.pyj +63 -59
  39. package/src/output/modules.pyj +52 -23
  40. package/src/output/operators.pyj +40 -29
  41. package/src/output/statements.pyj +20 -14
  42. package/src/output/stream.pyj +33 -34
  43. package/src/output/utils.pyj +12 -8
  44. package/src/parse.pyj +234 -233
  45. package/src/string_interpolation.pyj +5 -3
  46. package/src/tokenizer.pyj +176 -148
  47. package/src/utils.pyj +31 -14
  48. package/test/fmt.pyj +94 -4
  49. package/test/generic.pyj +9 -0
  50. package/test/lsp.pyj +35 -0
  51. package/test/str.pyj +8 -0
  52. package/tools/cli.mjs +25 -4
  53. package/tools/compile.mjs +7 -3
  54. package/tools/compiler.mjs +34 -2
  55. package/tools/fmt.mjs +269 -22
  56. package/tools/ini.mjs +112 -1
  57. package/tools/lsp.mjs +56 -6
  58. package/tools/repl.mjs +5 -2
  59. package/tools/self.mjs +15 -0
  60. package/tools/test.mjs +100 -0
  61. package/tools/web_repl_export.mjs +4 -0
  62. package/tree-sitter/package.json +1 -1
  63. package/tree-sitter/tree-sitter.json +1 -1
  64. package/editor-plugins/nvim/rapydscript/README.md +0 -184
  65. package/editor-plugins/nvim/rapydscript/bin/rapydscript.so +0 -0
  66. package/editor-plugins/nvim/rapydscript/ftdetect/rapydscript.lua +0 -10
  67. package/editor-plugins/nvim/rapydscript/lua/rapydscript/health.lua +0 -88
  68. package/editor-plugins/nvim/rapydscript/lua/rapydscript/init.lua +0 -279
  69. /package/tree-sitter/queries/{rapydscript/highlights.scm → highlights.scm} +0 -0
  70. /package/tree-sitter/queries/{rapydscript/indents.scm → indents.scm} +0 -0
  71. /package/tree-sitter/queries/{rapydscript/injections.scm → injections.scm} +0 -0
  72. /package/tree-sitter/queries/{rapydscript/locals.scm → locals.scm} +0 -0
package/tools/self.mjs CHANGED
@@ -163,10 +163,25 @@ async function run_single_compile(base_path, src_path, lib_path, profile) {
163
163
  return compiler_changed;
164
164
  }
165
165
 
166
+ async function write_stdlib_modules(src_path, out_path) {
167
+ var lib_dir = path.join(src_path, 'lib');
168
+ var entries = await fs.promises.readdir(lib_dir);
169
+ var modules = entries
170
+ .filter(function (name) { return name.slice(-4) === '.pyj'; })
171
+ .map(function (name) { return name.slice(0, -4); })
172
+ .sort();
173
+ await fs.promises.writeFile(
174
+ path.join(out_path, 'stdlib_modules.json'),
175
+ JSON.stringify(modules),
176
+ 'utf-8'
177
+ );
178
+ }
179
+
166
180
  export default async function compile_self(base_path, src_path, lib_path, complete, profile) {
167
181
  var changed;
168
182
  do {
169
183
  changed = await run_single_compile(base_path, src_path, lib_path, profile);
170
184
  lib_path = path.join(path.dirname(lib_path), 'dev');
171
185
  } while (changed && complete);
186
+ await write_stdlib_modules(src_path, lib_path);
172
187
  }
package/tools/test.mjs CHANGED
@@ -144,9 +144,109 @@ export default async function(argv, base_path, src_path, lib_path) {
144
144
  if (!failed) console.log(colored(file, 'green') + ": test completed successfully\n");
145
145
  else { console.log(colored(file, 'red') + ":\ttest failed\n"); }
146
146
  }
147
+ // Run the bun standalone binary test when doing a full test suite run and bun
148
+ // is available. Individual file runs (argv.files.length > 0) skip it.
149
+ if (!argv.files.length) {
150
+ const bun_test_name = 'bun_standalone_binary';
151
+ let bun_ok = false;
152
+ try {
153
+ bun_ok = await run_bun_standalone_test(base_path, colored);
154
+ } catch (e) {
155
+ console.error(colored(bun_test_name, 'red') + ': unexpected error: ' + (e.stack || e));
156
+ }
157
+ if (bun_ok) {
158
+ console.log(colored(bun_test_name, 'green') + ': test completed successfully\n');
159
+ } else {
160
+ failures.push(bun_test_name);
161
+ console.log(colored(bun_test_name, 'red') + ':\ttest failed\n');
162
+ }
163
+ }
164
+
147
165
  if (failures.length) {
148
166
  console.log(colored('There were ' + failures.length + ' test failure(s):', 'red'));
149
167
  console.log.apply(console, failures);
150
168
  } else console.log(colored('All tests passed!', 'green'));
151
169
  process.exit((failures.length) ? 1 : 0);
152
170
  }
171
+
172
+ async function run_bun_standalone_test(base_path, colored) {
173
+ const { spawnSync } = await import('child_process');
174
+ const test_name = 'bun_standalone_binary';
175
+
176
+ // Check if bun is available.
177
+ const bun_check = spawnSync('bun', ['--version'], { encoding: 'utf-8' });
178
+ if (bun_check.error || bun_check.status !== 0) {
179
+ console.log(colored(test_name, 'yellow') + ': bun not found, skipping');
180
+ return true;
181
+ }
182
+
183
+ // Generate bin/rapydscript.mjs via build.ts.
184
+ const build_ts = path.join(base_path, 'bin', 'build.ts');
185
+ const gen_result = spawnSync('bun', [build_ts], { encoding: 'utf-8', cwd: base_path });
186
+ if (gen_result.status !== 0) {
187
+ console.error(colored(test_name, 'red') + ': build.ts failed:\n' + (gen_result.stderr || gen_result.stdout));
188
+ return false;
189
+ }
190
+
191
+ // Compile the standalone binary.
192
+ const binary_path = path.join(os.tmpdir(), 'rapydscript-bun-test-binary');
193
+ const entry_mjs = path.join(base_path, 'bin', 'rapydscript.mjs');
194
+ const compile_result = spawnSync(
195
+ 'bun', ['build', entry_mjs, '--compile', '--outfile', binary_path],
196
+ { encoding: 'utf-8', cwd: base_path }
197
+ );
198
+ if (compile_result.status !== 0) {
199
+ console.error(colored(test_name, 'red') + ': bun build --compile failed:\n' + (compile_result.stderr || compile_result.stdout));
200
+ return false;
201
+ }
202
+
203
+ // Create test fixtures in a temp directory.
204
+ const tmp_dir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'rs-bun-test-'));
205
+ try {
206
+ // A local module the test will import from the filesystem.
207
+ await fs.promises.writeFile(path.join(tmp_dir, 'greeter.pyj'),
208
+ 'def greet(name):\n return "Hello, " + name\n');
209
+
210
+ // Main source: imports a stdlib module (re) AND the local module.
211
+ const src_file = path.join(tmp_dir, 'main.pyj');
212
+ await fs.promises.writeFile(src_file, [
213
+ 'import re',
214
+ 'from greeter import greet',
215
+ '',
216
+ 'pattern = re.compile(r"world", re.I)',
217
+ 'result = pattern.sub("RapydScript", "Hello world")',
218
+ 'if result != "Hello RapydScript":',
219
+ ' raise AssertionError("re.sub gave: " + result)',
220
+ '',
221
+ 'msg = greet("world")',
222
+ 'if msg != "Hello, world":',
223
+ ' raise AssertionError("greet gave: " + msg)',
224
+ ].join('\n'));
225
+
226
+ const out_file = path.join(tmp_dir, 'main.js');
227
+
228
+ // Compile main.pyj with the standalone binary.
229
+ const run_result = spawnSync(
230
+ binary_path, [src_file, '--output', out_file],
231
+ { encoding: 'utf-8', cwd: tmp_dir }
232
+ );
233
+ if (run_result.status !== 0) {
234
+ console.error(colored(test_name, 'red') + ': standalone binary compilation failed:\n' +
235
+ (run_result.stderr || run_result.stdout));
236
+ return false;
237
+ }
238
+
239
+ // Execute the compiled output with node to verify correctness.
240
+ const js_code = await fs.promises.readFile(out_file, 'utf-8');
241
+ try {
242
+ vm.runInNewContext(js_code, { console }, { filename: out_file });
243
+ } catch (e) {
244
+ console.error(colored(test_name, 'red') + ': compiled output threw:\n' + (e.stack || e));
245
+ return false;
246
+ }
247
+
248
+ return true;
249
+ } finally {
250
+ await fs.promises.rm(tmp_dir, { recursive: true, force: true });
251
+ }
252
+ }
@@ -10,6 +10,10 @@ import path from 'path';
10
10
  import * as utils from './utils.mjs';
11
11
 
12
12
  export default async function run_web_repl_export(base_path, lib_path, argv) {
13
+ if (globalThis.__rapydscript_embedded__) {
14
+ console.error('web-repl-export requires the full source repository and cannot run from a standalone binary.');
15
+ process.exit(1);
16
+ }
13
17
  var output_dir = argv.files[0];
14
18
  if (!output_dir) {
15
19
  console.error('Usage: rapydscript web-repl-export /path/to/export/directory');
@@ -36,7 +36,7 @@
36
36
  "pyj"
37
37
  ],
38
38
  "highlights": [
39
- "queries/rapydscript/highlights.scm"
39
+ "queries/highlights.scm"
40
40
  ]
41
41
  }
42
42
  ]
@@ -10,7 +10,7 @@
10
10
  "pyj"
11
11
  ],
12
12
  "highlights": [
13
- "queries/rapydscript/highlights.scm"
13
+ "queries/highlights.scm"
14
14
  ],
15
15
  "injection-regex": "^(rapydscript|pyj)$"
16
16
  }
@@ -1,184 +0,0 @@
1
- # rapydscript.nvim
2
-
3
- Neovim plugin that wires up the RapydScript LSP server (`rapydscript lsp`) as a
4
- native Neovim LSP client for `.pyj` files.
5
-
6
- ## Features
7
-
8
- Provided by the LSP server — no extra plugins required:
9
-
10
- - **Completions** — in-scope symbols, builtins, keywords, and member completion (`mod.<TAB>`)
11
- - **Diagnostics** — lint checks and unresolved import warnings, updated as you type
12
- - **Hover** (`K`) — kind, origin, and docstring of the symbol under the cursor
13
- - **Go to definition** (`gd`) — including jumping into imported modules
14
- - **Find references** (`gr`) — resolved across files through the import graph
15
- - **Rename** (`<leader>rn`) — renames everywhere across the workspace
16
- - **Code actions** (`<leader>ca`) — remove unused import/local, add `# noqa`, format document
17
- - **Document formatting** (`<leader>f`) — same as `rapydscript fmt`
18
- - **Document symbols** — outline view via any symbols picker
19
-
20
- ## Requirements
21
-
22
- - Neovim ≥ 0.10
23
- - `rapydscript` on your `$PATH` (install with `npm install -g rapydscript-ng`), or the
24
- plugin directory must be inside a RapydScript repository checkout (the repo's own
25
- `bin/rapydscript` is used automatically as a fallback)
26
- - **Syntax highlighting only**: a C compiler on your `$PATH` is required to compile the
27
- tree-sitter parser the first time the plugin loads. On Linux/macOS any `cc`-compatible
28
- compiler works (gcc, clang, etc.). On Windows, `cl` (MSVC) or `clang-cl` must be
29
- available — run Neovim from a Visual Studio Developer Command Prompt, or add the
30
- compiler to your `PATH` manually. LSP features work regardless of whether compilation
31
- succeeds.
32
-
33
- ## Installation
34
-
35
- ### lazy.nvim
36
-
37
- ```lua
38
- {
39
- dir = '/path/to/editor-plugins/nvim/rapydscript',
40
- ft = 'rapydscript',
41
- opts = {},
42
- }
43
- ```
44
-
45
- **Example with options:**
46
-
47
- ```lua
48
- {
49
- dir = "/path/to/editor-plugins/nvim/rapydscript",
50
- ft = "rapydscript",
51
- opts = {
52
- line_length = 100,
53
- preferred_quote = "double",
54
- },
55
- }
56
- ```
57
-
58
- ### Manual (no plugin manager)
59
-
60
- Add the plugin directory to your runtime path and call `setup()`:
61
-
62
- ```lua
63
- -- in ~/.config/nvim/init.lua
64
- vim.opt.rtp:prepend("/path/to/rapydscript/nvim-lsp-plugin")
65
- require("rapydscript").setup()
66
- ```
67
-
68
- ## Configuration reference
69
-
70
- | Key | Type | Default | Description |
71
- |-----|------|---------|-------------|
72
- | `cmd` | `string[]` | `{"rapydscript","lsp"}` | Command used to start the server |
73
- | `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` |
74
- | `line_length` | `number\|nil` | `nil` | Max line length for the formatter, passed as `--line-length` |
75
- | `preferred_quote` | `string\|nil` | `nil` | `"single"` or `"double"`, passed as `--preferred-quote` |
76
- | `filetypes` | `string[]` | `{"rapydscript"}` | Filetypes that trigger server attachment |
77
- | `root_markers` | `string[]` | `{".git","package.json","rapydscript.json"}` | Files/dirs used to detect the project root |
78
-
79
- ### Automatic import path detection
80
-
81
- When a project root is found (via `root_markers`), the plugin expands each pattern in
82
- `import_path_patterns` as a glob relative to that root. Any matching directory that
83
- contains at least one `.pyj` file is added to the server's import search path
84
- automatically — no manual configuration needed for standard project layouts.
85
-
86
- If no project root is detected the setting has no effect.
87
-
88
- To add custom search locations alongside the defaults:
89
-
90
- ```lua
91
- opts = {
92
- import_path_patterns = { ".", "src", "src/pyj", "vendor/pyj" },
93
- }
94
- ```
95
-
96
- To disable auto-detection entirely, pass an empty list:
97
-
98
- ```lua
99
- opts = {
100
- import_path_patterns = {},
101
- }
102
- ```
103
-
104
- ## Changing settings at runtime
105
-
106
- The server accepts live setting updates via the LSP
107
- `workspace/didChangeConfiguration` notification — no restart needed. Call
108
- `require("rapydscript").update_settings(opts)` with any subset of the two
109
- settings keys:
110
-
111
- ```lua
112
- require("rapydscript").update_settings({
113
- line_length = 100, -- new max line length for the formatter
114
- preferred_quote = "double", -- "single" or "double"
115
- })
116
- ```
117
-
118
- Only the keys you pass are updated; the rest keep their current values.
119
-
120
- ### Neovim user commands
121
-
122
- A convenient way to expose this as editor commands — add to your config after
123
- `setup()`:
124
-
125
- ```lua
126
- -- :RapydLineLength 100
127
- vim.api.nvim_create_user_command("RapydLineLength", function(args)
128
- require("rapydscript").update_settings({ line_length = tonumber(args.args) })
129
- end, { nargs = 1, desc = "Set RapydScript LSP line length" })
130
-
131
- -- :RapydQuote double
132
- vim.api.nvim_create_user_command("RapydQuote", function(args)
133
- require("rapydscript").update_settings({ preferred_quote = args.args })
134
- end, { nargs = 1, desc = "Set RapydScript LSP preferred quote style" })
135
- ```
136
-
137
- ### In lazy.nvim config
138
-
139
- If you want the commands available automatically, put them in the `config`
140
- function:
141
-
142
- ```lua
143
- {
144
- dir = "/path/to/rapydscript/nvim-lsp-plugin",
145
- ft = "rapydscript",
146
- opts = { line_length = 100 },
147
- config = function(_, opts)
148
- require("rapydscript").setup(opts)
149
-
150
- vim.api.nvim_create_user_command("RapydLineLength", function(args)
151
- require("rapydscript").update_settings({ line_length = tonumber(args.args) })
152
- end, { nargs = 1 })
153
-
154
- vim.api.nvim_create_user_command("RapydQuote", function(args)
155
- require("rapydscript").update_settings({ preferred_quote = args.args })
156
- end, { nargs = 1 })
157
- end,
158
- }
159
- ```
160
-
161
- Changes to `line_length` and `preferred_quote` take effect on the next format operation.
162
-
163
- ## Keymaps
164
-
165
- The plugin does not define any keymaps — it relies on the standard Neovim LSP
166
- keymaps that are set up by `vim.lsp.buf.*`. A minimal set to add to your LSP
167
- `on_attach` or a `LspAttach` autocmd:
168
-
169
- ```lua
170
- vim.api.nvim_create_autocmd("LspAttach", {
171
- callback = function(ev)
172
- local buf = ev.buf
173
- local map = function(mode, lhs, rhs)
174
- vim.keymap.set(mode, lhs, rhs, { buffer = buf })
175
- end
176
- map("n", "K", vim.lsp.buf.hover)
177
- map("n", "gd", vim.lsp.buf.definition)
178
- map("n", "gr", vim.lsp.buf.references)
179
- map("n", "<leader>rn", vim.lsp.buf.rename)
180
- map("n", "<leader>ca", vim.lsp.buf.code_action)
181
- map("n", "<leader>f", function() vim.lsp.buf.format({ async = true }) end)
182
- end,
183
- })
184
- ```
@@ -1,10 +0,0 @@
1
- #! /usr/bin/env lua
2
- --
3
- -- rapydscript.lua
4
- -- Copyright (C) 2026 Kovid Goyal <kovid at kovidgoyal.net>
5
- --
6
- -- Distributed under terms of the MIT license.
7
- --
8
-
9
-
10
- vim.filetype.add({ extension = { pyj = 'rapydscript' } })
@@ -1,88 +0,0 @@
1
- local M = {}
2
-
3
- local function ver_str(v)
4
- return v and table.concat(v, ".") or "unknown"
5
- end
6
-
7
- function M.check()
8
- local h = vim.health
9
- local rs_ok, rs = pcall(require, "rapydscript")
10
- if not rs_ok then
11
- h.start("rapydscript")
12
- h.error("Failed to load plugin: " .. tostring(rs))
13
- return
14
- end
15
-
16
- -- Binary -------------------------------------------------------------------
17
- h.start("rapydscript binary")
18
- local info = rs._binary_info
19
- local sys = info.system
20
- local repo = info.repo
21
-
22
- if sys then
23
- if sys.rejected then
24
- h.error(string.format(
25
- "System binary rejected: %s (v%s < minimum 0.8.0)",
26
- sys.path, ver_str(sys.version)
27
- ))
28
- elseif sys.version then
29
- h.ok(string.format("System binary: %s (v%s)", sys.path, ver_str(sys.version)))
30
- else
31
- h.warn(string.format("System binary: %s (version unknown)", sys.path))
32
- end
33
- else
34
- h.warn("rapydscript not found on PATH")
35
- end
36
-
37
- if repo then
38
- h.ok(string.format("Repo binary: %s (v%s)", repo.path, ver_str(repo.version)))
39
- else
40
- h.info("No repo binary present")
41
- end
42
-
43
- if not sys and not repo then
44
- h.error("No rapydscript binary found — LSP will not start")
45
- end
46
-
47
- -- Tree-sitter --------------------------------------------------------------
48
- h.start("rapydscript tree-sitter")
49
- if rs._ts_so then
50
- h.ok("Parser compiled: " .. rs._ts_so)
51
- else
52
- h.warn("Tree-sitter parser not compiled (syntax highlighting unavailable)")
53
- end
54
-
55
- -- LSP clients --------------------------------------------------------------
56
- h.start("rapydscript LSP")
57
- local clients = vim.lsp.get_clients({ name = "rapydscript" })
58
- if #clients == 0 then
59
- h.warn("No active rapydscript LSP client")
60
- else
61
- for _, client in ipairs(clients) do
62
- local cmd_str = type(client.config.cmd) == "table"
63
- and table.concat(client.config.cmd, " ")
64
- or tostring(client.config.cmd)
65
- h.ok(string.format("Client #%d root: %s", client.id, client.config.root_dir or "?"))
66
- h.info("cmd: " .. cmd_str)
67
- end
68
- end
69
-
70
- -- Settings -----------------------------------------------------------------
71
- h.start("rapydscript settings")
72
- local opts = rs._active_opts
73
- if not opts then
74
- h.warn("setup() has not been called — showing defaults")
75
- opts = rs.defaults
76
- else
77
- h.ok("setup() called")
78
- end
79
-
80
- h.info("cmd: " .. table.concat(opts.cmd, " "))
81
- h.info("line_length: " .. tostring(opts.line_length))
82
- h.info("preferred_quote: " .. tostring(opts.preferred_quote))
83
- h.info("filetypes: " .. table.concat(opts.filetypes, ", "))
84
- h.info("root_markers: " .. table.concat(opts.root_markers, ", "))
85
- h.info("import_path_patterns: " .. table.concat(opts.import_path_patterns, ", "))
86
- end
87
-
88
- return M