rapydscript-ns 0.8.3 → 0.9.0

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 (72) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +1351 -141
  3. package/TODO.md +12 -6
  4. package/language-service/index.js +184 -26
  5. package/package.json +1 -1
  6. package/release/baselib-plain-pretty.js +5895 -1928
  7. package/release/baselib-plain-ugly.js +140 -3
  8. package/release/compiler.js +16282 -5408
  9. package/release/signatures.json +25 -22
  10. package/src/ast.pyj +94 -1
  11. package/src/baselib-builtins.pyj +362 -3
  12. package/src/baselib-bytes.pyj +664 -0
  13. package/src/baselib-containers.pyj +99 -0
  14. package/src/baselib-errors.pyj +45 -1
  15. package/src/baselib-internal.pyj +346 -49
  16. package/src/baselib-itertools.pyj +17 -4
  17. package/src/baselib-str.pyj +46 -4
  18. package/src/lib/abc.pyj +317 -0
  19. package/src/lib/copy.pyj +120 -0
  20. package/src/lib/dataclasses.pyj +532 -0
  21. package/src/lib/enum.pyj +125 -0
  22. package/src/lib/pythonize.pyj +1 -1
  23. package/src/lib/re.pyj +35 -1
  24. package/src/lib/react.pyj +74 -0
  25. package/src/lib/typing.pyj +577 -0
  26. package/src/monaco-language-service/builtins.js +19 -4
  27. package/src/monaco-language-service/diagnostics.js +40 -19
  28. package/src/output/classes.pyj +161 -25
  29. package/src/output/codegen.pyj +16 -2
  30. package/src/output/exceptions.pyj +97 -1
  31. package/src/output/functions.pyj +87 -5
  32. package/src/output/jsx.pyj +164 -0
  33. package/src/output/literals.pyj +28 -2
  34. package/src/output/loops.pyj +5 -2
  35. package/src/output/modules.pyj +1 -1
  36. package/src/output/operators.pyj +108 -36
  37. package/src/output/statements.pyj +2 -2
  38. package/src/output/stream.pyj +1 -0
  39. package/src/parse.pyj +496 -128
  40. package/src/tokenizer.pyj +38 -4
  41. package/test/abc.pyj +291 -0
  42. package/test/arithmetic_nostrict.pyj +88 -0
  43. package/test/arithmetic_types.pyj +169 -0
  44. package/test/baselib.pyj +91 -0
  45. package/test/bytes.pyj +467 -0
  46. package/test/classes.pyj +1 -0
  47. package/test/comparison_ops.pyj +173 -0
  48. package/test/dataclasses.pyj +253 -0
  49. package/test/enum.pyj +134 -0
  50. package/test/eval_exec.pyj +56 -0
  51. package/test/format.pyj +148 -0
  52. package/test/object.pyj +64 -0
  53. package/test/python_compat.pyj +17 -15
  54. package/test/python_features.pyj +89 -21
  55. package/test/regexp.pyj +29 -1
  56. package/test/tuples.pyj +96 -0
  57. package/test/typing.pyj +469 -0
  58. package/test/unit/index.js +2292 -70
  59. package/test/unit/language-service.js +674 -4
  60. package/test/unit/web-repl.js +1106 -0
  61. package/test/vars_locals_globals.pyj +94 -0
  62. package/tools/cli.js +11 -0
  63. package/tools/compile.js +5 -0
  64. package/tools/embedded_compiler.js +15 -4
  65. package/tools/lint.js +16 -19
  66. package/tools/repl.js +1 -1
  67. package/web-repl/env.js +122 -0
  68. package/web-repl/main.js +1 -3
  69. package/web-repl/rapydscript.js +125 -3
  70. package/PYTHON_DIFFERENCES_REPORT.md +0 -291
  71. package/PYTHON_FEATURE_COVERAGE.md +0 -200
  72. package/hack_demo.pyj +0 -112
@@ -0,0 +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. &amp;lt; -> &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)
@@ -2,7 +2,7 @@
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_Binary, AST_ObjectSpread, is_node_type
5
+ from ast import AST_Binary, AST_ObjectSpread, AST_Spread, AST_SetItem, is_node_type
6
6
 
7
7
  def print_array(self, output):
8
8
  output.print('ρσ_list_decorate')
