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,309 @@
|
|
|
1
|
+
# vim:fileencoding=utf-8
|
|
2
|
+
# License: BSD Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
|
3
|
+
# globals:regenerate
|
|
4
|
+
from __python__ import hash_literals
|
|
5
|
+
|
|
6
|
+
from utils import make_predicate, defaults, repeat_string
|
|
7
|
+
from tokenizer import is_identifier_char
|
|
8
|
+
|
|
9
|
+
DANGEROUS = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def as_hex(code, sz):
|
|
13
|
+
val = code.toString(16)
|
|
14
|
+
if val.length < sz:
|
|
15
|
+
val = '0'.repeat(sz - val.length) + val
|
|
16
|
+
return val
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def to_ascii(str_, identifier):
|
|
20
|
+
return str_.replace(/[\u0080-\uffff]/g, def(ch):
|
|
21
|
+
code = ch.charCodeAt(0).toString(16)
|
|
22
|
+
if code.length <= 2 and not identifier:
|
|
23
|
+
return "\\x" + as_hex(code, 2)
|
|
24
|
+
else:
|
|
25
|
+
return '\\u' + as_hex(code, 4)
|
|
26
|
+
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def encode_string(str_):
|
|
30
|
+
return JSON.stringify(str_).replace(DANGEROUS, def(a):
|
|
31
|
+
return '\\u' + as_hex(a.charCodeAt(0), 4)
|
|
32
|
+
)
|
|
33
|
+
require_semi_colon_chars = make_predicate("( [ + * / - , .")
|
|
34
|
+
|
|
35
|
+
output_stream_defaults = {
|
|
36
|
+
'indent_start': 0,
|
|
37
|
+
'indent_level': 4,
|
|
38
|
+
'quote_keys': False,
|
|
39
|
+
'space_colon': True,
|
|
40
|
+
'ascii_only': False,
|
|
41
|
+
'width': 80,
|
|
42
|
+
'max_line_len': 32000,
|
|
43
|
+
'ie_proof': True,
|
|
44
|
+
'beautify': False,
|
|
45
|
+
'source_map': None,
|
|
46
|
+
'bracketize': False,
|
|
47
|
+
'semicolons': True,
|
|
48
|
+
'comments': False,
|
|
49
|
+
'preserve_line': False,
|
|
50
|
+
'omit_baselib': False,
|
|
51
|
+
'baselib_plain': None,
|
|
52
|
+
'private_scope': True,
|
|
53
|
+
'keep_docstrings': False,
|
|
54
|
+
'discard_asserts': False,
|
|
55
|
+
'module_cache_dir': '',
|
|
56
|
+
'js_version':5,
|
|
57
|
+
'write_name': True,
|
|
58
|
+
'omit_function_metadata': False,
|
|
59
|
+
'pythonize_strings': False,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
class OutputStream:
|
|
63
|
+
|
|
64
|
+
def __init__(self, options):
|
|
65
|
+
self.options = defaults(options, output_stream_defaults, True)
|
|
66
|
+
self._indentation = 0
|
|
67
|
+
self.current_col = 0
|
|
68
|
+
self.current_line = 1
|
|
69
|
+
self.current_pos = 0
|
|
70
|
+
self.OUTPUT = ""
|
|
71
|
+
self.might_need_space = False
|
|
72
|
+
self.might_need_semicolon = False
|
|
73
|
+
self._last = None
|
|
74
|
+
self._stack = []
|
|
75
|
+
self.index_counter = 0
|
|
76
|
+
self.with_counter = 0
|
|
77
|
+
self.try_else_counter = 0
|
|
78
|
+
self.match_counter = 0
|
|
79
|
+
|
|
80
|
+
def new_try_else_counter(self):
|
|
81
|
+
self.try_else_counter += 1
|
|
82
|
+
return 'ρσ_try_else_' + self.try_else_counter
|
|
83
|
+
|
|
84
|
+
def make_name(self, name):
|
|
85
|
+
name = name.toString()
|
|
86
|
+
if self.options.ascii_only:
|
|
87
|
+
name = to_ascii(name, True)
|
|
88
|
+
|
|
89
|
+
return name
|
|
90
|
+
|
|
91
|
+
def print_name(self, name):
|
|
92
|
+
self.print(self.make_name(name))
|
|
93
|
+
|
|
94
|
+
def make_indent(self, back):
|
|
95
|
+
return repeat_string(" ", self.options.indent_start + self._indentation - back * self.options.indent_level)
|
|
96
|
+
|
|
97
|
+
# -----[ beautification/minification ]-----
|
|
98
|
+
def last_char(self):
|
|
99
|
+
return self._last.charAt(self._last.length - 1)
|
|
100
|
+
|
|
101
|
+
def maybe_newline(self):
|
|
102
|
+
if self.options.max_line_len and self.current_col > self.options.max_line_len:
|
|
103
|
+
self.print("\n")
|
|
104
|
+
|
|
105
|
+
def print(self, str_):
|
|
106
|
+
str_ = v"String(str_)"
|
|
107
|
+
ch = str_.charAt(0)
|
|
108
|
+
if self.might_need_semicolon:
|
|
109
|
+
if (not ch or ";}".indexOf(ch) < 0) and not /[;]$/.test(self._last):
|
|
110
|
+
if self.options.semicolons or require_semi_colon_chars[ch]:
|
|
111
|
+
self.OUTPUT += ";"
|
|
112
|
+
self.current_col += 1
|
|
113
|
+
self.current_pos += 1
|
|
114
|
+
else:
|
|
115
|
+
self.OUTPUT += "\n"
|
|
116
|
+
self.current_pos += 1
|
|
117
|
+
self.current_line += 1
|
|
118
|
+
self.current_col = 0
|
|
119
|
+
|
|
120
|
+
if not self.options.beautify:
|
|
121
|
+
self.might_need_space = False
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
self.might_need_semicolon = False
|
|
125
|
+
self.maybe_newline()
|
|
126
|
+
|
|
127
|
+
if not self.options.beautify and self.options.preserve_line and self._stack[self._stack.length - 1]:
|
|
128
|
+
target_line = self._stack[self._stack.length - 1].start.line
|
|
129
|
+
while self.current_line < target_line:
|
|
130
|
+
self.OUTPUT += "\n"
|
|
131
|
+
self.current_pos += 1
|
|
132
|
+
self.current_line += 1
|
|
133
|
+
self.current_col = 0
|
|
134
|
+
self.might_need_space = False
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
if self.might_need_space:
|
|
138
|
+
prev = self.last_char()
|
|
139
|
+
if is_identifier_char(prev) and (is_identifier_char(ch) or ch is "\\")
|
|
140
|
+
or /^[\+\-\/]$/.test(ch) and ch is prev:
|
|
141
|
+
self.OUTPUT += " "
|
|
142
|
+
self.current_col += 1
|
|
143
|
+
self.current_pos += 1
|
|
144
|
+
|
|
145
|
+
self.might_need_space = False
|
|
146
|
+
|
|
147
|
+
a = str_.split(/\r?\n/)
|
|
148
|
+
n = a.length - 1
|
|
149
|
+
self.current_line += n
|
|
150
|
+
if n is 0:
|
|
151
|
+
self.current_col += a[n].length
|
|
152
|
+
else:
|
|
153
|
+
self.current_col = a[n].length
|
|
154
|
+
|
|
155
|
+
self.current_pos += str_.length
|
|
156
|
+
self._last = str_
|
|
157
|
+
self.OUTPUT += str_
|
|
158
|
+
|
|
159
|
+
def space(self):
|
|
160
|
+
if self.options.beautify:
|
|
161
|
+
self.print(' ')
|
|
162
|
+
else:
|
|
163
|
+
self.might_need_space = True
|
|
164
|
+
|
|
165
|
+
def indent(self, half):
|
|
166
|
+
if self.options.beautify:
|
|
167
|
+
self.print(self.make_indent((0.5 if half else 0)))
|
|
168
|
+
|
|
169
|
+
def with_indent(self, col, proceed):
|
|
170
|
+
if self.options.beautify:
|
|
171
|
+
if col is True:
|
|
172
|
+
col = self.next_indent()
|
|
173
|
+
|
|
174
|
+
save_indentation = self._indentation
|
|
175
|
+
self._indentation = col
|
|
176
|
+
ret = proceed()
|
|
177
|
+
self._indentation = save_indentation
|
|
178
|
+
return ret
|
|
179
|
+
else:
|
|
180
|
+
return proceed()
|
|
181
|
+
|
|
182
|
+
def indentation(self):
|
|
183
|
+
return self._indentation
|
|
184
|
+
|
|
185
|
+
def set_indentation(self, val):
|
|
186
|
+
if self.options.beautify:
|
|
187
|
+
self._indentation = val
|
|
188
|
+
|
|
189
|
+
def newline(self):
|
|
190
|
+
if self.options.beautify:
|
|
191
|
+
self.print("\n")
|
|
192
|
+
|
|
193
|
+
def semicolon(self):
|
|
194
|
+
if self.options.beautify:
|
|
195
|
+
self.print(";")
|
|
196
|
+
else:
|
|
197
|
+
self.might_need_semicolon = True
|
|
198
|
+
|
|
199
|
+
def force_semicolon(self):
|
|
200
|
+
self.might_need_semicolon = False
|
|
201
|
+
self.print(";")
|
|
202
|
+
|
|
203
|
+
def next_indent(self):
|
|
204
|
+
return self._indentation + self.options.indent_level
|
|
205
|
+
|
|
206
|
+
def spaced(self):
|
|
207
|
+
for v'var i=0; i < arguments.length; i++':
|
|
208
|
+
if i > 0:
|
|
209
|
+
self.space()
|
|
210
|
+
if jstype(arguments[i].print) is 'function':
|
|
211
|
+
arguments[i].print(self)
|
|
212
|
+
else:
|
|
213
|
+
self.print(arguments[i])
|
|
214
|
+
|
|
215
|
+
def end_statement(self):
|
|
216
|
+
self.semicolon()
|
|
217
|
+
self.newline()
|
|
218
|
+
|
|
219
|
+
def with_block(self, cont):
|
|
220
|
+
ret = None
|
|
221
|
+
self.print("{")
|
|
222
|
+
self.newline()
|
|
223
|
+
self.with_indent(self.next_indent(), def():
|
|
224
|
+
nonlocal ret
|
|
225
|
+
ret = cont()
|
|
226
|
+
)
|
|
227
|
+
self.indent()
|
|
228
|
+
self.print("}")
|
|
229
|
+
return ret
|
|
230
|
+
|
|
231
|
+
def with_parens(self, cont):
|
|
232
|
+
self.print("(")
|
|
233
|
+
ret = cont()
|
|
234
|
+
self.print(")")
|
|
235
|
+
return ret
|
|
236
|
+
|
|
237
|
+
def with_square(self, cont):
|
|
238
|
+
self.print("[")
|
|
239
|
+
ret = cont()
|
|
240
|
+
self.print("]")
|
|
241
|
+
return ret
|
|
242
|
+
|
|
243
|
+
def comma(self):
|
|
244
|
+
self.print(",")
|
|
245
|
+
self.space()
|
|
246
|
+
|
|
247
|
+
def colon(self):
|
|
248
|
+
self.print(":")
|
|
249
|
+
if self.options.space_colon:
|
|
250
|
+
self.space()
|
|
251
|
+
|
|
252
|
+
def dump_yield(self):
|
|
253
|
+
self.indent()
|
|
254
|
+
self.spaced('var', 'ρσ_regenerator', '=', '{}')
|
|
255
|
+
self.end_statement()
|
|
256
|
+
code = 'ρσ_regenerator.regeneratorRuntime = ' + regenerate(False, self.options.beautify)
|
|
257
|
+
if self.options.beautify:
|
|
258
|
+
code = code.replace(/\/\/.*$/mg, '\n').replace(/^\s*$/gm, '') # strip comments
|
|
259
|
+
ci = self.make_indent(0)
|
|
260
|
+
code = [ci + x for x in code.split('\n')].join('\n')
|
|
261
|
+
self.print(code + '})(ρσ_regenerator)')
|
|
262
|
+
self.end_statement()
|
|
263
|
+
|
|
264
|
+
def get(self):
|
|
265
|
+
return self.OUTPUT
|
|
266
|
+
toString = get
|
|
267
|
+
|
|
268
|
+
def assign(self, name):
|
|
269
|
+
# generates: '[name] = '
|
|
270
|
+
if jstype(name) is "string":
|
|
271
|
+
self.print(name)
|
|
272
|
+
else:
|
|
273
|
+
name.print(self)
|
|
274
|
+
self.space()
|
|
275
|
+
self.print("=")
|
|
276
|
+
self.space()
|
|
277
|
+
|
|
278
|
+
def current_width(self):
|
|
279
|
+
return self.current_col - self._indentation
|
|
280
|
+
|
|
281
|
+
def should_break(self):
|
|
282
|
+
return self.options.width and self.current_width() >= self.options.width
|
|
283
|
+
|
|
284
|
+
def last(self):
|
|
285
|
+
return self._last
|
|
286
|
+
|
|
287
|
+
def print_string(self, str_):
|
|
288
|
+
self.print(encode_string(str_))
|
|
289
|
+
|
|
290
|
+
def line(self):
|
|
291
|
+
return self.current_line
|
|
292
|
+
|
|
293
|
+
def col(self):
|
|
294
|
+
return self.current_col
|
|
295
|
+
|
|
296
|
+
def pos(self):
|
|
297
|
+
return self.current_pos
|
|
298
|
+
|
|
299
|
+
def push_node(self, node):
|
|
300
|
+
self._stack.push(node)
|
|
301
|
+
|
|
302
|
+
def pop_node(self):
|
|
303
|
+
return self._stack.pop()
|
|
304
|
+
|
|
305
|
+
def stack(self):
|
|
306
|
+
return self._stack
|
|
307
|
+
|
|
308
|
+
def parent(self, n):
|
|
309
|
+
return self._stack[self._stack.length - 2 - (n or 0)]
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# vim:fileencoding=utf-8
|
|
2
|
+
# License: BSD
|
|
3
|
+
from __python__ import hash_literals
|
|
4
|
+
|
|
5
|
+
from ast import (
|
|
6
|
+
AST_Function, AST_Class, AST_SimpleStatement, AST_Assign,
|
|
7
|
+
AST_SymbolRef, AST_Dot, AST_Sub, AST_Imports, TreeWalker, is_node_type
|
|
8
|
+
)
|
|
9
|
+
from utils import has_prop
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_top_level_name(stmt):
|
|
13
|
+
if is_node_type(stmt, AST_Function) or is_node_type(stmt, AST_Class):
|
|
14
|
+
if stmt.name:
|
|
15
|
+
return stmt.name.name
|
|
16
|
+
return None
|
|
17
|
+
if is_node_type(stmt, AST_SimpleStatement):
|
|
18
|
+
body = stmt.body
|
|
19
|
+
if is_node_type(body, AST_Assign):
|
|
20
|
+
lhs = body.left
|
|
21
|
+
if is_node_type(lhs, AST_SymbolRef):
|
|
22
|
+
return lhs.name
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def collect_refs_in_node(stmt, top_level_set, refs):
|
|
27
|
+
def visit_fn(node, descend):
|
|
28
|
+
if is_node_type(node, AST_SymbolRef):
|
|
29
|
+
if has_prop(top_level_set, node.name):
|
|
30
|
+
refs[node.name] = True
|
|
31
|
+
stmt.walk(TreeWalker(visit_fn))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def compute_transitive_closure(body, direct_names, nonlocalvars):
|
|
35
|
+
nonlocal_set = {}
|
|
36
|
+
if nonlocalvars:
|
|
37
|
+
for nv in nonlocalvars:
|
|
38
|
+
nonlocal_set[nv] = True
|
|
39
|
+
|
|
40
|
+
name_map = {}
|
|
41
|
+
unnamed_stmts = []
|
|
42
|
+
for stmt in body:
|
|
43
|
+
name = get_top_level_name(stmt)
|
|
44
|
+
if name is not None:
|
|
45
|
+
name_map[name] = stmt
|
|
46
|
+
else:
|
|
47
|
+
unnamed_stmts.push(stmt)
|
|
48
|
+
|
|
49
|
+
top_level_set = {}
|
|
50
|
+
for name in Object.keys(name_map):
|
|
51
|
+
top_level_set[name] = True
|
|
52
|
+
|
|
53
|
+
needed = {}
|
|
54
|
+
queue = []
|
|
55
|
+
|
|
56
|
+
# Always include top-level assignments to nonlocal vars — they affect the
|
|
57
|
+
# global JavaScript scope and must never be filtered out.
|
|
58
|
+
for name in Object.keys(name_map):
|
|
59
|
+
if has_prop(nonlocal_set, name):
|
|
60
|
+
needed[name] = True
|
|
61
|
+
queue.push(name)
|
|
62
|
+
|
|
63
|
+
# Add directly imported names
|
|
64
|
+
for name in Object.keys(direct_names):
|
|
65
|
+
if not has_prop(needed, name):
|
|
66
|
+
needed[name] = True
|
|
67
|
+
queue.push(name)
|
|
68
|
+
|
|
69
|
+
# Unnamed statements (imports, if-blocks, bare expressions) are always
|
|
70
|
+
# included in output, so their references to named top-level items must
|
|
71
|
+
# also be included transitively.
|
|
72
|
+
always_refs = {}
|
|
73
|
+
for stmt in unnamed_stmts:
|
|
74
|
+
collect_refs_in_node(stmt, top_level_set, always_refs)
|
|
75
|
+
for ref_name in Object.keys(always_refs):
|
|
76
|
+
if not has_prop(needed, ref_name):
|
|
77
|
+
needed[ref_name] = True
|
|
78
|
+
queue.push(ref_name)
|
|
79
|
+
|
|
80
|
+
while queue.length > 0:
|
|
81
|
+
current = queue.shift()
|
|
82
|
+
if not has_prop(name_map, current):
|
|
83
|
+
continue
|
|
84
|
+
refs = {}
|
|
85
|
+
collect_refs_in_node(name_map[current], top_level_set, refs)
|
|
86
|
+
for ref_name in Object.keys(refs):
|
|
87
|
+
if not has_prop(needed, ref_name):
|
|
88
|
+
needed[ref_name] = True
|
|
89
|
+
queue.push(ref_name)
|
|
90
|
+
|
|
91
|
+
return needed
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def check_module_attr_access(main_body, info, alias_set):
|
|
95
|
+
def visit_fn(node, descend):
|
|
96
|
+
if is_node_type(node, AST_Dot):
|
|
97
|
+
expr = node.expression
|
|
98
|
+
if is_node_type(expr, AST_SymbolRef) and has_prop(alias_set, expr.name):
|
|
99
|
+
info.direct_names[node.property] = True
|
|
100
|
+
return True
|
|
101
|
+
if is_node_type(node, AST_Sub):
|
|
102
|
+
expr = node.expression
|
|
103
|
+
if is_node_type(expr, AST_SymbolRef) and has_prop(alias_set, expr.name):
|
|
104
|
+
info.can_tree_shake = False
|
|
105
|
+
for stmt in main_body:
|
|
106
|
+
stmt.walk(TreeWalker(visit_fn))
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def analyze_imports(main_body):
|
|
110
|
+
result = {}
|
|
111
|
+
|
|
112
|
+
# First pass: walk the entire AST recursively to collect all from-imports
|
|
113
|
+
# (including those nested inside functions or other scopes).
|
|
114
|
+
def visit_from_imports(node, descend):
|
|
115
|
+
if is_node_type(node, AST_Imports):
|
|
116
|
+
for imp in node.imports:
|
|
117
|
+
if imp.argnames:
|
|
118
|
+
key = imp.key
|
|
119
|
+
if not has_prop(result, key):
|
|
120
|
+
result[key] = {'direct_names': {}, 'can_tree_shake': True}
|
|
121
|
+
for argname in imp.argnames:
|
|
122
|
+
result[key].direct_names[argname.name] = True
|
|
123
|
+
for stmt in main_body:
|
|
124
|
+
stmt.walk(TreeWalker(visit_from_imports))
|
|
125
|
+
|
|
126
|
+
# Second pass: handle top-level plain imports (import X as Y, import X).
|
|
127
|
+
# Attribute tracking via check_module_attr_access applies to main_body scope.
|
|
128
|
+
for stmt in main_body:
|
|
129
|
+
if not is_node_type(stmt, AST_Imports):
|
|
130
|
+
continue
|
|
131
|
+
for imp in stmt.imports:
|
|
132
|
+
if imp.argnames:
|
|
133
|
+
continue # already handled in first pass
|
|
134
|
+
key = imp.key
|
|
135
|
+
if not has_prop(result, key):
|
|
136
|
+
result[key] = {'direct_names': {}, 'can_tree_shake': True}
|
|
137
|
+
info = result[key]
|
|
138
|
+
if imp.alias:
|
|
139
|
+
alias_set = {}
|
|
140
|
+
alias_set[imp.alias.name] = True
|
|
141
|
+
check_module_attr_access(main_body, info, alias_set)
|
|
142
|
+
else:
|
|
143
|
+
parts = key.split('.')
|
|
144
|
+
if parts.length > 1:
|
|
145
|
+
# import X.Y with no alias: user accesses via X.Y.attr which
|
|
146
|
+
# requires two-level dot traversal — disable tree-shaking safely
|
|
147
|
+
info.can_tree_shake = False
|
|
148
|
+
else:
|
|
149
|
+
alias_set = {}
|
|
150
|
+
alias_set[parts[0]] = True
|
|
151
|
+
check_module_attr_access(main_body, info, alias_set)
|
|
152
|
+
|
|
153
|
+
return result
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def tree_shake(ast, context):
|
|
157
|
+
import_infos = analyze_imports(ast.body)
|
|
158
|
+
for mod_key in Object.keys(import_infos):
|
|
159
|
+
info = import_infos[mod_key]
|
|
160
|
+
if not info.can_tree_shake:
|
|
161
|
+
continue
|
|
162
|
+
if not has_prop(ast.imports, mod_key):
|
|
163
|
+
continue
|
|
164
|
+
mod = ast.imports[mod_key]
|
|
165
|
+
# If body is missing (cached module), re-parse to get it
|
|
166
|
+
if not mod.body and mod.src_code:
|
|
167
|
+
parsed = context.parse(mod.src_code, {
|
|
168
|
+
'filename': mod.filename,
|
|
169
|
+
'module_id': mod_key,
|
|
170
|
+
'libdir': context.libdir,
|
|
171
|
+
'import_dirs': context.import_dirs or [],
|
|
172
|
+
'discard_asserts': context.discard_asserts,
|
|
173
|
+
'for_linting': True,
|
|
174
|
+
})
|
|
175
|
+
mod.body = parsed.body
|
|
176
|
+
mod.localvars = parsed.localvars
|
|
177
|
+
if not mod.nonlocalvars:
|
|
178
|
+
mod.nonlocalvars = parsed.nonlocalvars
|
|
179
|
+
if not mod.body:
|
|
180
|
+
continue
|
|
181
|
+
needed = compute_transitive_closure(mod.body, info.direct_names, mod.nonlocalvars)
|
|
182
|
+
mod.needed_names = needed
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# vim:fileencoding=utf-8
|
|
2
|
+
# License: BSD Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
|
3
|
+
from __python__ import hash_literals
|
|
4
|
+
|
|
5
|
+
from ast import AST_BlockStatement, is_node_type
|
|
6
|
+
|
|
7
|
+
def best_of(a):
|
|
8
|
+
best = a[0]
|
|
9
|
+
len_ = best.length
|
|
10
|
+
for i in range(1, a.length):
|
|
11
|
+
if a[i].length < len_:
|
|
12
|
+
best = a[i]
|
|
13
|
+
len_ = best.length
|
|
14
|
+
return best
|
|
15
|
+
|
|
16
|
+
def make_num(num):
|
|
17
|
+
str_ = num.toString(10)
|
|
18
|
+
a = [ str_.replace(/^0\./, ".").replace("e+", "e") ]
|
|
19
|
+
m = None
|
|
20
|
+
|
|
21
|
+
if Math.floor(num) is num:
|
|
22
|
+
if num >= 0:
|
|
23
|
+
a.push("0x" + num.toString(16).toLowerCase(), # probably pointless
|
|
24
|
+
"0" + num.toString(8))
|
|
25
|
+
else:
|
|
26
|
+
a.push("-0x" + (-num).toString(16).toLowerCase(), # probably pointless
|
|
27
|
+
"-0" + (-num).toString(8))
|
|
28
|
+
|
|
29
|
+
if m = /^(.*?)(0+)$/.exec(num):
|
|
30
|
+
a.push(m[1] + "e" + m[2].length)
|
|
31
|
+
|
|
32
|
+
elif m = /^0?\.(0+)(.*)$/.exec(num):
|
|
33
|
+
a.push(m[2] + "e-" + (m[1].length + m[2].length), str_.substr(str_.indexOf(".")))
|
|
34
|
+
|
|
35
|
+
return best_of(a)
|
|
36
|
+
|
|
37
|
+
def make_block(stmt, output):
|
|
38
|
+
if is_node_type(stmt, AST_BlockStatement):
|
|
39
|
+
stmt.print(output)
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
output.with_block(def():
|
|
43
|
+
output.indent()
|
|
44
|
+
stmt.print(output)
|
|
45
|
+
output.newline()
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def create_doctring(docstrings):
|
|
49
|
+
ans = v'[]'
|
|
50
|
+
for ds in docstrings:
|
|
51
|
+
ds = str.rstrip(ds.value)
|
|
52
|
+
lines = v'[]'
|
|
53
|
+
min_leading_whitespace = ''
|
|
54
|
+
for line in ds.split(/$/gm):
|
|
55
|
+
r = /^\s+/.exec(line)
|
|
56
|
+
leading_whitespace = ''
|
|
57
|
+
if r:
|
|
58
|
+
leading_whitespace = r[0].replace(/[\n\r]/g, '') if r else ''
|
|
59
|
+
line = line[r[0].length:]
|
|
60
|
+
if not str.strip(line):
|
|
61
|
+
lines.push(v'["", ""]')
|
|
62
|
+
else:
|
|
63
|
+
leading_whitespace = leading_whitespace.replace(/\t/g, ' ')
|
|
64
|
+
if leading_whitespace and (not min_leading_whitespace or leading_whitespace.length < min_leading_whitespace.length):
|
|
65
|
+
min_leading_whitespace = leading_whitespace
|
|
66
|
+
lines.push(v'[leading_whitespace, line]')
|
|
67
|
+
for lw, l in lines:
|
|
68
|
+
if min_leading_whitespace:
|
|
69
|
+
lw = lw[min_leading_whitespace.length:]
|
|
70
|
+
ans.push(lw + l)
|
|
71
|
+
ans.push('')
|
|
72
|
+
return str.rstrip(ans.join('\n'))
|