rapydscript-ns 0.9.2 → 0.9.3

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 (151) hide show
  1. package/.agignore +1 -1
  2. package/.github/workflows/ci.yml +38 -38
  3. package/=template.pyj +5 -5
  4. package/CHANGELOG.md +19 -0
  5. package/HACKING.md +103 -103
  6. package/LICENSE +24 -24
  7. package/PYTHON_GAPS.md +420 -0
  8. package/README.md +153 -29
  9. package/TODO.md +16 -118
  10. package/add-toc-to-readme +2 -2
  11. package/bin/export +75 -75
  12. package/bin/rapydscript +70 -70
  13. package/bin/web-repl-export +102 -102
  14. package/build +2 -2
  15. package/language-service/index.js +237 -8
  16. package/memory/project_string_impl.md +43 -0
  17. package/package.json +1 -1
  18. package/publish.py +37 -37
  19. package/release/baselib-plain-pretty.js +248 -38
  20. package/release/baselib-plain-ugly.js +8 -8
  21. package/release/compiler.js +778 -277
  22. package/release/signatures.json +30 -30
  23. package/session.vim +4 -4
  24. package/setup.cfg +2 -2
  25. package/src/ast.pyj +4 -1
  26. package/src/baselib-builtins.pyj +56 -2
  27. package/src/baselib-containers.pyj +2 -0
  28. package/src/baselib-errors.pyj +7 -3
  29. package/src/baselib-internal.pyj +51 -6
  30. package/src/baselib-str.pyj +5 -3
  31. package/src/compiler.pyj +36 -36
  32. package/src/errors.pyj +30 -30
  33. package/src/lib/aes.pyj +646 -646
  34. package/src/lib/asyncio.pyj +534 -0
  35. package/src/lib/base64.pyj +399 -0
  36. package/src/lib/bisect.pyj +73 -0
  37. package/src/lib/collections.pyj +1 -1
  38. package/src/lib/copy.pyj +120 -120
  39. package/src/lib/csv.pyj +494 -0
  40. package/src/lib/elementmaker.pyj +83 -83
  41. package/src/lib/encodings.pyj +126 -126
  42. package/src/lib/gettext.pyj +569 -569
  43. package/src/lib/heapq.pyj +98 -0
  44. package/src/lib/html.pyj +382 -0
  45. package/src/lib/http/__init__.pyj +98 -0
  46. package/src/lib/http/client.pyj +304 -0
  47. package/src/lib/http/cookies.pyj +236 -0
  48. package/src/lib/itertools.pyj +580 -580
  49. package/src/lib/logging.pyj +672 -0
  50. package/src/lib/math.pyj +193 -193
  51. package/src/lib/operator.pyj +11 -11
  52. package/src/lib/pythonize.pyj +20 -20
  53. package/src/lib/random.pyj +118 -118
  54. package/src/lib/react.pyj +74 -74
  55. package/src/lib/string.pyj +357 -0
  56. package/src/lib/textwrap.pyj +329 -0
  57. package/src/lib/traceback.pyj +63 -63
  58. package/src/lib/urllib/__init__.pyj +14 -0
  59. package/src/lib/urllib/error.pyj +66 -0
  60. package/src/lib/urllib/parse.pyj +475 -0
  61. package/src/lib/urllib/request.pyj +86 -0
  62. package/src/lib/uuid.pyj +77 -77
  63. package/src/monaco-language-service/analyzer.js +5 -2
  64. package/src/monaco-language-service/completions.js +26 -0
  65. package/src/monaco-language-service/diagnostics.js +202 -3
  66. package/src/monaco-language-service/dts.js +550 -550
  67. package/src/monaco-language-service/scope.js +1 -0
  68. package/src/output/comments.pyj +45 -45
  69. package/src/output/exceptions.pyj +201 -201
  70. package/src/output/functions.pyj +152 -6
  71. package/src/output/jsx.pyj +164 -164
  72. package/src/output/loops.pyj +17 -2
  73. package/src/output/modules.pyj +1 -1
  74. package/src/output/operators.pyj +15 -0
  75. package/src/output/stream.pyj +0 -1
  76. package/src/output/treeshake.pyj +182 -182
  77. package/src/output/utils.pyj +72 -72
  78. package/src/parse.pyj +80 -17
  79. package/src/string_interpolation.pyj +72 -72
  80. package/src/tokenizer.pyj +1 -1
  81. package/src/unicode_aliases.pyj +576 -576
  82. package/src/utils.pyj +192 -192
  83. package/test/_import_one.pyj +37 -37
  84. package/test/_import_two/__init__.pyj +11 -11
  85. package/test/_import_two/level2/deep.pyj +4 -4
  86. package/test/_import_two/other.pyj +6 -6
  87. package/test/_import_two/sub.pyj +13 -13
  88. package/test/aes_vectors.pyj +421 -421
  89. package/test/annotations.pyj +80 -80
  90. package/test/async_generators.pyj +144 -0
  91. package/test/asyncio.pyj +307 -0
  92. package/test/base64.pyj +202 -0
  93. package/test/bisect.pyj +178 -0
  94. package/test/csv.pyj +405 -0
  95. package/test/decorators.pyj +77 -77
  96. package/test/docstrings.pyj +39 -39
  97. package/test/elementmaker_test.pyj +45 -45
  98. package/test/float_special.pyj +64 -0
  99. package/test/functions.pyj +151 -151
  100. package/test/generators.pyj +41 -41
  101. package/test/generic.pyj +370 -370
  102. package/test/heapq.pyj +174 -0
  103. package/test/html.pyj +212 -0
  104. package/test/http.pyj +259 -0
  105. package/test/imports.pyj +79 -72
  106. package/test/internationalization.pyj +73 -73
  107. package/test/lint.pyj +164 -164
  108. package/test/logging.pyj +356 -0
  109. package/test/long.pyj +130 -0
  110. package/test/loops.pyj +85 -85
  111. package/test/numpy.pyj +734 -734
  112. package/test/parenthesized_with.pyj +141 -0
  113. package/test/python_compat.pyj +3 -5
  114. package/test/python_modulo.pyj +76 -0
  115. package/test/python_modulo_off.pyj +21 -0
  116. package/test/repl.pyj +121 -121
  117. package/test/scoped_flags.pyj +76 -76
  118. package/test/str.pyj +14 -0
  119. package/test/string.pyj +245 -0
  120. package/test/textwrap.pyj +172 -0
  121. package/test/type_display.pyj +48 -0
  122. package/test/type_enforcement.pyj +164 -0
  123. package/test/unit/index.js +14 -6
  124. package/test/unit/language-service-completions.js +119 -0
  125. package/test/unit/language-service-dts.js +543 -543
  126. package/test/unit/language-service-hover.js +455 -455
  127. package/test/unit/language-service-scope.js +32 -0
  128. package/test/unit/language-service.js +127 -3
  129. package/test/unit/run-language-service.js +17 -3
  130. package/test/unit/web-repl.js +2094 -29
  131. package/test/urllib.pyj +193 -0
  132. package/tools/compile.js +1 -1
  133. package/tools/compiler.d.ts +367 -367
  134. package/tools/completer.js +131 -131
  135. package/tools/embedded_compiler.js +7 -7
  136. package/tools/gettext.js +185 -185
  137. package/tools/ini.js +65 -65
  138. package/tools/msgfmt.js +187 -187
  139. package/tools/repl.js +223 -223
  140. package/tools/test.js +118 -118
  141. package/tools/utils.js +128 -128
  142. package/tools/web_repl.js +95 -95
  143. package/try +41 -41
  144. package/web-repl/env.js +196 -196
  145. package/web-repl/index.html +163 -163
  146. package/web-repl/main.js +1 -1
  147. package/web-repl/prism.css +139 -139
  148. package/web-repl/prism.js +113 -113
  149. package/web-repl/rapydscript.js +224 -224
  150. package/web-repl/sha1.js +25 -25
  151. package/test/omit_function_metadata.pyj +0 -20