@@ -15,7 +15,11 @@ def print_array(self, output):
15
15
  for i, exp in enumerate(a):
16
16
  if i:
17
17
  output.comma()
18
- exp.print(output)
18
+ if is_node_type(exp, AST_Spread):
19
+ output.print('...')
20
+ exp.expression.print(output)
21
+ else:
22
+ exp.print(output)
19
23
  if len_ > 0:
20
24
  output.space()
21
25
  )
@@ -78,6 +82,28 @@ def print_object(self, output):
78
82
  output.print("Object.create(null)" if self.is_jshash else '{}')
79
83
 
80
84
  def print_set(self, output):
85
+ # Check for spread items: {*a, 1, 2, *b}
86
+ has_spread = False
87
+ for item in self.items:
88
+ if is_node_type(item, AST_Spread):
89
+ has_spread = True
90
+ break
91
+ if has_spread:
92
+ # Compile as ρσ_set([...a, 1, 2, ...b])
93
+ output.print('ρσ_set')
94
+ output.with_parens(def():
95
+ output.with_square(def():
96
+ for i, item in enumerate(self.items):
97
+ if i:
98
+ output.comma()
99
+ if is_node_type(item, AST_Spread):
100
+ output.print('...')
101
+ item.expression.print(output)
102
+ else:
103
+ item.value.print(output)
104
+ )
105
+ )
106
+ return
81
107
  if self.items.length is 0:
82
108
  output.print('ρσ_set()')
83
109
  return
@@ -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
- body_out.comma()
462
- i.print(body_out)
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)
@@ -183,7 +183,7 @@ def print_top_level(self, output):
183
183
  write_imports(self, output)
184
184
  write_main_name(output)
185
185
 
186
- declare_vars(self.localvars, output)
186
+ declare_vars(self.localvars, output, "var" if output.options.repl_mode else "let")
187
187
  display_body(self.body, True, output)
188
188
  if self.comments_after and self.comments_after.length:
189
189
  output_comments(self.comments_after, output)
@@ -5,7 +5,7 @@ from ast import (
5
5
  AST_Array, AST_Assign, AST_BaseCall, AST_Binary, AST_Conditional,
6
6
  AST_ItemAccess, AST_NamedExpr, AST_Number, AST_Object, AST_Return, AST_Seq, AST_Set,
7
7
  AST_SimpleStatement, AST_Statement, AST_String, AST_Sub, AST_Symbol,
8
- AST_SymbolRef, AST_Starred, AST_Unary, is_node_type
8
+ AST_SymbolRef, AST_Starred, AST_Unary, AST_AnnotatedAssign, is_node_type
9
9
  )
10
10
  from output.loops import unpack_tuple
11
11
 
@@ -13,6 +13,11 @@ from output.loops import unpack_tuple
13
13
  def print_getattr(self, output, skip_expression): # AST_Dot
14
14
  if not skip_expression:
15
15
  expr = self.expression
16
+ # Redirect JSON.parse → ρσ_json_parse so RapydScript code gets dict-aware
17
+ # parsing without polluting the global JSON.parse for other JS on the page.
18
+ if is_node_type(expr, AST_SymbolRef) and expr.name == 'JSON' and self.property == 'parse':
19
+ output.print('ρσ_json_parse')
20
+ return
16
21
  expr.print(output)
17
22
  if is_node_type(expr, AST_Number) and expr.value >= 0:
18
23
  if not /[xa-f.]/i.test(output.last()):
@@ -38,7 +43,7 @@ def print_getitem(self, output): # AST_Sub
38
43
  expr.print(output)
39
44
  output.print('['), prop.print(output), output.print(']')
40
45
  return
41
- 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
42
47
  is_repeatable = is_node_type(expr, AST_SymbolRef)
43
48
  if is_repeatable:
44
49
  expr.print(output)
@@ -182,7 +187,7 @@ function_ops = {
182
187
  'nin': '!ρσ_in',
183
188
  }
184
189
 
