rapydscript-ns 0.8.3 → 0.8.4

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 (116) 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 +8 -0
  5. package/HACKING.md +103 -103
  6. package/LICENSE +24 -24
  7. package/PYTHON_DIFFERENCES_REPORT.md +2 -2
  8. package/PYTHON_FEATURE_COVERAGE.md +13 -13
  9. package/README.md +670 -6
  10. package/TODO.md +5 -6
  11. package/add-toc-to-readme +2 -2
  12. package/bin/export +75 -75
  13. package/bin/rapydscript +70 -70
  14. package/bin/web-repl-export +102 -102
  15. package/build +2 -2
  16. package/language-service/index.js +155 -6
  17. package/package.json +1 -1
  18. package/publish.py +37 -37
  19. package/release/baselib-plain-pretty.js +2006 -229
  20. package/release/baselib-plain-ugly.js +70 -3
  21. package/release/compiler.js +11554 -3870
  22. package/release/signatures.json +31 -29
  23. package/session.vim +4 -4
  24. package/setup.cfg +2 -2
  25. package/src/ast.pyj +93 -1
  26. package/src/baselib-builtins.pyj +22 -1
  27. package/src/baselib-containers.pyj +99 -0
  28. package/src/baselib-errors.pyj +44 -0
  29. package/src/baselib-internal.pyj +94 -4
  30. package/src/baselib-itertools.pyj +97 -97
  31. package/src/baselib-str.pyj +24 -0
  32. package/src/compiler.pyj +36 -36
  33. package/src/errors.pyj +30 -30
  34. package/src/lib/aes.pyj +646 -646
  35. package/src/lib/copy.pyj +120 -0
  36. package/src/lib/elementmaker.pyj +83 -83
  37. package/src/lib/encodings.pyj +126 -126
  38. package/src/lib/gettext.pyj +569 -569
  39. package/src/lib/itertools.pyj +580 -580
  40. package/src/lib/math.pyj +193 -193
  41. package/src/lib/operator.pyj +11 -11
  42. package/src/lib/pythonize.pyj +20 -20
  43. package/src/lib/random.pyj +118 -118
  44. package/src/lib/re.pyj +470 -470
  45. package/src/lib/react.pyj +74 -0
  46. package/src/lib/traceback.pyj +63 -63
  47. package/src/lib/uuid.pyj +77 -77
  48. package/src/monaco-language-service/builtins.js +5 -0
  49. package/src/monaco-language-service/diagnostics.js +25 -3
  50. package/src/monaco-language-service/dts.js +550 -550
  51. package/src/output/classes.pyj +108 -8
  52. package/src/output/codegen.pyj +16 -2
  53. package/src/output/comments.pyj +45 -45
  54. package/src/output/exceptions.pyj +201 -105
  55. package/src/output/functions.pyj +9 -0
  56. package/src/output/jsx.pyj +164 -0
  57. package/src/output/literals.pyj +28 -2
  58. package/src/output/modules.pyj +1 -1
  59. package/src/output/operators.pyj +8 -2
  60. package/src/output/statements.pyj +2 -2
  61. package/src/output/stream.pyj +1 -0
  62. package/src/output/treeshake.pyj +182 -182
  63. package/src/output/utils.pyj +72 -72
  64. package/src/parse.pyj +417 -113
  65. package/src/string_interpolation.pyj +72 -72
  66. package/src/tokenizer.pyj +29 -0
  67. package/src/unicode_aliases.pyj +576 -576
  68. package/src/utils.pyj +192 -192
  69. package/test/_import_one.pyj +37 -37
  70. package/test/_import_two/__init__.pyj +11 -11
  71. package/test/_import_two/level2/deep.pyj +4 -4
  72. package/test/_import_two/other.pyj +6 -6
  73. package/test/_import_two/sub.pyj +13 -13
  74. package/test/aes_vectors.pyj +421 -421
  75. package/test/annotations.pyj +80 -80
  76. package/test/decorators.pyj +77 -77
  77. package/test/docstrings.pyj +39 -39
  78. package/test/elementmaker_test.pyj +45 -45
  79. package/test/functions.pyj +151 -151
  80. package/test/generators.pyj +41 -41
  81. package/test/generic.pyj +370 -370
  82. package/test/imports.pyj +72 -72
  83. package/test/internationalization.pyj +73 -73
  84. package/test/lint.pyj +164 -164
  85. package/test/loops.pyj +85 -85
  86. package/test/numpy.pyj +734 -734
  87. package/test/omit_function_metadata.pyj +20 -20
  88. package/test/python_features.pyj +19 -6
  89. package/test/regexp.pyj +55 -55
  90. package/test/repl.pyj +121 -121
  91. package/test/scoped_flags.pyj +76 -76
  92. package/test/unit/index.js +2177 -64
  93. package/test/unit/language-service-dts.js +543 -543
  94. package/test/unit/language-service-hover.js +455 -455
  95. package/test/unit/language-service.js +590 -4
  96. package/test/unit/web-repl.js +303 -0
  97. package/tools/cli.js +547 -547
  98. package/tools/compile.js +219 -219
  99. package/tools/completer.js +131 -131
  100. package/tools/embedded_compiler.js +251 -251
  101. package/tools/gettext.js +185 -185
  102. package/tools/ini.js +65 -65
  103. package/tools/msgfmt.js +187 -187
  104. package/tools/repl.js +223 -223
  105. package/tools/test.js +118 -118
  106. package/tools/utils.js +128 -128
  107. package/tools/web_repl.js +95 -95
  108. package/try +41 -41
  109. package/web-repl/env.js +196 -74
  110. package/web-repl/index.html +163 -163
  111. package/web-repl/main.js +252 -254
  112. package/web-repl/prism.css +139 -139
  113. package/web-repl/prism.js +113 -113
  114. package/web-repl/rapydscript.js +224 -102
  115. package/web-repl/sha1.js +25 -25
  116. package/hack_demo.pyj +0 -112
