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.
- package/.agignore +1 -0
- package/.gitattributes +4 -0
- package/.github/workflows/ci.yml +38 -0
- package/.github/workflows/web-repl-page-deploy.yml +42 -0
- package/=template.pyj +5 -0
- package/CHANGELOG.md +456 -0
- package/CONTRIBUTORS +13 -0
- package/HACKING.md +103 -0
- package/LICENSE +24 -0
- package/README.md +2512 -0
- package/TODO.md +327 -0
- package/add-toc-to-readme +2 -0
- package/bin/export +75 -0
- package/bin/rapydscript +70 -0
- package/bin/web-repl-export +102 -0
- package/build +3 -0
- package/package.json +46 -0
- package/publish.py +37 -0
- package/release/baselib-plain-pretty.js +4370 -0
- package/release/baselib-plain-ugly.js +3 -0
- package/release/compiler.js +18394 -0
- package/release/signatures.json +31 -0
- package/session.vim +4 -0
- package/setup.cfg +2 -0
- package/src/ast.pyj +1356 -0
- package/src/baselib-builtins.pyj +279 -0
- package/src/baselib-containers.pyj +723 -0
- package/src/baselib-errors.pyj +37 -0
- package/src/baselib-internal.pyj +421 -0
- package/src/baselib-itertools.pyj +97 -0
- package/src/baselib-str.pyj +798 -0
- package/src/compiler.pyj +36 -0
- package/src/errors.pyj +30 -0
- package/src/lib/aes.pyj +646 -0
- package/src/lib/collections.pyj +695 -0
- package/src/lib/elementmaker.pyj +83 -0
- package/src/lib/encodings.pyj +126 -0
- package/src/lib/functools.pyj +148 -0
- package/src/lib/gettext.pyj +569 -0
- package/src/lib/itertools.pyj +580 -0
- package/src/lib/math.pyj +193 -0
- package/src/lib/numpy.pyj +2101 -0
- package/src/lib/operator.pyj +11 -0
- package/src/lib/pythonize.pyj +20 -0
- package/src/lib/random.pyj +118 -0
- package/src/lib/re.pyj +470 -0
- package/src/lib/traceback.pyj +63 -0
- package/src/lib/uuid.pyj +77 -0
- package/src/monaco-language-service/analyzer.js +526 -0
- package/src/monaco-language-service/builtins.js +543 -0
- package/src/monaco-language-service/completions.js +498 -0
- package/src/monaco-language-service/diagnostics.js +643 -0
- package/src/monaco-language-service/dts.js +550 -0
- package/src/monaco-language-service/hover.js +121 -0
- package/src/monaco-language-service/index.js +386 -0
- package/src/monaco-language-service/scope.js +162 -0
- package/src/monaco-language-service/signature.js +144 -0
- package/src/output/__init__.pyj +0 -0
- package/src/output/classes.pyj +296 -0
- package/src/output/codegen.pyj +492 -0
- package/src/output/comments.pyj +45 -0
- package/src/output/exceptions.pyj +105 -0
- package/src/output/functions.pyj +491 -0
- package/src/output/literals.pyj +109 -0
- package/src/output/loops.pyj +444 -0
- package/src/output/modules.pyj +329 -0
- package/src/output/operators.pyj +429 -0
- package/src/output/statements.pyj +463 -0
- package/src/output/stream.pyj +309 -0
- package/src/output/treeshake.pyj +182 -0
- package/src/output/utils.pyj +72 -0
- package/src/parse.pyj +3106 -0
- package/src/string_interpolation.pyj +72 -0
- package/src/tokenizer.pyj +702 -0
- package/src/unicode_aliases.pyj +576 -0
- package/src/utils.pyj +192 -0
- package/test/_import_one.pyj +37 -0
- package/test/_import_two/__init__.pyj +11 -0
- package/test/_import_two/level2/__init__.pyj +0 -0
- package/test/_import_two/level2/deep.pyj +4 -0
- package/test/_import_two/other.pyj +6 -0
- package/test/_import_two/sub.pyj +13 -0
- package/test/aes_vectors.pyj +421 -0
- package/test/annotations.pyj +80 -0
- package/test/baselib.pyj +319 -0
- package/test/classes.pyj +452 -0
- package/test/collections.pyj +152 -0
- package/test/decorators.pyj +77 -0
- package/test/dict_spread.pyj +76 -0
- package/test/docstrings.pyj +39 -0
- package/test/elementmaker_test.pyj +45 -0
- package/test/ellipsis.pyj +49 -0
- package/test/functions.pyj +151 -0
- package/test/generators.pyj +41 -0
- package/test/generic.pyj +370 -0
- package/test/imports.pyj +72 -0
- package/test/internationalization.pyj +73 -0
- package/test/lint.pyj +164 -0
- package/test/loops.pyj +85 -0
- package/test/numpy.pyj +734 -0
- package/test/omit_function_metadata.pyj +20 -0
- package/test/regexp.pyj +55 -0
- package/test/repl.pyj +121 -0
- package/test/scoped_flags.pyj +76 -0
- package/test/starargs.pyj +506 -0
- package/test/starred_assign.pyj +104 -0
- package/test/str.pyj +198 -0
- package/test/subscript_tuple.pyj +53 -0
- package/test/unit/fixtures/fibonacci_expected.js +46 -0
- package/test/unit/index.js +2989 -0
- package/test/unit/language-service-builtins.js +815 -0
- package/test/unit/language-service-completions.js +1067 -0
- package/test/unit/language-service-dts.js +543 -0
- package/test/unit/language-service-hover.js +455 -0
- package/test/unit/language-service-scope.js +833 -0
- package/test/unit/language-service-signature.js +458 -0
- package/test/unit/language-service.js +705 -0
- package/test/unit/run-language-service.js +41 -0
- package/test/unit/web-repl.js +484 -0
- package/tools/build-language-service.js +190 -0
- package/tools/cli.js +547 -0
- package/tools/compile.js +219 -0
- package/tools/compiler.js +108 -0
- package/tools/completer.js +131 -0
- package/tools/embedded_compiler.js +251 -0
- package/tools/export.js +316 -0
- package/tools/gettext.js +185 -0
- package/tools/ini.js +65 -0
- package/tools/lint.js +705 -0
- package/tools/msgfmt.js +187 -0
- package/tools/repl.js +223 -0
- package/tools/self.js +162 -0
- package/tools/test.js +118 -0
- package/tools/utils.js +128 -0
- package/tools/web_repl.js +95 -0
- package/try +41 -0
- package/web-repl/env.js +74 -0
- package/web-repl/index.html +163 -0
- package/web-repl/language-service.js +4084 -0
- package/web-repl/main.js +254 -0
- package/web-repl/prism.css +139 -0
- package/web-repl/prism.js +113 -0
- package/web-repl/rapydscript.js +435 -0
- 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
|
+
}
|