185
- # Maps binary operators to their overloading helper function names.
190
+ # Maps binary operators to their overloading helper function names (strict_arithmetic on).
186
191
  overloaded_binary_ops = {
187
192
  '+': 'ρσ_op_add',
188
193
  '-': 'ρσ_op_sub',
@@ -196,9 +201,34 @@ overloaded_binary_ops = {
196
201
  '^': 'ρσ_op_xor',
197
202
  '<<': 'ρσ_op_lshift',
198
203
  '>>': 'ρσ_op_rshift',
204
+ '<': 'ρσ_op_lt',
205
+ '>': 'ρσ_op_gt',
206
+ '<=': 'ρσ_op_le',
207
+ '>=': 'ρσ_op_ge',
199
208
  }
200
209
 
201
- # Maps augmented-assignment operators to their overloading helper names.
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).
202
232
  overloaded_augmented_ops = {
203
233
  '+=': 'ρσ_op_iadd',
204
234
  '-=': 'ρσ_op_isub',
@@ -214,10 +244,78 @@ overloaded_augmented_ops = {
214
244
  '>>=': 'ρσ_op_irshift',
215
245
  }
216
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
+
217
263
 
218
264
  def print_binary_op(self, output):
219
- if self.overloaded and overloaded_binary_ops[self.operator]:
220
- output.print(overloaded_binary_ops[self.operator] + '(')
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] + '(')
221
319
  self.left.print(output)
222
320
  output.comma()
223
321
  self.right.print(output)
@@ -230,34 +328,6 @@ def print_binary_op(self, output):
230
328
  output.comma()
231
329
  self.right.print(output)
232
330
  )
233
- elif comparators[self.operator] and is_node_type(self.left, AST_Binary) and comparators[self.left.operator]:
234
- # A chained comparison such as a < b < c
235
- if is_node_type(self.left.right, AST_Symbol):
236
- # left side compares against a regular variable,
237
- # no caching needed
238
- self.left.print(output)
239
- leftvar = self.left.right.name
240
- else:
241
- # some logic is being performed, let's cache it
242
- self.left.left.print(output)
243
- output.space()
244
- output.print(self.left.operator)
245
- output.space()
246
- output.with_parens(def():
247
- nonlocal leftvar
248
- output.assign("ρσ_cond_temp")
249
- self.left.right.print(output)
250
- leftvar = "ρσ_cond_temp"
251
- )
252
-
253
- output.space()
254
- output.print("&&")
255
- output.space()
256
- output.print(leftvar)
257
- output.space()
258
- output.print(self.operator)
259
- output.space()
260
- self.right.print(output)
261
331
  elif self.operator is '//':
262
332
  output.print('Math.floor')
263
333
  output.with_parens(def():
@@ -395,7 +465,8 @@ def print_assignment(self, output):
395
465
 
396
466
  def print_assign(self, output):
397
467
  if self.overloaded and overloaded_augmented_ops[self.operator]:
398
- helper = overloaded_augmented_ops[self.operator]
468
+ aug_map = overloaded_augmented_ops if self.strict_arith else overloaded_augmented_ops_ns
469
+ helper = aug_map[self.operator]
399
470
  output.assign(self.left)
400
471
  output.print(helper + '(')
401
472
  self.left.print(output)
@@ -482,7 +553,8 @@ def print_seq(output):
482
553
  or is_node_type(p, AST_Return)
483
554
  or is_node_type(p, AST_Array)
484
555
  or is_node_type(p, AST_BaseCall)
485
- or is_node_type(p, AST_SimpleStatement):
556
+ or is_node_type(p, AST_SimpleStatement)
557
+ or is_node_type(p, AST_AnnotatedAssign):
486
558
  output.with_square(print_seq)
487
559
  else:
488
560
  print_seq()
@@ -54,11 +54,11 @@ def first_in_statement(output):
54
54
  return False
55
55
 
56
56
 
57
- def declare_vars(vars, output):
57
+ def declare_vars(vars, output, kind="var"):
58
58
  # declare all variables as local, unless explictly set otherwise
59
59
  if vars.length:
60
60
  output.indent()
61
- output.print("var")
61
+ output.print(kind)
62
62
  output.space()
63
63
  for i, arg in enumerate(vars):
64
64
  if i:
@@ -56,6 +56,7 @@ output_stream_defaults = {
56
56
  'write_name': True,
57
57
  'omit_function_metadata': False,
58
58
  'pythonize_strings': False,
59
+ 'repl_mode': False,
59
60
  }
60
61
 
61
62
  class OutputStream: