rapydscript-ns 0.8.0

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 (144) hide show
  1. package/.agignore +1 -0
  2. package/.gitattributes +4 -0
  3. package/.github/workflows/ci.yml +38 -0
  4. package/.github/workflows/web-repl-page-deploy.yml +42 -0
  5. package/=template.pyj +5 -0
  6. package/CHANGELOG.md +456 -0
  7. package/CONTRIBUTORS +13 -0
  8. package/HACKING.md +103 -0
  9. package/LICENSE +24 -0
  10. package/README.md +2512 -0
  11. package/TODO.md +327 -0
  12. package/add-toc-to-readme +2 -0
  13. package/bin/export +75 -0
  14. package/bin/rapydscript +70 -0
  15. package/bin/web-repl-export +102 -0
  16. package/build +3 -0
  17. package/package.json +46 -0
  18. package/publish.py +37 -0
  19. package/release/baselib-plain-pretty.js +4370 -0
  20. package/release/baselib-plain-ugly.js +3 -0
  21. package/release/compiler.js +18394 -0
  22. package/release/signatures.json +31 -0
  23. package/session.vim +4 -0
  24. package/setup.cfg +2 -0
  25. package/src/ast.pyj +1356 -0
  26. package/src/baselib-builtins.pyj +279 -0
  27. package/src/baselib-containers.pyj +723 -0
  28. package/src/baselib-errors.pyj +37 -0
  29. package/src/baselib-internal.pyj +421 -0
  30. package/src/baselib-itertools.pyj +97 -0
  31. package/src/baselib-str.pyj +798 -0
  32. package/src/compiler.pyj +36 -0
  33. package/src/errors.pyj +30 -0
  34. package/src/lib/aes.pyj +646 -0
  35. package/src/lib/collections.pyj +695 -0
  36. package/src/lib/elementmaker.pyj +83 -0
  37. package/src/lib/encodings.pyj +126 -0
  38. package/src/lib/functools.pyj +148 -0
  39. package/src/lib/gettext.pyj +569 -0
  40. package/src/lib/itertools.pyj +580 -0
  41. package/src/lib/math.pyj +193 -0
  42. package/src/lib/numpy.pyj +2101 -0
  43. package/src/lib/operator.pyj +11 -0
  44. package/src/lib/pythonize.pyj +20 -0
  45. package/src/lib/random.pyj +118 -0
  46. package/src/lib/re.pyj +470 -0
  47. package/src/lib/traceback.pyj +63 -0
  48. package/src/lib/uuid.pyj +77 -0
  49. package/src/monaco-language-service/analyzer.js +526 -0
  50. package/src/monaco-language-service/builtins.js +543 -0
  51. package/src/monaco-language-service/completions.js +498 -0
  52. package/src/monaco-language-service/diagnostics.js +643 -0
  53. package/src/monaco-language-service/dts.js +550 -0
  54. package/src/monaco-language-service/hover.js +121 -0
  55. package/src/monaco-language-service/index.js +386 -0
  56. package/src/monaco-language-service/scope.js +162 -0
  57. package/src/monaco-language-service/signature.js +144 -0
  58. package/src/output/__init__.pyj +0 -0
  59. package/src/output/classes.pyj +296 -0
  60. package/src/output/codegen.pyj +492 -0
  61. package/src/output/comments.pyj +45 -0
  62. package/src/output/exceptions.pyj +105 -0
  63. package/src/output/functions.pyj +491 -0
  64. package/src/output/literals.pyj +109 -0
  65. package/src/output/loops.pyj +444 -0
  66. package/src/output/modules.pyj +329 -0
  67. package/src/output/operators.pyj +429 -0
  68. package/src/output/statements.pyj +463 -0
  69. package/src/output/stream.pyj +309 -0
  70. package/src/output/treeshake.pyj +182 -0
  71. package/src/output/utils.pyj +72 -0
  72. package/src/parse.pyj +3106 -0
  73. package/src/string_interpolation.pyj +72 -0
  74. package/src/tokenizer.pyj +702 -0
  75. package/src/unicode_aliases.pyj +576 -0
  76. package/src/utils.pyj +192 -0
  77. package/test/_import_one.pyj +37 -0
  78. package/test/_import_two/__init__.pyj +11 -0
  79. package/test/_import_two/level2/__init__.pyj +0 -0
  80. package/test/_import_two/level2/deep.pyj +4 -0
  81. package/test/_import_two/other.pyj +6 -0
  82. package/test/_import_two/sub.pyj +13 -0
  83. package/test/aes_vectors.pyj +421 -0
  84. package/test/annotations.pyj +80 -0
  85. package/test/baselib.pyj +319 -0
  86. package/test/classes.pyj +452 -0
  87. package/test/collections.pyj +152 -0
  88. package/test/decorators.pyj +77 -0
  89. package/test/dict_spread.pyj +76 -0
  90. package/test/docstrings.pyj +39 -0
  91. package/test/elementmaker_test.pyj +45 -0
  92. package/test/ellipsis.pyj +49 -0
  93. package/test/functions.pyj +151 -0
  94. package/test/generators.pyj +41 -0
  95. package/test/generic.pyj +370 -0
  96. package/test/imports.pyj +72 -0
  97. package/test/internationalization.pyj +73 -0
  98. package/test/lint.pyj +164 -0
  99. package/test/loops.pyj +85 -0
  100. package/test/numpy.pyj +734 -0
  101. package/test/omit_function_metadata.pyj +20 -0
  102. package/test/regexp.pyj +55 -0
  103. package/test/repl.pyj +121 -0
  104. package/test/scoped_flags.pyj +76 -0
  105. package/test/starargs.pyj +506 -0
  106. package/test/starred_assign.pyj +104 -0
  107. package/test/str.pyj +198 -0
  108. package/test/subscript_tuple.pyj +53 -0
  109. package/test/unit/fixtures/fibonacci_expected.js +46 -0
  110. package/test/unit/index.js +2989 -0
  111. package/test/unit/language-service-builtins.js +815 -0
  112. package/test/unit/language-service-completions.js +1067 -0
  113. package/test/unit/language-service-dts.js +543 -0
  114. package/test/unit/language-service-hover.js +455 -0
  115. package/test/unit/language-service-scope.js +833 -0
  116. package/test/unit/language-service-signature.js +458 -0
  117. package/test/unit/language-service.js +705 -0
  118. package/test/unit/run-language-service.js +41 -0
  119. package/test/unit/web-repl.js +484 -0
  120. package/tools/build-language-service.js +190 -0
  121. package/tools/cli.js +547 -0
  122. package/tools/compile.js +219 -0
  123. package/tools/compiler.js +108 -0
  124. package/tools/completer.js +131 -0
  125. package/tools/embedded_compiler.js +251 -0
  126. package/tools/export.js +316 -0
  127. package/tools/gettext.js +185 -0
  128. package/tools/ini.js +65 -0
  129. package/tools/lint.js +705 -0
  130. package/tools/msgfmt.js +187 -0
  131. package/tools/repl.js +223 -0
  132. package/tools/self.js +162 -0
  133. package/tools/test.js +118 -0
  134. package/tools/utils.js +128 -0
  135. package/tools/web_repl.js +95 -0
  136. package/try +41 -0
  137. package/web-repl/env.js +74 -0
  138. package/web-repl/index.html +163 -0
  139. package/web-repl/language-service.js +4084 -0
  140. package/web-repl/main.js +254 -0
  141. package/web-repl/prism.css +139 -0
  142. package/web-repl/prism.js +113 -0
  143. package/web-repl/rapydscript.js +435 -0
  144. package/web-repl/sha1.js +25 -0