@@ -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
@@ -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
@@ -31,7 +31,7 @@ TreeWalker
31
31
  from tokenizer import tokenizer, is_token, RESERVED_WORDS
32
32
 
33
33
  COMPILER_VERSION = '__COMPILER_VERSION__'
34
- PYTHON_FLAGS = {'dict_literals':True, 'overload_getitem':True, 'bound_methods':True, 'hash_literals':True, 'overload_operators':True, 'truthiness':True, 'jsx':True, 'strict_arithmetic':True}
34
+ PYTHON_FLAGS = {'dict_literals':True, 'overload_getitem':True, 'bound_methods':True, 'hash_literals':True, 'overload_operators':True, 'truthiness':True, 'jsx':True, 'strict_arithmetic':True, 'python_modulo':True, 'type_enforcement':True}
35
35
 
36
36
 
37
37
  def get_compiler_version():
@@ -620,8 +620,15 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
620
620
  })
621
621
  elif tmp_ is "async":
622
622
  start = prev()
623
+ if is_("keyword", "for"):
624
+ # async for: consume an async iterable using JS `for await ... of`
625
+ next()
626
+ forstmt = for_()
627
+ forstmt.is_async = True
628
+ forstmt.start = start
629
+ return forstmt
623
630
  if not is_("keyword", "def"):
624
- croak("Expected 'def' after 'async'")
631
+ croak("Expected 'def' or 'for' after 'async'")
625
632
  next()
626
633
  func = function_(S.in_class[-1], False, True)
627
634
  func.start = start
@@ -708,21 +715,70 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
708
715
  def with_():
709
716
  clauses = v'[]'
