rapydscript-ns 0.8.1 → 0.8.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.
- package/CHANGELOG.md +31 -0
- package/CONTRIBUTORS +3 -2
- package/PYTHON_DIFFERENCES_REPORT.md +291 -0
- package/PYTHON_FEATURE_COVERAGE.md +200 -0
- package/README.md +480 -79
- package/TODO.md +6 -318
- package/hack_demo.pyj +112 -0
- package/language-service/index.js +4474 -0
- package/language-service/language-service.d.ts +40 -0
- package/package.json +9 -10
- package/src/ast.pyj +30 -6
- package/src/baselib-builtins.pyj +181 -11
- package/src/baselib-containers.pyj +154 -5
- package/src/baselib-errors.pyj +3 -0
- package/src/baselib-internal.pyj +40 -1
- package/src/baselib-str.pyj +42 -1
- package/src/lib/collections.pyj +1 -1
- package/src/lib/numpy.pyj +10 -10
- package/src/monaco-language-service/analyzer.js +132 -22
- package/src/monaco-language-service/builtins.js +22 -2
- package/src/monaco-language-service/completions.js +224 -3
- package/src/monaco-language-service/diagnostics.js +55 -5
- package/src/monaco-language-service/index.js +26 -5
- package/src/monaco-language-service/scope.js +3 -0
- package/src/output/classes.pyj +20 -3
- package/src/output/codegen.pyj +38 -3
- package/src/output/functions.pyj +35 -25
- package/src/output/loops.pyj +64 -11
- package/src/output/modules.pyj +1 -4
- package/src/output/operators.pyj +67 -1
- package/src/output/statements.pyj +7 -3
- package/src/output/stream.pyj +6 -13
- package/src/parse.pyj +94 -14
- package/src/tokenizer.pyj +1 -0
- package/test/baselib.pyj +4 -4
- package/test/classes.pyj +56 -17
- package/test/collections.pyj +5 -5
- package/test/python_compat.pyj +326 -0
- package/test/python_features.pyj +1271 -0
- package/test/slice.pyj +105 -0
- package/test/str.pyj +25 -0
- package/test/unit/fixtures/fibonacci_expected.js +1 -1
- package/test/unit/index.js +119 -7
- package/test/unit/language-service-builtins.js +70 -0
- package/test/unit/language-service-bundle.js +83 -0
- package/test/unit/language-service-completions.js +289 -0
- package/test/unit/language-service-index.js +350 -0
- package/test/unit/language-service-scope.js +255 -0
- package/test/unit/language-service.js +158 -1
- package/test/unit/run-language-service.js +2 -0
- package/test/unit/web-repl.js +134 -0
- package/tools/build-language-service.js +2 -2
- package/tools/compiler.js +0 -24
- package/tools/export.js +3 -37
- package/tools/lint.js +1 -1
- package/tools/self.js +1 -9
- package/web-repl/rapydscript.js +6 -40
- package/web-repl/language-service.js +0 -4084
package/src/output/functions.pyj
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# vim:fileencoding=utf-8
|
|
2
2
|
# License: BSD Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
|
3
|
-
# globals:regenerate
|
|
4
3
|
from __python__ import hash_literals
|
|
5
4
|
|
|
6
5
|
from ast import AST_ClassCall, AST_New, has_calls, AST_Dot, AST_SymbolRef, is_node_type
|
|
@@ -45,7 +44,7 @@ def function_args(argnames, output, strip_first):
|
|
|
45
44
|
|
|
46
45
|
def function_preamble(node, output, offset):
|
|
47
46
|
a = node.argnames
|
|
48
|
-
if
|
|
47
|
+
if a is None or a is undefined or a.is_simple_func:
|
|
49
48
|
return
|
|
50
49
|
# If this function has optional parameters/*args/**kwargs declare it differently
|
|
51
50
|
fname = node.name.name if node.name else anonfunc
|
|
@@ -263,21 +262,10 @@ def function_definition(self, output, strip_first, as_expression):
|
|
|
263
262
|
if self.is_generator:
|
|
264
263
|
output.print('()'), output.space()
|
|
265
264
|
output.with_block(def():
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
print_bracketed(self, output, True, function_preamble)
|
|
271
|
-
else:
|
|
272
|
-
temp = OutputStream({'beautify':True})
|
|
273
|
-
temp.print('function* js_generator')
|
|
274
|
-
function_args(self.argnames, temp, strip_first)
|
|
275
|
-
print_bracketed(self, temp, True, function_preamble)
|
|
276
|
-
transpiled = regenerate(temp.get(), output.options.beautify).replace(/regeneratorRuntime.(wrap|mark)/g, 'ρσ_regenerator.regeneratorRuntime.$1')
|
|
277
|
-
if output.options.beautify:
|
|
278
|
-
ci = output.make_indent(0)
|
|
279
|
-
transpiled = [ci + x for x in transpiled.split('\n')].join('\n')
|
|
280
|
-
output.print(transpiled)
|
|
265
|
+
output.indent()
|
|
266
|
+
output.print('function* js_generator')
|
|
267
|
+
function_args(self.argnames, output, strip_first)
|
|
268
|
+
print_bracketed(self, output, True, function_preamble)
|
|
281
269
|
output.newline()
|
|
282
270
|
output.indent()
|
|
283
271
|
output.spaced('var', 'result', '=', 'js_generator.apply(this,', 'arguments)')
|
|
@@ -433,20 +421,42 @@ def print_function_call(self, output):
|
|
|
433
421
|
return # new A is the same as new A() in javascript
|
|
434
422
|
|
|
435
423
|
if not has_kwargs and not self.args.starargs:
|
|
436
|
-
# A simple function call
|
|
424
|
+
# A simple function call
|
|
437
425
|
if is_new:
|
|
438
426
|
output.print('new'), output.space()
|
|
439
|
-
|
|
440
|
-
|
|
427
|
+
print_function_name()
|
|
428
|
+
output.with_parens(def():
|
|
429
|
+
for i, a in enumerate(self.args):
|
|
430
|
+
if i:
|
|
431
|
+
output.comma()
|
|
432
|
+
a.print(output)
|
|
433
|
+
)
|
|
434
|
+
elif is_node_type(self.expression, AST_SymbolRef) and self.expression.name is 'print':
|
|
435
|
+
# Bare print(...) → console.log(...) to avoid clobbering window.print
|
|
441
436
|
output.print('console.log')
|
|
442
|
-
|
|
437
|
+
output.with_parens(def():
|
|
438
|
+
for i, a in enumerate(self.args):
|
|
439
|
+
if i:
|
|
440
|
+
output.comma()
|
|
441
|
+
a.print(output)
|
|
442
|
+
)
|
|
443
|
+
elif is_node_type(self.expression, AST_SymbolRef) and self.python_truthiness:
|
|
444
|
+
# __call__-aware dispatch when from __python__ import truthiness is active
|
|
445
|
+
output.print('ρσ_callable_call(')
|
|
443
446
|
print_function_name()
|
|
444
|
-
output.with_parens(def():
|
|
445
447
|
for i, a in enumerate(self.args):
|
|
446
|
-
|
|
447
|
-
output.comma()
|
|
448
|
+
output.comma()
|
|
448
449
|
a.print(output)
|
|
449
|
-
|
|
450
|
+
output.print(')')
|
|
451
|
+
else:
|
|
452
|
+
# Method calls and other complex expressions — keep existing behaviour
|
|
453
|
+
print_function_name()
|
|
454
|
+
output.with_parens(def():
|
|
455
|
+
for i, a in enumerate(self.args):
|
|
456
|
+
if i:
|
|
457
|
+
output.comma()
|
|
458
|
+
a.print(output)
|
|
459
|
+
)
|
|
450
460
|
return
|
|
451
461
|
|
|
452
462
|
is_repeatable = is_new or not has_calls(self.expression)
|
package/src/output/loops.pyj
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# vim:fileencoding=utf-8
|
|
2
2
|
# License: BSD Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
|
3
|
-
# globals: regenerate
|
|
4
3
|
from __python__ import hash_literals
|
|
5
4
|
|
|
6
5
|
from ast import AST_BaseCall, AST_SymbolRef, AST_Array, AST_Unary, AST_Number, has_calls, AST_Seq, AST_ListComprehension, AST_Starred, is_node_type
|
|
@@ -47,16 +46,49 @@ def print_do_loop(self, output):
|
|
|
47
46
|
output.space()
|
|
48
47
|
output.print("while")
|
|
49
48
|
output.space()
|
|
50
|
-
|
|
49
|
+
if self.python_truthiness:
|
|
50
|
+
output.with_parens(def(): output.print('ρσ_bool('), self.condition.print(output), output.print(')');)
|
|
51
|
+
else:
|
|
52
|
+
output.with_parens(def(): self.condition.print(output);)
|
|
51
53
|
output.semicolon()
|
|
52
54
|
|
|
53
55
|
def print_while_loop(self, output):
|
|
56
|
+
# while/else: wrap in a labeled block; break inside will skip the else
|
|
57
|
+
forelse_label = None
|
|
58
|
+
if self.belse:
|
|
59
|
+
forelse_label = output.new_forelse_label()
|
|
60
|
+
output.forelse_stack.push(forelse_label)
|
|
61
|
+
output.print(forelse_label + ':')
|
|
62
|
+
output.space()
|
|
63
|
+
output.print('{')
|
|
64
|
+
output.newline()
|
|
65
|
+
output.set_indentation(output.next_indent())
|
|
66
|
+
output.indent()
|
|
67
|
+
else:
|
|
68
|
+
output.forelse_stack.push(None)
|
|
69
|
+
|
|
54
70
|
output.print("while")
|
|
55
71
|
output.space()
|
|
56
|
-
|
|
72
|
+
if self.python_truthiness:
|
|
73
|
+
output.with_parens(def(): output.print('ρσ_bool('), self.condition.print(output), output.print(')');)
|
|
74
|
+
else:
|
|
75
|
+
output.with_parens(def(): self.condition.print(output);)
|
|
57
76
|
output.space()
|
|
58
77
|
self._do_print_body(output)
|
|
59
78
|
|
|
79
|
+
output.forelse_stack.pop()
|
|
80
|
+
|
|
81
|
+
if forelse_label:
|
|
82
|
+
if self.belse and self.belse.body and self.belse.body.length:
|
|
83
|
+
output.newline()
|
|
84
|
+
for stmt in self.belse.body:
|
|
85
|
+
output.indent()
|
|
86
|
+
stmt.print(output)
|
|
87
|
+
output.newline()
|
|
88
|
+
output.set_indentation(output._indentation - output.options.indent_level)
|
|
89
|
+
output.indent()
|
|
90
|
+
output.print('}')
|
|
91
|
+
|
|
60
92
|
def is_simple_for_in(self):
|
|
61
93
|
# return true if this loop can be simplified into a basic for (i in j) loop
|
|
62
94
|
if is_node_type(self.object, AST_BaseCall)
|
|
@@ -131,6 +163,21 @@ def print_for_in(self, output):
|
|
|
131
163
|
else:
|
|
132
164
|
self.object.print(output)
|
|
133
165
|
|
|
166
|
+
# for/else: wrap in a labeled block; break inside will use the label to skip else
|
|
167
|
+
forelse_label = None
|
|
168
|
+
if self.belse:
|
|
169
|
+
forelse_label = output.new_forelse_label()
|
|
170
|
+
output.forelse_stack.push(forelse_label)
|
|
171
|
+
output.print(forelse_label + ':')
|
|
172
|
+
output.space()
|
|
173
|
+
output.print('{')
|
|
174
|
+
output.newline()
|
|
175
|
+
output.set_indentation(output.next_indent())
|
|
176
|
+
output.indent()
|
|
177
|
+
else:
|
|
178
|
+
# Push null so nested for loops don't mistakenly grab an outer forelse label
|
|
179
|
+
output.forelse_stack.push(None)
|
|
180
|
+
|
|
134
181
|
if is_simple_for(self):
|
|
135
182
|
# optimize range() into a simple for loop
|
|
136
183
|
increment = None
|
|
@@ -227,6 +274,20 @@ def print_for_in(self, output):
|
|
|
227
274
|
output.space()
|
|
228
275
|
self._do_print_body(output)
|
|
229
276
|
|
|
277
|
+
output.forelse_stack.pop()
|
|
278
|
+
|
|
279
|
+
if forelse_label:
|
|
280
|
+
# Emit else body, then close the labeled block
|
|
281
|
+
if self.belse and self.belse.body and self.belse.body.length:
|
|
282
|
+
output.newline()
|
|
283
|
+
for stmt in self.belse.body:
|
|
284
|
+
output.indent()
|
|
285
|
+
stmt.print(output)
|
|
286
|
+
output.newline()
|
|
287
|
+
output.set_indentation(output._indentation - output.options.indent_level)
|
|
288
|
+
output.indent()
|
|
289
|
+
output.print('}')
|
|
290
|
+
|
|
230
291
|
def print_list_comprehension(self, output):
|
|
231
292
|
tname = self.constructor.name.slice(4)
|
|
232
293
|
result_obj = {'ListComprehension':'[]', 'DictComprehension':('Object.create(null)' if self.is_jshash else '{}'), 'SetComprehension':'ρσ_set()'}[tname]
|
|
@@ -374,8 +435,6 @@ def print_list_comprehension(self, output):
|
|
|
374
435
|
output.with_block(def():
|
|
375
436
|
body_out = output
|
|
376
437
|
if is_generator:
|
|
377
|
-
if es5:
|
|
378
|
-
body_out = OutputStream({'beautify':True})
|
|
379
438
|
body_out.indent()
|
|
380
439
|
body_out.print('function* js_generator()'), body_out.space(), body_out.print('{')
|
|
381
440
|
body_out.newline()
|
|
@@ -423,12 +482,6 @@ def print_list_comprehension(self, output):
|
|
|
423
482
|
if is_generator:
|
|
424
483
|
output.set_indentation(previous_indentation)
|
|
425
484
|
body_out.newline(), body_out.indent(), body_out.print('}') # end js_generator
|
|
426
|
-
if es5:
|
|
427
|
-
transpiled = regenerate(body_out.get(), output.options.beautify).replace(/regeneratorRuntime.(wrap|mark)/g, 'ρσ_regenerator.regeneratorRuntime.$1')
|
|
428
|
-
if output.options.beautify:
|
|
429
|
-
ci = output.make_indent(0)
|
|
430
|
-
transpiled = [ci + x for x in transpiled.split('\n')].join('\n')
|
|
431
|
-
output.print(transpiled)
|
|
432
485
|
output.newline(), output.indent()
|
|
433
486
|
output.spaced('var', 'result', '=', 'js_generator.call(this)')
|
|
434
487
|
output.end_statement()
|
package/src/output/modules.pyj
CHANGED
|
@@ -18,7 +18,7 @@ def write_imports(module, output):
|
|
|
18
18
|
imports = []
|
|
19
19
|
for import_id in Object.keys(module.imports):
|
|
20
20
|
imports.push(module.imports[import_id])
|
|
21
|
-
imports.
|
|
21
|
+
imports.jssort(def(a, b):
|
|
22
22
|
a, b = a.import_order, b.import_order
|
|
23
23
|
return -1 if a < b else (1 if a > b else 0)
|
|
24
24
|
)
|
|
@@ -122,9 +122,6 @@ def prologue(module, output):
|
|
|
122
122
|
output.indent(), output.spaced('if(', 'typeof', 'HTMLCollection', '!==', '"undefined"', '&&', 'typeof', 'Symbol', '===', '"function")',
|
|
123
123
|
'NodeList.prototype[Symbol.iterator]', '=', 'HTMLCollection.prototype[Symbol.iterator]', '=', 'NamedNodeMap.prototype[Symbol.iterator]', '=', 'Array.prototype[Symbol.iterator]')
|
|
124
124
|
output.end_statement()
|
|
125
|
-
needs_yield = output.options.js_version < 6 and module.baselib['yield']
|
|
126
|
-
if needs_yield:
|
|
127
|
-
output.dump_yield()
|
|
128
125
|
# output the baselib
|
|
129
126
|
if not output.options.baselib_plain:
|
|
130
127
|
raise ValueError('The baselib is missing! Remember to set the baselib_plain field on the options for OutputStream')
|
package/src/output/operators.pyj
CHANGED
|
@@ -110,6 +110,16 @@ def print_unary_prefix(self, output):
|
|
|
110
110
|
if op is '~':
|
|
111
111
|
output.print('ρσ_op_invert('), self.expression.print(output), output.print(')')
|
|
112
112
|
return
|
|
113
|
+
if op is '!' and self.python_truthiness:
|
|
114
|
+
output.print('!ρσ_bool(')
|
|
115
|
+
if self.parenthesized:
|
|
116
|
+
output.with_parens(def():
|
|
117
|
+
self.expression.print(output)
|
|
118
|
+
)
|
|
119
|
+
else:
|
|
120
|
+
self.expression.print(output)
|
|
121
|
+
output.print(')')
|
|
122
|
+
return
|
|
113
123
|
output.print(op)
|
|
114
124
|
if /^[a-z]/i.test(op):
|
|
115
125
|
output.space()
|
|
@@ -270,6 +280,12 @@ def print_binary_op(self, output):
|
|
|
270
280
|
write_smart_equality(self, output)
|
|
271
281
|
elif self.operator is 'instanceof':
|
|
272
282
|
write_instanceof(self.left, self.right, output)
|
|
283
|
+
elif self.operator is '+':
|
|
284
|
+
output.print('ρσ_list_add(')
|
|
285
|
+
self.left.print(output)
|
|
286
|
+
output.comma()
|
|
287
|
+
self.right.print(output)
|
|
288
|
+
output.print(')')
|
|
273
289
|
elif self.operator is '*' and is_node_type(self.left, AST_String):
|
|
274
290
|
self.left.print(output), output.print('.repeat('), self.right.print(output), output.print(')')
|
|
275
291
|
elif self.operator is '===' or self.operator is '!==':
|
|
@@ -284,6 +300,36 @@ def print_binary_op(self, output):
|
|
|
284
300
|
output.spaced(nan_check, '!==' if self.operator is '===' else '===', nan_check)
|
|
285
301
|
else:
|
|
286
302
|
output.spaced(self.left, self.operator, self.right)
|
|
303
|
+
elif (self.operator is '&&' or self.operator is '||') and self.python_truthiness:
|
|
304
|
+
# Python `and`/`or` use Python truthiness (only when from __python__ import truthiness is set).
|
|
305
|
+
# `a and b` → `ρσ_bool(a) ? b : a` (returns a if falsy, else b)
|
|
306
|
+
# `a or b` → `ρσ_bool(a) ? a : b` (returns a if truthy, else b)
|
|
307
|
+
# For non-simple left operands, cache in ρσ_expr_temp to avoid double evaluation.
|
|
308
|
+
is_and = self.operator is '&&'
|
|
309
|
+
if is_node_type(self.left, AST_SymbolRef):
|
|
310
|
+
output.print('(ρσ_bool(')
|
|
311
|
+
self.left.print(output)
|
|
312
|
+
output.print(') ? ')
|
|
313
|
+
if is_and:
|
|
314
|
+
self.right.print(output)
|
|
315
|
+
output.print(' : ')
|
|
316
|
+
self.left.print(output)
|
|
317
|
+
else:
|
|
318
|
+
self.left.print(output)
|
|
319
|
+
output.print(' : ')
|
|
320
|
+
self.right.print(output)
|
|
321
|
+
output.print(')')
|
|
322
|
+
else:
|
|
323
|
+
output.print('(ρσ_expr_temp = ')
|
|
324
|
+
self.left.print(output)
|
|
325
|
+
output.print(', ρσ_bool(ρσ_expr_temp) ? ')
|
|
326
|
+
if is_and:
|
|
327
|
+
self.right.print(output)
|
|
328
|
+
output.print(' : ρσ_expr_temp)')
|
|
329
|
+
else:
|
|
330
|
+
output.print('ρσ_expr_temp : ')
|
|
331
|
+
self.right.print(output)
|
|
332
|
+
output.print(')')
|
|
287
333
|
else:
|
|
288
334
|
output.spaced(self.left, self.operator, self.right)
|
|
289
335
|
|
|
@@ -368,6 +414,21 @@ def print_assign(self, output):
|
|
|
368
414
|
self.right.print(output)
|
|
369
415
|
)
|
|
370
416
|
return
|
|
417
|
+
if self.operator is '**=':
|
|
418
|
+
output.assign(self.left)
|
|
419
|
+
if output.options.js_version > 6:
|
|
420
|
+
output.print('(('), self.left.print(output), output.spaced(')', '**', '('), self.right.print(output), output.print('))')
|
|
421
|
+
else:
|
|
422
|
+
output.print('Math.pow('), self.left.print(output), output.comma(), self.right.print(output), output.print(')')
|
|
423
|
+
return
|
|
424
|
+
if self.operator is '+=':
|
|
425
|
+
output.assign(self.left)
|
|
426
|
+
output.print('ρσ_list_iadd(')
|
|
427
|
+
self.left.print(output)
|
|
428
|
+
output.comma()
|
|
429
|
+
self.right.print(output)
|
|
430
|
+
output.print(')')
|
|
431
|
+
return
|
|
371
432
|
if self.operator is '=' and self.is_chained():
|
|
372
433
|
left_hand_sides, rhs = self.traverse_chain()
|
|
373
434
|
is_compound_assign = False
|
|
@@ -390,7 +451,12 @@ def print_assign(self, output):
|
|
|
390
451
|
|
|
391
452
|
def print_conditional(self, output, condition, consequent, alternative):
|
|
392
453
|
condition, consequent, alternative = self.condition, self.consequent, self.alternative
|
|
393
|
-
|
|
454
|
+
if self.python_truthiness:
|
|
455
|
+
output.print('ρσ_bool(')
|
|
456
|
+
condition.print(output)
|
|
457
|
+
output.print(')')
|
|
458
|
+
else:
|
|
459
|
+
output.with_parens(def(): condition.print(output);)
|
|
394
460
|
output.space()
|
|
395
461
|
output.print("?")
|
|
396
462
|
output.space()
|
|
@@ -156,15 +156,16 @@ def print_with(self, output):
|
|
|
156
156
|
output.with_block(def():
|
|
157
157
|
output.indent(), output.assign('ρσ_with_exception'), output.print('e'), output.end_statement()
|
|
158
158
|
)
|
|
159
|
+
reversed_exits = exits.slice().reverse()
|
|
159
160
|
output.newline(), output.indent(), output.spaced('if', '(ρσ_with_exception', '===', 'undefined)')
|
|
160
161
|
output.with_block(def():
|
|
161
|
-
for clause in
|
|
162
|
+
for clause in reversed_exits:
|
|
162
163
|
output.indent(), output.print(clause + '.__exit__()'), output.end_statement()
|
|
163
164
|
)
|
|
164
165
|
output.space(), output.print('else'), output.space()
|
|
165
166
|
output.with_block(def():
|
|
166
167
|
output.indent(), output.assign('ρσ_with_suppress'), output.print('false'), output.end_statement()
|
|
167
|
-
for clause in
|
|
168
|
+
for clause in reversed_exits:
|
|
168
169
|
output.indent()
|
|
169
170
|
output.spaced('ρσ_with_suppress', '|=', 'ρσ_bool(' + clause + '.__exit__(ρσ_with_exception.constructor,',
|
|
170
171
|
'ρσ_with_exception,', 'ρσ_with_exception.stack))')
|
|
@@ -175,7 +176,10 @@ def print_with(self, output):
|
|
|
175
176
|
def print_assert(self, output):
|
|
176
177
|
if output.options.discard_asserts:
|
|
177
178
|
return
|
|
178
|
-
|
|
179
|
+
if self.python_truthiness:
|
|
180
|
+
output.spaced('if', '(!ρσ_bool('), self.condition.print(output), output.spaced('))', 'throw new AssertionError')
|
|
181
|
+
else:
|
|
182
|
+
output.spaced('if', '(!('), self.condition.print(output), output.spaced('))', 'throw new AssertionError')
|
|
179
183
|
if self.message:
|
|
180
184
|
output.print('(')
|
|
181
185
|
self.message.print(output)
|
package/src/output/stream.pyj
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# vim:fileencoding=utf-8
|
|
2
2
|
# License: BSD Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
|
3
|
-
# globals:regenerate
|
|
4
3
|
from __python__ import hash_literals
|
|
5
4
|
|
|
6
5
|
from utils import make_predicate, defaults, repeat_string
|
|
@@ -76,6 +75,12 @@ class OutputStream:
|
|
|
76
75
|
self.with_counter = 0
|
|
77
76
|
self.try_else_counter = 0
|
|
78
77
|
self.match_counter = 0
|
|
78
|
+
self.forelse_counter = 0
|
|
79
|
+
self.forelse_stack = v'[]'
|
|
80
|
+
|
|
81
|
+
def new_forelse_label(self):
|
|
82
|
+
self.forelse_counter += 1
|
|
83
|
+
return 'ρσ_forelse_' + self.forelse_counter
|
|
79
84
|
|
|
80
85
|
def new_try_else_counter(self):
|
|
81
86
|
self.try_else_counter += 1
|
|
@@ -249,18 +254,6 @@ class OutputStream:
|
|
|
249
254
|
if self.options.space_colon:
|
|
250
255
|
self.space()
|
|
251
256
|
|
|
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
257
|
def get(self):
|
|
265
258
|
return self.OUTPUT
|
|
266
259
|
toString = get
|
package/src/parse.pyj
CHANGED
|
@@ -24,12 +24,13 @@ AST_Undefined, AST_Var, AST_VarDef, AST_Verbatim, AST_While, AST_With, AST_WithC
|
|
|
24
24
|
AST_Yield, AST_Await, AST_Assert, AST_Existential, AST_NamedExpr, AST_AnnotatedAssign, AST_Super, AST_Starred, is_node_type,
|
|
25
25
|
AST_Match, AST_MatchCase,
|
|
26
26
|
AST_MatchWildcard, AST_MatchCapture, AST_MatchLiteral, AST_MatchOr,
|
|
27
|
-
AST_MatchAs, AST_MatchStar, AST_MatchSequence, AST_MatchMapping, AST_MatchClass
|
|
27
|
+
AST_MatchAs, AST_MatchStar, AST_MatchSequence, AST_MatchMapping, AST_MatchClass,
|
|
28
|
+
TreeWalker
|
|
28
29
|
)
|
|
29
30
|
from tokenizer import tokenizer, is_token, RESERVED_WORDS
|
|
30
31
|
|
|
31
32
|
COMPILER_VERSION = '__COMPILER_VERSION__'
|
|
32
|
-
PYTHON_FLAGS = {'dict_literals':True, 'overload_getitem':True, 'bound_methods':True, 'hash_literals':True, 'overload_operators':True}
|
|
33
|
+
PYTHON_FLAGS = {'dict_literals':True, 'overload_getitem':True, 'bound_methods':True, 'hash_literals':True, 'overload_operators':True, 'truthiness':True}
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
def get_compiler_version():
|
|
@@ -116,7 +117,7 @@ FORBIDDEN_CLASS_VARS = 'prototype constructor'.split(' ')
|
|
|
116
117
|
# -----[ Parser (constants) ]-----
|
|
117
118
|
UNARY_PREFIX = make_predicate('typeof void delete ~ - + ! @')
|
|
118
119
|
|
|
119
|
-
ASSIGNMENT = make_predicate('= += -= /= //= *= %= >>= <<= >>>= |= ^= &=')
|
|
120
|
+
ASSIGNMENT = make_predicate('= += -= /= //= *= **= %= >>= <<= >>>= |= ^= &=')
|
|
120
121
|
|
|
121
122
|
PRECEDENCE = (def(a, ret):
|
|
122
123
|
for i in range(a.length):
|
|
@@ -336,7 +337,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
336
337
|
continue
|
|
337
338
|
|
|
338
339
|
# recursive descent into conditional, loop and exception bodies
|
|
339
|
-
for option in ('body', 'alternative', 'bcatch', 'condition'):
|
|
340
|
+
for option in ('body', 'alternative', 'bcatch', 'belse', 'condition'):
|
|
340
341
|
opt = stmt[option]
|
|
341
342
|
if opt:
|
|
342
343
|
extend(scan_for_local_vars(opt))
|
|
@@ -402,6 +403,23 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
402
403
|
|
|
403
404
|
elif is_node_type(body, AST_ForIn):
|
|
404
405
|
add_for_in(body)
|
|
406
|
+
# Also scan conditions for walrus operators (e.g. comprehension filters)
|
|
407
|
+
if body.condition:
|
|
408
|
+
extend(scan_for_local_vars(body.condition))
|
|
409
|
+
if body.clauses:
|
|
410
|
+
for clause in body.clauses:
|
|
411
|
+
if clause.condition:
|
|
412
|
+
extend(scan_for_local_vars(clause.condition))
|
|
413
|
+
|
|
414
|
+
else:
|
|
415
|
+
# Generic expression node — walk for walrus operators, stopping at scope boundaries
|
|
416
|
+
if body.walk:
|
|
417
|
+
body.walk(new TreeWalker(def(node):
|
|
418
|
+
if is_node_type(node, AST_NamedExpr):
|
|
419
|
+
push(node.name.name)
|
|
420
|
+
if is_node_type(node, AST_Scope):
|
|
421
|
+
return True # prune: don't descend into inner function scopes
|
|
422
|
+
))
|
|
405
423
|
|
|
406
424
|
return localvars
|
|
407
425
|
|
|
@@ -550,7 +568,8 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
550
568
|
croak('Assignments in do loop conditions are not allowed')
|
|
551
569
|
semicolon()
|
|
552
570
|
return tmp
|
|
553
|
-
)()
|
|
571
|
+
)(),
|
|
572
|
+
'python_truthiness': S.scoped_flags.get('truthiness', False)
|
|
554
573
|
})
|
|
555
574
|
elif tmp_ is "while":
|
|
556
575
|
while_cond = expression(True)
|
|
@@ -558,9 +577,21 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
558
577
|
croak('Assignments in while loop conditions are not allowed')
|
|
559
578
|
if not is_('punc', ':'):
|
|
560
579
|
croak('Expected a colon after the while statement')
|
|
580
|
+
while_body = in_loop(statement)
|
|
581
|
+
while_belse = None
|
|
582
|
+
if is_("keyword", "else"):
|
|
583
|
+
start = S.token
|
|
584
|
+
next()
|
|
585
|
+
while_belse = new AST_Else({
|
|
586
|
+
'start': start,
|
|
587
|
+
'body': block_(),
|
|
588
|
+
'end': prev()
|
|
589
|
+
})
|
|
561
590
|
return new AST_While({
|
|
562
591
|
'condition': while_cond,
|
|
563
|
-
'body':
|
|
592
|
+
'body': while_body,
|
|
593
|
+
'belse': while_belse,
|
|
594
|
+
'python_truthiness': S.scoped_flags.get('truthiness', False)
|
|
564
595
|
})
|
|
565
596
|
elif tmp_ is "for":
|
|
566
597
|
if is_('js'):
|
|
@@ -622,7 +653,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
622
653
|
if is_('punc', ','):
|
|
623
654
|
next()
|
|
624
655
|
msg = expression(False)
|
|
625
|
-
return new AST_Assert({'start': start, 'condition':cond, 'message':msg, 'end':prev()})
|
|
656
|
+
return new AST_Assert({'start': start, 'condition':cond, 'message':msg, 'end':prev(), 'python_truthiness': S.scoped_flags.get('truthiness', False)})
|
|
626
657
|
elif tmp_ is "if":
|
|
627
658
|
return if_()
|
|
628
659
|
elif tmp_ is "pass":
|
|
@@ -647,10 +678,17 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
647
678
|
})
|
|
648
679
|
|
|
649
680
|
tmp = expression(True)
|
|
681
|
+
cause = None
|
|
682
|
+
if is_("keyword", "from"):
|
|
683
|
+
next()
|
|
684
|
+
cause = expression(True)
|
|
650
685
|
semicolon()
|
|
651
|
-
|
|
686
|
+
node = new AST_Throw({
|
|
652
687
|
'value': tmp
|
|
653
688
|
})
|
|
689
|
+
if cause is not None:
|
|
690
|
+
node.cause = cause
|
|
691
|
+
return node
|
|
654
692
|
elif tmp_ is "try":
|
|
655
693
|
return try_()
|
|
656
694
|
elif tmp_ is "nonlocal":
|
|
@@ -1116,11 +1154,22 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
1116
1154
|
'object': obj
|
|
1117
1155
|
}
|
|
1118
1156
|
|
|
1157
|
+
body = in_loop(statement)
|
|
1158
|
+
belse = None
|
|
1159
|
+
if is_("keyword", "else"):
|
|
1160
|
+
start = S.token
|
|
1161
|
+
next()
|
|
1162
|
+
belse = new AST_Else({
|
|
1163
|
+
'start': start,
|
|
1164
|
+
'body': block_(),
|
|
1165
|
+
'end': prev()
|
|
1166
|
+
})
|
|
1119
1167
|
return new AST_ForIn({
|
|
1120
1168
|
'init': init,
|
|
1121
1169
|
'name': lhs,
|
|
1122
1170
|
'object': obj,
|
|
1123
|
-
'body':
|
|
1171
|
+
'body': body,
|
|
1172
|
+
'belse': belse
|
|
1124
1173
|
})
|
|
1125
1174
|
|
|
1126
1175
|
# A native JavaScript for loop - for v"var i=0; i<5000; i++":
|
|
@@ -1367,6 +1416,18 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
1367
1416
|
bases = v'[]'
|
|
1368
1417
|
class_parent = None
|
|
1369
1418
|
|
|
1419
|
+
# If this class is nested inside another class, register it under its
|
|
1420
|
+
# full dotted path (e.g. "Outer.Inner") in the module-level class scope
|
|
1421
|
+
# (S.classes[0]). This lets get_class_in_scope() find "Outer.Inner" as
|
|
1422
|
+
# a class and produce a correct AST_New constructor call instead of
|
|
1423
|
+
# treating it as a method call on Outer.
|
|
1424
|
+
outer_class_parts = []
|
|
1425
|
+
for _outer in S.in_class:
|
|
1426
|
+
if _outer:
|
|
1427
|
+
outer_class_parts.push(_outer)
|
|
1428
|
+
if outer_class_parts.length > 0:
|
|
1429
|
+
S.classes[0][outer_class_parts.join('.') + '.' + name.name] = class_details
|
|
1430
|
+
|
|
1370
1431
|
# read the bases of the class, if any
|
|
1371
1432
|
if is_("punc", "("):
|
|
1372
1433
|
S.in_parenthesized_expr = True
|
|
@@ -1472,7 +1533,11 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
1472
1533
|
visitor = new walker()
|
|
1473
1534
|
|
|
1474
1535
|
for stmt in definition.body:
|
|
1475
|
-
if
|
|
1536
|
+
if is_node_type(stmt, AST_Class):
|
|
1537
|
+
# Nested class: include in statements but don't walk through the
|
|
1538
|
+
# class-var mangling visitor (nested classes have their own scope).
|
|
1539
|
+
definition.statements.push(stmt)
|
|
1540
|
+
else:
|
|
1476
1541
|
stmt.walk(visitor)
|
|
1477
1542
|
definition.statements.push(stmt)
|
|
1478
1543
|
return definition
|
|
@@ -1865,7 +1930,8 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
1865
1930
|
return new AST_If({
|
|
1866
1931
|
'condition': cond,
|
|
1867
1932
|
'body': body,
|
|
1868
|
-
'alternative': belse
|
|
1933
|
+
'alternative': belse,
|
|
1934
|
+
'python_truthiness': S.scoped_flags.get('truthiness', False)
|
|
1869
1935
|
})
|
|
1870
1936
|
|
|
1871
1937
|
def is_docstring(stmt):
|
|
@@ -1917,10 +1983,19 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
1917
1983
|
next()
|
|
1918
1984
|
exceptions = []
|
|
1919
1985
|
if not is_("punc", ":") and not is_("keyword", "as"):
|
|
1986
|
+
# Accept both: except TypeError, ValueError:
|
|
1987
|
+
# and: except (TypeError, ValueError):
|
|
1988
|
+
paren_wrapped = is_("punc", "(")
|
|
1989
|
+
if paren_wrapped:
|
|
1990
|
+
next()
|
|
1920
1991
|
exceptions.push(as_symbol(AST_SymbolVar))
|
|
1921
1992
|
while is_("punc", ","):
|
|
1922
1993
|
next()
|
|
1994
|
+
if paren_wrapped and is_("punc", ")"):
|
|
1995
|
+
break # trailing comma inside parens
|
|
1923
1996
|
exceptions.push(as_symbol(AST_SymbolVar))
|
|
1997
|
+
if paren_wrapped:
|
|
1998
|
+
expect(")")
|
|
1924
1999
|
|
|
1925
2000
|
name = None
|
|
1926
2001
|
if is_("keyword", "as"):
|
|
@@ -2670,7 +2745,8 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
2670
2745
|
'start': start,
|
|
2671
2746
|
'expression': expr,
|
|
2672
2747
|
'args': func_call_list(),
|
|
2673
|
-
'end': prev()
|
|
2748
|
+
'end': prev(),
|
|
2749
|
+
'python_truthiness': S.scoped_flags.get('truthiness', False) and is_node_type(expr, AST_SymbolRef)
|
|
2674
2750
|
}), True)
|
|
2675
2751
|
S.in_parenthesized_expr = False
|
|
2676
2752
|
return ret
|
|
@@ -2760,6 +2836,8 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
2760
2836
|
ex.end = prev()
|
|
2761
2837
|
if S.scoped_flags.get('overload_operators', False) and (start.value is '-' or start.value is '+' or start.value is '~'):
|
|
2762
2838
|
ex.overloaded = True
|
|
2839
|
+
if S.scoped_flags.get('truthiness', False) and start.value is '!':
|
|
2840
|
+
ex.python_truthiness = True
|
|
2763
2841
|
return ex
|
|
2764
2842
|
|
|
2765
2843
|
val = expr_atom(allow_calls)
|
|
@@ -2795,7 +2873,8 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
2795
2873
|
'operator': op,
|
|
2796
2874
|
'right': right,
|
|
2797
2875
|
'end': right.end,
|
|
2798
|
-
'overloaded': S.scoped_flags.get('overload_operators', False)
|
|
2876
|
+
'overloaded': S.scoped_flags.get('overload_operators', False),
|
|
2877
|
+
'python_truthiness': S.scoped_flags.get('truthiness', False) and (op is '&&' or op is '||')
|
|
2799
2878
|
})
|
|
2800
2879
|
return expr_op(ret, min_prec, no_in)
|
|
2801
2880
|
return left
|
|
@@ -2815,7 +2894,8 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
2815
2894
|
'condition': ne,
|
|
2816
2895
|
'consequent': expr,
|
|
2817
2896
|
'alternative': expression(False, no_in),
|
|
2818
|
-
'end': peek()
|
|
2897
|
+
'end': peek(),
|
|
2898
|
+
'python_truthiness': S.scoped_flags.get('truthiness', False)
|
|
2819
2899
|
})
|
|
2820
2900
|
return conditional
|
|
2821
2901
|
return expr
|