@@ -82,19 +82,75 @@ def print_class(output):
82
82
  output.with_block(def():
83
83
  output.indent()
84
84
  cname = self.name.name
85
- output.print('if (!(this instanceof ' + cname + ')) return new ' + cname + '(...arguments);')
86
- output.newline()
85
+ if self.has_new:
86
+ # When __new__ is defined, call it to get the instance
87
+ output.print('if (!(this instanceof ' + cname + '))')
88
+ output.space()
89
+ output.with_block(def():
90
+ output.indent()
91
+ output.print('var ρσ_instance = ')
92
+ self.name.print(output)
93
+ output.print('.__new__(')
94
+ self.name.print(output)
95
+ output.print(', ...arguments)')
96
+ output.end_statement()
97
+ output.indent()
98
+ output.print('if (ρσ_instance instanceof ')
99
+ self.name.print(output)
100
+ output.print(')')
101
+ output.space()
102
+ output.with_block(def():
103
+ output.indent()
104
+ output.spaced('if', '(ρσ_instance.ρσ_object_id', '===', 'undefined)', 'Object.defineProperty(ρσ_instance,', '"ρσ_object_id",', '{"value":++ρσ_object_counter})')
105
+ output.end_statement()
106
+ if self.bound.length:
107
+ output.indent()
108
+ self.name.print(output), output.print('.prototype.__bind_methods__ && ')
109
+ self.name.print(output), output.print('.prototype.__bind_methods__.call(ρσ_instance)')
110
+ output.end_statement()
111
+ output.indent()
112
+ self.name.print(output)
113
+ output.print('.prototype.__init__.apply(ρσ_instance'), output.comma(), output.print('arguments)')
114
+ output.end_statement()
115
+ )
116
+ output.newline()
117
+ output.indent()
118
+ output.print('return ρσ_instance')
119
+ output.end_statement()
120
+ )
121
+ output.newline()
122
+ else:
123
+ output.print('if (!(this instanceof ' + cname + ')) return new ' + cname + '(...arguments);')
124
+ output.newline()
87
125
  output.indent()
88
126
  output.spaced('if', '(this.ρσ_object_id', '===', 'undefined)', 'Object.defineProperty(this,', '"ρσ_object_id",', '{"value":++ρσ_object_counter})')
89
127
  output.end_statement()
90
- if self.bound.length:
128
+ if self.has_attr_dunders:
129
+ # Wrap in a Proxy so __getattr__/__setattr__/__delattr__/__getattribute__ are triggered.
91
130
  output.indent()
92
- self.name.print(output), output.print(".prototype.__bind_methods__.call(this)")
131
+ output.print('var ρσ_proxy = ρσ_JS_Proxy ? new ρσ_JS_Proxy(this, ρσ_attr_proxy_handler) : this')
132
+ output.end_statement()
133
+ if self.bound.length:
134
+ output.indent()
135
+ self.name.print(output), output.print(".prototype.__bind_methods__ && ")
136
+ self.name.print(output), output.print(".prototype.__bind_methods__.call(ρσ_proxy)")
137
+ output.end_statement()
138
+ output.indent()
139
+ self.name.print(output)
140
+ output.print(".prototype.__init__.apply(ρσ_proxy"), output.comma(), output.print('arguments)')
141
+ output.end_statement()
142
+ output.indent()
143
+ output.print('return ρσ_proxy')
144
+ output.end_statement()
145
+ else:
146
+ if self.bound.length:
147
+ output.indent()
148
+ self.name.print(output), output.print(".prototype.__bind_methods__.call(this)")
149
+ output.end_statement()
150
+ output.indent()
151
+ self.name.print(output)
152
+ output.print(".prototype.__init__.apply(this"), output.comma(), output.print('arguments)')
93
153
  output.end_statement()
94
- output.indent()
95
- self.name.print(output)
96
- output.print(".prototype.__init__.apply(this"), output.comma(), output.print('arguments)')
97
- output.end_statement()
98
154
  )