710
717
  start = S.token
711
- while True:
712
- if is_('eof'):
713
- unexpected()
714
- expr = expression()
715
- alias = None
716
- if is_('keyword', 'as'):
717
- next()
718
- alias = as_symbol(AST_SymbolAlias)
719
- clauses.push(new AST_WithClause({'expression':expr, 'alias':alias}))
720
- if is_('punc', ','):
721
- next()
722
- continue
723
- if not is_('punc', ':'):
724
- unexpected()
725
- break
718
+
719
+ # Detect Python 3.10+ parenthesized form: with (cm1 as a, cm2 as b):
720
+ # vs traditional: with cm as a: or with (expr) as a:
721
+ # Lookahead: scan tokens inside the `(` for an `as` keyword at depth 0
722
+ # before the matching `)`. If found, it's the parenthesized form.
723
+ is_parenthesized_with = False
724
+ if is_('punc', '('):
725
+ depth = 0
726
+ i = 0
727
+ while True:
728
+ while S.peeked.length <= i:
729
+ S.peeked.push(S.input())
730
+ tok = S.peeked[i]
731
+ if tok.type is 'eof':
732
+ break
733
+ if tok.type is 'punc':
734
+ if tok.value is '(' or tok.value is '[' or tok.value is '{':
735
+ depth += 1
736
+ elif tok.value is ')' or tok.value is ']' or tok.value is '}':
737
+ if depth is 0:
738
+ break
739
+ depth -= 1
740
+ elif tok.type is 'keyword' and tok.value is 'as' and depth is 0:
741
+ is_parenthesized_with = True
742
+ break
743
+ i += 1
744
+
745
+ if is_parenthesized_with:
746
+ next() # consume `(`
747
+ while True:
748
+ if is_('eof'):
749
+ unexpected()
750
+ if is_('punc', ')'):
751
+ next() # trailing comma before closing `)`
752
+ break
753
+ expr = expression()
754
+ alias = None
755
+ if is_('keyword', 'as'):
756
+ next()
757
+ alias = as_symbol(AST_SymbolAlias)
758
+ clauses.push(new AST_WithClause({'expression': expr, 'alias': alias}))
759
+ if is_('punc', ','):
760
+ next()
761
+ continue
762
+ if not is_('punc', ')'):
763
+ unexpected()
764
+ next() # consume `)`
765
+ break
766
+ else:
767
+ while True:
768
+ if is_('eof'):
769
+ unexpected()
770
+ expr = expression()
771
+ alias = None
772
+ if is_('keyword', 'as'):
773
+ next()
774
+ alias = as_symbol(AST_SymbolAlias)
775
+ clauses.push(new AST_WithClause({'expression':expr, 'alias':alias}))
776
+ if is_('punc', ','):
777
+ next()
778
+ continue
779
+ if not is_('punc', ':'):
780
+ unexpected()
781
+ break
726
782
 
727
783
  if not clauses.length:
728
784
  token_error(start, 'with statement must have at least one clause')
@@ -1396,6 +1452,9 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1396
1452
  argnames.push(aname)
1397
1453
  if is_('punc', ','):
1398
1454
  next()
1455
+ if bracketed and is_('punc', ')'):
1456
+ next()
1457
+ break
1399
1458
  else:
1400
1459
  if bracketed:
1401
1460
  if is_('punc', ')'):
@@ -1824,6 +1883,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1824
1883
  )(S.in_loop, S.labels)
1825
1884
  })
1826
1885
  definition.return_annotation = return_annotation
1886
+ definition.type_enforce = S.scoped_flags.get('type_enforcement', False)
1827
1887
  definition.is_generator = is_generator[0]
1828
1888
  definition.is_async = is_async
1829
1889
  if is_node_type(definition, AST_Method):
@@ -3242,6 +3302,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
3242
3302
  'end': right.end,
3243
3303
  'overloaded': S.scoped_flags.get('overload_operators', False),
3244
3304
  'strict_arith': S.scoped_flags.get('overload_operators', False) and S.scoped_flags.get('strict_arithmetic', True),
3305
+ 'python_mod': (op is '%') and S.scoped_flags.get('python_modulo', True),
3245
3306
  'python_truthiness': S.scoped_flags.get('truthiness', False) and (op is '&&' or op is '||')
3246
3307
  })
3247
3308
  return expr_op(ret, min_prec, no_in)
@@ -3324,6 +3385,8 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
3324
3385
  if S.scoped_flags.get('overload_operators', False) and val is not '=':
3325
3386
  asgn.overloaded = True
3326
3387
  asgn.strict_arith = S.scoped_flags.get('strict_arithmetic', True)
3388
+ if val is '%=':
3389
+ asgn.python_mod = S.scoped_flags.get('python_modulo', True)
3327
3390
  return asgn
3328
3391
  return left
3329
3392