rapydscript-ns 0.9.3 → 0.9.5
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 -1
- package/.github/workflows/ci.yml +38 -38
- package/=template.pyj +5 -5
- package/CHANGELOG.md +18 -0
- package/HACKING.md +103 -103
- package/LICENSE +24 -24
- package/PYTHON_GAPS.md +52 -142
- package/README.md +51 -21
- package/TODO.md +1 -26
- package/add-toc-to-readme +2 -2
- package/bin/export +75 -75
- package/bin/rapydscript +0 -0
- package/bin/web-repl-export +102 -102
- package/build +2 -2
- package/language-service/index.js +88 -36
- package/package.json +1 -1
- package/publish.py +37 -37
- package/release/baselib-plain-pretty.js +157 -31
- package/release/baselib-plain-ugly.js +5 -5
- package/release/compiler.js +724 -426
- package/release/signatures.json +29 -29
- package/session.vim +4 -4
- package/setup.cfg +2 -2
- package/src/ast.pyj +7 -0
- package/src/baselib-containers.pyj +41 -4
- package/src/baselib-errors.pyj +4 -3
- package/src/baselib-internal.pyj +47 -18
- package/src/baselib-str.pyj +16 -3
- package/src/compiler.pyj +36 -36
- package/src/errors.pyj +30 -30
- package/src/lib/aes.pyj +646 -646
- package/src/lib/collections.pyj +227 -3
- package/src/lib/copy.pyj +120 -120
- package/src/lib/elementmaker.pyj +83 -83
- package/src/lib/encodings.pyj +126 -126
- package/src/lib/gettext.pyj +569 -569
- package/src/lib/itertools.pyj +580 -580
- package/src/lib/math.pyj +193 -193
- package/src/lib/operator.pyj +11 -11
- package/src/lib/pprint.pyj +455 -0
- package/src/lib/random.pyj +118 -118
- package/src/lib/react.pyj +74 -74
- package/src/lib/statistics.pyj +0 -0
- package/src/lib/traceback.pyj +63 -63
- package/src/lib/uuid.pyj +77 -77
- package/src/monaco-language-service/completions.js +21 -14
- package/src/monaco-language-service/diagnostics.js +2 -2
- package/src/monaco-language-service/dts.js +58 -15
- package/src/monaco-language-service/package.json +3 -0
- package/src/output/classes.pyj +25 -2
- package/src/output/codegen.pyj +4 -1
- package/src/output/comments.pyj +45 -45
- package/src/output/exceptions.pyj +201 -201
- package/src/output/jsx.pyj +164 -164
- package/src/output/treeshake.pyj +182 -182
- package/src/output/utils.pyj +72 -72
- package/src/parse.pyj +42 -7
- package/src/string_interpolation.pyj +72 -72
- package/src/tokenizer.pyj +18 -2
- package/src/unicode_aliases.pyj +576 -576
- package/src/utils.pyj +192 -192
- package/test/_import_one.pyj +37 -37
- package/test/_import_two/__init__.pyj +11 -11
- package/test/_import_two/level2/deep.pyj +4 -4
- package/test/_import_two/other.pyj +6 -6
- package/test/_import_two/sub.pyj +13 -13
- package/test/aes_vectors.pyj +421 -421
- package/test/annotations.pyj +80 -80
- package/test/baselib.pyj +23 -0
- package/test/chainmap.pyj +185 -0
- package/test/dataclasses.pyj +3 -4
- package/test/decorators.pyj +77 -77
- package/test/docstrings.pyj +39 -39
- package/test/elementmaker_test.pyj +45 -45
- package/test/enum.pyj +1 -1
- package/test/functions.pyj +151 -151
- package/test/generators.pyj +41 -41
- package/test/generic.pyj +370 -370
- package/test/internationalization.pyj +73 -73
- package/test/lint.pyj +164 -164
- package/test/loops.pyj +85 -85
- package/test/numpy.pyj +734 -734
- package/test/pprint.pyj +232 -0
- package/test/python_features.pyj +1 -1
- package/test/repl.pyj +121 -121
- package/test/scoped_flags.pyj +76 -76
- package/test/statistics.pyj +224 -0
- package/test/str.pyj +4 -4
- package/test/unit/index.js +455 -0
- package/test/unit/language-service-completions.js +2 -0
- package/test/unit/language-service-dts.js +113 -0
- package/test/unit/language-service-hover.js +455 -455
- package/test/unit/language-service.js +135 -2
- package/test/unit/web-repl.js +349 -1
- package/tools/compiler.d.ts +367 -367
- package/tools/completer.js +131 -131
- package/tools/export.js +4 -2
- package/tools/gettext.js +185 -185
- package/tools/ini.js +65 -65
- package/tools/msgfmt.js +187 -187
- package/tools/repl.js +223 -223
- package/tools/test.js +118 -118
- package/tools/utils.js +141 -128
- package/tools/web_repl.js +95 -95
- package/try +41 -41
- package/web-repl/env.js +196 -196
- package/web-repl/index.html +163 -163
- package/web-repl/prism.css +139 -139
- package/web-repl/prism.js +113 -113
- package/web-repl/rapydscript.js +228 -226
- package/web-repl/sha1.js +25 -25
package/src/output/treeshake.pyj
CHANGED
|
@@ -1,182 +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
|
|
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
|
package/src/output/utils.pyj
CHANGED
|
@@ -1,72 +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'))
|
|
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'))
|
package/src/parse.pyj
CHANGED
|
@@ -6,7 +6,7 @@ from __python__ import hash_literals
|
|
|
6
6
|
from utils import make_predicate, array_to_hash, defaults, has_prop, cache_file_name
|
|
7
7
|
from errors import SyntaxError, ImportError
|
|
8
8
|
from ast import (
|
|
9
|
-
AST_Array, AST_Assign, AST_Binary, AST_BlockStatement, AST_Break,
|
|
9
|
+
AST_Array, AST_Assign, AST_BigInt, AST_Binary, AST_BlockStatement, AST_Break,
|
|
10
10
|
AST_Call, AST_Catch, AST_Class, AST_ClassCall, AST_Conditional,
|
|
11
11
|
AST_Constant, AST_Continue, AST_DWLoop, AST_Debugger, AST_Decorator,
|
|
12
12
|
AST_Definitions, AST_DictComprehension, AST_Directive, AST_Do, AST_Dot,
|
|
@@ -144,7 +144,7 @@ PRECEDENCE = (def(a, ret):
|
|
|
144
144
|
|
|
145
145
|
STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ])
|
|
146
146
|
|
|
147
|
-
ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "imaginary", "string", "bytes_literal", "regexp", "name", "js" ])
|
|
147
|
+
ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "bigint", "imaginary", "string", "bytes_literal", "regexp", "name", "js" ])
|
|
148
148
|
|
|
149
149
|
compile_time_decorators = ['staticmethod', 'classmethod', 'external', 'property']
|
|
150
150
|
|
|
@@ -520,7 +520,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
520
520
|
return new AST_Directive({
|
|
521
521
|
'value': tmp_
|
|
522
522
|
})
|
|
523
|
-
elif tmp_ is "num" or tmp_ is "regexp" or tmp_ is "operator" or tmp_ is "atom" or tmp_ is "js":
|
|
523
|
+
elif tmp_ is "num" or tmp_ is "bigint" or tmp_ is "regexp" or tmp_ is "operator" or tmp_ is "atom" or tmp_ is "js":
|
|
524
524
|
return simple_statement()
|
|
525
525
|
elif tmp_ is "punc":
|
|
526
526
|
tmp_ = S.token.value
|
|
@@ -797,6 +797,8 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
797
797
|
return new AST_String({'start': tok, 'value': tok.value, 'end': tok})
|
|
798
798
|
elif tok.type is 'num':
|
|
799
799
|
return new AST_Number({'start': tok, 'value': tok.value, 'end': tok})
|
|
800
|
+
elif tok.type is 'bigint':
|
|
801
|
+
return new AST_BigInt({'start': tok, 'value': tok.value, 'end': tok})
|
|
800
802
|
elif tok.type is 'atom':
|
|
801
803
|
if tok.value is 'True':
|
|
802
804
|
return new AST_True({'start': tok, 'end': tok})
|
|
@@ -810,15 +812,18 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
810
812
|
"""Parse a single atomic/closed pattern (no OR, no AS)."""
|
|
811
813
|
start = S.token
|
|
812
814
|
|
|
813
|
-
# Negative number literal: -42
|
|
814
|
-
if is_('operator', '-') and peek().type is 'num':
|
|
815
|
+
# Negative number literal: -42 or -42n
|
|
816
|
+
if is_('operator', '-') and (peek().type is 'num' or peek().type is 'bigint'):
|
|
815
817
|
next() # consume '-'
|
|
816
|
-
val = -S.token.value
|
|
817
818
|
tok = S.token
|
|
819
|
+
if tok.type is 'bigint':
|
|
820
|
+
val_node = new AST_BigInt({'start': start, 'value': '-' + tok.value, 'end': tok})
|
|
821
|
+
else:
|
|
822
|
+
val_node = new AST_Number({'start': start, 'value': -tok.value, 'end': tok})
|
|
818
823
|
next()
|
|
819
824
|
return new AST_MatchLiteral({
|
|
820
825
|
'start': start,
|
|
821
|
-
'value':
|
|
826
|
+
'value': val_node,
|
|
822
827
|
'end': prev()
|
|
823
828
|
})
|
|
824
829
|
|
|
@@ -832,6 +837,16 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
832
837
|
'end': prev()
|
|
833
838
|
})
|
|
834
839
|
|
|
840
|
+
# BigInt literal
|
|
841
|
+
if is_('bigint'):
|
|
842
|
+
tok = S.token
|
|
843
|
+
next()
|
|
844
|
+
return new AST_MatchLiteral({
|
|
845
|
+
'start': tok,
|
|
846
|
+
'value': new AST_BigInt({'start': tok, 'value': tok.value, 'end': tok}),
|
|
847
|
+
'end': prev()
|
|
848
|
+
})
|
|
849
|
+
|
|
835
850
|
# String literal
|
|
836
851
|
if is_('string'):
|
|
837
852
|
tok = S.token
|
|
@@ -1607,6 +1622,20 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
1607
1622
|
if parent_details and parent_details.has_attr_dunders:
|
|
1608
1623
|
definition.has_attr_dunders = True
|
|
1609
1624
|
class_details.has_attr_dunders = True
|
|
1625
|
+
# Detect __slots__ = [...] in class body
|
|
1626
|
+
for stmt in definition.body:
|
|
1627
|
+
assign = stmt
|
|
1628
|
+
if is_node_type(stmt, AST_SimpleStatement):
|
|
1629
|
+
assign = stmt.body
|
|
1630
|
+
if is_node_type(assign, AST_Assign) and is_node_type(assign.left, AST_SymbolRef) and assign.left.name is '__slots__':
|
|
1631
|
+
if is_node_type(assign.right, AST_Array):
|
|
1632
|
+
slot_names = []
|
|
1633
|
+
for elem in assign.right.elements:
|
|
1634
|
+
if is_node_type(elem, AST_String):
|
|
1635
|
+
slot_names.push(elem.value)
|
|
1636
|
+
definition.slots = slot_names
|
|
1637
|
+
class_details.slots = slot_names
|
|
1638
|
+
break
|
|
1610
1639
|
# find the class variables
|
|
1611
1640
|
class_var_names = {}
|
|
1612
1641
|
# Ensure that if a class variable refers to another class variable in
|
|
@@ -2245,6 +2274,12 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
2245
2274
|
'end': tok,
|
|
2246
2275
|
'value': tok.value
|
|
2247
2276
|
})
|
|
2277
|
+
elif tmp_ is "bigint":
|
|
2278
|
+
return new AST_BigInt({
|
|
2279
|
+
'start': tok,
|
|
2280
|
+
'end': tok,
|
|
2281
|
+
'value': tok.value
|
|
2282
|
+
})
|
|
2248
2283
|
elif tmp_ is "imaginary":
|
|
2249
2284
|
return new AST_Call({
|
|
2250
2285
|
'start': tok,
|