rapydscript-ns 0.8.4 → 0.9.1
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 +26 -0
- package/HACKING.md +103 -103
- package/LICENSE +24 -24
- package/README.md +716 -169
- package/TODO.md +7 -2
- package/add-toc-to-readme +2 -2
- package/bin/export +75 -75
- package/bin/rapydscript +70 -70
- package/bin/web-repl-export +102 -102
- package/build +2 -2
- package/language-service/index.js +36 -27
- package/package.json +1 -1
- package/publish.py +37 -37
- package/release/baselib-plain-pretty.js +2358 -168
- package/release/baselib-plain-ugly.js +73 -3
- package/release/compiler.js +6283 -3093
- package/release/signatures.json +31 -30
- package/session.vim +4 -4
- package/setup.cfg +2 -2
- package/src/ast.pyj +1 -0
- package/src/baselib-builtins.pyj +340 -2
- package/src/baselib-bytes.pyj +664 -0
- package/src/baselib-errors.pyj +1 -1
- package/src/baselib-internal.pyj +267 -60
- package/src/baselib-itertools.pyj +110 -97
- package/src/baselib-str.pyj +22 -4
- package/src/compiler.pyj +36 -36
- package/src/errors.pyj +30 -30
- package/src/lib/abc.pyj +317 -0
- package/src/lib/aes.pyj +646 -646
- package/src/lib/contextlib.pyj +379 -0
- package/src/lib/copy.pyj +120 -120
- package/src/lib/dataclasses.pyj +532 -0
- package/src/lib/datetime.pyj +712 -0
- package/src/lib/elementmaker.pyj +83 -83
- package/src/lib/encodings.pyj +126 -126
- package/src/lib/enum.pyj +125 -0
- package/src/lib/gettext.pyj +569 -569
- package/src/lib/io.pyj +500 -0
- package/src/lib/itertools.pyj +580 -580
- package/src/lib/json.pyj +227 -0
- package/src/lib/math.pyj +193 -193
- package/src/lib/operator.pyj +11 -11
- package/src/lib/pythonize.pyj +20 -20
- package/src/lib/random.pyj +118 -118
- package/src/lib/re.pyj +504 -470
- package/src/lib/react.pyj +74 -74
- package/src/lib/traceback.pyj +63 -63
- package/src/lib/typing.pyj +577 -0
- package/src/lib/uuid.pyj +77 -77
- package/src/monaco-language-service/builtins.js +14 -4
- package/src/monaco-language-service/diagnostics.js +19 -20
- package/src/monaco-language-service/dts.js +550 -550
- package/src/output/classes.pyj +62 -26
- package/src/output/comments.pyj +45 -45
- package/src/output/exceptions.pyj +201 -201
- package/src/output/functions.pyj +78 -5
- package/src/output/jsx.pyj +164 -164
- package/src/output/loops.pyj +5 -2
- package/src/output/operators.pyj +100 -34
- package/src/output/treeshake.pyj +182 -182
- package/src/output/utils.pyj +72 -72
- package/src/parse.pyj +80 -16
- package/src/string_interpolation.pyj +72 -72
- package/src/tokenizer.pyj +10 -5
- 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/abc.pyj +291 -0
- package/test/aes_vectors.pyj +421 -421
- package/test/annotations.pyj +80 -80
- package/test/arithmetic_nostrict.pyj +88 -0
- package/test/arithmetic_types.pyj +169 -0
- package/test/baselib.pyj +91 -0
- package/test/bytes.pyj +467 -0
- package/test/classes.pyj +1 -0
- package/test/comparison_ops.pyj +173 -0
- package/test/contextlib.pyj +362 -0
- package/test/dataclasses.pyj +253 -0
- package/test/datetime.pyj +500 -0
- package/test/debugger_stmt.pyj +41 -0
- package/test/decorators.pyj +77 -77
- package/test/docstrings.pyj +39 -39
- package/test/elementmaker_test.pyj +45 -45
- package/test/enum.pyj +134 -0
- package/test/eval_exec.pyj +56 -0
- package/test/format.pyj +148 -0
- package/test/functions.pyj +151 -151
- package/test/generators.pyj +41 -41
- package/test/generic.pyj +370 -370
- package/test/imports.pyj +72 -72
- package/test/internationalization.pyj +73 -73
- package/test/io.pyj +316 -0
- package/test/json.pyj +196 -0
- package/test/lint.pyj +164 -164
- package/test/loops.pyj +85 -85
- package/test/numpy.pyj +734 -734
- package/test/object.pyj +64 -0
- package/test/omit_function_metadata.pyj +20 -20
- package/test/python_compat.pyj +17 -15
- package/test/python_features.pyj +70 -15
- package/test/regexp.pyj +83 -55
- package/test/repl.pyj +121 -121
- package/test/scoped_flags.pyj +76 -76
- package/test/tuples.pyj +96 -0
- package/test/typing.pyj +469 -0
- package/test/unit/index.js +116 -7
- package/test/unit/language-service-dts.js +543 -543
- package/test/unit/language-service-hover.js +455 -455
- package/test/unit/language-service.js +84 -0
- package/test/unit/web-repl.js +1337 -1
- package/test/vars_locals_globals.pyj +94 -0
- package/tools/cli.js +558 -547
- package/tools/compile.js +224 -219
- package/tools/completer.js +131 -131
- package/tools/embedded_compiler.js +262 -251
- package/tools/gettext.js +185 -185
- package/tools/ini.js +65 -65
- package/tools/lint.js +16 -19
- package/tools/msgfmt.js +187 -187
- package/tools/repl.js +223 -223
- package/tools/test.js +118 -118
- package/tools/utils.js +128 -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/main.js +252 -252
- package/web-repl/prism.css +139 -139
- package/web-repl/prism.js +113 -113
- package/web-repl/rapydscript.js +224 -224
- package/web-repl/sha1.js +25 -25
- package/PYTHON_DIFFERENCES_REPORT.md +0 -291
- package/PYTHON_FEATURE_COVERAGE.md +0 -200
package/src/output/functions.pyj
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
# License: BSD Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
|
3
3
|
from __python__ import hash_literals
|
|
4
4
|
|
|
5
|
-
from ast import AST_ClassCall, AST_New, has_calls, AST_Dot, AST_SymbolRef, is_node_type
|
|
5
|
+
from ast import AST_ClassCall, AST_New, has_calls, AST_Dot, AST_SymbolRef, AST_String, is_node_type
|
|
6
6
|
from output.stream import OutputStream
|
|
7
|
+
from parse import parse as ρσ_rs_parse
|
|
7
8
|
from output.statements import print_bracketed
|
|
8
9
|
from output.utils import create_doctring
|
|
9
10
|
from output.operators import print_getattr
|
|
@@ -449,6 +450,73 @@ def print_function_call(self, output):
|
|
|
449
450
|
output.comma()
|
|
450
451
|
a.print(output)
|
|
451
452
|
)
|
|
453
|
+
elif (is_node_type(self.expression, AST_SymbolRef) and
|
|
454
|
+
self.expression.name is 'vars' and self.args.length is 0):
|
|
455
|
+
# vars() with no args → vars(this) so it captures the caller's instance
|
|
456
|
+
output.print('ρσ_vars')
|
|
457
|
+
output.with_parens(def():
|
|
458
|
+
output.print('this')
|
|
459
|
+
)
|
|
460
|
+
elif (is_node_type(self.expression, AST_SymbolRef) and
|
|
461
|
+
(self.expression.name is 'eval' or self.expression.name is 'exec') and
|
|
462
|
+
self.args.length >= 1 and is_node_type(self.args[0], AST_String)):
|
|
463
|
+
# Compile-time RapydScript → JS transformation for eval/exec string literals.
|
|
464
|
+
# The RapydScript source string is parsed and compiled to JS now; the emitted
|
|
465
|
+
# call receives the compiled JS string instead of the original RapydScript.
|
|
466
|
+
fn_name = self.expression.name
|
|
467
|
+
source = self.args[0].value
|
|
468
|
+
inner_ast = ρσ_rs_parse(source, {
|
|
469
|
+
'filename': '<eval>',
|
|
470
|
+
'module_id': '__eval__',
|
|
471
|
+
'for_linting': True,
|
|
472
|
+
'discard_asserts': output.options.discard_asserts,
|
|
473
|
+
})
|
|
474
|
+
inner_os = OutputStream({
|
|
475
|
+
'beautify': False,
|
|
476
|
+
'js_version': output.options.js_version,
|
|
477
|
+
'private_scope': False,
|
|
478
|
+
'write_name': False,
|
|
479
|
+
'omit_baselib': True,
|
|
480
|
+
'discard_asserts': output.options.discard_asserts,
|
|
481
|
+
})
|
|
482
|
+
inner_ast.print(inner_os)
|
|
483
|
+
compiled_js = v'inner_os.get().trim()'
|
|
484
|
+
|
|
485
|
+
def _print_extra_args():
|
|
486
|
+
for a in self.args.slice(1):
|
|
487
|
+
output.comma()
|
|
488
|
+
a.print(output)
|
|
489
|
+
|
|
490
|
+
if fn_name is 'eval' and self.args.length is 1:
|
|
491
|
+
# eval("expr") → native direct eval for scope access
|
|
492
|
+
output.print('eval')
|
|
493
|
+
output.with_parens(def():
|
|
494
|
+
output.print(JSON.stringify(compiled_js))
|
|
495
|
+
)
|
|
496
|
+
elif fn_name is 'eval':
|
|
497
|
+
# eval("expr", globals, locals) → ρσ_eval(compiled_js, globals, locals)
|
|
498
|
+
output.print('ρσ_eval')
|
|
499
|
+
output.with_parens(def():
|
|
500
|
+
output.print(JSON.stringify(compiled_js))
|
|
501
|
+
_print_extra_args()
|
|
502
|
+
)
|
|
503
|
+
else:
|
|
504
|
+
# exec("code", ...) → ρσ_exec(compiled_js, ...)
|
|
505
|
+
output.print('ρσ_exec')
|
|
506
|
+
output.with_parens(def():
|
|
507
|
+
output.print(JSON.stringify(compiled_js))
|
|
508
|
+
_print_extra_args()
|
|
509
|
+
)
|
|
510
|
+
elif is_node_type(self.expression, AST_SymbolRef) and self.expression.name is 'eval' and self.args.length > 1:
|
|
511
|
+
# eval(dynamic_expr, globals, locals) → ρσ_eval(dynamic_expr, globals, locals)
|
|
512
|
+
# The 1-arg form eval(expr) is left as native JS direct eval for scope access.
|
|
513
|
+
output.print('ρσ_eval')
|
|
514
|
+
output.with_parens(def():
|
|
515
|
+
for i, a in enumerate(self.args):
|
|
516
|
+
if i:
|
|
517
|
+
output.comma()
|
|
518
|
+
a.print(output)
|
|
519
|
+
)
|
|
452
520
|
elif is_node_type(self.expression, AST_SymbolRef) and self.python_truthiness:
|
|
453
521
|
# __call__-aware dispatch when from __python__ import truthiness is active
|
|
454
522
|
output.print('ρσ_callable_call(')
|
|
@@ -461,10 +529,15 @@ def print_function_call(self, output):
|
|
|
461
529
|
# Method calls and other complex expressions — keep existing behaviour
|
|
462
530
|
print_function_name()
|
|
463
531
|
output.with_parens(def():
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
532
|
+
# .split() with no args: default to ' ' so JS splits on space
|
|
533
|
+
# rather than returning [whole_string] (native JS no-arg behavior)
|
|
534
|
+
if is_node_type(self.expression, AST_Dot) and self.expression.property is 'split' and not self.args.length:
|
|
535
|
+
output.print('" "')
|
|
536
|
+
else:
|
|
537
|
+
for i, a in enumerate(self.args):
|
|
538
|
+
if i:
|
|
539
|
+
output.comma()
|
|
540
|
+
a.print(output)
|
|
468
541
|
)
|
|
469
542
|
return
|
|
470
543
|
|
package/src/output/jsx.pyj
CHANGED
|
@@ -1,164 +1,164 @@
|
|
|
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_String, AST_JSXText, AST_JSXSpread, AST_JSXExprContainer, is_node_type
|
|
6
|
-
|
|
7
|
-
def _is_component_tag(tag):
|
|
8
|
-
# Components start with uppercase or use dot notation (e.g. Router.Route)
|
|
9
|
-
first = tag[0]
|
|
10
|
-
return (first >= 'A' and first <= 'Z') or '.' in tag
|
|
11
|
-
|
|
12
|
-
def _needs_quoting(name):
|
|
13
|
-
# Attribute names containing non-identifier chars (e.g. aria-label) need quoting
|
|
14
|
-
return not v'/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)'
|
|
15
|
-
|
|
16
|
-
def _decode_html_entities(text):
|
|
17
|
-
# Decode HTML entities in a single pass to avoid double-decoding (e.g. &lt; -> <)
|
|
18
|
-
return text.replace(/&(?:#x([0-9a-fA-F]+)|#(\d+)|([a-zA-Z]+));/g, def(match, hex, dec, name):
|
|
19
|
-
if hex:
|
|
20
|
-
return String.fromCharCode(parseInt(hex, 16))
|
|
21
|
-
if dec:
|
|
22
|
-
return String.fromCharCode(parseInt(dec, 10))
|
|
23
|
-
if name is 'amp':
|
|
24
|
-
return '&'
|
|
25
|
-
if name is 'lt':
|
|
26
|
-
return '<'
|
|
27
|
-
if name is 'gt':
|
|
28
|
-
return '>'
|
|
29
|
-
if name is 'quot':
|
|
30
|
-
return '"'
|
|
31
|
-
if name is 'apos':
|
|
32
|
-
return "'"
|
|
33
|
-
if name is 'nbsp':
|
|
34
|
-
return '\u00a0'
|
|
35
|
-
return match
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
def _normalize_jsx_whitespace(text):
|
|
39
|
-
# Implements the Babel JSX whitespace algorithm:
|
|
40
|
-
# - Split by newlines; trim leading whitespace from all lines except the first,
|
|
41
|
-
# trailing whitespace from all lines except the last.
|
|
42
|
-
# - Lines that are empty after trimming are dropped.
|
|
43
|
-
# - Remaining non-empty lines are joined; each line except the last non-empty
|
|
44
|
-
# gets a trailing space to separate it from the next.
|
|
45
|
-
lines = text.split('\n')
|
|
46
|
-
last_non_empty = -1
|
|
47
|
-
for i in range(lines.length):
|
|
48
|
-
if /[^ \t]/.test(lines[i]):
|
|
49
|
-
last_non_empty = i
|
|
50
|
-
result = ''
|
|
51
|
-
for i in range(lines.length):
|
|
52
|
-
line = lines[i].replace(/\t/g, ' ')
|
|
53
|
-
is_first = (i is 0)
|
|
54
|
-
is_last = (i is lines.length - 1)
|
|
55
|
-
if not is_first:
|
|
56
|
-
line = line.replace(/^[ ]+/, '')
|
|
57
|
-
if not is_last:
|
|
58
|
-
line = line.replace(/[ ]+$/, '')
|
|
59
|
-
if line:
|
|
60
|
-
if i is not last_non_empty:
|
|
61
|
-
line += ' '
|
|
62
|
-
result += line
|
|
63
|
-
return result
|
|
64
|
-
|
|
65
|
-
def _process_jsx_text(text):
|
|
66
|
-
text = _normalize_jsx_whitespace(text)
|
|
67
|
-
if text:
|
|
68
|
-
text = _decode_html_entities(text)
|
|
69
|
-
return text
|
|
70
|
-
|
|
71
|
-
def _print_tag(tag, output):
|
|
72
|
-
if _is_component_tag(tag):
|
|
73
|
-
output.print(tag)
|
|
74
|
-
else:
|
|
75
|
-
output.print('"')
|
|
76
|
-
output.print(tag)
|
|
77
|
-
output.print('"')
|
|
78
|
-
|
|
79
|
-
def _print_props(props, output):
|
|
80
|
-
if not props or not props.length:
|
|
81
|
-
output.print('null')
|
|
82
|
-
return
|
|
83
|
-
output.print('{')
|
|
84
|
-
first = True
|
|
85
|
-
for prop in props:
|
|
86
|
-
if not first:
|
|
87
|
-
output.print(', ')
|
|
88
|
-
first = False
|
|
89
|
-
if is_node_type(prop, AST_JSXSpread):
|
|
90
|
-
output.print('...')
|
|
91
|
-
prop.expression.print(output)
|
|
92
|
-
else:
|
|
93
|
-
if _needs_quoting(prop.name):
|
|
94
|
-
output.print('"')
|
|
95
|
-
output.print(prop.name)
|
|
96
|
-
output.print('"')
|
|
97
|
-
else:
|
|
98
|
-
output.print(prop.name)
|
|
99
|
-
output.print(': ')
|
|
100
|
-
if prop.value is None:
|
|
101
|
-
output.print('true')
|
|
102
|
-
elif is_node_type(prop.value, AST_String):
|
|
103
|
-
output.print_string(prop.value.value)
|
|
104
|
-
else:
|
|
105
|
-
prop.value.print(output)
|
|
106
|
-
output.print('}')
|
|
107
|
-
|
|
108
|
-
def _print_children(children, output):
|
|
109
|
-
for child in children:
|
|
110
|
-
if is_node_type(child, AST_JSXText):
|
|
111
|
-
text = _process_jsx_text(child.value)
|
|
112
|
-
if text:
|
|
113
|
-
output.print(', ')
|
|
114
|
-
output.print_string(text)
|
|
115
|
-
elif is_node_type(child, AST_JSXExprContainer):
|
|
116
|
-
output.print(', ')
|
|
117
|
-
child.expression.print(output)
|
|
118
|
-
else:
|
|
119
|
-
output.print(', ')
|
|
120
|
-
child.print(output)
|
|
121
|
-
|
|
122
|
-
def print_jsx_element(self, output):
|
|
123
|
-
output.print('React.createElement(')
|
|
124
|
-
_print_tag(self.tag, output)
|
|
125
|
-
output.print(', ')
|
|
126
|
-
_print_props(self.props, output)
|
|
127
|
-
if not self.self_closing:
|
|
128
|
-
_print_children(self.children, output)
|
|
129
|
-
output.print(')')
|
|
130
|
-
|
|
131
|
-
def print_jsx_fragment(self, output):
|
|
132
|
-
output.print('React.createElement(React.Fragment, null')
|
|
133
|
-
_print_children(self.children, output)
|
|
134
|
-
output.print(')')
|
|
135
|
-
|
|
136
|
-
def print_jsx_attribute(self, output):
|
|
137
|
-
# Handled directly by _print_props; kept for completeness
|
|
138
|
-
if _needs_quoting(self.name):
|
|
139
|
-
output.print('"')
|
|
140
|
-
output.print(self.name)
|
|
141
|
-
output.print('"')
|
|
142
|
-
else:
|
|
143
|
-
output.print(self.name)
|
|
144
|
-
if self.value is not None:
|
|
145
|
-
output.print(': ')
|
|
146
|
-
if is_node_type(self.value, AST_String):
|
|
147
|
-
output.print_string(self.value.value)
|
|
148
|
-
else:
|
|
149
|
-
self.value.print(output)
|
|
150
|
-
|
|
151
|
-
def print_jsx_spread(self, output):
|
|
152
|
-
# Handled directly by _print_props; kept for completeness
|
|
153
|
-
output.print('...')
|
|
154
|
-
self.expression.print(output)
|
|
155
|
-
|
|
156
|
-
def print_jsx_text(self, output):
|
|
157
|
-
# Handled directly by _print_children; kept for completeness
|
|
158
|
-
text = _process_jsx_text(self.value)
|
|
159
|
-
if text:
|
|
160
|
-
output.print_string(text)
|
|
161
|
-
|
|
162
|
-
def print_jsx_expr_container(self, output):
|
|
163
|
-
# Handled directly by _print_children; kept for completeness
|
|
164
|
-
self.expression.print(output)
|
|
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_String, AST_JSXText, AST_JSXSpread, AST_JSXExprContainer, is_node_type
|
|
6
|
+
|
|
7
|
+
def _is_component_tag(tag):
|
|
8
|
+
# Components start with uppercase or use dot notation (e.g. Router.Route)
|
|
9
|
+
first = tag[0]
|
|
10
|
+
return (first >= 'A' and first <= 'Z') or '.' in tag
|
|
11
|
+
|
|
12
|
+
def _needs_quoting(name):
|
|
13
|
+
# Attribute names containing non-identifier chars (e.g. aria-label) need quoting
|
|
14
|
+
return not v'/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)'
|
|
15
|
+
|
|
16
|
+
def _decode_html_entities(text):
|
|
17
|
+
# Decode HTML entities in a single pass to avoid double-decoding (e.g. &lt; -> <)
|
|
18
|
+
return text.replace(/&(?:#x([0-9a-fA-F]+)|#(\d+)|([a-zA-Z]+));/g, def(match, hex, dec, name):
|
|
19
|
+
if hex:
|
|
20
|
+
return String.fromCharCode(parseInt(hex, 16))
|
|
21
|
+
if dec:
|
|
22
|
+
return String.fromCharCode(parseInt(dec, 10))
|
|
23
|
+
if name is 'amp':
|
|
24
|
+
return '&'
|
|
25
|
+
if name is 'lt':
|
|
26
|
+
return '<'
|
|
27
|
+
if name is 'gt':
|
|
28
|
+
return '>'
|
|
29
|
+
if name is 'quot':
|
|
30
|
+
return '"'
|
|
31
|
+
if name is 'apos':
|
|
32
|
+
return "'"
|
|
33
|
+
if name is 'nbsp':
|
|
34
|
+
return '\u00a0'
|
|
35
|
+
return match
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def _normalize_jsx_whitespace(text):
|
|
39
|
+
# Implements the Babel JSX whitespace algorithm:
|
|
40
|
+
# - Split by newlines; trim leading whitespace from all lines except the first,
|
|
41
|
+
# trailing whitespace from all lines except the last.
|
|
42
|
+
# - Lines that are empty after trimming are dropped.
|
|
43
|
+
# - Remaining non-empty lines are joined; each line except the last non-empty
|
|
44
|
+
# gets a trailing space to separate it from the next.
|
|
45
|
+
lines = text.split('\n')
|
|
46
|
+
last_non_empty = -1
|
|
47
|
+
for i in range(lines.length):
|
|
48
|
+
if /[^ \t]/.test(lines[i]):
|
|
49
|
+
last_non_empty = i
|
|
50
|
+
result = ''
|
|
51
|
+
for i in range(lines.length):
|
|
52
|
+
line = lines[i].replace(/\t/g, ' ')
|
|
53
|
+
is_first = (i is 0)
|
|
54
|
+
is_last = (i is lines.length - 1)
|
|
55
|
+
if not is_first:
|
|
56
|
+
line = line.replace(/^[ ]+/, '')
|
|
57
|
+
if not is_last:
|
|
58
|
+
line = line.replace(/[ ]+$/, '')
|
|
59
|
+
if line:
|
|
60
|
+
if i is not last_non_empty:
|
|
61
|
+
line += ' '
|
|
62
|
+
result += line
|
|
63
|
+
return result
|
|
64
|
+
|
|
65
|
+
def _process_jsx_text(text):
|
|
66
|
+
text = _normalize_jsx_whitespace(text)
|
|
67
|
+
if text:
|
|
68
|
+
text = _decode_html_entities(text)
|
|
69
|
+
return text
|
|
70
|
+
|
|
71
|
+
def _print_tag(tag, output):
|
|
72
|
+
if _is_component_tag(tag):
|
|
73
|
+
output.print(tag)
|
|
74
|
+
else:
|
|
75
|
+
output.print('"')
|
|
76
|
+
output.print(tag)
|
|
77
|
+
output.print('"')
|
|
78
|
+
|
|
79
|
+
def _print_props(props, output):
|
|
80
|
+
if not props or not props.length:
|
|
81
|
+
output.print('null')
|
|
82
|
+
return
|
|
83
|
+
output.print('{')
|
|
84
|
+
first = True
|
|
85
|
+
for prop in props:
|
|
86
|
+
if not first:
|
|
87
|
+
output.print(', ')
|
|
88
|
+
first = False
|
|
89
|
+
if is_node_type(prop, AST_JSXSpread):
|
|
90
|
+
output.print('...')
|
|
91
|
+
prop.expression.print(output)
|
|
92
|
+
else:
|
|
93
|
+
if _needs_quoting(prop.name):
|
|
94
|
+
output.print('"')
|
|
95
|
+
output.print(prop.name)
|
|
96
|
+
output.print('"')
|
|
97
|
+
else:
|
|
98
|
+
output.print(prop.name)
|
|
99
|
+
output.print(': ')
|
|
100
|
+
if prop.value is None:
|
|
101
|
+
output.print('true')
|
|
102
|
+
elif is_node_type(prop.value, AST_String):
|
|
103
|
+
output.print_string(prop.value.value)
|
|
104
|
+
else:
|
|
105
|
+
prop.value.print(output)
|
|
106
|
+
output.print('}')
|
|
107
|
+
|
|
108
|
+
def _print_children(children, output):
|
|
109
|
+
for child in children:
|
|
110
|
+
if is_node_type(child, AST_JSXText):
|
|
111
|
+
text = _process_jsx_text(child.value)
|
|
112
|
+
if text:
|
|
113
|
+
output.print(', ')
|
|
114
|
+
output.print_string(text)
|
|
115
|
+
elif is_node_type(child, AST_JSXExprContainer):
|
|
116
|
+
output.print(', ')
|
|
117
|
+
child.expression.print(output)
|
|
118
|
+
else:
|
|
119
|
+
output.print(', ')
|
|
120
|
+
child.print(output)
|
|
121
|
+
|
|
122
|
+
def print_jsx_element(self, output):
|
|
123
|
+
output.print('React.createElement(')
|
|
124
|
+
_print_tag(self.tag, output)
|
|
125
|
+
output.print(', ')
|
|
126
|
+
_print_props(self.props, output)
|
|
127
|
+
if not self.self_closing:
|
|
128
|
+
_print_children(self.children, output)
|
|
129
|
+
output.print(')')
|
|
130
|
+
|
|
131
|
+
def print_jsx_fragment(self, output):
|
|
132
|
+
output.print('React.createElement(React.Fragment, null')
|
|
133
|
+
_print_children(self.children, output)
|
|
134
|
+
output.print(')')
|
|
135
|
+
|
|
136
|
+
def print_jsx_attribute(self, output):
|
|
137
|
+
# Handled directly by _print_props; kept for completeness
|
|
138
|
+
if _needs_quoting(self.name):
|
|
139
|
+
output.print('"')
|
|
140
|
+
output.print(self.name)
|
|
141
|
+
output.print('"')
|
|
142
|
+
else:
|
|
143
|
+
output.print(self.name)
|
|
144
|
+
if self.value is not None:
|
|
145
|
+
output.print(': ')
|
|
146
|
+
if is_node_type(self.value, AST_String):
|
|
147
|
+
output.print_string(self.value.value)
|
|
148
|
+
else:
|
|
149
|
+
self.value.print(output)
|
|
150
|
+
|
|
151
|
+
def print_jsx_spread(self, output):
|
|
152
|
+
# Handled directly by _print_props; kept for completeness
|
|
153
|
+
output.print('...')
|
|
154
|
+
self.expression.print(output)
|
|
155
|
+
|
|
156
|
+
def print_jsx_text(self, output):
|
|
157
|
+
# Handled directly by _print_children; kept for completeness
|
|
158
|
+
text = _process_jsx_text(self.value)
|
|
159
|
+
if text:
|
|
160
|
+
output.print_string(text)
|
|
161
|
+
|
|
162
|
+
def print_jsx_expr_container(self, output):
|
|
163
|
+
# Handled directly by _print_children; kept for completeness
|
|
164
|
+
self.expression.print(output)
|
package/src/output/loops.pyj
CHANGED
|
@@ -456,10 +456,13 @@ def print_list_comprehension(self, output):
|
|
|
456
456
|
body_out.print(result_obj)
|
|
457
457
|
# Locally scope all loop variables (outer and inner clauses)
|
|
458
458
|
def decl_loop_vars(init_node):
|
|
459
|
+
# Recursively declare all leaf variable names from nested tuples/seqs
|
|
459
460
|
if is_node_type(init_node, AST_Array):
|
|
460
461
|
for i in init_node.elements:
|
|
461
|
-
|
|
462
|
-
|
|
462
|
+
decl_loop_vars(i)
|
|
463
|
+
elif is_node_type(init_node, AST_Seq):
|
|
464
|
+
for i in init_node.to_array():
|
|
465
|
+
decl_loop_vars(i)
|
|
463
466
|
else:
|
|
464
467
|
body_out.comma()
|
|
465
468
|
init_node.print(body_out)
|
package/src/output/operators.pyj
CHANGED
|
@@ -43,7 +43,7 @@ def print_getitem(self, output): # AST_Sub
|
|
|
43
43
|
expr.print(output)
|
|
44
44
|
output.print('['), prop.print(output), output.print(']')
|
|
45
45
|
return
|
|
46
|
-
is_negative_number = is_node_type(prop, AST_Unary) and prop.operator is "-" and is_node_type(prop.expression, AST_Number)
|
|
46
|
+
is_negative_number = is_node_type(prop, AST_Unary) and prop.operator is "-" and is_node_type(prop.expression, AST_Number) and not prop.overloaded
|
|
47
47
|
is_repeatable = is_node_type(expr, AST_SymbolRef)
|
|
48
48
|
if is_repeatable:
|
|
49
49
|
expr.print(output)
|
|
@@ -187,7 +187,7 @@ function_ops = {
|
|
|
187
187
|
'nin': '!ρσ_in',
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
# Maps binary operators to their overloading helper function names.
|
|
190
|
+
# Maps binary operators to their overloading helper function names (strict_arithmetic on).
|
|
191
191
|
overloaded_binary_ops = {
|
|
192
192
|
'+': 'ρσ_op_add',
|
|
193
193
|
'-': 'ρσ_op_sub',
|
|
@@ -201,9 +201,34 @@ overloaded_binary_ops = {
|
|
|
201
201
|
'^': 'ρσ_op_xor',
|
|
202
202
|
'<<': 'ρσ_op_lshift',
|
|
203
203
|
'>>': 'ρσ_op_rshift',
|
|
204
|
+
'<': 'ρσ_op_lt',
|
|
205
|
+
'>': 'ρσ_op_gt',
|
|
206
|
+
'<=': 'ρσ_op_le',
|
|
207
|
+
'>=': 'ρσ_op_ge',
|
|
204
208
|
}
|
|
205
209
|
|
|
206
|
-
# Maps
|
|
210
|
+
# Maps binary operators to no-strict helpers (overload_operators on, strict_arithmetic off).
|
|
211
|
+
# Operators that never threw TypeError reuse the same helper; others use _ns variants.
|
|
212
|
+
overloaded_binary_ops_ns = {
|
|
213
|
+
'+': 'ρσ_op_add_ns',
|
|
214
|
+
'-': 'ρσ_op_sub_ns',
|
|
215
|
+
'*': 'ρσ_op_mul_ns',
|
|
216
|
+
'/': 'ρσ_op_truediv_ns',
|
|
217
|
+
'//': 'ρσ_op_floordiv_ns',
|
|
218
|
+
'%': 'ρσ_op_mod_ns',
|
|
219
|
+
'**': 'ρσ_op_pow_ns',
|
|
220
|
+
'&': 'ρσ_op_and',
|
|
221
|
+
'|': 'ρσ_op_or',
|
|
222
|
+
'^': 'ρσ_op_xor',
|
|
223
|
+
'<<': 'ρσ_op_lshift',
|
|
224
|
+
'>>': 'ρσ_op_rshift',
|
|
225
|
+
'<': 'ρσ_op_lt_ns',
|
|
226
|
+
'>': 'ρσ_op_gt_ns',
|
|
227
|
+
'<=': 'ρσ_op_le_ns',
|
|
228
|
+
'>=': 'ρσ_op_ge_ns',
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
# Maps augmented-assignment operators to their overloading helper names (strict_arithmetic on).
|
|
207
232
|
overloaded_augmented_ops = {
|
|
208
233
|
'+=': 'ρσ_op_iadd',
|
|
209
234
|
'-=': 'ρσ_op_isub',
|
|
@@ -219,10 +244,78 @@ overloaded_augmented_ops = {
|
|
|
219
244
|
'>>=': 'ρσ_op_irshift',
|
|
220
245
|
}
|
|
221
246
|
|
|
247
|
+
# Maps augmented-assignment operators to no-strict helpers (strict_arithmetic off).
|
|
248
|
+
overloaded_augmented_ops_ns = {
|
|
249
|
+
'+=': 'ρσ_op_iadd_ns',
|
|
250
|
+
'-=': 'ρσ_op_isub_ns',
|
|
251
|
+
'*=': 'ρσ_op_imul_ns',
|
|
252
|
+
'/=': 'ρσ_op_itruediv_ns',
|
|
253
|
+
'//=': 'ρσ_op_ifloordiv_ns',
|
|
254
|
+
'%=': 'ρσ_op_imod_ns',
|
|
255
|
+
'**=': 'ρσ_op_ipow_ns',
|
|
256
|
+
'&=': 'ρσ_op_iand',
|
|
257
|
+
'|=': 'ρσ_op_ior',
|
|
258
|
+
'^=': 'ρσ_op_ixor',
|
|
259
|
+
'<<=': 'ρσ_op_ilshift',
|
|
260
|
+
'>>=': 'ρσ_op_irshift',
|
|
261
|
+
}
|
|
262
|
+
|
|
222
263
|
|
|
223
264
|
def print_binary_op(self, output):
|
|
224
|
-
if self.
|
|
225
|
-
|
|
265
|
+
if comparators[self.operator] and is_node_type(self.left, AST_Binary) and comparators[self.left.operator]:
|
|
266
|
+
# A chained comparison such as a < b < c
|
|
267
|
+
# With overload_operators: ρσ_op_lt(a, b) && ρσ_op_lt(b, c)
|
|
268
|
+
# Without: a < b && b < c
|
|
269
|
+
if is_node_type(self.left.right, AST_Symbol):
|
|
270
|
+
# Middle is a simple variable — no caching needed.
|
|
271
|
+
# self.left.print recurses into print_binary_op and handles overloaded case.
|
|
272
|
+
self.left.print(output)
|
|
273
|
+
leftvar = self.left.right.name
|
|
274
|
+
else:
|
|
275
|
+
# Middle is a complex expression — must cache to avoid double evaluation.
|
|
276
|
+
left_ops_map = overloaded_binary_ops if self.left.strict_arith else overloaded_binary_ops_ns
|
|
277
|
+
if self.left.overloaded and left_ops_map[self.left.operator]:
|
|
278
|
+
output.print(left_ops_map[self.left.operator] + '(')
|
|
279
|
+
self.left.left.print(output)
|
|
280
|
+
output.comma()
|
|
281
|
+
output.with_parens(def():
|
|
282
|
+
nonlocal leftvar
|
|
283
|
+
output.assign("ρσ_cond_temp")
|
|
284
|
+
self.left.right.print(output)
|
|
285
|
+
leftvar = "ρσ_cond_temp"
|
|
286
|
+
)
|
|
287
|
+
output.print(')')
|
|
288
|
+
else:
|
|
289
|
+
self.left.left.print(output)
|
|
290
|
+
output.space()
|
|
291
|
+
output.print(self.left.operator)
|
|
292
|
+
output.space()
|
|
293
|
+
output.with_parens(def():
|
|
294
|
+
nonlocal leftvar
|
|
295
|
+
output.assign("ρσ_cond_temp")
|
|
296
|
+
self.left.right.print(output)
|
|
297
|
+
leftvar = "ρσ_cond_temp"
|
|
298
|
+
)
|
|
299
|
+
ops_map = overloaded_binary_ops if self.strict_arith else overloaded_binary_ops_ns
|
|
300
|
+
output.space()
|
|
301
|
+
output.print("&&")
|
|
302
|
+
output.space()
|
|
303
|
+
if self.overloaded and ops_map[self.operator]:
|
|
304
|
+
output.print(ops_map[self.operator] + '(')
|
|
305
|
+
output.print(leftvar)
|
|
306
|
+
output.comma()
|
|
307
|
+
self.right.print(output)
|
|
308
|
+
output.print(')')
|
|
309
|
+
else:
|
|
310
|
+
output.print(leftvar)
|
|
311
|
+
output.space()
|
|
312
|
+
output.print(self.operator)
|
|
313
|
+
output.space()
|
|
314
|
+
self.right.print(output)
|
|
315
|
+
return
|
|
316
|
+
ops_map = overloaded_binary_ops if self.strict_arith else overloaded_binary_ops_ns
|
|
317
|
+
if self.overloaded and ops_map[self.operator]:
|
|
318
|
+
output.print(ops_map[self.operator] + '(')
|
|
226
319
|
self.left.print(output)
|
|
227
320
|
output.comma()
|
|
228
321
|
self.right.print(output)
|
|
@@ -235,34 +328,6 @@ def print_binary_op(self, output):
|
|
|
235
328
|
output.comma()
|
|
236
329
|
self.right.print(output)
|
|
237
330
|
)
|
|
238
|
-
elif comparators[self.operator] and is_node_type(self.left, AST_Binary) and comparators[self.left.operator]:
|
|
239
|
-
# A chained comparison such as a < b < c
|
|
240
|
-
if is_node_type(self.left.right, AST_Symbol):
|
|
241
|
-
# left side compares against a regular variable,
|
|
242
|
-
# no caching needed
|
|
243
|
-
self.left.print(output)
|
|
244
|
-
leftvar = self.left.right.name
|
|
245
|
-
else:
|
|
246
|
-
# some logic is being performed, let's cache it
|
|
247
|
-
self.left.left.print(output)
|
|
248
|
-
output.space()
|
|
249
|
-
output.print(self.left.operator)
|
|
250
|
-
output.space()
|
|
251
|
-
output.with_parens(def():
|
|
252
|
-
nonlocal leftvar
|
|
253
|
-
output.assign("ρσ_cond_temp")
|
|
254
|
-
self.left.right.print(output)
|
|
255
|
-
leftvar = "ρσ_cond_temp"
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
output.space()
|
|
259
|
-
output.print("&&")
|
|
260
|
-
output.space()
|
|
261
|
-
output.print(leftvar)
|
|
262
|
-
output.space()
|
|
263
|
-
output.print(self.operator)
|
|
264
|
-
output.space()
|
|
265
|
-
self.right.print(output)
|
|
266
331
|
elif self.operator is '//':
|
|
267
332
|
output.print('Math.floor')
|
|
268
333
|
output.with_parens(def():
|
|
@@ -400,7 +465,8 @@ def print_assignment(self, output):
|
|
|
400
465
|
|
|
401
466
|
def print_assign(self, output):
|
|
402
467
|
if self.overloaded and overloaded_augmented_ops[self.operator]:
|
|
403
|
-
|
|
468
|
+
aug_map = overloaded_augmented_ops if self.strict_arith else overloaded_augmented_ops_ns
|
|
469
|
+
helper = aug_map[self.operator]
|
|
404
470
|
output.assign(self.left)
|
|
405
471
|
output.print(helper + '(')
|
|
406
472
|
self.left.print(output)
|