99
155
 
100
156
  decorators = self.decorators or v'[]'
@@ -235,6 +291,13 @@ def print_class(output):
235
291
  output.end_statement()
236
292
  )
237
293
 
294
+ # Python semantics: defining __eq__ without __hash__ makes the class unhashable
295
+ if defined_methods['__eq__'] and not defined_methods['__hash__']:
296
+ output.indent()
297
+ self.name.print(output)
298
+ output.print('.prototype.__hash__ = null')
299
+ output.end_statement()
300
+
238
301
  # Multiple inheritance
239
302
  add_hidden_property('__bases__', def():
240
303
  output.print('[')
@@ -256,6 +319,43 @@ def print_class(output):
256
319
  output.print('.prototype, "__class__", {get: function() { return this.constructor; }, configurable: true})')
257
320
  output.end_statement()
258
321
 
322
+ # __init_subclass__ hook: call after identity properties so cls.__name__ is correct
323
+ if self.parent:
324
+ output.indent()
325
+ output.print('if (typeof ')
326
+ self.parent.print(output)
327
+ output.print('.__init_subclass__ === "function")')
328
+ output.space()
329
+ kws = self.class_kwargs
330
+ if kws and kws.length:
331
+ output.with_block(def():
332
+ output.indent()
333
+ output.print('var ρσ_isc_kw = {')
334
+ for v'var ρσ_kwi = 0; ρσ_kwi < kws.length; ρσ_kwi++':
335
+ if ρσ_kwi > 0:
336
+ output.comma()
337
+ output.print(kws[ρσ_kwi][0].name + ':')
338
+ output.space()
339
+ kws[ρσ_kwi][1].print(output)
340
+ output.print('}')
341
+ output.end_statement()
342
+ output.indent()
343
+ output.print('ρσ_isc_kw[ρσ_kwargs_symbol] = true')
344
+ output.end_statement()
345
+ output.indent()
346
+ self.parent.print(output)
347
+ output.print('.__init_subclass__.call(')
348
+ self.name.print(output)
349
+ output.print(', ρσ_isc_kw)')
350
+ output.end_statement()
351
+ )
352
+ else:
353
+ self.parent.print(output)
354
+ output.print('.__init_subclass__.call(')
355
+ self.name.print(output)
356
+ output.print(')')
357
+ output.end_statement()
358
+
259
359
  if self.bases.length > 1:
260
360
  output.indent()
261
361
  output.print("ρσ_mixin(")
@@ -13,13 +13,14 @@ from ast import (
13
13
  AST_EmptyStatement, AST_Exit, AST_ExpressiveObject, AST_ForIn,
14
14
  AST_ForJS, AST_Function, AST_Hole, AST_If, AST_Imports, AST_Infinity,
15
15
  AST_Lambda, AST_ListComprehension, AST_LoopControl, AST_NaN, AST_NamedExpr, AST_New, AST_Node,
16
- AST_Number, AST_Object, AST_ObjectKeyVal, AST_ObjectProperty, AST_ObjectSpread, AST_PropAccess,
16
+ AST_Number, AST_Object, AST_ObjectKeyVal, AST_ObjectProperty, AST_ObjectSpread, AST_Spread, AST_PropAccess,
17
17
  AST_RegExp, AST_Return, AST_Set, AST_Seq, AST_SimpleStatement, AST_Splice,
18
18
  AST_Statement, AST_StatementWithBody, AST_String, AST_Sub, AST_ItemAccess,
19
19
  AST_Symbol, AST_This, AST_Throw, AST_Toplevel, AST_Try, AST_Unary,
20
20
  AST_UnaryPrefix, AST_Undefined, AST_Var, AST_VarDef, AST_Assert,
21
21
  AST_Verbatim, AST_While, AST_With, AST_Yield, AST_Await, TreeWalker, AST_Existential,
22
- AST_Match, AST_AnnotatedAssign, AST_Super
22
+ AST_Match, AST_AnnotatedAssign, AST_Super,
23
+ AST_JSXElement, AST_JSXFragment, AST_JSXAttribute, AST_JSXSpread, AST_JSXText, AST_JSXExprContainer
23
24
  )
24
25
  from output.exceptions import print_try
25
26
  from output.classes import print_class
@@ -34,6 +35,7 @@ from output.operators import (
34
35
  )
35
36
  from output.functions import print_function, print_function_call
36
37
  from output.statements import print_bracketed, first_in_statement, force_statement, print_with, print_assert, print_match, print_annotated_assign
38
+ from output.jsx import print_jsx_element, print_jsx_fragment, print_jsx_attribute, print_jsx_spread, print_jsx_text, print_jsx_expr_container
37
39
  from output.utils import make_block, make_num
38
40
 
39
41
  # -----[ code generators ]-----
@@ -486,6 +488,10 @@ def generate_code():
486
488
  output.print('**')
487
489
  self.value.print(output)
488
490
  )
