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
package/tools/lint.js ADDED
@@ -0,0 +1,705 @@
1
+ /* vim:fileencoding=utf-8
2
+ *
3
+ * Copyright (C) 2015 Kovid Goyal <kovid at kovidgoyal.net>
4
+ *
5
+ * Distributed under terms of the BSD license
6
+ */
7
+ "use strict"; /*jshint node:true */
8
+
9
+ var fs = require('fs');
10
+ var RapydScript = require("./compiler").create_compiler();
11
+ var path = require('path');
12
+ var utils = require('./utils');
13
+ var colored = utils.safe_colored;
14
+
15
+ var WARN = 1, ERROR = 2;
16
+ var MESSAGES = {
17
+ 'undef': 'undefined symbol: "{name}"',
18
+ 'unused-import': '"{name}" is imported but not used',
19
+ 'unused-local' : '"{name}" is defined but not used',
20
+ 'loop-shadowed': 'The loop variable "{name}" was previously used in this scope at line: {line}',
21
+ 'extra-semicolon': 'This semi-colon is not needed',
22
+ 'eol-semicolon': 'Semi-colons at the end of the line are unnecessary',
23
+ 'func-in-branch': 'JavaScript in strict mode does not allow the definition of named functions/classes inside a branch such as an if/try/switch',
24
+ 'syntax-err': 'A syntax error caused compilation to abort',
25
+ 'import-err': 'An import error caused compilation to abort',
26
+ 'def-after-use': 'The symbol "{name}" is defined (at line {line}) after it is used',
27
+ 'dup-key': 'JavaScript in strict mode does not allow for duplicate keys ("{name}" is duplicated) in object mode',
28
+ 'dup-method': 'The method {name} was defined previously at line: {line}',
29
+ };
30
+
31
+ var BUILTINS = Object.create(null);
32
+ ('this self window document chr ord iterator_symbol print len range dir' +
33
+ ' eval undefined arguments abs max min enumerate pow callable reversed sum' +
34
+ ' getattr isFinite setattr hasattr parseInt parseFloat options_object' +
35
+ ' isNaN JSON Math list set list_wrap ρσ_modules require bool int bin' +
36
+ ' float iter Error EvalError set_wrap RangeError ReferenceError SyntaxError' +
37
+ ' str TypeError URIError Exception AssertionError IndexError AttributeError KeyError' +
38
+ ' ValueError ZeroDivisionError map hex filter zip dict dict_wrap UnicodeDecodeError HTMLCollection' +
39
+ ' NodeList alert console Node Symbol NamedNodeMap ρσ_eslice ρσ_delslice Number' +
40
+ ' Boolean encodeURIComponent decodeURIComponent setTimeout setInterval' +
41
+ ' setImmediate clearTimeout clearInterval clearImmediate requestAnimationFrame' +
42
+ ' id repr sorted __name__ equals get_module ρσ_str jstype divmod NaN Ellipsis'
43
+ ).split(' ').forEach(function(x) { BUILTINS[x] = true; });
44
+
45
+ Object.keys(RapydScript.NATIVE_CLASSES).forEach(function (name) { BUILTINS[name] = true; });
46
+ var has_prop = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty);
47
+
48
+ if (!String.prototype.startsWith) {
49
+ String.prototype.startsWith = function(searchString, position) {
50
+ position = position || 0;
51
+ return this.indexOf(searchString, position) === position;
52
+ };
53
+ }
54
+
55
+ function cmp(a, b) {
56
+ return (a < b) ? -1 : ((a > b) ? 1 : 0);
57
+ }
58
+
59
+ function parse_file(code, filename) {
60
+ return RapydScript.parse(code, {
61
+ filename: filename,
62
+ basedir: path.dirname(filename),
63
+ libdir: path.dirname(filename),
64
+ for_linting: true,
65
+ });
66
+ }
67
+
68
+ function msg_from_node(filename, ident, name, node, level, line) {
69
+ name = name || ((node.name) ? ((node.name.name) ? node.name.name : node.name) : '');
70
+ if (node instanceof RapydScript.AST_Lambda && node.name) name = node.name.name;
71
+ var msg = MESSAGES[ident].replace('{name}', name || '').replace('{line}', line || '');
72
+ return {
73
+ filename: filename,
74
+ start_line: (node.start) ? node.start.line : undefined,
75
+ start_col: (node.start) ? node.start.col : undefined,
76
+ end_line: (node.end) ? node.end.line : undefined,
77
+ end_col: (node.end) ? node.end.col: undefined,
78
+ ident: ident,
79
+ message: msg,
80
+ level: level || ERROR,
81
+ name:name,
82
+ other_line: line,
83
+ };
84
+ }
85
+
86
+ function Binding(name, node, options) {
87
+ options = options || {};
88
+ this.node = node;
89
+ this.name = name;
90
+ this.is_import = !!options.is_import;
91
+ this.is_function = !!options.is_function;
92
+ this.is_func_arg = !!options.is_func_arg;
93
+ this.is_method = !!options.is_method;
94
+
95
+ this.is_loop = false;
96
+ this.used = false;
97
+ }
98
+
99
+ var merge = utils.merge;
100
+
101
+ function Scope(is_toplevel, parent_scope, filename, is_class) {
102
+ this.parent_scope = parent_scope;
103
+ this.is_toplevel = !!is_toplevel;
104
+ this.is_class = !!is_class;
105
+ this.bindings = {};
106
+ this.children = [];
107
+ this.shadowed = [];
108
+ this.undefined_references = {};
109
+ this.unused_bindings = {};
110
+ this.nonlocals = {};
111
+ this.defined_after_use = {};
112
+ this.seen_method_names = {};
113
+ this.methods = {};
114
+
115
+ this.add_binding = function(name, node, options) {
116
+ var already_bound = has_prop(this.bindings, name);
117
+ var b = new Binding(name, node, options);
118
+ if (already_bound) {
119
+ if (this.bindings[name].used) b.used = true;
120
+ this.shadowed.push([name, this.bindings[name], b]);
121
+ }
122
+ this.bindings[name] = b;
123
+ return b;
124
+ };
125
+
126
+ this.add_nonlocal = function(name) {
127
+ this.nonlocals[name] = true;
128
+ };
129
+
130
+ this.register_use = function(name, node) {
131
+ if (has_prop(this.bindings, name)) {
132
+ this.bindings[name].used = true;
133
+ } else {
134
+ this.undefined_references[name] = node;
135
+ }
136
+ };
137
+
138
+ this.finalize = function() {
139
+ // Find defined after use
140
+ Object.keys(this.undefined_references).forEach(function (name) {
141
+ if (has_prop(this.bindings, name) && !has_prop(this.nonlocals, name)) {
142
+ var b = this.bindings[name];
143
+ b.used = true;
144
+ if (!has_prop(this.defined_after_use, name)) {
145
+ this.defined_after_use[name] = [this.undefined_references[name], b];
146
+ }
147
+ delete this.undefined_references[name];
148
+ }
149
+ if (has_prop(this.methods, name)) delete this.undefined_references[name];
150
+ }, this);
151
+
152
+ // Find unused bindings
153
+ Object.keys(this.bindings).forEach(function(name) {
154
+ var b = this.bindings[name];
155
+ // Check if it is used in a descendant scope
156
+ var found = false;
157
+ this.for_descendants(function (scope) {
158
+ if (has_prop(scope.undefined_references, name)) {
159
+ found = true;
160
+ // Remove from childs' undefined references
161
+ delete scope.undefined_references[name];
162
+ } else if (has_prop(scope.nonlocals, name) && has_prop(scope.bindings, name)) found = true;
163
+ });
164
+ if (!found && !b.used && !b.is_loop)
165
+ // We deliberately ignore unused loop variables so as not to complain for the
166
+ // common idiom of using a for loop to repeat an action, without referring to the
167
+ // loop variable
168
+ this.unused_bindings[name] = b;
169
+ }, this);
170
+ };
171
+
172
+ this.for_descendants = function(func) {
173
+ this.children.forEach(function (child) {
174
+ func(child);
175
+ child.for_descendants(func);
176
+ });
177
+ };
178
+
179
+ this.messages = function() {
180
+ var ans = [];
181
+
182
+ Object.keys(this.undefined_references).forEach(function (name) {
183
+ if (!(this.is_toplevel && has_prop(this.nonlocals, name))) {
184
+ var node = this.undefined_references[name];
185
+ ans.push(msg_from_node(filename, 'undef', name, node));
186
+ }
187
+ }, this);
188
+
189
+ Object.keys(this.unused_bindings).forEach(function (name) {
190
+ var b = this.unused_bindings[name];
191
+ if (b.is_import) {
192
+ ans.push(msg_from_node(filename, 'unused-import', name, b.node));
193
+ } else if (!this.is_toplevel && !this.is_class && !b.is_func_arg && !b.is_method && !has_prop(this.nonlocals, name)) {
194
+ ans.push(msg_from_node(filename, 'unused-local', name, b.node));
195
+ }
196
+ }, this);
197
+
198
+ this.shadowed.forEach(function(x) {
199
+ var name = x[0], first = x[1], second = x[2];
200
+ if (second.is_loop && !first.is_loop) {
201
+ var line = (first.node.start) ? first.node.start.line : undefined;
202
+ ans.push(msg_from_node(filename, 'loop-shadowed', name, second.node, ERROR, line));
203
+ }
204
+ });
205
+
206
+ Object.keys(this.defined_after_use).forEach(function (name) {
207
+ var use = this.defined_after_use[name][0], binding = this.defined_after_use[name][1];
208
+ ans.push(msg_from_node(filename, 'def-after-use', name, use, ERROR, binding.node.start.line));
209
+ }, this);
210
+
211
+ return ans;
212
+ };
213
+
214
+ }
215
+
216
+ function Linter(toplevel, filename, code, options) {
217
+
218
+ this.scopes = [];
219
+ this.walked_scopes = [];
220
+ this.current_node = null;
221
+ this.in_assign = false;
222
+ this.branches = [];
223
+ this.messages = [];
224
+ this.builtins = utils.merge(BUILTINS, options.builtins || {});
225
+
226
+ this.add_binding = function(name, binding_node) {
227
+ var scope = this.scopes[this.scopes.length - 1];
228
+ var node = this.current_node;
229
+ var options = {
230
+ is_import: (node instanceof RapydScript.AST_Import || node instanceof RapydScript.AST_ImportedVar),
231
+ is_function: (node instanceof RapydScript.AST_Lambda),
232
+ is_method: (node instanceof RapydScript.AST_Method),
233
+ is_func_arg: (node instanceof RapydScript.AST_SymbolFunarg),
234
+ };
235
+ return scope.add_binding(name, (binding_node || node), options);
236
+ };
237
+
238
+ this.add_nonlocal = function(name) {
239
+ var scope = this.scopes[this.scopes.length - 1];
240
+ return scope.add_nonlocal(name);
241
+ };
242
+
243
+ this.register_use = function(name) {
244
+ var scope = this.scopes[this.scopes.length - 1];
245
+ var node = this.current_node;
246
+ return scope.register_use(name, node);
247
+ };
248
+
249
+ this.handle_import = function() {
250
+ var node = this.current_node;
251
+ if (!node.argnames) {
252
+ var name = (node.alias) ? node.alias.name : node.key.split('.', 1)[0];
253
+ this.add_binding(name, (node.alias || node));
254
+ }
255
+ };
256
+
257
+ this.handle_imported_var = function() {
258
+ var node = this.current_node;
259
+ var name = (node.alias) ? node.alias.name : node.name;
260
+ this.add_binding(name);
261
+ };
262
+
263
+ this.handle_lambda = function() {
264
+ var node = this.current_node;
265
+ var name = (node.name) ? node.name.name : undefined;
266
+ var scope = this.scopes[this.scopes.length - 1];
267
+ if (this.branches.length && name) {
268
+ this.messages.push(msg_from_node(filename, 'func-in-branch', node.name, node));
269
+ }
270
+ if (name) {
271
+ if (node instanceof RapydScript.AST_Method) {
272
+ scope.methods[name] = true;
273
+ if (has_prop(scope.seen_method_names, name)) {
274
+ if (!node.is_setter) this.messages.push(msg_from_node(filename, 'dup-method', node.name, node, WARN, scope.seen_method_names[name]));
275
+ } else scope.seen_method_names[name] = node.start.line;
276
+ } else this.add_binding(name);
277
+ }
278
+ };
279
+
280
+ this.handle_assign = function() {
281
+ var node = this.current_node;
282
+
283
+ var handle_destructured = function(self, flat) {
284
+ for (var i = 0; i < flat.length; i++) {
285
+ var cnode = flat[i];
286
+ if (cnode instanceof RapydScript.AST_SymbolRef) {
287
+ self.current_node = cnode;
288
+ cnode.lint_visited = true;
289
+ self.add_binding(cnode.name);
290
+ self.current_node = node;
291
+ }
292
+ }
293
+ };
294
+
295
+ if (node.left instanceof RapydScript.AST_SymbolRef) {
296
+ node.left.lint_visited = node.operator === '='; // Could be compound assignment like: +=
297
+ if (node.operator === '=') {
298
+ // Only create a binding if the operator is not
299
+ // a compound assignment operator
300
+ this.current_node = node.left;
301
+ this.add_binding(node.left.name);
302
+ this.current_node = node;
303
+ }
304
+ } else if (node.left instanceof RapydScript.AST_Array) {
305
+ // destructuring assignment: a, b = 1, 2
306
+ var flat = node.left.flatten();
307
+ handle_destructured(this, node.left.flatten());
308
+
309
+ } else if (node.left instanceof RapydScript.AST_Seq && node.left.car instanceof RapydScript.AST_SymbolRef) {
310
+ handle_destructured(this, node.left.to_array());
311
+ }
312
+
313
+ };
314
+
315
+ this.handle_vardef = function() {
316
+ var node = this.current_node;
317
+ if (node.value) this.current_node = node.value;
318
+ if (node.name instanceof RapydScript.AST_SymbolNonlocal) {
319
+ this.add_nonlocal(node.name.name);
320
+ } else {
321
+ this.add_binding(node.name.name, node.name);
322
+ }
323
+ this.current_node = node;
324
+ };
325
+
326
+ this.handle_symbol_ref = function() {
327
+ var node = this.current_node;
328
+ this.register_use(node.name);
329
+ };
330
+
331
+ this.handle_decorator = function() {
332
+ var node = this.current_node.expression;
333
+ if (node instanceof RapydScript.AST_SymbolRef && RapydScript.compile_time_decorators.indexOf(node.name) != -1) node.link_visited = true;
334
+ };
335
+
336
+ this.handle_scope = function() {
337
+ var node = this.current_node;
338
+ var nscope = new Scope(node instanceof RapydScript.AST_Toplevel, this.scopes[this.scopes.length - 1], filename, node instanceof RapydScript.AST_Class);
339
+ if (this.scopes.length) this.scopes[this.scopes.length - 1].children.push(nscope);
340
+ this.scopes.push(nscope);
341
+ };
342
+
343
+ this.handle_symbol_funarg = function() {
344
+ // Arguments in a function definition
345
+ var node = this.current_node;
346
+ this.add_binding(node.name);
347
+ };
348
+
349
+ this.handle_comprehension = function() {
350
+ this.handle_scope(); // Comprehensions create their own scope
351
+ this.handle_for_in();
352
+ };
353
+
354
+ this.handle_for_in = function() {
355
+ var node = this.current_node;
356
+ if (node.init instanceof RapydScript.AST_SymbolRef) {
357
+ this.add_binding(node.init.name).is_loop = true;
358
+ node.init.lint_visited = true;
359
+ } else if (node.init instanceof RapydScript.AST_Array) {
360
+ // destructuring assignment: for a, b in []
361
+ for (var i = 0; i < node.init.elements.length; i++) {
362
+ var cnode = node.init.elements[i];
363
+ if (cnode instanceof RapydScript.AST_Seq) cnode = cnode.to_array();
364
+ if (cnode instanceof RapydScript.AST_SymbolRef) cnode = [cnode];
365
+ if (Array.isArray(cnode)) {
366
+ for (var j = 0; j < cnode.length; j++) {
367
+ var elem = cnode[j];
368
+ if (elem instanceof RapydScript.AST_SymbolRef) {
369
+ this.current_node = elem;
370
+ elem.lint_visited = true;
371
+ this.add_binding(elem.name).is_loop = true;
372
+ this.current_node = node;
373
+ }
374
+ }
375
+ }
376
+ }
377
+
378
+ }
379
+ };
380
+
381
+ this.handle_for_js = function() {
382
+ var node = this.current_node;
383
+ var js = node.condition.value;
384
+ var statements = js.split(';');
385
+ var decl = statements[0].trim();
386
+ if (decl.startsWith('var ')) decl = decl.slice(4);
387
+ var self = this;
388
+ decl.split(',').forEach(function (part) {
389
+ var name = /^[a-zA-Z0-9_]+/.exec(part.trimLeft())[0];
390
+ self.add_binding(name);
391
+ });
392
+ };
393
+
394
+ this.handle_except = function() {
395
+ var node = this.current_node;
396
+ if (node.argname) {
397
+ this.add_binding(node.argname.name, node.argname);
398
+ }
399
+ };
400
+
401
+ this.handle_empty_statement = function() {
402
+ var node = this.current_node;
403
+ if (node.stype == ';') {
404
+ this.messages.push(msg_from_node(filename, 'extra-semicolon', ';', node, WARN));
405
+ }
406
+ };
407
+
408
+ this.handle_class = function() {
409
+ var node = this.current_node;
410
+ if (node.name) {
411
+ node.name.lint_visited = true;
412
+ this.add_binding(node.name.name, node.name);
413
+ }
414
+ };
415
+
416
+ this.handle_object_literal = function() {
417
+ var node = this.current_node;
418
+ var seen = {};
419
+ (node.properties || []).forEach(function (prop) {
420
+ if (prop.key instanceof RapydScript.AST_Constant) {
421
+ var val = prop.key.value;
422
+ if (has_prop(seen, val))
423
+ this.messages.push(msg_from_node(filename, 'dup-key', val, prop));
424
+ seen[val] = true;
425
+ }
426
+ }, this);
427
+ };
428
+
429
+ this.handle_call = function() {
430
+ var node = this.current_node;
431
+ if (node.args.kwargs) node.args.kwargs.forEach(function(kw) { kw[0].lint_visited = true; });
432
+ };
433
+
434
+ this.handle_with_clause = function() {
435
+ var node = this.current_node;
436
+ if (node.alias) this.add_binding(node.alias.name);
437
+ };
438
+
439
+ this._visit = function (node, cont) {
440
+ if (node.lint_visited) return;
441
+ this.current_node = node;
442
+ var scope_count = this.scopes.length;
443
+ var branch_count = this.branches.length;
444
+ if (node instanceof RapydScript.AST_If || node instanceof RapydScript.AST_Try || node instanceof RapydScript.AST_Catch || node instanceof RapydScript.AST_Except || node instanceof RapydScript.AST_Else) {
445
+ this.branches.push(1);
446
+ }
447
+
448
+ if (node instanceof RapydScript.AST_Lambda) {
449
+ this.handle_lambda();
450
+ } else if (node instanceof RapydScript.AST_Import) {
451
+ this.handle_import();
452
+ } else if (node instanceof RapydScript.AST_ImportedVar) {
453
+ this.handle_imported_var();
454
+ } else if (node instanceof RapydScript.AST_Class) {
455
+ this.handle_class();
456
+ } else if (node instanceof RapydScript.AST_BaseCall) {
457
+ this.handle_call();
458
+ } else if (node instanceof RapydScript.AST_Assign) {
459
+ this.handle_assign();
460
+ } else if (node instanceof RapydScript.AST_VarDef) {
461
+ this.handle_vardef();
462
+ } else if (node instanceof RapydScript.AST_SymbolRef) {
463
+ this.handle_symbol_ref();
464
+ } else if (node instanceof RapydScript.AST_Decorator) {
465
+ this.handle_decorator();
466
+ } else if (node instanceof RapydScript.AST_SymbolFunarg) {
467
+ this.handle_symbol_funarg();
468
+ } else if (node instanceof RapydScript.AST_ListComprehension) {
469
+ this.handle_comprehension();
470
+ } else if (node instanceof RapydScript.AST_ForIn) {
471
+ this.handle_for_in();
472
+ } else if (node instanceof RapydScript.AST_ForJS) {
473
+ this.handle_for_js();
474
+ } else if (node instanceof RapydScript.AST_Except) {
475
+ this.handle_except();
476
+ } else if (node instanceof RapydScript.AST_EmptyStatement) {
477
+ this.handle_empty_statement();
478
+ } else if (node instanceof RapydScript.AST_WithClause) {
479
+ this.handle_with_clause();
480
+ } else if (node instanceof RapydScript.AST_Object) {
481
+ this.handle_object_literal();
482
+ }
483
+
484
+ if (node instanceof RapydScript.AST_Scope) {
485
+ this.handle_scope();
486
+ }
487
+
488
+ if (cont !== undefined) cont();
489
+
490
+ if (this.scopes.length > scope_count) {
491
+ this.scopes[this.scopes.length - 1].finalize();
492
+ this.walked_scopes.push(this.scopes.pop());
493
+ }
494
+
495
+ if (this.branches.length > branch_count) this.branches.pop();
496
+ };
497
+
498
+ this.resolve = function() {
499
+ var messages = this.messages;
500
+ var line_filters = {};
501
+
502
+ code.split('\n').forEach(function(line, num) {
503
+ line = line.trimRight();
504
+ num++;
505
+ if (line[line.length - 1] === ';') {
506
+ var ident = 'eol-semicolon';
507
+ messages.push({filename:filename, ident:ident, message:MESSAGES[ident],
508
+ level:WARN, name:';', start_line:num, start_col:line.lastIndexOf(';')});
509
+ }
510
+ var parts = line.split('#');
511
+ var last = parts[parts.length - 1], filters;
512
+ if (last && last.trimLeft().substr(0, 4).toLowerCase() === 'noqa') {
513
+ parts = last.split(':').slice(1);
514
+ if (parts.length) {
515
+ filters = {};
516
+ parts = parts[0].split(',');
517
+ for (var i = 0; i < parts.length; i++) filters[parts[i].trim()] = true;
518
+ } else filters = MESSAGES;
519
+ }
520
+ if (filters) line_filters[num] = filters;
521
+ });
522
+
523
+ this.walked_scopes.forEach(function (scope) {
524
+ messages = messages.concat(scope.messages());
525
+ });
526
+ var noqa = options.noqa || {};
527
+ messages = messages.filter(function(msg) {
528
+ var ignore = (msg.start_line !== undefined && has_prop(line_filters, msg.start_line) && has_prop(line_filters[msg.start_line], msg.ident));
529
+ var filter = has_prop(noqa, msg.ident);
530
+ return !ignore && !filter && (msg.ident != 'undef' || !has_prop(this.builtins, msg.name));
531
+ }, this);
532
+ messages.sort(function (a, b) { return cmp(a.start_line, b.start_line) || cmp(a.start_col, b.start_col_); });
533
+ return messages;
534
+ };
535
+
536
+ }
537
+
538
+ function lint_code(code, options) {
539
+ options = options || {};
540
+ var reportcb = {'json':cli_json_report, 'vim': cli_vim_report, 'undef': cli_undef_report}[options.errorformat] || (options.report || cli_report);
541
+ var filename = options.filename || '<eval>';
542
+ var toplevel, messages;
543
+ var lines = code.split('\n'); // Can be used (in the future) to display extract from code corresponding to error location
544
+
545
+ try {
546
+ toplevel = parse_file(code, filename);
547
+ } catch(e) {
548
+ if (e instanceof RapydScript.ImportError) {
549
+ messages = [{filename:filename, start_line:e.line, start_col:e.col, level:ERROR, ident:'import-err', message:e.message}];
550
+ } else if (e instanceof RapydScript.SyntaxError) {
551
+ messages = [{filename:filename, start_line:e.line, start_col:e.col, level:ERROR, ident:'syntax-err', message:e.message}];
552
+ } else throw e;
553
+ }
554
+
555
+ if (toplevel) {
556
+ var linter = new Linter(toplevel, filename, code, options);
557
+ toplevel.walk(linter);
558
+ messages = linter.resolve();
559
+ }
560
+ messages.forEach(function(msg, i) { msg.code_lines = lines; reportcb(msg, i, messages); });
561
+ return messages;
562
+ }
563
+
564
+ // CLI {{{
565
+
566
+ function read_whole_file(filename, cb) {
567
+ if (!filename || filename === '-') {
568
+ var chunks = [];
569
+ process.stdin.setEncoding('utf-8');
570
+ process.stdin.on('data', function (chunk) {
571
+ chunks.push(chunk);
572
+ }).on('end', function () {
573
+ cb(null, chunks.join(""));
574
+ });
575
+ process.openStdin();
576
+ } else {
577
+ fs.readFile(filename, "utf-8", cb);
578
+ }
579
+ }
580
+
581
+ function cli_report(r, i, messages) {
582
+ var parts = [];
583
+ function push(x, color) {
584
+ parts.push((x === undefined) ? '' : colored(x.toString(), color));
585
+ }
586
+ push(r.filename); push((r.level === WARN) ? 'WARN' : 'ERR', (r.level === WARN) ? 'yellow' : 'red');
587
+ push(r.start_line, 'green'); push((r.start_col === undefined) ? '' : r.start_col + 1);
588
+ console.log(parts.join(':') + ':' + r.message + colored(' [' + r.ident + ']', 'green'));
589
+ if (i < messages.length - 1) console.log();
590
+ }
591
+
592
+ var undef_buf = {};
593
+
594
+ function cli_undef_report(r, i, messages) {
595
+ if (r.ident == 'undef' && r.name) undef_buf[r.name] = true;
596
+ if (i == messages.length - 1) console.log(Object.keys(undef_buf).sort().join(', '));
597
+ }
598
+
599
+ function cli_json_report(r, i, messages) {
600
+ var j = {};
601
+ Object.keys(r).forEach(function(key) {
602
+ var val = r[key];
603
+ if (val !== undefined && key != 'code_lines') {
604
+ if (key === 'level') val = (val === WARN) ? 'WARN' : 'ERR';
605
+ j[key] = val;
606
+ }
607
+ });
608
+ if (i === 0) console.log('[');
609
+ console.log(JSON.stringify(j, null, 2));
610
+ console.log((i < messages.length - 1) ? ',' : ']');
611
+ }
612
+
613
+ function cli_vim_report(r) {
614
+ var parts = [];
615
+ parts.push(r.filename); parts.push(r.start_line || 0); parts.push((r.start_col === undefined) ? 0 : r.start_col + 1);
616
+ parts.push((r.level === WARN) ? 'W' : 'E'); parts.push(r.name || ''); parts.push(r.message + ' [' + r.ident + ']');
617
+ console.log(parts.join(':'));
618
+ }
619
+
620
+ var ini_cache = {};
621
+
622
+ function get_ini(toplevel_dir) {
623
+ if (has_prop(ini_cache, toplevel_dir)) return ini_cache[toplevel_dir];
624
+ var rl = require('./ini').read_config(toplevel_dir).rapydscript || {};
625
+ ini_cache[toplevel_dir] = rl;
626
+ return rl;
627
+ }
628
+
629
+ module.exports.cli = function(argv, base_path, src_path, lib_path) {
630
+ var files = argv.files.slice();
631
+ var num_of_files = files.length || 1;
632
+ var read_config = require('./ini');
633
+
634
+ if (argv.noqa_list) {
635
+ Object.keys(MESSAGES).forEach(function(ident) {
636
+ var i = (ident + utils.repeat(' ', 20)).slice(0, 20);
637
+ var h = utils.wrap(MESSAGES[ident].split('\n'), 59);
638
+ console.log(i + h[0]);
639
+ h.slice(1).forEach(function(t) { console.log(utils.repeat(' ', 20) + t); });
640
+ console.log();
641
+ });
642
+ process.exit(0);
643
+ }
644
+
645
+ if (files.filter(function(el){ return el === "-"; }).length > 1) {
646
+ console.error("ERROR: Can read a single file from STDIN (two or more dashes specified)");
647
+ process.exit(1);
648
+ }
649
+
650
+ var all_ok = true;
651
+ var builtins = {};
652
+ var noqa = {};
653
+ if (argv.globals) argv.globals.split(',').forEach(function(sym) { builtins[sym] = true; });
654
+ if (argv.noqa) argv.noqa.split(',').forEach(function(sym) { noqa[sym] = true; });
655
+
656
+ function path_for_filename(x) {
657
+ return x === '-' ? argv.stdin_filename : x;
658
+ }
659
+
660
+ function lint_single_file(err, code) {
661
+ var output, final_builtins = merge(builtins), final_noqa = merge(noqa), rl;
662
+ if (err) {
663
+ console.error("ERROR: can't read file: " + files[0]);
664
+ process.exit(1);
665
+ }
666
+
667
+ // Read setup.cfg
668
+ rl = get_ini(path.dirname(path_for_filename(files[0])));
669
+ var g = {};
670
+ (rl.globals || rl.builtins || '').split(',').forEach(function (x) { g[x.trim()] = true; });
671
+ final_builtins = merge(final_builtins, g);
672
+ g = {};
673
+ (rl.noqa || '').split(',').forEach(function (x) { g[x.trim()] = true; });
674
+ final_noqa = merge(final_noqa, g);
675
+
676
+ // Look for # globals: or # noqa: in the first few lines of the file
677
+ code.split('\n', 20).forEach(function (line) {
678
+ var lq = line.replace(/\s+/g, '');
679
+ if (lq.startsWith('#globals:')) {
680
+ (lq.split(':', 2)[1] || '').split(',').forEach(function (item) { final_builtins[item] = true; });
681
+ }
682
+ else if (lq.startsWith('#noqa:')) {
683
+ (lq.split(':', 2)[1] || '').split(',').forEach(function (item) { final_noqa[item] = true; });
684
+ }
685
+ });
686
+
687
+ // Lint!
688
+ if (lint_code(code, {filename:path_for_filename(files[0]), builtins:final_builtins, noqa:final_noqa, errorformat:argv.errorformat || false}).length) all_ok = false;
689
+
690
+ files = files.slice(1);
691
+ if (files.length) {
692
+ setImmediate(read_whole_file, files[0], lint_single_file);
693
+ return;
694
+ } else process.exit((all_ok) ? 0 : 1);
695
+ }
696
+
697
+ setImmediate(read_whole_file, files[0], lint_single_file);
698
+
699
+ };
700
+
701
+ module.exports.lint_code = lint_code;
702
+ module.exports.WARN = WARN;
703
+ module.exports.ERROR = ERROR;
704
+ module.exports.MESSAGES = MESSAGES;
705
+ // }}}