@@ -0,0 +1,643 @@
1
+ // diagnostics.js — RapydScript linter for Monaco
2
+ // Ported from tools/lint.js; browser-compatible ES module (no Node.js deps).
3
+ // Usage:
4
+ // const d = new Diagnostics(compiler, extraBuiltins);
5
+ // const markers = d.check(sourceCode); // returns Monaco IMarkerData[]
6
+
7
+ const WARN = 1, ERROR = 2;
8
+
9
+ const MESSAGES = {
10
+ 'undef': 'Undefined symbol: "{name}"',
11
+ 'unused-import': '"{name}" is imported but not used',
12
+ 'unused-local': '"{name}" is defined but not used',
13
+ 'loop-shadowed': 'The loop variable "{name}" was previously used in this scope at line: {line}',
14
+ 'extra-semicolon':'This semi-colon is not needed',
15
+ 'eol-semicolon': 'Semi-colons at the end of the line are unnecessary',
16
+ 'func-in-branch': 'Named functions/classes inside a branch are not allowed in strict mode',
17
+ 'syntax-err': 'Syntax error: {name}',
18
+ 'import-err': 'Import error: {name}',
19
+ 'def-after-use': 'The symbol "{name}" is defined (at line {line}) after it is used',
20
+ 'dup-key': 'Duplicate key "{name}" in object literal',
21
+ 'dup-method': 'The method "{name}" was defined previously at line: {line}',
22
+ };
23
+
24
+ // Symbols always available in RapydScript (from tools/lint.js BUILTINS list).
25
+ export const BASE_BUILTINS = (
26
+ 'this self window document chr ord iterator_symbol print len range dir' +
27
+ ' eval undefined arguments abs max min enumerate pow callable reversed sum' +
28
+ ' getattr isFinite setattr hasattr parseInt parseFloat options_object' +
29
+ ' isNaN JSON Math list set list_wrap ρσ_modules require bool int bin' +
30
+ ' float iter Error EvalError set_wrap RangeError ReferenceError SyntaxError' +
31
+ ' str TypeError URIError Exception AssertionError IndexError AttributeError KeyError' +
32
+ ' ValueError ZeroDivisionError map hex filter zip dict dict_wrap UnicodeDecodeError HTMLCollection' +
33
+ ' NodeList alert console Node Symbol NamedNodeMap ρσ_eslice ρσ_delslice Number' +
34
+ ' Boolean encodeURIComponent decodeURIComponent setTimeout setInterval' +
35
+ ' setImmediate clearTimeout clearInterval clearImmediate requestAnimationFrame' +
36
+ ' id repr sorted __name__ equals get_module ρσ_str jstype divmod NaN super Ellipsis'
37
+ ).split(' ');
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // Helpers
41
+ // ---------------------------------------------------------------------------
42
+
43
+ function has_prop(obj, name) {
44
+ return Object.prototype.hasOwnProperty.call(obj, name);
45
+ }
46
+
47
+ function build_scoped_flags(flags_str) {
48
+ const result = Object.create(null);
49
+ if (!flags_str) return result;
50
+ flags_str.split(',').forEach(flag => {
51
+ flag = flag.trim();
52
+ if (!flag) return;
53
+ let val = true;
54
+ if (flag.startsWith('no_')) { val = false; flag = flag.slice(3); }
55
+ result[flag] = val;
56
+ });
57
+ return result;
58
+ }
59
+
60
+ function msg_from_node(ident, name, node, level, line) {
61
+ name = name || (node.name ? (node.name.name || node.name) : '');
62
+ const msg = MESSAGES[ident]
63
+ .replace('{name}', name || '')
64
+ .replace('{line}', line !== undefined ? line : '');
65
+ return {
66
+ start_line: node.start ? node.start.line : undefined,
67
+ start_col: node.start ? node.start.col : undefined,
68
+ end_line: node.end ? node.end.line : undefined,
69
+ end_col: node.end ? node.end.col : undefined,
70
+ ident,
71
+ message: msg,
72
+ level: level || ERROR,
73
+ name,
74
+ other_line: line,
75
+ };
76
+ }
77
+
78
+ // ---------------------------------------------------------------------------
79
+ // Binding
80
+ // ---------------------------------------------------------------------------
81
+
82
+ function Binding(name, node, options) {
83
+ options = options || {};
84
+ this.node = node;
85
+ this.name = name;
86
+ this.is_import = !!options.is_import;
87
+ this.is_function = !!options.is_function;
88
+ this.is_func_arg = !!options.is_func_arg;
89
+ this.is_method = !!options.is_method;
90
+ this.is_loop = false;
91
+ this.used = false;
92
+ }
93
+
94
+ // ---------------------------------------------------------------------------
95
+ // Scope
96
+ // ---------------------------------------------------------------------------
97
+
98
+ function Scope(is_toplevel, parent_scope, is_class) {
99
+ this.parent_scope = parent_scope;
100
+ this.is_toplevel = !!is_toplevel;
101
+ this.is_class = !!is_class;
102
+ this.bindings = Object.create(null);
103
+ this.children = [];
104
+ this.shadowed = [];
105
+ this.undefined_references = Object.create(null);
106
+ this.unused_bindings = Object.create(null);
107
+ this.nonlocals = Object.create(null);
108
+ this.defined_after_use = Object.create(null);
109
+ this.seen_method_names = Object.create(null);
110
+ this.methods = Object.create(null);
111
+ }
112
+
113
+ Scope.prototype.add_binding = function(name, node, options) {
114
+ const already_bound = has_prop(this.bindings, name);
115
+ const b = new Binding(name, node, options);
116
+ if (already_bound) {
117
+ if (this.bindings[name].used) b.used = true;
118
+ this.shadowed.push([name, this.bindings[name], b]);
119
+ }
120
+ this.bindings[name] = b;
121
+ return b;
122
+ };
123
+
124
+ Scope.prototype.add_nonlocal = function(name) {
125
+ this.nonlocals[name] = true;
126
+ };
127
+
128
+ Scope.prototype.register_use = function(name, node) {
129
+ if (has_prop(this.bindings, name)) {
130
+ this.bindings[name].used = true;
131
+ } else {
132
+ this.undefined_references[name] = node;
133
+ }
134
+ };
135
+
136
+ Scope.prototype.for_descendants = function(func) {
137
+ this.children.forEach(child => { func(child); child.for_descendants(func); });
138
+ };
139
+
140
+ Scope.prototype.finalize = function() {
141
+ // Detect defined-after-use
142
+ Object.keys(this.undefined_references).forEach(name => {
143
+ if (has_prop(this.bindings, name) && !has_prop(this.nonlocals, name)) {
144
+ const b = this.bindings[name];
145
+ b.used = true;
146
+ if (!has_prop(this.defined_after_use, name))
147
+ this.defined_after_use[name] = [this.undefined_references[name], b];
148
+ delete this.undefined_references[name];
149
+ }
150
+ if (has_prop(this.methods, name)) delete this.undefined_references[name];
151
+ });
152
+
153
+ // Detect unused bindings
154
+ Object.keys(this.bindings).forEach(name => {
155
+ const b = this.bindings[name];
156
+ let found = false;
157
+ this.for_descendants(scope => {
158
+ if (has_prop(scope.undefined_references, name)) {
159
+ found = true;
160
+ delete scope.undefined_references[name];
161
+ } else if (has_prop(scope.nonlocals, name) && has_prop(scope.bindings, name)) {
162
+ found = true;
163
+ }
164
+ });
165
+ if (!found && !b.used && !b.is_loop)
166
+ this.unused_bindings[name] = b;
167
+ });
168
+ };
169
+
170
+ Scope.prototype.messages = function() {
171
+ const ans = [];
172
+
173
+ Object.keys(this.undefined_references).forEach(name => {
174
+ if (!(this.is_toplevel && has_prop(this.nonlocals, name))) {
175
+ const node = this.undefined_references[name];
176
+ ans.push(msg_from_node('undef', name, node));
177
+ }
178
+ });
179
+
180
+ Object.keys(this.unused_bindings).forEach(name => {
181
+ const b = this.unused_bindings[name];
182
+ if (b.is_import) {
183
+ ans.push(msg_from_node('unused-import', name, b.node));
184
+ } else if (!this.is_toplevel && !this.is_class && !b.is_func_arg && !b.is_method && !has_prop(this.nonlocals, name)) {
185
+ ans.push(msg_from_node('unused-local', name, b.node));
186
+ }
187
+ });
188
+
189
+ this.shadowed.forEach(([name, first, second]) => {
190
+ if (second.is_loop && !first.is_loop) {
191
+ const line = first.node.start ? first.node.start.line : undefined;
192
+ ans.push(msg_from_node('loop-shadowed', name, second.node, ERROR, line));
193
+ }
194
+ });
195
+
196
+ Object.keys(this.defined_after_use).forEach(name => {
197
+ const [use, binding] = this.defined_after_use[name];
198
+ ans.push(msg_from_node('def-after-use', name, use, ERROR, binding.node.start.line));
199
+ });
200
+
201
+ return ans;
202
+ };
203
+
204
+ // ---------------------------------------------------------------------------
205
+ // Linter — mirrors the Linter in tools/lint.js
206
+ // ---------------------------------------------------------------------------
207
+
208
+ function Linter(RS, toplevel, code, builtins) {
209
+ this.RS = RS;
210
+ this.scopes = [];
211
+ this.walked_scopes = [];
212
+ this.current_node = null;
213
+ this.branches = [];
214
+ this.messages = [];
215
+ this.builtins = builtins;
216
+
217
+ this.add_binding = function(name, binding_node) {
218
+ const scope = this.scopes[this.scopes.length - 1];
219
+ const node = this.current_node;
220
+ const opts = {
221
+ is_import: (node instanceof RS.AST_Import || node instanceof RS.AST_ImportedVar),
222
+ is_function: (node instanceof RS.AST_Lambda),
223
+ is_method: (node instanceof RS.AST_Method),
224
+ is_func_arg: (node instanceof RS.AST_SymbolFunarg),
225
+ };
226
+ return scope.add_binding(name, binding_node || node, opts);
227
+ };
228
+
229
+ this.add_nonlocal = function(name) {
230
+ this.scopes[this.scopes.length - 1].add_nonlocal(name);
231
+ };
232
+
233
+ this.register_use = function(name) {
234
+ this.scopes[this.scopes.length - 1].register_use(name, this.current_node);
235
+ };
236
+
237
+ this.handle_import = function() {
238
+ const node = this.current_node;
239
+ if (!node.argnames) {
240
+ const name = node.alias ? node.alias.name : node.key.split('.', 1)[0];
241
+ this.add_binding(name, node.alias || node);
242
+ }
243
+ };
244
+
245
+ this.handle_imported_var = function() {
246
+ const node = this.current_node;
247
+ const name = node.alias ? node.alias.name : node.name;
248
+ this.add_binding(name);
249
+ };
250
+
251
+ this.handle_lambda = function() {
252
+ const node = this.current_node;
253
+ const name = node.name ? node.name.name : undefined;
254
+ const scope = this.scopes[this.scopes.length - 1];
255
+ if (this.branches.length && name)
256
+ this.messages.push(msg_from_node('func-in-branch', name, node));
257
+ if (name) {
258
+ if (node instanceof RS.AST_Method) {
259
+ scope.methods[name] = true;
260
+ if (has_prop(scope.seen_method_names, name)) {
261
+ if (!node.is_setter)
262
+ this.messages.push(msg_from_node('dup-method', name, node, WARN, scope.seen_method_names[name]));
263
+ } else {
264
+ scope.seen_method_names[name] = node.start.line;
265
+ }
266
+ } else {
267
+ this.add_binding(name);
268
+ }
269
+ }
270
+ };
271
+
272
+ this.handle_assign = function() {
273
+ const node = this.current_node;
274
+ const handle_destructured = (flat) => {
275
+ flat.forEach(cnode => {
276
+ if (cnode instanceof RS.AST_Starred && cnode.expression instanceof RS.AST_SymbolRef) {
277
+ const sym = cnode.expression;
278
+ this.current_node = sym;
279
+ sym.lint_visited = true;
280
+ this.add_binding(sym.name);
281
+ this.current_node = node;
282
+ } else if (cnode instanceof RS.AST_SymbolRef) {
283
+ this.current_node = cnode;
284
+ cnode.lint_visited = true;
285
+ this.add_binding(cnode.name);
286
+ this.current_node = node;
287
+ }
288
+ });
289
+ };
290
+ if (node.left instanceof RS.AST_SymbolRef) {
291
+ node.left.lint_visited = node.operator === '=';
292
+ if (node.operator === '=') {
293
+ this.current_node = node.left;
294
+ this.add_binding(node.left.name);
295
+ this.current_node = node;
296
+ }
297
+ } else if (node.left instanceof RS.AST_Array) {
298
+ handle_destructured(node.left.flatten());
299
+ } else if (node.left instanceof RS.AST_Seq && node.left.car instanceof RS.AST_SymbolRef) {
300
+ handle_destructured(node.left.to_array());
301
+ }
302
+ };
303
+
304
+ this.handle_named_expr = function() {
305
+ const node = this.current_node;
306
+ // Walrus operator: name := value — treat the name as a new binding.
307
+ if (node.name instanceof RS.AST_SymbolRef) {
308
+ node.name.lint_visited = true;
309
+ this.current_node = node.name;
310
+ this.add_binding(node.name.name);
311
+ this.current_node = node;
312
+ }
313
+ };
314
+
315
+ this.handle_vardef = function() {
316
+ const node = this.current_node;
317
+ if (node.name instanceof RS.AST_SymbolNonlocal) {
318
+ this.add_nonlocal(node.name.name);
319
+ } else {
320
+ this.add_binding(node.name.name, node.name);
321
+ }
322
+ };
323
+
324
+ this.handle_symbol_ref = function() {
325
+ this.register_use(this.current_node.name);
326
+ };
327
+
328
+ this.handle_decorator = function() {
329
+ const node = this.current_node.expression;
330
+ if (node instanceof RS.AST_SymbolRef &&
331
+ RS.compile_time_decorators.indexOf(node.name) !== -1)
332
+ node.lint_visited = true;
333
+ };
334
+
335
+ this.handle_scope = function() {
336
+ const node = this.current_node;
337
+ const parent = this.scopes.length ? this.scopes[this.scopes.length - 1] : null;
338
+ const nscope = new Scope(
339
+ node instanceof RS.AST_Toplevel,
340
+ parent,
341
+ node instanceof RS.AST_Class
342
+ );
343
+ if (parent) parent.children.push(nscope);
344
+ this.scopes.push(nscope);
345
+ };
346
+
347
+ this.handle_symbol_funarg = function() {
348
+ this.add_binding(this.current_node.name);
349
+ };
350
+
351
+ this.handle_comprehension = function() {
352
+ this.handle_scope();
353
+ this.handle_for_in();
354
+ // Add bindings for inner clause loop variables (nested comprehensions)
355
+ const node = this.current_node;
356
+ if (node.clauses && node.clauses.length) {
357
+ node.clauses.forEach(clause => {
358
+ this.current_node = clause;
359
+ this.handle_for_in();
360
+ });
361
+ this.current_node = node;
362
+ }
363
+ };
364
+
365
+ this.handle_for_in = function() {
366
+ const node = this.current_node;
367
+ if (node.init instanceof RS.AST_SymbolRef) {
368
+ this.add_binding(node.init.name).is_loop = true;
369
+ node.init.lint_visited = true;
370
+ } else if (node.init instanceof RS.AST_Array) {
371
+ node.init.elements.forEach(cnode => {
372
+ if (cnode instanceof RS.AST_Seq) cnode = cnode.to_array();
373
+ if (cnode instanceof RS.AST_SymbolRef) cnode = [cnode];
374
+ if (Array.isArray(cnode)) {
375
+ cnode.forEach(elem => {
376
+ if (elem instanceof RS.AST_SymbolRef) {
377
+ this.current_node = elem;
378
+ elem.lint_visited = true;
379
+ this.add_binding(elem.name).is_loop = true;
380
+ this.current_node = node;
381
+ }
382
+ });
383
+ }
384
+ });
385
+ }
386
+ };
387
+
388
+ this.handle_for_js = function() {
389
+ const js = this.current_node.condition.value;
390
+ const statements = js.split(';');
391
+ let decl = statements[0].trim();
392
+ if (decl.startsWith('var ')) decl = decl.slice(4);
393
+ decl.split(',').forEach(part => {
394
+ const m = /^[a-zA-Z0-9_]+/.exec(part.trimLeft());
395
+ if (m) this.add_binding(m[0]);
396
+ });
397
+ };
398
+
399
+ this.handle_except = function() {
400
+ const node = this.current_node;
401
+ if (node.argname) this.add_binding(node.argname.name, node.argname);
402
+ };
403
+
404
+ this.handle_empty_statement = function() {
405
+ if (this.current_node.stype === ';')
406
+ this.messages.push(msg_from_node('extra-semicolon', ';', this.current_node, WARN));
407
+ };
408
+
409
+ this.handle_class = function() {
410
+ const node = this.current_node;
411
+ if (node.name) {
412
+ node.name.lint_visited = true;
413
+ this.add_binding(node.name.name, node.name);
414
+ }
415
+ };
416
+
417
+ this.handle_object_literal = function() {
418
+ const node = this.current_node;
419
+ const seen = Object.create(null);
420
+ (node.properties || []).forEach(prop => {
421
+ if (prop instanceof RS.AST_ObjectSpread) return;
422
+ if (prop.key instanceof RS.AST_Constant) {
423
+ const val = prop.key.value;
424
+ if (has_prop(seen, val))
425
+ this.messages.push(msg_from_node('dup-key', val, prop));
426
+ seen[val] = true;
427
+ }
428
+ });
429
+ };
430
+
431
+ this.handle_call = function() {
432
+ const node = this.current_node;
433
+ if (node.args.kwargs)
434
+ node.args.kwargs.forEach(kw => { kw[0].lint_visited = true; });
435
+ };
436
+
437
+ this.handle_with_clause = function() {
438
+ const node = this.current_node;
439
+ if (node.alias) this.add_binding(node.alias.name);
440
+ };
441
+
442
+ // The visitor function called by toplevel.walk()
443
+ this._visit = function(node, cont) {
444
+ if (node.lint_visited) return;
445
+ this.current_node = node;
446
+
447
+ const scope_count = this.scopes.length;
448
+ const branch_count = this.branches.length;
449
+
450
+ if (node instanceof RS.AST_If || node instanceof RS.AST_Try ||
451
+ node instanceof RS.AST_Catch || node instanceof RS.AST_Except ||
452
+ node instanceof RS.AST_Else) {
453
+ this.branches.push(1);
454
+ }
455
+
456
+ if (node instanceof RS.AST_Lambda) this.handle_lambda();
457
+ else if (node instanceof RS.AST_Import) this.handle_import();
458
+ else if (node instanceof RS.AST_ImportedVar) this.handle_imported_var();
459
+ else if (node instanceof RS.AST_Class) this.handle_class();
460
+ else if (node instanceof RS.AST_BaseCall) this.handle_call();
461
+ else if (node instanceof RS.AST_Assign) this.handle_assign();
462
+ else if (node instanceof RS.AST_NamedExpr) this.handle_named_expr();
463
+ else if (node instanceof RS.AST_VarDef) this.handle_vardef();
464
+ else if (node instanceof RS.AST_SymbolRef) this.handle_symbol_ref();
465
+ else if (node instanceof RS.AST_Decorator) this.handle_decorator();
466
+ else if (node instanceof RS.AST_SymbolFunarg) this.handle_symbol_funarg();
467
+ else if (node instanceof RS.AST_ListComprehension) this.handle_comprehension();
468
+ else if (node instanceof RS.AST_ForIn) this.handle_for_in();
469
+ else if (node instanceof RS.AST_ForJS) this.handle_for_js();
470
+ else if (node instanceof RS.AST_Except) this.handle_except();
471
+ else if (node instanceof RS.AST_EmptyStatement) this.handle_empty_statement();
472
+ else if (node instanceof RS.AST_WithClause) this.handle_with_clause();
473
+ else if (node instanceof RS.AST_Object) this.handle_object_literal();
474
+
475
+ if (node instanceof RS.AST_Scope) this.handle_scope();
476
+
477
+ if (cont !== undefined) cont();
478
+
479
+ if (this.scopes.length > scope_count) {
480
+ this.scopes[this.scopes.length - 1].finalize();
481
+ this.walked_scopes.push(this.scopes.pop());
482
+ }
483
+ if (this.branches.length > branch_count) this.branches.pop();
484
+ };
485
+
486
+ // Collect all messages, apply noqa filters
487
+ this.resolve = function(noqa) {
488
+ noqa = noqa || {};
489
+ let messages = this.messages.slice();
490
+
491
+ // eol-semicolon: scan source lines
492
+ code.split('\n').forEach((line, idx) => {
493
+ const trimmed = line.trimRight();
494
+ if (trimmed[trimmed.length - 1] === ';') {
495
+ const ident = 'eol-semicolon';
496
+ messages.push({
497
+ ident, message: MESSAGES[ident], level: WARN, name: ';',
498
+ start_line: idx + 1, start_col: trimmed.lastIndexOf(';'),
499
+ });
500
+ }
501
+ });
502
+
503
+ // Collect scope messages
504
+ this.walked_scopes.forEach(scope => {
505
+ messages = messages.concat(scope.messages());
506
+ });
507
+
508
+ // Filter: builtins, noqa
509
+ messages = messages.filter(msg => {
510
+ if (has_prop(noqa, msg.ident)) return false;
511
+ if (msg.ident === 'undef' && has_prop(this.builtins, msg.name)) return false;
512
+ return true;
513
+ });
514
+
515
+ messages.sort((a, b) => {
516
+ const dl = (a.start_line || 0) - (b.start_line || 0);
517
+ return dl !== 0 ? dl : (a.start_col || 0) - (b.start_col || 0);
518
+ });
519
+
520
+ return messages;
521
+ };
522
+ }
523
+
524
+ // ---------------------------------------------------------------------------
525
+ // Convert lint message → Monaco IMarkerData
526
+ // markerSeverity should be { Error, Warning, Info, Hint } numeric values
527
+ // ---------------------------------------------------------------------------
528
+
529
+ function to_marker(msg, markerSeverity) {
530
+ const sline = msg.start_line || 1;
531
+ const scol = (msg.start_col !== undefined ? msg.start_col : 0);
532
+ const eline = msg.end_line || sline;
533
+ const ecol = (msg.end_col !== undefined ? msg.end_col : scol);
534
+
535
+ return {
536
+ severity: msg.level === WARN ? markerSeverity.Warning : markerSeverity.Error,
537
+ message: msg.message,
538
+ source: 'rapydscript',
539
+ startLineNumber: sline,
540
+ startColumn: scol + 1, // lint uses 0-indexed cols
541
+ endLineNumber: eline,
542
+ endColumn: ecol + 2, // exclusive end, 1-indexed
543
+ };
544
+ }
545
+
546
+ // ---------------------------------------------------------------------------
547
+ // Public class
548
+ // ---------------------------------------------------------------------------
549
+
550
+ export class Diagnostics {
551
+ /**
552
+ * @param {object} compiler - window.RapydScript (the compiled compiler)
553
+ * @param {object} [extraBuiltins] - additional symbol names to treat as defined
554
+ * @param {string} [pythonFlags] - comma-separated python flags to enable globally
555
+ * (e.g. "dict_literals,overload_getitem"). Equivalent to a global
556
+ * `from __python__ import` at the top of every file.
557
+ */
558
+ constructor(compiler, extraBuiltins, pythonFlags) {
559
+ this._RS = compiler;
560
+
561
+ // Build the builtins lookup table
562
+ this._builtins = Object.create(null);
563
+ BASE_BUILTINS.forEach(s => { this._builtins[s] = true; });
564
+
565
+ // Add NATIVE_CLASSES from the compiler (Array, Promise, Map, etc.)
566
+ if (compiler.NATIVE_CLASSES) {
567
+ Object.keys(compiler.NATIVE_CLASSES).forEach(name => {
568
+ this._builtins[name] = true;
569
+ });
570
+ }
571
+
572
+ // Caller-supplied extra builtins (e.g. from d.ts globals)
573
+ if (extraBuiltins) {
574
+ Object.keys(extraBuiltins).forEach(name => {
575
+ this._builtins[name] = true;
576
+ });
577
+ }
578
+
579
+ // Pre-set Python compatibility flags
580
+ this._scoped_flags = build_scoped_flags(pythonFlags);
581
+ }
582
+
583
+ /**
584
+ * Add additional global names (called by DtsRegistry when d.ts files are loaded).
585
+ * @param {string[]} names
586
+ */
587
+ addGlobals(names) {
588
+ names.forEach(n => { this._builtins[n] = true; });
589
+ }
590
+
591
+ /**
592
+ * Run the linter on a source string and return Monaco marker objects.
593
+ *
594
+ * @param {string} code - RapydScript source
595
+ * @param {object} [options]
596
+ * @param {object} [options.virtualFiles] - map of module name → source
597
+ * @param {object} [options.noqa] - map of ident → true to suppress
598
+ * @param {object} [options.markerSeverity] - monaco.MarkerSeverity enum
599
+ * @returns {Array} Monaco IMarkerData[]
600
+ */
601
+ check(code, options) {
602
+ options = options || {};
603
+ const RS = this._RS;
604
+ const markerSeverity = options.markerSeverity || { Error: 8, Warning: 4 };
605
+ const noqa = options.noqa || {};
606
+
607
+ let toplevel, messages;
608
+
609
+ // --- Parse ---
610
+ try {
611
+ toplevel = RS.parse(code, {
612
+ filename: 'editor.pyj',
613
+ for_linting: true,
614
+ // virtual files let the parser resolve imports without disk access
615
+ ...(options.virtualFiles ? { virtual_files: options.virtualFiles } : {}),
616
+ ...(Object.keys(this._scoped_flags).length ? { scoped_flags: this._scoped_flags } : {}),
617
+ });
618
+ } catch (e) {
619
+ if (e instanceof RS.SyntaxError || e instanceof RS.ImportError) {
620
+ const ident = (e instanceof RS.SyntaxError) ? 'syntax-err' : 'import-err';
621
+ const line = e.line || 1;
622
+ const col = e.col || 0;
623
+ return [{
624
+ severity: markerSeverity.Error,
625
+ message: e.message,
626
+ source: 'rapydscript',
627
+ startLineNumber: line,
628
+ startColumn: col + 1,
629
+ endLineNumber: line,
630
+ endColumn: col + 2,
631
+ }];
632
+ }
633
+ throw e;
634
+ }
635
+
636
+ // --- Lint ---
637
+ const linter = new Linter(RS, toplevel, code, this._builtins);
638
+ toplevel.walk(linter);
639
+ messages = linter.resolve(noqa);
640
+
641
+ return messages.map(m => to_marker(m, markerSeverity));
642
+ }
643
+ }