491
+ DEFPRINT(AST_Spread, def(self, output):
492
+ output.print('...')
493
+ self.expression.print(output)
494
+ )
489
495
  DEFPRINT(AST_Set, print_set)
490
496
 
491
497
  AST_Symbol.prototype.definition = def():
@@ -525,3 +531,11 @@ def generate_code():
525
531
  output.print(make_num(self.value))
526
532
  )
527
533
  DEFPRINT(AST_RegExp, print_regexp)
534
+
535
+ # -----[ JSX ]-----
536
+ DEFPRINT(AST_JSXElement, print_jsx_element)
537
+ DEFPRINT(AST_JSXFragment, print_jsx_fragment)
538
+ DEFPRINT(AST_JSXAttribute, print_jsx_attribute)
539
+ DEFPRINT(AST_JSXSpread, print_jsx_spread)
540
+ DEFPRINT(AST_JSXText, print_jsx_text)
541
+ DEFPRINT(AST_JSXExprContainer, print_jsx_expr_container)
@@ -1,45 +1,45 @@
1
- # vim:fileencoding=utf-8
2
- # License: BSD Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
3
- from __python__ import hash_literals
4
-
5
- from ast import AST_Exit, is_node_type
6
-
7
-
8
- def output_comments(comments, output, nlb):
9
- for comm in comments:
10
- if comm.type is "comment1":
11
- output.print("//" + comm.value + "\n")
12
- output.indent()
13
- elif comm.type is "comment2":
14
- output.print("/*" + comm.value + "*/")
15
- if nlb:
16
- output.print("\n")
17
- output.indent()
18
- else:
19
- output.space()
20
-
21
-
22
- def print_comments(self, output):
23
- c = output.options.comments
24
- if c:
25
- start = self.start
26
- if start and not start._comments_dumped:
27
- start._comments_dumped = True
28
- comments = start.comments_before
29
- # XXX: ugly fix for https://github.com/mishoo/RapydScript2/issues/112
30
- # if this node is `return` or `throw`, we cannot allow comments before
31
- # the returned or thrown value.
32
- if is_node_type(self, AST_Exit) and self.value and self.value.start.comments_before and self.value.start.comments_before.length > 0:
33
- comments = (comments or v'[]').concat(self.value.start.comments_before)
34
- self.value.start.comments_before = v'[]'
35
-
36
- if c.test:
37
- comments = comments.filter(def(comment):
38
- return c.test(comment.value)
39
- )
40
- elif jstype(c) is "function":
41
- comments = comments.filter(def(comment):
42
- return c(self, comment)
43
- )
44
-
45
- output_comments(comments, output, start.nlb)
1
+ # vim:fileencoding=utf-8
2
+ # License: BSD Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
3
+ from __python__ import hash_literals
4
+
5
+ from ast import AST_Exit, is_node_type
6
+
7
+
8
+ def output_comments(comments, output, nlb):
9
+ for comm in comments:
10
+ if comm.type is "comment1":
11
+ output.print("//" + comm.value + "\n")
12
+ output.indent()
13
+ elif comm.type is "comment2":
14
+ output.print("/*" + comm.value + "*/")
15
+ if nlb:
16
+ output.print("\n")
17
+ output.indent()
18
+ else:
19
+ output.space()
20
+
21
+
22
+ def print_comments(self, output):
23
+ c = output.options.comments
24
+ if c:
25
+ start = self.start
26
+ if start and not start._comments_dumped:
27
+ start._comments_dumped = True
28
+ comments = start.comments_before
29
+ # XXX: ugly fix for https://github.com/mishoo/RapydScript2/issues/112
30
+ # if this node is `return` or `throw`, we cannot allow comments before
31
+ # the returned or thrown value.
32
+ if is_node_type(self, AST_Exit) and self.value and self.value.start.comments_before and self.value.start.comments_before.length > 0:
33
+ comments = (comments or v'[]').concat(self.value.start.comments_before)
34
+ self.value.start.comments_before = v'[]'
35
+
36
+ if c.test:
37
+ comments = comments.filter(def(comment):
38
+ return c.test(comment.value)
39
+ )
40
+ elif jstype(c) is "function":
41
+ comments = comments.filter(def(comment):
42
+ return c(self, comment)
43
+ )
44
+
45
+ output_comments(comments, output, start.nlb)
@@ -1,105 +1,201 @@
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 output.statements import print_bracketed
6
-
7
-
8
- def print_try(self, output):
9
- else_var_name = None
10
- def update_output_var(output):
11
- output.indent(), output.assign(else_var_name), output.print('true'), output.end_statement()
12
- if self.belse:
13
- else_var_name = output.new_try_else_counter()
14
- output.assign('var ' + else_var_name), output.print('false'), output.end_statement(), output.indent()
15
- output.print("try")
16
- output.space()
17
- print_bracketed(self, output, False, None, None, update_output_var if else_var_name else None)
18
- if self.bcatch:
19
- output.space()
20
- print_catch(self.bcatch, output)
21
-
22
- if self.bfinally:
23
- output.space()
24
- print_finally(self.bfinally, output, self.belse, else_var_name)
25
- elif self.belse:
26
- output.newline()
27
- print_else(self.belse, else_var_name, output)
28
-
29
-
30
- def print_catch(self, output):
31
- output.print("catch")
32
- output.space()
33
- output.with_parens(def():
34
- output.print("ρσ_Exception")
35
- )
36
- output.space()
37
- output.with_block(def():
38
- output.indent()
39
- output.spaced('ρσ_last_exception', '=', 'ρσ_Exception'), output.end_statement()
40
- output.indent()
41
- no_default = True
42
- for i, exception in enumerate(self.body):
43
- if i:
44
- output.print("else ")
45
-
46
- if exception.errors.length:
47
- output.print("if")
48
- output.space()
49
- output.with_parens(def():
50
- for i, err in enumerate(exception.errors):
51
- if i:
52
- output.newline()
53
- output.indent()
54
- output.print("||")
55
- output.space()
56
-
57
- output.print("ρσ_Exception")
58
- output.space()
59
- output.print("instanceof")
60
- output.space()
61
- if err.name is 'Exception':
62
- output.print('Error')
63
- else:
64
- err.print(output)
65
- )
66
- output.space()
67
- else:
68
- no_default = False
69
- print_bracketed(exception, output, True)
70
- output.space()
71
- if no_default:
72
- output.print("else")
73
- output.space()
74
- output.with_block(def():
75
- output.indent()
76
- output.print("throw")
77
- output.space()
78
- output.print("ρσ_Exception")
79
- output.semicolon()
80
- output.newline()
81
- )
82
- output.newline()
83
- )
84
-
85
-
86
- def print_finally(self, output, belse, else_var_name):
87
- output.print("finally")
88
- output.space()
89
- if else_var_name:
90
- output.with_block(def():
91
- output.indent(), output.print("try")
92
- output.space()
93
- output.with_block(def():
94
- print_else(belse, else_var_name, output)
95
- )
96
- print_finally(self, output)
97
- )
98
- else:
99
- print_bracketed(self, output)
100
-
101
-
102
- def print_else(self, else_var_name, output):
103
- output.indent(), output.spaced('if', '(' + else_var_name + ')')
104
- output.space()
105
- print_bracketed(self, 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 output.statements import print_bracketed, display_body
6
+
7
+
8
+ def print_try(self, output):
9
+ else_var_name = None
10
+ def update_output_var(output):
11
+ output.indent(), output.assign(else_var_name), output.print('true'), output.end_statement()
12
+ if self.belse:
13
+ else_var_name = output.new_try_else_counter()
14
+ output.assign('var ' + else_var_name), output.print('false'), output.end_statement(), output.indent()
15
+ output.print("try")
16
+ output.space()
17
+ print_bracketed(self, output, False, None, None, update_output_var if else_var_name else None)
18
+ if self.bcatch:
19
+ output.space()
20
+ print_catch(self.bcatch, output)
21
+
22
+ if self.bfinally:
23
+ output.space()
24
+ print_finally(self.bfinally, output, self.belse, else_var_name)
25
+ elif self.belse:
26
+ output.newline()
27
+ print_else(self.belse, else_var_name, output)
28
+
29
+
30
+ def print_catch(self, output):
31
+ # Dispatch to except* handler when any clause is an exception group handler
32
+ if self.body.length and self.body[0].is_star:
33
+ print_catch_star(self, output)
34
+ return
35
+ output.print("catch")
36
+ output.space()
37
+ output.with_parens(def():
38
+ output.print("ρσ_Exception")
39
+ )
40
+ output.space()
41
+ output.with_block(def():
42
+ output.indent()
43
+ output.spaced('ρσ_last_exception', '=', 'ρσ_Exception'), output.end_statement()
44
+ output.indent()
45
+ no_default = True
46
+ for i, exception in enumerate(self.body):
47
+ if i:
48
+ output.print("else ")
49
+
50
+ if exception.errors.length:
51
+ output.print("if")
52
+ output.space()
53
+ output.with_parens(def():
54
+ for i, err in enumerate(exception.errors):
55
+ if i:
56
+ output.newline()
57
+ output.indent()
58
+ output.print("||")
59
+ output.space()
60
+
61
+ output.print("ρσ_Exception")
62
+ output.space()
63
+ output.print("instanceof")
64
+ output.space()
65
+ if err.name is 'Exception':
66
+ output.print('Error')
67
+ else:
68
+ err.print(output)
69
+ )
70
+ output.space()
71
+ else:
72
+ no_default = False
73
+ print_bracketed(exception, output, True)
74
+ output.space()
75
+ if no_default:
76
+ output.print("else")
77
+ output.space()
78
+ output.with_block(def():
79
+ output.indent()
80
+ output.print("throw")
81
+ output.space()
82
+ output.print("ρσ_Exception")
83
+ output.semicolon()
84
+ output.newline()
85
+ )
86
+ output.newline()
87
+ )
88
+
89
+
90
+ def _eg_type_cond(errors, varname):
91
+ # Build a JS condition string testing varname against each error type
92
+ parts = []
93
+ for err in errors:
94
+ if err.name is 'Exception':
95
+ parts.push(varname + " instanceof Error")
96
+ else:
97
+ parts.push(varname + " instanceof " + err.name)
98
+ return parts.join(" || ")
99
+
100
+
101
+ def print_catch_star(self, output):
102
+ output.print("catch")
103
+ output.space()
104
+ output.with_parens(def():
105
+ output.print("ρσ_Exception")
106
+ )
107
+ output.space()
108
+ output.with_block(def():
109
+ output.indent()
110
+ output.spaced('ρσ_last_exception', '=', 'ρσ_Exception'), output.end_statement()
111
+
112
+ # Normalise: wrap non-ExceptionGroup exception into a one-element array
113
+ output.indent()
114
+ output.print("var ρσ_eg_exceptions = (ρσ_Exception instanceof ExceptionGroup) ? ρσ_Exception.exceptions.slice() : [ρσ_Exception]")
115
+ output.semicolon(), output.newline()
116
+ output.indent()
117
+ output.print("var ρσ_eg_raised = []")
118
+ output.semicolon(), output.newline()
119
+
120
+ for ei, exception in enumerate(self.body):
121
+ idx = str(ei)
122
+ if exception.errors.length:
123
+ cond = _eg_type_cond(exception.errors, "ρσ_et")
124
+ output.indent()
125
+ output.print("var ρσ_eg_matched_" + idx + " = ρσ_eg_exceptions.filter(function(ρσ_et) { return " + cond + "; })")
126
+ output.semicolon(), output.newline()
127
+ output.indent()
128
+ output.print("ρσ_eg_exceptions = ρσ_eg_exceptions.filter(function(ρσ_et) { return !(" + cond + "); })")
129
+ output.semicolon(), output.newline()
130
+ else:
131
+ # Bare except*: take all remaining
132
+ output.indent()
133
+ output.print("var ρσ_eg_matched_" + idx + " = ρσ_eg_exceptions.slice()")
134
+ output.semicolon(), output.newline()
135
+ output.indent()
136
+ output.print("ρσ_eg_exceptions = []")
137
+ output.semicolon(), output.newline()
138
+
139
+ output.indent()
140
+ output.print("if (ρσ_eg_matched_" + idx + ".length > 0)")
141
+ output.space()
142
+ output.with_block(def():
143
+ if exception.argname:
144
+ output.indent()
145
+ output.print("var ")
146
+ output.assign(exception.argname)
147
+ output.print("(ρσ_Exception instanceof ExceptionGroup) ? new ExceptionGroup(ρσ_Exception.message, ρσ_eg_matched_" + idx + ") : ρσ_eg_matched_" + idx + "[0]")
148
+ output.semicolon(), output.newline()
149
+ output.indent()
150
+ output.print("try")
151
+ output.space()
152
+ output.with_block(def():
153
+ display_body(exception.body, False, output)
154
+ )
155
+ output.print(" catch (ρσ_eg_exc_" + idx + ")")
156
+ output.space()
157
+ output.with_block(def():
158
+ output.indent()
159
+ output.print("ρσ_eg_raised.push(ρσ_eg_exc_" + idx + ")")
160
+ output.semicolon(), output.newline()
161
+ )
162
+ output.newline()
163
+ )
164
+ output.newline()
165
+
166
+ # Re-raise unmatched exceptions and any raised by handlers
167
+ output.indent()
168
+ output.print("var ρσ_eg_unhandled = ρσ_eg_exceptions.concat(ρσ_eg_raised)")
169
+ output.semicolon(), output.newline()
170
+ output.indent()
171
+ output.print("if (ρσ_eg_unhandled.length > 0)")
172
+ output.space()
173
+ output.with_block(def():
174
+ output.indent()
175
+ output.print("throw (ρσ_eg_unhandled.length === 1 && !(ρσ_Exception instanceof ExceptionGroup)) ? ρσ_eg_unhandled[0] : new ExceptionGroup((ρσ_Exception instanceof ExceptionGroup) ? ρσ_Exception.message : \"unhandled exceptions\", ρσ_eg_unhandled)")
176
+ output.semicolon(), output.newline()
177
+ )
178
+ output.newline()
179
+ )
180
+
181
+
182
+ def print_finally(self, output, belse, else_var_name):
183
+ output.print("finally")
184
+ output.space()
185
+ if else_var_name:
186
+ output.with_block(def():
187
+ output.indent(), output.print("try")
188
+ output.space()
189
+ output.with_block(def():
190
+ print_else(belse, else_var_name, output)
191
+ )
192
+ print_finally(self, output)
193
+ )
194
+ else:
195
+ print_bracketed(self, output)
196
+
197
+
198
+ def print_else(self, else_var_name, output):
199
+ output.indent(), output.spaced('if', '(' + else_var_name + ')')
200
+ output.space()
201
+ print_bracketed(self, output)
@@ -150,6 +150,15 @@ def function_preamble(node, output, offset):
150
150
  output.print('.pop()')
151
151
  output.end_statement()
152
152
 
153
+ # Convert the plain JS kwargs object to a proper Python dict so that
154
+ # kw.items(), kw.keys(), etc. work in the function body.
155
+ # This must happen AFTER defaults/kwonly/starargs processing which rely on
156
+ # plain property access (kw.name) and ρσ_kwargs_symbol checks.
157
+ if a.kwargs:
158
+ output.indent()
159
+ output.spaced(kw, '=', 'ρσ_kwargs_to_dict(' + kw + ')')
160
+ output.end_statement()
161
+
153
162
  def has_annotations(self):
154
163
  if self.return_annotation:
155
164
  return True